diff --git a/.circleci/config.yml b/.circleci/config.yml index 2abd7304e4f..5e7b92e0a3c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -619,18 +619,6 @@ jobs: command: cond_run_script end-to-end ./scripts/run_tests_local e2e_token_contract.test.ts environment: { DEBUG: "aztec:*" } - e2e-private-airdrop: - machine: - image: ubuntu-2204:2023.07.2 - resource_class: large - steps: - - *checkout - - *setup_env - - run: - name: "Test" - command: cond_run_script end-to-end ./scripts/run_tests_local e2e_private_airdrop.test.ts - environment: { DEBUG: "aztec:*" } - e2e-sandbox-example: machine: image: ubuntu-2204:2023.07.2 @@ -643,18 +631,6 @@ jobs: command: cond_run_script end-to-end ./scripts/run_tests_local e2e_sandbox_example.test.ts ./scripts/docker-compose-e2e-sandbox.yml environment: { DEBUG: "aztec:*" } - e2e-multi-transfer-contract: - machine: - image: ubuntu-2204:2023.07.2 - resource_class: large - steps: - - *checkout - - *setup_env - - run: - name: "Test" - command: cond_run_script end-to-end ./scripts/run_tests_local e2e_multi_transfer.test.ts - environment: { DEBUG: "aztec:*" } - e2e-block-building: machine: image: ubuntu-2204:2023.07.2 @@ -811,18 +787,6 @@ jobs: command: cond_run_script end-to-end ./scripts/run_tests_local integration_l1_publisher.test.ts environment: { DEBUG: "aztec:*" } - e2e-public-token-contract: - machine: - image: ubuntu-2204:2023.07.2 - resource_class: large - steps: - - *checkout - - *setup_env - - run: - name: "Test" - command: cond_run_script end-to-end ./scripts/run_tests_local e2e_public_token_contract.test.ts - environment: { DEBUG: "aztec:*" } - e2e-cli: machine: image: ubuntu-2204:2023.07.2 @@ -1342,14 +1306,11 @@ workflows: - e2e-deploy-contract: *e2e_test - e2e-lending-contract: *e2e_test - e2e-token-contract: *e2e_test - - e2e-private-airdrop: *e2e_test - e2e-sandbox-example: *e2e_test - - e2e-multi-transfer-contract: *e2e_test - e2e-block-building: *e2e_test - e2e-nested-contract: *e2e_test - e2e-non-contract-account: *e2e_test - e2e-multiple-accounts-1-enc-key: *e2e_test - - e2e-public-token-contract: *e2e_test - e2e-cli: *e2e_test - e2e-cross-chain-messaging: *e2e_test - e2e-public-cross-chain-messaging: *e2e_test @@ -1380,14 +1341,11 @@ workflows: - e2e-deploy-contract - e2e-lending-contract - e2e-token-contract - - e2e-private-airdrop - e2e-sandbox-example - - e2e-multi-transfer-contract - e2e-block-building - e2e-nested-contract - e2e-non-contract-account - e2e-multiple-accounts-1-enc-key - - e2e-public-token-contract - e2e-cli - e2e-cross-chain-messaging - e2e-public-cross-chain-messaging diff --git a/docs/docs/dev_docs/contracts/syntax/events.md b/docs/docs/dev_docs/contracts/syntax/events.md index 97c24de0c42..d3f3f6c192b 100644 --- a/docs/docs/dev_docs/contracts/syntax/events.md +++ b/docs/docs/dev_docs/contracts/syntax/events.md @@ -100,11 +100,11 @@ They can be emitted by both public and private functions. To emit unencrypted logs first import the `emit_unencrypted_log` utility function inside your contract: -#include_code unencrypted_import /yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr rust +#include_code unencrypted_import /yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr rust Then you can call the function: -#include_code unencrypted_log /yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr rust +#include_code emit_unencrypted /yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr rust Once emitted, unencrypted events are stored in AztecNode and can be queried by anyone: diff --git a/docs/docs/dev_docs/contracts/syntax/functions.md b/docs/docs/dev_docs/contracts/syntax/functions.md index 5085d194a23..c21ce8c56af 100644 --- a/docs/docs/dev_docs/contracts/syntax/functions.md +++ b/docs/docs/dev_docs/contracts/syntax/functions.md @@ -39,12 +39,12 @@ While `staticcall` and `delegatecall` both have flags in the call context, they - A special `constructor` function MUST be declared within a contract's scope. - A constructor doesn't have a name, because its purpose is clear: to initialize contract state. -- In Aztec terminology, a constructor is always a '`private` function' (i.e. it cannot be a `public` function, in the current version of the sandbox it cannot call public functions either). +- In Aztec terminology, a constructor is always a '`private` function' (i.e. it cannot be a `public` function). - A constructor behaves almost identically to any other function. It's just important for Aztec to be able to identify this function as special: it may only be called once, and will not be deployed as part of the contract. An example of a somewhat boring constructor is as follows: -#include_code empty-constructor /yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr rust +#include_code empty-constructor /yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr rust Although you can have a constructor that does nothing, you might want to do something with it, such as setting the deployer as an owner. diff --git a/docs/docs/dev_docs/tutorials/writing_dapp/contract_interaction.md b/docs/docs/dev_docs/tutorials/writing_dapp/contract_interaction.md index 4d3d2ae0e22..6cc2e502d11 100644 --- a/docs/docs/dev_docs/tutorials/writing_dapp/contract_interaction.md +++ b/docs/docs/dev_docs/tutorials/writing_dapp/contract_interaction.md @@ -21,7 +21,7 @@ import { readFileSync } from "fs"; import TokenContractArtifact from "../contracts/token/target/Token.json" assert { type: "json" }; ``` -And then add the following code for initialising the `Contract` instances: +And then add the following code for initializing the `Contract` instances: #include_code get-tokens yarn-project/end-to-end/src/sample-dapp/contracts.mjs javascript @@ -93,15 +93,16 @@ At the time of this writing, there are no events emitted when new private notes ## Working with public state -While they are [fundamentally differently](../../../concepts/foundation/state_model.md), the API for working with private and public functions and state from `aztec.js` is equivalent. To query the balance in public tokens for our user accounts, we can just call the `publicBalanceOf` view function in the contract: +While they are [fundamentally differently](../../../concepts/foundation/state_model.md), the API for working with private and public functions and state from `aztec.js` is equivalent. To query the balance in public tokens for our user accounts, we can just call the `balance_of_public` view function in the contract: #include_code showPublicBalances yarn-project/end-to-end/src/sample-dapp/index.mjs javascript :::info -Since this is a public token contract we are working with, we can now query the balance for any address, not just those registered in our local PXE. We can also send funds to addresses for which we don't know their [public encryption key](../../../concepts/foundation/accounts/keys.md#encryption-keys). +Since this we are working with pubic balances, we can now query the balance for any address, not just those registered in our local PXE. We can also send funds to addresses for which we don't know their [public encryption key](../../../concepts/foundation/accounts/keys.md#encryption-keys). ::: -Here, since the public token contract does not mint any initial funds upon deployment, the balances for all of our user's accounts will be zero. But we can send a transaction to mint tokens to change this, using very similar code to the one for sending private funds: +Here, since the token contract does not mint any initial funds upon deployment, the balances for all of our user's accounts will be zero. +But we can send a transaction to mint tokens, using very similar code to the one for sending private funds: #include_code mintPublicFunds yarn-project/end-to-end/src/sample-dapp/index.mjs javascript @@ -121,18 +122,6 @@ Balance of 0x226f8087792beff8d5009eb94e65d2a4a505b70baf4a9f28d33c8d620b0ba972: 0 Balance of 0x0e1f60e8566e2c6d32378bdcadb7c63696e853281be798c107266b8c3a88ea9b: 0 ``` -Public functions can emit [unencrypted public logs](../../contracts/syntax/events.md#unencrypted-events), which we can query via the PXE interface. In particular, the public token contract emits a generic `Coins minted` whenever the `mint` method is called: - -#include_code unencrypted_log yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr rust - -We can extend our code by querying the logs emitted on the last block when the minting transaction is mined: - -#include_code showLogs yarn-project/end-to-end/src/sample-dapp/index.mjs javascript - -:::info -At the time of this writing, there is no event-based mechanism in the `aztec.js` library to subscribe to events. The only option to consume them is to poll on every new block detected. This will change in a future version. -::: - ## Next steps In the next and final section, we'll [set up automated tests for our application](./testing.md). diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index 58b7c5d49ae..f5ecbb713f9 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -37,7 +37,6 @@ import { ImportTestContractArtifact, ParentContractArtifact, PendingCommitmentsContractArtifact, - PrivateTokenAirdropContractArtifact, StatefulTestContractArtifact, TestContractArtifact, TokenContractArtifact, @@ -191,188 +190,7 @@ describe('Private Execution test suite', () => { }); }); - describe('private token airdrop contract', () => { - const contractAddress = defaultContractAddress; - const mockFirstNullifier = new Fr(1111); - let currentNoteIndex = 0n; - - const buildNote = (amount: bigint, owner: AztecAddress, storageSlot = Fr.random()) => { - // WARNING: this is not actually how nonces are computed! - // For the purpose of this test we use a mocked firstNullifier and and a random number - // to compute the nonce. Proper nonces are only enforced later by the kernel/later circuits - // which are not relevant to this test. In practice, the kernel first squashes all transient - // noteHashes with their matching nullifiers. It then reorders the remaining "persistable" - // noteHashes. A TX's real first nullifier (generated by the initial kernel) and a noteHash's - // array index at the output of the final kernel/ordering circuit are used to derive nonce via: - // `hash(firstNullifier, noteHashIndex)` - const noteHashIndex = Math.floor(Math.random()); // mock index in TX's final newNoteHashes array - const nonce = computeCommitmentNonce(circuitsWasm, mockFirstNullifier, noteHashIndex); - const preimage = [new Fr(amount), owner.toField(), Fr.random()]; - const innerNoteHash = Fr.fromBuffer(hash(preimage.map(p => p.toBuffer()))); - return { - contractAddress, - storageSlot, - nonce, - preimage, - innerNoteHash, - siloedNullifier: new Fr(0), - index: currentNoteIndex++, - }; - }; - - beforeEach(() => { - oracle.getCompleteAddress.mockImplementation((address: AztecAddress) => { - if (address.equals(owner)) return Promise.resolve(ownerCompleteAddress); - if (address.equals(recipient)) return Promise.resolve(recipientCompleteAddress); - throw new Error(`Unknown address ${address}`); - }); - - oracle.getFunctionArtifact.mockImplementation((_, selector) => - Promise.resolve( - PrivateTokenAirdropContractArtifact.functions.find(f => - selector.equals(FunctionSelector.fromNameAndParameters(f.name, f.parameters)), - )!, - ), - ); - }); - - it('should have an artifact for computing note hash and nullifier', async () => { - const storageSlot = Fr.random(); - const note = buildNote(60n, owner, storageSlot); - - // Should be the same as how we compute the values for the ValueNote in the Aztec.nr library. - const valueNoteHash = hashFields(note.preimage); - const innerNoteHash = hashFields([storageSlot, valueNoteHash]); - const siloedNoteHash = siloCommitment(circuitsWasm, contractAddress, innerNoteHash); - const uniqueSiloedNoteHash = computeUniqueCommitment(circuitsWasm, note.nonce, siloedNoteHash); - const innerNullifier = hashFields([uniqueSiloedNoteHash, ownerPk.low, ownerPk.high]); - - const result = await acirSimulator.computeNoteHashAndNullifier( - contractAddress, - note.nonce, - storageSlot, - note.preimage, - ); - - expect(result).toEqual({ - innerNoteHash, - siloedNoteHash, - uniqueSiloedNoteHash, - innerNullifier, - }); - }); - - it('should a constructor with arguments that inserts notes', async () => { - const artifact = getFunctionArtifact(PrivateTokenAirdropContractArtifact, 'constructor'); - - const result = await runSimulator({ args: [140, owner], artifact }); - - expect(result.newNotes).toHaveLength(1); - const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner.toField(), circuitsWasm)); - - const newCommitments = result.callStackItem.publicInputs.newCommitments.filter(field => !field.equals(Fr.ZERO)); - expect(newCommitments).toHaveLength(1); - - const [commitment] = newCommitments; - expect(commitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, newNote.storageSlot, newNote.preimage), - ); - }); - - it('should run the mint function', async () => { - const artifact = getFunctionArtifact(PrivateTokenAirdropContractArtifact, 'mint'); - - const result = await runSimulator({ args: [140, owner], artifact }); - - expect(result.newNotes).toHaveLength(1); - const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner.toField(), circuitsWasm)); - - const newCommitments = result.callStackItem.publicInputs.newCommitments.filter(field => !field.equals(Fr.ZERO)); - expect(newCommitments).toHaveLength(1); - - const [commitment] = newCommitments; - expect(commitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, newNote.storageSlot, newNote.preimage), - ); - }); - - it('should run the transfer function', async () => { - const amountToTransfer = 100n; - const artifact = getFunctionArtifact(PrivateTokenAirdropContractArtifact, 'transfer'); - - const storageSlot = computeSlotForMapping(new Fr(1n), owner.toField(), circuitsWasm); - const recipientStorageSlot = computeSlotForMapping(new Fr(1n), recipient.toField(), circuitsWasm); - - const notes = [buildNote(60n, owner, storageSlot), buildNote(80n, owner, storageSlot)]; - oracle.getNotes.mockResolvedValue(notes); - - const consumedNotes = await asyncMap(notes, ({ nonce, preimage }) => - acirSimulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, preimage), - ); - await insertLeaves(consumedNotes.map(n => n.siloedNoteHash)); - - const args = [amountToTransfer, recipient]; - const result = await runSimulator({ args, artifact, msgSender: owner }); - - // The two notes were nullified - const newNullifiers = result.callStackItem.publicInputs.newNullifiers.filter(field => !field.equals(Fr.ZERO)); - expect(newNullifiers).toHaveLength(consumedNotes.length); - expect(newNullifiers).toEqual(expect.arrayContaining(consumedNotes.map(n => n.innerNullifier))); - - expect(result.newNotes).toHaveLength(2); - const [changeNote, recipientNote] = result.newNotes; - expect(recipientNote.storageSlot).toEqual(recipientStorageSlot); - - const newCommitments = result.callStackItem.publicInputs.newCommitments.filter(field => !field.equals(Fr.ZERO)); - expect(newCommitments).toHaveLength(2); - - const [changeNoteCommitment, recipientNoteCommitment] = newCommitments; - expect(recipientNoteCommitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, recipientStorageSlot, recipientNote.preimage), - ); - expect(changeNoteCommitment).toEqual( - await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, changeNote.preimage), - ); - - expect(recipientNote.preimage[0]).toEqual(new Fr(amountToTransfer)); - expect(changeNote.preimage[0]).toEqual(new Fr(40n)); - - const readRequests = result.callStackItem.publicInputs.readRequests.filter(field => !field.equals(Fr.ZERO)); - expect(readRequests).toHaveLength(consumedNotes.length); - expect(readRequests).toEqual(expect.arrayContaining(consumedNotes.map(n => n.uniqueSiloedNoteHash))); - }); - - it('should be able to transfer with dummy notes', async () => { - const amountToTransfer = 100n; - const balance = 160n; - const artifact = getFunctionArtifact(PrivateTokenAirdropContractArtifact, 'transfer'); - - const storageSlot = computeSlotForMapping(new Fr(1n), owner.toField(), circuitsWasm); - - const notes = [buildNote(balance, owner, storageSlot)]; - oracle.getNotes.mockResolvedValue(notes); - - const consumedNotes = await asyncMap(notes, ({ nonce, preimage }) => - acirSimulator.computeNoteHashAndNullifier(contractAddress, nonce, storageSlot, preimage), - ); - await insertLeaves(consumedNotes.map(n => n.siloedNoteHash)); - - const args = [amountToTransfer, recipient]; - const result = await runSimulator({ args, artifact, msgSender: owner }); - - const newNullifiers = result.callStackItem.publicInputs.newNullifiers.filter(field => !field.equals(Fr.ZERO)); - expect(newNullifiers).toEqual(consumedNotes.map(n => n.innerNullifier)); - - expect(result.newNotes).toHaveLength(2); - const [changeNote, recipientNote] = result.newNotes; - expect(recipientNote.preimage[0]).toEqual(new Fr(amountToTransfer)); - expect(changeNote.preimage[0]).toEqual(new Fr(balance - amountToTransfer)); - }); - }); - - describe('stateful test contract contract', () => { + describe('stateful test contract', () => { const contractAddress = defaultContractAddress; const mockFirstNullifier = new Fr(1111); let currentNoteIndex = 0n; @@ -443,7 +261,7 @@ describe('Private Execution test suite', () => { }); }); - it('should a constructor with arguments that inserts notes', async () => { + it('should have a constructor with arguments that inserts notes', async () => { const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'constructor'); const result = await runSimulator({ args: [owner, 140], artifact }); diff --git a/yarn-project/acir-simulator/src/public/index.test.ts b/yarn-project/acir-simulator/src/public/index.test.ts index bda1757eccc..0fd804b42d8 100644 --- a/yarn-project/acir-simulator/src/public/index.test.ts +++ b/yarn-project/acir-simulator/src/public/index.test.ts @@ -1,7 +1,6 @@ import { CallContext, CircuitsWasm, - ContractStorageRead, FunctionData, GlobalVariables, HistoricBlockData, @@ -16,7 +15,6 @@ import { toBigInt } from '@aztec/foundation/serialize'; import { ChildContractArtifact, ParentContractArtifact, - PublicTokenContractArtifact, TestContractArtifact, TokenContractArtifact, } from '@aztec/noir-contracts/artifacts'; @@ -54,7 +52,7 @@ describe('ACIR public execution simulator', () => { executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, blockData); }, 10000); - describe('PublicToken contract', () => { + describe('Token contract', () => { let recipient: AztecAddress; beforeEach(() => { @@ -62,14 +60,17 @@ describe('ACIR public execution simulator', () => { }); describe('mint', () => { - it('should run the mint function', async () => { + it('should run the mint_public function', async () => { const contractAddress = AztecAddress.random(); - const mintArtifact = PublicTokenContractArtifact.functions.find(f => f.name === 'mint')!; + const mintArtifact = TokenContractArtifact.functions.find(f => f.name === 'mint_public')!; const functionData = FunctionData.fromAbi(mintArtifact); - const args = encodeArguments(mintArtifact, [140, recipient]); + const mintAmount = 140n; + const args = encodeArguments(mintArtifact, [recipient, mintAmount]); + + const msgSender = AztecAddress.random(); const callContext = CallContext.from({ - msgSender: AztecAddress.random(), + msgSender, storageContractAddress: contractAddress, portalContractAddress: EthAddress.random(), functionSelector: FunctionSelector.empty(), @@ -81,27 +82,57 @@ describe('ACIR public execution simulator', () => { publicContracts.getBytecode.mockResolvedValue(Buffer.from(mintArtifact.bytecode, 'base64')); // Mock the old value for the recipient balance to be 20 + const isMinter = new Fr(1n); // 1n means true const previousBalance = new Fr(20n); - publicState.storageRead.mockResolvedValue(previousBalance); + const previousTotalSupply = new Fr(previousBalance.value + 100n); + publicState.storageRead + .mockResolvedValueOnce(isMinter) // reading whether msg_sender is minter + .mockResolvedValueOnce(previousBalance) // reading user's balance + .mockResolvedValueOnce(previousTotalSupply); // reading total supply const execution: PublicExecution = { contractAddress, functionData, args, callContext }; const result = await executor.simulate(execution, GlobalVariables.empty()); - const expectedBalance = new Fr(160n); - expect(result.returnValues[0]).toEqual(expectedBalance); + expect(result.returnValues[0]).toEqual(new Fr(1n)); + + const recipientBalanceStorageSlot = computeSlotForMapping(new Fr(6n), recipient.toField(), circuitsWasm); + const totalSupplyStorageSlot = new Fr(4n); - const storageSlot = computeSlotForMapping(new Fr(1n), recipient.toField(), circuitsWasm); + const expectedBalance = new Fr(previousBalance.value + mintAmount); + const expectedTotalSupply = new Fr(previousTotalSupply.value + mintAmount); + // There should be 2 storage updates, one for the recipient's balance and one for the total supply expect(result.contractStorageUpdateRequests).toEqual([ - { storageSlot, oldValue: previousBalance, newValue: expectedBalance, sideEffectCounter: 1 }, // 0th is a read + { + storageSlot: recipientBalanceStorageSlot, + oldValue: previousBalance, + newValue: expectedBalance, + sideEffectCounter: 3, + }, + { + storageSlot: totalSupplyStorageSlot, + oldValue: previousTotalSupply, + newValue: expectedTotalSupply, + sideEffectCounter: 4, + }, ]); - expect(result.contractStorageReads).toEqual([]); + const mintersStorageSlot = new Fr(2n); + const isMinterStorageSlot = computeSlotForMapping(mintersStorageSlot, msgSender.toField(), circuitsWasm); + // Note: There is only 1 storage read (for the isMinter value) because the other 2 reads get overwritten by + // the updates + expect(result.contractStorageReads).toEqual([ + { + storageSlot: isMinterStorageSlot, + currentValue: isMinter, + sideEffectCounter: 0, + }, + ]); }); }); describe('transfer', () => { let contractAddress: AztecAddress; - let artifact: FunctionArtifact; + let transferArtifact: FunctionArtifact; let functionData: FunctionData; let args: Fr[]; let sender: AztecAddress; @@ -112,10 +143,10 @@ describe('ACIR public execution simulator', () => { beforeEach(() => { contractAddress = AztecAddress.random(); - artifact = PublicTokenContractArtifact.functions.find(f => f.name === 'transfer')!; + transferArtifact = TokenContractArtifact.functions.find(f => f.name === 'transfer_public')!; functionData = new FunctionData(FunctionSelector.empty(), false, false, false); - args = encodeArguments(artifact, [140, recipient]); sender = AztecAddress.random(); + args = encodeArguments(transferArtifact, [sender, recipient, 140n, 0n]); callContext = CallContext.from({ msgSender: sender, @@ -127,10 +158,10 @@ describe('ACIR public execution simulator', () => { isStaticCall: false, }); - recipientStorageSlot = computeSlotForMapping(new Fr(1n), recipient.toField(), circuitsWasm); - senderStorageSlot = computeSlotForMapping(new Fr(1n), Fr.fromBuffer(sender.toBuffer()), circuitsWasm); + recipientStorageSlot = computeSlotForMapping(new Fr(6n), recipient.toField(), circuitsWasm); + senderStorageSlot = computeSlotForMapping(new Fr(6n), Fr.fromBuffer(sender.toBuffer()), circuitsWasm); - publicContracts.getBytecode.mockResolvedValue(Buffer.from(artifact.bytecode, 'base64')); + publicContracts.getBytecode.mockResolvedValue(Buffer.from(transferArtifact.bytecode, 'base64')); execution = { contractAddress, functionData, args, callContext }; }); @@ -158,42 +189,34 @@ describe('ACIR public execution simulator', () => { const expectedRecipientBalance = new Fr(160n); const expectedSenderBalance = new Fr(60n); - expect(result.returnValues[0]).toEqual(expectedRecipientBalance); + expect(result.returnValues[0]).toEqual(new Fr(1n)); expect(result.contractStorageUpdateRequests).toEqual([ { storageSlot: senderStorageSlot, oldValue: senderBalance, newValue: expectedSenderBalance, - sideEffectCounter: 2, - }, // 0th, 1st are reads + sideEffectCounter: 1, // 1 read (sender balance) + }, { storageSlot: recipientStorageSlot, oldValue: recipientBalance, newValue: expectedRecipientBalance, - sideEffectCounter: 3, + sideEffectCounter: 3, // 1 read (sender balance), 1 write (new sender balance), 1 read (recipient balance) }, ]); expect(result.contractStorageReads).toEqual([]); }); - it('should fail the transfer function without enough sender balance', async () => { + it('should throw underflow error when executing transfer function without enough sender balance', async () => { const senderBalance = new Fr(10n); const recipientBalance = new Fr(20n); mockStore(senderBalance, recipientBalance); - const result = await executor.simulate(execution, GlobalVariables.empty()); - expect(result.returnValues[0]).toEqual(recipientBalance); - - expect(result.contractStorageReads).toEqual( - [ - { storageSlot: senderStorageSlot, currentValue: senderBalance, sideEffectCounter: 0 }, - { storageSlot: recipientStorageSlot, currentValue: recipientBalance, sideEffectCounter: 1 }, - ].map(ContractStorageRead.from), + await expect(executor.simulate(execution, GlobalVariables.empty())).rejects.toThrowError( + 'Assertion failed: Underflow', ); - - expect(result.contractStorageUpdateRequests).toEqual([]); }); }); }); diff --git a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts index cfa14445de5..0a66236dd0d 100644 --- a/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts +++ b/yarn-project/end-to-end/src/cli_docs_sandbox.test.ts @@ -103,13 +103,10 @@ EcdsaAccountContractArtifact EscrowContractArtifact ImportTestContractArtifact LendingContractArtifact -MultiTransferContractArtifact ParentContractArtifact PendingCommitmentsContractArtifact PokeableTokenContractArtifact PriceFeedContractArtifact -PrivateTokenAirdropContractArtifact -PublicTokenContractArtifact SchnorrAccountContractArtifact SchnorrHardcodedAccountContractArtifact SchnorrSingleKeyAccountContractArtifact diff --git a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts deleted file mode 100644 index 80b7259a356..00000000000 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { AztecAddress, Contract, Wallet } from '@aztec/aztec.js'; -import { DebugLogger } from '@aztec/foundation/log'; -import { MultiTransferContract, PrivateTokenAirdropContract } from '@aztec/noir-contracts/types'; -import { AztecNode, CompleteAddress } from '@aztec/types'; - -import { expectsNumOfEncryptedLogsInTheLastBlockToBe, setup } from './fixtures/utils.js'; - -/** - * Multi-transfer payments is an example application to demonstrate how a payroll application could be built using aztec. - * In the current version of aztec, each multi-transfer can support only 12 recipients per transaction. The sender - * can decide which note can be spent. - */ -describe('multi-transfer payments', () => { - const numberOfAccounts = 12; - - let aztecNode: AztecNode | undefined; - let wallet: Wallet; - let logger: DebugLogger; - let teardown: () => Promise; - - let ownerAddress: AztecAddress; - let recipients: AztecAddress[]; - let initialBalance: bigint; - - let zkTokenContract: PrivateTokenAirdropContract; - let multiTransferContract: MultiTransferContract; - - beforeEach(async () => { - let accounts: CompleteAddress[]; - ({ teardown, aztecNode, accounts, logger, wallet } = await setup(numberOfAccounts + 1)); // 1st being the `owner` - ownerAddress = accounts[0].address; - recipients = accounts.slice(1).map(a => a.address); - - logger(`Deploying zk token contract...`); - initialBalance = 1000n; - await deployZkTokenContract(initialBalance, ownerAddress); - - logger(`Deploying multi-transfer contract...`); - await deployMultiTransferContract(); - }, 100_000); - - afterEach(() => teardown(), 30_000); - - const deployZkTokenContract = async (initialBalance: bigint, owner: AztecAddress) => { - logger(`Deploying zk token contract...`); - zkTokenContract = await PrivateTokenAirdropContract.deploy(wallet, initialBalance, owner).send().deployed(); - logger(`zk token contract deployed at ${zkTokenContract.address}`); - }; - - const deployMultiTransferContract = async () => { - logger(`Deploying multi-transfer contract...`); - multiTransferContract = await MultiTransferContract.deploy(wallet).send().deployed(); - logger(`multi-transfer contract deployed at ${multiTransferContract.address}`); - }; - - const expectBalance = async (tokenContract: Contract, owner: AztecAddress, expectedBalance: bigint) => { - const balance = await tokenContract.methods.getBalance(owner).view({ from: owner }); - logger(`Account ${owner} balance: ${balance}`); - expect(balance).toBe(expectedBalance); - }; - - /** - * Payroll example - * - * Transaction 1: - * The sender first splits 1000 to create new notes (for himself) with values 100, 200, 300, 400: - * 0: sender: [1000] - * | - * +-- [100 (change), 200, 300, 400] - * - * Transaction 2: - * In the next transaction, the sender wants to spend all four notes created in the previous transaction: - * index: [0 1 2 3 4 5 6 7] - * 0: sender: [100, 200, 300, 400] - * | - * +-- [25 (change), 20, 25, 30] // first batchTx call - * - * index: [0 1 2 3 4 5 6 7] - * 1: sender: [200, 300, 400, 25] - * | - * +-- [50 (change), 40, 50, 60] // second batchTx call - * - * index: [0 1 2 3 4 5 6 7] - * 2: sender: [300, 400, 25, 50] - * | - * +-- [60 (change), 75, 80, 85] // third batchTx call - * - * index: [0 1 2 3 4 5 6 7] - * 3: sender: [400, 25, 50, 60] - * | - * +-- [50 (change), 100, 120, 130] // fourth batchTx call - * - */ - it('12 transfers per transactions should work', async () => { - // Transaction 1 - logger(`self batchTransfer()`); - await zkTokenContract.methods - .batchTransfer(ownerAddress, [200n, 300n, 400n], [ownerAddress, ownerAddress, ownerAddress], 0) - .send() - .wait(); - - await expectBalance(zkTokenContract, ownerAddress, initialBalance); - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 4); - - const amounts: bigint[] = [20n, 25n, 30n, 40n, 50n, 60n, 75n, 80n, 85n, 100n, 120n, 130n]; - const amountSum = amounts.reduce((a, b) => a + b, 0n); - const noteOffsets: bigint[] = [0n, 0n, 0n, 0n]; - - // Transaction 2 - logger(`multiTransfer()...`); - await multiTransferContract.methods - .multiTransfer(zkTokenContract.address.toField(), recipients, amounts, ownerAddress, noteOffsets) - .send() - .wait({ timeout: 1000 }); // mining timeout ≥ time needed for the test to finish. - - await expectBalance(zkTokenContract, ownerAddress, initialBalance - amountSum); - for (let index = 0; index < numberOfAccounts; index++) { - await expectBalance(zkTokenContract, recipients[index], amounts[index]); - } - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); - }, 100_000); - - /** - * Creating change notes for self. - * - * Transaction 1: Splits the 1000 note to create 12 notes x 50 each. - * index: [0 1 2 3 4 5 6 7] - * 0: sender: [1000] - * | - * +-- [850, 50, 50, 50] - * - * index: [0 1 2 3 4 5 6 7] - * 1: sender: [850, 50, 50, 50] - * | - * +-- [700, 50, 50, 50] - * - * index: [0 1 2 3 4 5 6 7] - * 2: sender: [50, 50, 50, 700, 50, 50, 50] - * | - * +-- [550, 50, 50, 50] - * - * index: [0 1 2 3 4 5 6 7] - * 3: sender: [50, 50, 50, 50, 50, 50, 550, 50, 50, 50] - * | - * +-- [400, 50, 50, 50] - * - * End state: - * sender: [50, 50, 50, 50, 50, 50, 50, 50, 50, 400, 50, 50, 50] - * - * Transaction 2: Spend more notes than it's allowed in a single call, to transfer some amount to a recipient. - * It will destroy the largest note (400n) plus 8 small notes (50n * 8n). - * 4 notes will be destroyed first: [400n, 50n, 50n, 50n] - * And another 4 + 1 notes will be burnt in two function calls: [50n, 50n, 50n, 50n] and [50n] - * One change note (10n) will be created for the sender. - * One note will be created for the recipient. - */ - it('create 12 small notes out of 1 large note and transfer to a recipient', async () => { - // Transaction 1 - logger(`split multiTransfer()...`); - { - const amounts: bigint[] = [50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n]; - const noteOffsets: bigint[] = [0n, 0n, 3n, 6n]; - const repeatedSelfAddress: AztecAddress[] = Array(12).fill(ownerAddress); - - await multiTransferContract.methods - .multiTransfer(zkTokenContract.address.toField(), repeatedSelfAddress, amounts, ownerAddress, noteOffsets) - .send() - .wait({ timeout: 100 }); // mining timeout ≥ time needed for the test to finish. - - await expectBalance(zkTokenContract, ownerAddress, initialBalance); - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); - } - - // Transaction 2 - logger(`transfer()`); - { - const transferAmount = 400n + 50n * 7n + 40n; - const recipient = recipients[0]; - await expectBalance(zkTokenContract, recipient, 0n); - - await zkTokenContract.methods.transfer(transferAmount, recipient).send().wait(); - - await expectBalance(zkTokenContract, ownerAddress, initialBalance - transferAmount); - await expectBalance(zkTokenContract, recipient, transferAmount); - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 2); - } - }, 100_000); -}); diff --git a/yarn-project/end-to-end/src/e2e_private_airdrop.test.ts b/yarn-project/end-to-end/src/e2e_private_airdrop.test.ts deleted file mode 100644 index 2a81c41ddf3..00000000000 --- a/yarn-project/end-to-end/src/e2e_private_airdrop.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { CompleteAddress, NotePreimage, TxHash, Wallet } from '@aztec/aztec.js'; -import { MAX_NEW_COMMITMENTS_PER_CALL } from '@aztec/circuits.js'; -import { Fr } from '@aztec/foundation/fields'; -import { DebugLogger } from '@aztec/foundation/log'; -import { PrivateTokenAirdropContract } from '@aztec/noir-contracts/types'; - -import { setup } from './fixtures/utils.js'; - -class Claim { - static EMPTY = new Claim(0n, Fr.ZERO); - - constructor(public readonly amount: bigint, public readonly secret: Fr) {} - - get preimage() { - return new NotePreimage([new Fr(this.amount), this.secret]); - } -} - -describe('private airdrop', () => { - const numberOfAccounts = 3; - const initialSupply = 1000n; - const claimsStorageSlot = new Fr(2n); - - let wallets: Wallet[]; - let contracts: PrivateTokenAirdropContract[]; - let accounts: CompleteAddress[]; - let claims: Claim[]; - let logger: DebugLogger; - let teardown: () => Promise; - - beforeEach(async () => { - ({ teardown, accounts, wallets, logger } = await setup(numberOfAccounts)); - - logger(`Deploying zk token contract...`); - const owner = accounts[0].address; - const contract = await PrivateTokenAirdropContract.deploy(wallets[0], initialSupply, owner).send().deployed(); - logger(`zk token contract deployed at ${contract.address}`); - - contracts = [contract]; - for (let i = 1; i < accounts.length; ++i) { - contracts.push(await PrivateTokenAirdropContract.at(contract.address, wallets[i])); - } - }, 100_000); - - afterEach(() => teardown()); - - const expectBalance = async (accountIndex: number, expectedBalance: bigint) => { - const account = accounts[accountIndex].address; - const balance = await contracts[accountIndex].methods.getBalance(account).view({ from: account }); - logger(`Account ${accountIndex} balance: ${balance}`); - expect(balance).toBe(expectedBalance); - }; - - const createClaims = (amounts: bigint[]) => { - claims = amounts.map(amount => new Claim(amount, Fr.random())); - claims.push( - ...Array(MAX_NEW_COMMITMENTS_PER_CALL - amounts.length) - .fill(0) - .map(() => Claim.EMPTY), - ); - }; - - const claimToken = async (accountIndex: number, claim: Claim, txHash: TxHash, nonceIndex = 0) => { - const contract = contracts[accountIndex]; - const account = accounts[accountIndex].address; - const wallet = wallets[accountIndex]; - const nonces = await wallet.getNoteNonces(contract.address, claimsStorageSlot, claim.preimage, txHash); - - const preimageBuf = claim.preimage.toBuffer(); - const numNonces = claims.reduce((count, c) => count + (c.preimage.toBuffer().equals(preimageBuf) ? 1 : 0), 0); - expect(nonces.length).toBe(numNonces); - expect(nonces[nonceIndex]).not.toEqual(Fr.ZERO); - - const nonce = nonces[nonceIndex]; - await wallet.addNote(account, contract.address, claimsStorageSlot, claim.preimage, txHash, nonce); - - return contract.methods.claim(claim.amount, claim.secret).send().wait(); - }; - - it('should create claim notes for any accounts to claim', async () => { - let txHash: TxHash; - - // Transaction 1 - { - logger(`Create claims...`); - const accountIndex = 0; - await expectBalance(accountIndex, initialSupply); - - createClaims([12n, 345n]); - // Create a claim that has the exact same preimage as another claim. - claims[2] = claims[0]; - - const amounts = claims.map(c => c.amount); - const secrets = claims.map(c => c.secret); - ({ txHash } = await contracts[accountIndex].methods.createClaims(amounts, secrets).send().wait()); - - const amountSum = amounts.reduce((sum, a) => sum + a, 0n); - await expectBalance(accountIndex, initialSupply - amountSum); - } - - // Transaction 2 - { - logger(`Account 1 claims note 0...`); - const accountIndex = 1; - const claim = claims[0]; - await expectBalance(accountIndex, 0n); - - await claimToken(accountIndex, claim, txHash); - - await expectBalance(accountIndex, claim.amount); - - logger(`Fails to claim note 0 again...`); - await expect(claimToken(accountIndex, claim, txHash)).rejects.toThrow(); - } - - // Transaction 3 - { - logger(`Account 2 claims note 1...`); - const accountIndex = 2; - const claim = claims[1]; - await expectBalance(accountIndex, 0n); - - await claimToken(accountIndex, claim, txHash); - - await expectBalance(accountIndex, claim.amount); - - logger(`Fails to claim note 1 again...`); - await expect(claimToken(accountIndex, claim, txHash)).rejects.toThrow(); - - logger(`Fails to claim note 0...`); - await expect(claimToken(accountIndex, claims[0], txHash)).rejects.toThrow(); - } - - // Transaction 4 - { - logger(`Account 1 claims note 2...`); - const accountIndex = 1; - const claim0 = claims[0]; - const claim2 = claims[2]; - expect(claim2.preimage).toEqual(claim0.preimage); - - await expectBalance(accountIndex, claim0.amount); - - // Claim 2 has the same preimage as claim 0. - // `getNoteNonces` will return 2 nonces. And we need to use nonce 1 to spend the duplicated claim. - const nonceIndex = 1; - await claimToken(accountIndex, claim2, txHash, nonceIndex); - - await expectBalance(accountIndex, claim0.amount + claim2.amount); - } - }, 100_000); -}); diff --git a/yarn-project/end-to-end/src/e2e_public_token_contract.test.ts b/yarn-project/end-to-end/src/e2e_public_token_contract.test.ts deleted file mode 100644 index 2707988a2fc..00000000000 --- a/yarn-project/end-to-end/src/e2e_public_token_contract.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { AztecAddress, Wallet } from '@aztec/aztec.js'; -import { DebugLogger } from '@aztec/foundation/log'; -import { PublicTokenContract } from '@aztec/noir-contracts/types'; -import { CompleteAddress, PXE, TxStatus } from '@aztec/types'; - -import times from 'lodash.times'; - -import { expectUnencryptedLogsFromLastBlockToBe, expectUnencryptedLogsInTxToBe, setup } from './fixtures/utils.js'; - -describe('e2e_public_token_contract', () => { - let pxe: PXE; - let wallet: Wallet; - let logger: DebugLogger; - let recipient: AztecAddress; - let teardown: () => Promise; - - let contract: PublicTokenContract; - - const deployContract = async () => { - logger(`Deploying L2 public contract...`); - const txReceipt = await PublicTokenContract.deploy(wallet).send().wait(); - contract = txReceipt.contract; - logger(`L2 contract deployed at ${txReceipt.contractAddress}`); - return { contract, txReceipt }; - }; - - beforeEach(async () => { - let accounts: CompleteAddress[]; - ({ teardown, pxe, accounts, wallet, logger } = await setup()); - recipient = accounts[0].address; - }, 100_000); - - afterEach(() => teardown()); - - it('should deploy a public token contract', async () => { - const { txReceipt } = await deployContract(); - expect(txReceipt.status).toEqual(TxStatus.MINED); - }, 30_000); - - it('should deploy a public token contract and mint tokens to a recipient', async () => { - const mintAmount = 359n; - - await deployContract(); - - const tx = contract.methods.mint(mintAmount, recipient).send(); - - await tx.isMined({ interval: 0.1 }); - const receipt = await tx.getReceipt(); - - expect(receipt.status).toBe(TxStatus.MINED); - - const balance = await contract.methods.publicBalanceOf(recipient.toField()).view({ from: recipient }); - expect(balance).toBe(mintAmount); - - await expectUnencryptedLogsInTxToBe(tx, ['Coins minted']); - }, 45_000); - - // Regression for https://github.com/AztecProtocol/aztec-packages/issues/640 - it('should mint tokens thrice to a recipient within the same block', async () => { - const mintAmount = 42n; - - await deployContract(); - - // Assemble two mint txs sequentially (no parallel calls to circuits!) and send them simultaneously - const methods = times(3, () => contract.methods.mint(mintAmount, recipient)); - for (const method of methods) await method.simulate(); - const txs = await Promise.all(methods.map(method => method.send())); - - // Check that all txs got mined in the same block - await Promise.all(txs.map(tx => tx.isMined())); - const receipts = await Promise.all(txs.map(tx => tx.getReceipt())); - expect(receipts.map(r => r.status)).toEqual(times(3, () => TxStatus.MINED)); - expect(receipts.map(r => r.blockNumber)).toEqual(times(3, () => receipts[0].blockNumber)); - - const balance = await contract.methods.publicBalanceOf(recipient.toField()).view({ from: recipient }); - expect(balance).toBe(mintAmount * 3n); - - await expectUnencryptedLogsFromLastBlockToBe(pxe, ['Coins minted', 'Coins minted', 'Coins minted']); - }, 60_000); -}); diff --git a/yarn-project/end-to-end/src/sample-dapp/index.mjs b/yarn-project/end-to-end/src/sample-dapp/index.mjs index dc6ba0c08be..3aad3e254b9 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.mjs @@ -99,7 +99,7 @@ async function mintPublicFunds(pxe) { // docs:start:showLogs const blockNumber = await pxe.getBlockNumber(); const logs = (await pxe.getUnencryptedLogs(blockNumber, 1)).logs; - const textLogs = L2BlockL2Logs.unrollLogs(logs).map(log => log.toString('ascii')); + const textLogs = logs.map(extendedLog => extendedLog.log.data.toString('ascii')); for (const log of textLogs) console.log(`Log emitted: ${log}`); // docs:end:showLogs } diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index 1097b1ae118..74708cbe79d 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -9,13 +9,10 @@ members = [ "src/contracts/escrow_contract", "src/contracts/import_test_contract", "src/contracts/lending_contract", - "src/contracts/multi_transfer_contract", "src/contracts/parent_contract", "src/contracts/pending_commitments_contract", "src/contracts/pokeable_token_contract", "src/contracts/price_feed_contract", - "src/contracts/private_token_airdrop_contract", - "src/contracts/public_token_contract", "src/contracts/schnorr_account_contract", "src/contracts/schnorr_hardcoded_account_contract", "src/contracts/schnorr_single_key_account_contract", diff --git a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/Nargo.toml deleted file mode 100644 index cccba64b2ea..00000000000 --- a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "multi_transfer_contract" -authors = [""] -compiler_version = "0.1" -type = "contract" - -[dependencies] -aztec = { path = "../../../../aztec-nr/aztec" } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr deleted file mode 100644 index 05e173019c5..00000000000 --- a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr +++ /dev/null @@ -1,64 +0,0 @@ -mod private_token_airdrop_interface; - -// Demonstrates how to perform 4 x 4 = 16 transfers in one transaction. Uses the private airdrop contract in the backend. -contract MultiTransfer { - // Interfaces - use crate::private_token_airdrop_interface::PrivateTokenAirdropPrivateContextInterface; - - #[aztec(private)] - fn constructor() {} - - // Transfers 12 amounts to 12 recipients. - // multiTransfer() => 4 calls to batchTransfer() on the private airdrop contract. - // Each batchTransfer() call allows sending new notes to 3 recipients, so 3 x 4 = 12 recipients in total. - // Note that all the notes stay on the airdrop contract, the multi transfer contract must interact with - // methods in the private airdrop contract to initiate multiple transfers in one transaction. - #[aztec(private)] - fn multiTransfer( - asset: Field, // Asset to distribute - addresses: [Field; 12], // Addresses to distribute to - amounts: [Field; 12], // Amounts to distribute - owner: Field, // Owner of the asset - note_offsets: [Field; 4], // Offsets from which 4 notes of the owner would be read. - ) -> [Field; 4] { - let token = PrivateTokenAirdropPrivateContextInterface::at(asset); - - // First batch transfer call - let result1 = token.batchTransfer( - &mut context, - owner, - [amounts[0], amounts[1], amounts[2]], - [addresses[0], addresses[1], addresses[2]], - note_offsets[0] as u32, - )[0]; - - // Second batch transfer call - let result2 = token.batchTransfer( - &mut context, - owner, - [amounts[3], amounts[4], amounts[5]], - [addresses[3], addresses[4], addresses[5]], - note_offsets[1] as u32, - )[0]; - - // Third batch transfer call - let result3 = token.batchTransfer( - &mut context, - owner, - [amounts[6], amounts[7], amounts[8]], - [addresses[6], addresses[7], addresses[8]], - note_offsets[2] as u32, - )[0]; - - // Fourth batch transfer call - let result4 = token.batchTransfer( - &mut context, - owner, - [amounts[9], amounts[10], amounts[11]], - [addresses[9], addresses[10], addresses[11]], - note_offsets[3] as u32, - )[0]; - - [result1, result2, result3, result4] - } -} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/private_token_airdrop_interface.nr b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/private_token_airdrop_interface.nr deleted file mode 120000 index 3fe452c3079..00000000000 --- a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/private_token_airdrop_interface.nr +++ /dev/null @@ -1 +0,0 @@ -../../private_token_airdrop_contract/src/interface.nr \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/Nargo.toml deleted file mode 100644 index c41c12b4351..00000000000 --- a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/Nargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "private_token_airdrop_contract" -authors = [""] -compiler_version = "0.1" -type = "contract" - -[dependencies] -aztec = { path = "../../../../aztec-nr/aztec" } -value_note = { path = "../../../../aztec-nr/value-note"} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/claim_note.nr b/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/claim_note.nr deleted file mode 100644 index 2fe27299969..00000000000 --- a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/claim_note.nr +++ /dev/null @@ -1,90 +0,0 @@ -use dep::std::hash::pedersen; -use dep::aztec::note::{ - note_header::NoteHeader, - note_interface::NoteInterface, - utils::compute_note_hash_for_read_or_nullify, -}; - -global CLAIM_NOTE_LEN: Field = 2; - -struct ClaimNote { - value: Field, - secret_hash: Field, - header: NoteHeader, -} - -impl ClaimNote { - pub fn new(value: Field, secret_hash: Field) -> Self { - ClaimNote { - value, - secret_hash, - header: NoteHeader::empty(), - } - } - - pub fn serialize(self) -> [Field; CLAIM_NOTE_LEN] { - [self.value, self.secret_hash] - } - - pub fn deserialize(preimage: [Field; CLAIM_NOTE_LEN]) -> Self { - ClaimNote { - value: preimage[0], - secret_hash: preimage[1], - header: NoteHeader::empty(), - } - } - - pub fn compute_note_hash(self) -> Field { - // TODO(#1205) Should use a non-zero generator index. - dep::std::hash::pedersen([ - self.value, - self.secret_hash, - ])[0] - } - - pub fn compute_nullifier(self) -> Field { - let note_hash_for_nullify = compute_note_hash_for_read_or_nullify(ClaimNoteMethods, self); - // TODO(#1205) Should use a non-zero generator index. - dep::std::hash::pedersen([ - note_hash_for_nullify, - self.secret_hash, // Include the secret_hash again so that the public won't know the note has been claimed. - ])[0] - } - - pub fn set_header(&mut self, header: NoteHeader) { - self.header = header; - } -} - -fn deserialize(preimage: [Field; CLAIM_NOTE_LEN]) -> ClaimNote { - ClaimNote::deserialize(preimage) -} - -fn serialize(note: ClaimNote) -> [Field; CLAIM_NOTE_LEN] { - note.serialize() -} - -fn compute_note_hash(note: ClaimNote) -> Field { - note.compute_note_hash() -} - -fn compute_nullifier(note: ClaimNote) -> Field { - note.compute_nullifier() -} - -fn get_header(note: ClaimNote) -> NoteHeader { - note.header -} - -fn set_header(note: &mut ClaimNote, header: NoteHeader) { - note.set_header(header) -} - -global ClaimNoteMethods = NoteInterface { - deserialize, - serialize, - compute_note_hash, - compute_nullifier, - get_header, - set_header, -}; diff --git a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/interface.nr b/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/interface.nr deleted file mode 100644 index 1c6bc62fb5a..00000000000 --- a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/interface.nr +++ /dev/null @@ -1,161 +0,0 @@ -/* Autogenerated file, do not edit! */ - -use dep::std; -use dep::aztec::context::{ PrivateContext, PublicContext }; -use dep::aztec::constants_gen::RETURN_VALUES_LENGTH; - - - -// Interface for calling PrivateTokenAirdrop functions from a private context -struct PrivateTokenAirdropPrivateContextInterface { - address: Field, -} - -impl PrivateTokenAirdropPrivateContextInterface { - pub fn at(address: Field) -> Self { - Self { - address, - } - } - - pub fn batchTransfer( - self, - context: &mut PrivateContext, - sender: Field, - amounts: [Field;3], - recipients: [Field;3], - spend_note_offset: u32 - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 8]; - serialized_args[0] = sender; - serialized_args[1] = amounts[0]; - serialized_args[2] = amounts[1]; - serialized_args[3] = amounts[2]; - serialized_args[4] = recipients[0]; - serialized_args[5] = recipients[1]; - serialized_args[6] = recipients[2]; - serialized_args[7] = spend_note_offset as Field; - - context.call_private_function(self.address, 0xbf748730, serialized_args) - } - - - pub fn burn( - self, - context: &mut PrivateContext, - amount: Field, - owner: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = owner; - - context.call_private_function(self.address, 0xa4fa3a6f, serialized_args) - } - - - pub fn claim( - self, - context: &mut PrivateContext, - amount: Field, - secret: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = secret; - - context.call_private_function(self.address, 0xe642f6a0, serialized_args) - } - - - pub fn createClaims( - self, - context: &mut PrivateContext, - amounts: [Field;16], - secrets: [Field;16] - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 32]; - serialized_args[0] = amounts[0]; - serialized_args[1] = amounts[1]; - serialized_args[2] = amounts[2]; - serialized_args[3] = amounts[3]; - serialized_args[4] = amounts[4]; - serialized_args[5] = amounts[5]; - serialized_args[6] = amounts[6]; - serialized_args[7] = amounts[7]; - serialized_args[8] = amounts[8]; - serialized_args[9] = amounts[9]; - serialized_args[10] = amounts[10]; - serialized_args[11] = amounts[11]; - serialized_args[12] = amounts[12]; - serialized_args[13] = amounts[13]; - serialized_args[14] = amounts[14]; - serialized_args[15] = amounts[15]; - serialized_args[16] = secrets[0]; - serialized_args[17] = secrets[1]; - serialized_args[18] = secrets[2]; - serialized_args[19] = secrets[3]; - serialized_args[20] = secrets[4]; - serialized_args[21] = secrets[5]; - serialized_args[22] = secrets[6]; - serialized_args[23] = secrets[7]; - serialized_args[24] = secrets[8]; - serialized_args[25] = secrets[9]; - serialized_args[26] = secrets[10]; - serialized_args[27] = secrets[11]; - serialized_args[28] = secrets[12]; - serialized_args[29] = secrets[13]; - serialized_args[30] = secrets[14]; - serialized_args[31] = secrets[15]; - - context.call_private_function(self.address, 0x2eebe7ab, serialized_args) - } - - - pub fn mint( - self, - context: &mut PrivateContext, - amount: Field, - owner: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = owner; - - context.call_private_function(self.address, 0x1535439c, serialized_args) - } - - - pub fn transfer( - self, - context: &mut PrivateContext, - amount: Field, - recipient: Field - ) -> [Field; RETURN_VALUES_LENGTH] { - let mut serialized_args = [0; 2]; - serialized_args[0] = amount; - serialized_args[1] = recipient; - - context.call_private_function(self.address, 0xc0888d22, serialized_args) - } - -} - - - - -// Interface for calling PrivateTokenAirdrop functions from a public context -struct PrivateTokenAirdropPublicContextInterface { - address: Field, -} - -impl PrivateTokenAirdropPublicContextInterface { - pub fn at(address: Field) -> Self { - Self { - address, - } - } - -} - - diff --git a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/main.nr deleted file mode 100644 index 125a5333dd2..00000000000 --- a/yarn-project/noir-contracts/src/contracts/private_token_airdrop_contract/src/main.nr +++ /dev/null @@ -1,237 +0,0 @@ -mod claim_note; -mod interface; - -contract PrivateTokenAirdrop { - // Libs - use dep::std::option::Option; - use dep::value_note::{ - balance_utils, - utils::{increment, decrement, decrement_by_at_most}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, - }; - use dep::aztec::{ - constants_gen::MAX_NEW_COMMITMENTS_PER_CALL, - context::{PrivateContext, PublicContext, Context}, - state_vars::{map::Map, set::Set}, - note::{ - note_getter_options::NoteGetterOptions, - note_header::NoteHeader, - utils as note_utils, - }, - log::emit_unencrypted_log_from_private, - }; - - use crate::claim_note::{ClaimNote, ClaimNoteMethods, CLAIM_NOTE_LEN}; - use crate::interface::PrivateTokenAirdropPrivateContextInterface; - - - struct Storage { - balances: Map>, - claims: Set, - } - - impl Storage { - fn init(context: Context) -> pub Self { - Storage { - balances: Map::new( - context, - 1, // Storage slot - |context, slot| { - Set::new(context, slot, ValueNoteMethods) - }, - ), - claims: Set::new(context, 2, ClaimNoteMethods), - } - } - } - - // Constructs the contract and sets `initial_supply` which is fully owned by `owner`. - #[aztec(private)] - fn constructor( - initial_supply: Field, - owner: Field - ) { - // Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call. - let owner_balance = storage.balances.at(owner); - if (initial_supply != 0) { - increment(owner_balance, initial_supply, owner); - } - } - - // Mints `amount` of tokens to `owner`. - #[aztec(private)] - fn mint( - amount: Field, - owner: Field - ) { - // Insert new note to a set of user notes and emit the newly created encrypted note preimage via oracle call. - let owner_balance = storage.balances.at(owner); - increment(owner_balance, amount, owner); - } - - // Burn `amount` of tokens from `owner`'s balance. - // This is an example of a recursive function. It calls itself until enough notes have been burned to burn the `amount`. - #[aztec(private)] - fn burn( - amount: Field, - owner: Field, - ) { - let msg_sender = context.msg_sender(); - let this_address = context.this_address(); - - // If this function is not called by another function in the same contract, the owner must be msg_sender. - if msg_sender != this_address { - assert(owner == msg_sender); - } - - let balance = storage.balances.at(owner); - let sum = decrement_by_at_most(balance, amount, owner); - - // If sum is 0, there are no notes to be burned. - assert(sum != 0); - - if sum != amount { - // The destroyed notes' sum is not enough. Keep burning. - let amount_to_burn = amount - sum; - let this = PrivateTokenAirdropPrivateContextInterface::at(this_address); - let _res = this.burn(&mut context, amount_to_burn, owner); - } - } - - // Transfers `amount` of tokens from `sender` to a `recipient`. - #[aztec(private)] - fn transfer( - amount: Field, - recipient: Field, - ) { - let sender = context.msg_sender(); - let sender_balance = storage.balances.at(sender); - - let sum = decrement_by_at_most(sender_balance, amount, sender); - - // If sum is 0, there are no notes to be used. - assert(sum != 0); - - if sum != amount { - // The destroyed notes' sum is not enough for the transfer. - // Burn the remaining amount. - // We only call burn() when decrement_by_at_most() didn't destroy enough notes. - let amount_to_burn = amount - sum; - let this_address = context.this_address(); - let this = PrivateTokenAirdropPrivateContextInterface::at(this_address); - let _res = this.burn(&mut context, amount_to_burn, sender); - } - - // Create a new note for the recipient. - let recipient_balance = storage.balances.at(recipient); - increment(recipient_balance, amount, recipient); - } - - #[aztec(private)] - fn createClaims( - amounts: [Field; MAX_NEW_COMMITMENTS_PER_CALL], - secrets: [Field; MAX_NEW_COMMITMENTS_PER_CALL], - ) { - let sender = context.msg_sender(); - - // Pick from the set of sender's notes to spend amount. - let sender_balance = storage.balances.at(sender); - let total = amounts.fold(0, |sum, a| sum + a); - decrement(sender_balance, total, sender); - - // Create claim notes. - let claims = storage.claims; - for i in 0..amounts.len() { - let amount = amounts[i]; - if amount != 0 { - let mut note = ClaimNote::new(amount, secrets[i]); - claims.insert(&mut note); - } - } - } - - #[aztec(private)] - fn claim(amount: Field, secret: Field) { - let owner = context.msg_sender(); - - // Find a claim note with the exact amount (field_index = 0) and secret (field_index = 1). - let options = NoteGetterOptions::new().select(0, amount).select(1, secret).set_limit(1); - let opt_notes = storage.claims.get_notes(options); - let note = opt_notes[0].unwrap_unchecked(); - - // Remove the claim note from the set. - storage.claims.remove(note); - - // Send the value note. - let balance = storage.balances.at(owner); - increment(balance, note.value, owner); - } - - // Transfers `amounts` of tokens from `sender` to 3 `recipients`. - // Aztec only allows `MAX_NEW_COMMITMENTS_PER_CALL = 4` notes per tx => - // 1 new note for sender's new balance and 1 note each for 3 recipients (for their new balance) - #[aztec(private)] - fn batchTransfer( - sender: Field, - amounts: [Field; 3], - recipients: [Field; 3], - spend_note_offset: u32, - ) { - // Gets the set of sender's notes and picks 4 of those based on the offset. - // Spends the first of those 4 notes. - let sender_balance = storage.balances.at(sender); - let total = amounts[0] + amounts[1] + amounts[2]; - - let options = NoteGetterOptions::new().set_limit(1).set_offset(spend_note_offset); - let opt_notes = sender_balance.get_notes(options); - - // The note should always exist. - let note = opt_notes[0].unwrap_unchecked(); - - assert(note.owner == sender); - - sender_balance.remove(note); - - // Assert that the note chosen to spend has enough funds. - assert(note.value as u64 >= total as u64); - - // Add the change value back to the owner's balance. - let change_value = note.value - total; - increment(sender_balance, change_value, sender); - - // Creates new note for the recipient. - for i in 0..3 { - let recipient_balance = storage.balances.at(recipients[i]); - increment(recipient_balance, amounts[i], recipients[i]); - } - - // Also emit an unencrypted log, eg. "Coins transferred" - // In this example, we emit the first output note's commitment to ensure that the unencrypted log - // for each call to this function is distinct. This is done to detect any issues while collecting - // logs when building a transaction. See: https://github.com/AztecProtocol/aztec-packages/issues/987 - emit_unencrypted_log_from_private(&mut context, context.new_commitments.storage[0]); - } - - // Helper function to get the balance of a user ("unconstrained" is a Noir alternative of Solidity's "view" function). - unconstrained fn getBalance( - owner: Field, - ) -> Field { - // Get the set of notes owned by the user. - let owner_balance = storage.balances.at(owner); - - // Return the sum of all notes in the set. - balance_utils::get_balance(owner_balance) - } - - // Computes note hash and nullifier. - // Note 1: Needs to be defined by every contract producing logs. - // Note 2: Having it in all the contracts gives us the ability to compute the note hash and nullifier differently for different kind of notes. - unconstrained fn compute_note_hash_and_nullifier(contract_address: Field, nonce: Field, storage_slot: Field, preimage: [Field; VALUE_NOTE_LEN]) -> [Field; 4] { - let note_header = NoteHeader::new(contract_address, nonce, storage_slot); - if (storage_slot == 2) { - note_utils::compute_note_hash_and_nullifier(ClaimNoteMethods, note_header, preimage) - } else { - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } - } -} diff --git a/yarn-project/noir-contracts/src/contracts/public_token_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/public_token_contract/Nargo.toml deleted file mode 100644 index c3098dda9ee..00000000000 --- a/yarn-project/noir-contracts/src/contracts/public_token_contract/Nargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "public_token_contract" -authors = [""] -compiler_version = "0.1" -type = "contract" - -[dependencies] -aztec = { path = "../../../../aztec-nr/aztec" } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr deleted file mode 100644 index 23815b7c740..00000000000 --- a/yarn-project/noir-contracts/src/contracts/public_token_contract/src/main.nr +++ /dev/null @@ -1,102 +0,0 @@ -// docs:start:all -contract PublicToken { - use dep::std::option::Option; - - // docs:start:unencrypted_import - use dep::aztec::log::emit_unencrypted_log; - // docs:end:unencrypted_import - - use dep::aztec::{ - context::{PrivateContext, PublicContext, Context}, - state_vars::{ - map::Map, - public_state::PublicState, - }, - types::type_serialization::field_serialization::{ - FieldSerializationMethods, FIELD_SERIALIZED_LEN, - }, - }; - - struct Storage { - balances: Map>, - } - - impl Storage { - fn init(context: Context) -> pub Self { - Storage { - balances: Map::new( - context, - 1, - |context, slot| { - PublicState::new(context, slot, FieldSerializationMethods) - }, - ), - } - } - } - - // Constructs the contract. - // docs:start:empty-constructor - #[aztec(private)] - fn constructor() {} - // docs:end:empty-constructor - - // Mints `amount` of tokens to a `recipient`. - #[aztec(public)] - fn mint( - amount: Field, - recipient: Field, - ) -> Field { - - - let recipient_balance = storage.balances.at(recipient); - let new_amount = recipient_balance.read() + amount; - // TODO: Remove return value. - // docs:start:unencrypted_log - emit_unencrypted_log(&mut context, "Coins minted"); - // docs:end:unencrypted_log - recipient_balance.write(new_amount); - - new_amount - } - - // Transfers `amount` of tokens from `msg_sender` to `recipient`. - #[aztec(public)] - fn transfer( - amount: Field, - recipient: Field, - ) -> Field { - - let sender = context.msg_sender(); - - let sender_balance = storage.balances.at(sender); - let recipient_balance = storage.balances.at(recipient); - - let current_sender_balance: Field = sender_balance.read(); - let current_recipient_balance = recipient_balance.read(); - - // TODO: Should support larger numbers. - let mut return_value = 0; - - if (current_sender_balance as u126 > amount as u126) { - sender_balance.write(current_sender_balance - amount); - // TODO: Compiler complains if we don't assign the result of the write to anything - emit_unencrypted_log(&mut context, "Coins transferred"); - let amount = current_recipient_balance + amount; - recipient_balance.write(amount); - return_value = amount; - } else { - // TODO: Revert if there is not enough balance - return_value = current_recipient_balance; - } - return_value - } - - unconstrained fn publicBalanceOf( - owner: Field, - ) -> Field { - - storage.balances.at(owner).read() - } -} -// docs:end:all \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr index 7b527777416..82a276afddb 100644 --- a/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/test_contract/src/main.nr @@ -1,5 +1,9 @@ // A contract used for testing a random hodgepodge of small features from simulator and end-to-end tests. contract Test { + // docs:start:unencrypted_import + use dep::aztec::log::emit_unencrypted_log; + // docs:end:unencrypted_import + use dep::aztec::{ abi, abi::PrivateContextInputs, @@ -9,7 +13,6 @@ contract Test { context::get_portal_address, rand::rand }, - log::emit_unencrypted_log, types::vec::BoundedVec, constants_gen::EMPTY_NULLIFIED_COMMITMENT, }; @@ -21,7 +24,9 @@ contract Test { } #[aztec(private)] + // docs:start:empty-constructor fn constructor() {} + // docs:end:empty-constructor #[aztec(private)] fn getPublicKey( @@ -128,15 +133,15 @@ contract Test { } // docs:end:is-time-equal - // docs:start:emit_unencrypted_log #[aztec(public)] fn emit_unencrypted( value: Field ) -> Field { + // docs:start:emit_unencrypted emit_unencrypted_log(&mut context, value); + // docs:end:emit_unencrypted 0 } - // docs:end:emit_unencrypted_log #[aztec(public)] fn consume_mint_public_message(