From 841e6c75a06c9cac07aaeab7dd5f12e515a75d3d Mon Sep 17 00:00:00 2001 From: LHerskind Date: Wed, 13 Sep 2023 16:18:22 +0000 Subject: [PATCH 1/9] refactor: remove the native and private token from dapp_testing --- .../src/guides/dapp_testing.test.ts | 141 +++++++++++++----- 1 file changed, 100 insertions(+), 41 deletions(-) diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index f8adaec436b..fe2a860ae0b 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -5,13 +5,17 @@ import { CheatCodes, Fr, L2BlockL2Logs, + TxStatus, + computeMessageSecretHash, createAccount, createAztecRpcClient, getSandboxAccountsWallets, waitForSandbox, } from '@aztec/aztec.js'; import { toBigIntBE } from '@aztec/foundation/bigint-buffer'; -import { NativeTokenContract, PrivateTokenContract, TestContract } from '@aztec/noir-contracts/types'; +import { TestContract, TokenContract } from '@aztec/noir-contracts/types'; + +const { SANDBOX_URL = 'http://localhost:8080', ETHEREUM_HOST = 'http://localhost:8545' } = process.env; describe('guides/dapp/testing', () => { describe('on in-proc sandbox', () => { @@ -20,7 +24,7 @@ describe('guides/dapp/testing', () => { let stop: () => Promise; let owner: AccountWallet; let recipient: AccountWallet; - let token: PrivateTokenContract; + let token: TokenContract; beforeAll(async () => { // docs:start:in-proc-sandbox @@ -28,78 +32,95 @@ describe('guides/dapp/testing', () => { // docs:end:in-proc-sandbox owner = await createAccount(rpc); recipient = await createAccount(rpc); - token = await PrivateTokenContract.deploy(owner, 100n, owner.getAddress()).send().deployed(); + token = await TokenContract.deploy(owner).send().deployed(); + expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( + TxStatus.MINED, + ); }, 60_000); // docs:start:stop-in-proc-sandbox afterAll(() => stop()); // docs:end:stop-in-proc-sandbox - it('increases recipient funds on transfer', async () => { - expect(await token.methods.getBalance(recipient.getAddress()).view()).toEqual(0n); - await token.methods.transfer(20n, recipient.getAddress()).send().wait(); - expect(await token.methods.getBalance(recipient.getAddress()).view()).toEqual(20n); + it('increases recipient funds on mint', async () => { + expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(0n); + const secret = Fr.random(); + const secretHash = await computeMessageSecretHash(secret); + expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); + expect( + await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(), + ).toEqual(TxStatus.MINED); + expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); }); }); describe('on local sandbox', () => { beforeAll(async () => { - const { SANDBOX_URL = 'http://localhost:8080' } = process.env; const rpc = createAztecRpcClient(SANDBOX_URL); await waitForSandbox(rpc); }); // docs:start:sandbox-example describe('private token contract', () => { - const { SANDBOX_URL = 'http://localhost:8080' } = process.env; - let rpc: AztecRPC; let owner: AccountWallet; let recipient: AccountWallet; - let token: PrivateTokenContract; + let token: TokenContract; beforeEach(async () => { rpc = createAztecRpcClient(SANDBOX_URL); owner = await createAccount(rpc); recipient = await createAccount(rpc); - token = await PrivateTokenContract.deploy(owner, 100n, owner.getAddress()).send().deployed(); + token = await TokenContract.deploy(owner).send().deployed(); + expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( + TxStatus.MINED, + ); }, 30_000); - it('increases recipient funds on transfer', async () => { - expect(await token.methods.getBalance(recipient.getAddress()).view()).toEqual(0n); - await token.methods.transfer(20n, recipient.getAddress()).send().wait(); - expect(await token.methods.getBalance(recipient.getAddress()).view()).toEqual(20n); + it('increases recipient funds on mint', async () => { + expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(0n); + const secret = Fr.random(); + const secretHash = await computeMessageSecretHash(secret); + expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); + expect( + await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(), + ).toEqual(TxStatus.MINED); + expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); }); // docs:end:sandbox-example describe('private token contract with initial accounts', () => { - const { SANDBOX_URL = 'http://localhost:8080' } = process.env; - let rpc: AztecRPC; let owner: AccountWallet; let recipient: AccountWallet; - let token: PrivateTokenContract; + let token: TokenContract; beforeEach(async () => { // docs:start:use-existing-wallets rpc = createAztecRpcClient(SANDBOX_URL); [owner, recipient] = await getSandboxAccountsWallets(rpc); - token = await PrivateTokenContract.deploy(owner, 100n, owner.getAddress()).send().deployed(); + token = await TokenContract.deploy(owner).send().deployed(); + expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( + TxStatus.MINED, + ); // docs:end:use-existing-wallets }, 30_000); - it('increases recipient funds on transfer', async () => { - expect(await token.methods.getBalance(recipient.getAddress()).view()).toEqual(0n); - await token.methods.transfer(20n, recipient.getAddress()).send().wait(); - expect(await token.methods.getBalance(recipient.getAddress()).view()).toEqual(20n); + it('increases recipient funds on mint', async () => { + expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(0n); + const secret = Fr.random(); + const secretHash = await computeMessageSecretHash(secret); + expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); + expect( + await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(), + ).toEqual(TxStatus.MINED); + expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); }); describe('cheats', () => { - const { SANDBOX_URL = 'http://localhost:8080', ETHEREUM_HOST = 'http://localhost:8545' } = process.env; - let rpc: AztecRPC; let owner: AccountWallet; let testContract: TestContract; @@ -122,13 +143,10 @@ describe('guides/dapp/testing', () => { }); describe('assertions', () => { - const { SANDBOX_URL = 'http://localhost:8080', ETHEREUM_HOST = 'http://localhost:8545' } = process.env; - let rpc: AztecRPC; let owner: AccountWallet; let recipient: AccountWallet; - let token: PrivateTokenContract; - let nativeToken: NativeTokenContract; + let token: TokenContract; let cheats: CheatCodes; let ownerSlot: Fr; @@ -136,8 +154,19 @@ describe('guides/dapp/testing', () => { rpc = createAztecRpcClient(SANDBOX_URL); owner = await createAccount(rpc); recipient = await createAccount(rpc); - token = await PrivateTokenContract.deploy(owner, 100n, owner.getAddress()).send().deployed(); - nativeToken = await NativeTokenContract.deploy(owner, 100n, owner.getAddress()).send().deployed(); + token = await TokenContract.deploy(owner).send().deployed(); + expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( + TxStatus.MINED, + ); + const secret = Fr.random(); + const secretHash = await computeMessageSecretHash(secret); + expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( + TxStatus.MINED, + ); + expect((await token.methods.mint_private(secretHash, 100n).send().wait()).status).toEqual(TxStatus.MINED); + expect( + (await token.methods.redeem_shield({ address: owner.getAddress() }, 100n, secretHash).send().wait()).status, + ).toEqual(TxStatus.MINED); // docs:start:calc-slot cheats = await CheatCodes.create(ETHEREUM_HOST, rpc); @@ -157,16 +186,16 @@ describe('guides/dapp/testing', () => { it('checks public storage', async () => { // docs:start:public-storage - await nativeToken.methods.owner_mint_pub(owner.getAddress(), 100n).send().wait(); + await token.methods.mint_public({ address: owner.getAddress() }, 100n).send().wait(); const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(4n, owner.getAddress()); - const balance = await rpc.getPublicStorageAt(nativeToken.address, ownerPublicBalanceSlot); + const balance = await rpc.getPublicStorageAt(token.address, ownerPublicBalanceSlot); expect(toBigIntBE(balance!)).toEqual(100n); // docs:end:public-storage }); it('checks unencrypted logs', async () => { // docs:start:unencrypted-logs - const tx = await nativeToken.methods.owner_mint_pub(owner.getAddress(), 100n).send().wait(); + const tx = await token.methods.mint_public({ address: owner.getAddress() }, 100n).send().wait(); const logs = await rpc.getUnencryptedLogs(tx.blockNumber!, 1); const textLogs = L2BlockL2Logs.unrollLogs(logs).map(log => log.toString('ascii')); expect(textLogs).toEqual(['Coins minted']); @@ -175,22 +204,42 @@ describe('guides/dapp/testing', () => { it('asserts a local transaction simulation fails by calling simulate', async () => { // docs:start:local-tx-fails - const call = token.methods.transfer(200n, recipient.getAddress()); + const call = token.methods.transfer( + { address: owner.getAddress() }, + { address: recipient.getAddress() }, + 200n, + 0, + ); await expect(call.simulate()).rejects.toThrowError(/Balance too low/); // docs:end:local-tx-fails }); it('asserts a local transaction simulation fails by calling send', async () => { // docs:start:local-tx-fails-send - const call = token.methods.transfer(200n, recipient.getAddress()); + const call = token.methods.transfer( + { address: owner.getAddress() }, + { address: recipient.getAddress() }, + 200n, + 0, + ); await expect(call.send().wait()).rejects.toThrowError(/Balance too low/); // docs:end:local-tx-fails-send }); it('asserts a transaction is dropped', async () => { // docs:start:tx-dropped - const call1 = token.methods.transfer(80n, recipient.getAddress()); - const call2 = token.methods.transfer(50n, recipient.getAddress()); + const call1 = token.methods.transfer( + { address: owner.getAddress() }, + { address: recipient.getAddress() }, + 80n, + 0, + ); + const call2 = token.methods.transfer( + { address: owner.getAddress() }, + { address: recipient.getAddress() }, + 50n, + 0, + ); await call1.simulate(); await call2.simulate(); @@ -202,14 +251,24 @@ describe('guides/dapp/testing', () => { it('asserts a simulation for a public function call fails', async () => { // docs:start:local-pub-fails - const call = nativeToken.methods.transfer_pub(recipient.getAddress(), 1000n); + const call = token.methods.transfer_public( + { address: owner.getAddress() }, + { address: recipient.getAddress() }, + 1000n, + 0, + ); await expect(call.simulate()).rejects.toThrowError(/Balance too low/); // docs:end:local-pub-fails }); it('asserts a transaction with a failing public call is dropped (until we get public reverts)', async () => { // docs:start:pub-dropped - const call = nativeToken.methods.transfer_pub(recipient.getAddress(), 1000n); + const call = token.methods.transfer_public( + { address: owner.getAddress() }, + { address: recipient.getAddress() }, + 1000n, + 0, + ); await expect(call.send({ skipPublicSimulation: true }).wait()).rejects.toThrowError(/dropped/); // docs:end:pub-dropped }); From 50d03cd21fe8b7182a8cf5a027dc29b9b9ec719e Mon Sep 17 00:00:00 2001 From: LHerskind Date: Wed, 13 Sep 2023 16:39:52 +0000 Subject: [PATCH 2/9] fix: update storage slots --- .../end-to-end/src/guides/dapp_testing.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index fe2a860ae0b..be1fcd5475b 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -48,7 +48,7 @@ describe('guides/dapp/testing', () => { const secretHash = await computeMessageSecretHash(secret); expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); expect( - await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(), + (await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait()).status, ).toEqual(TxStatus.MINED); expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); @@ -84,7 +84,7 @@ describe('guides/dapp/testing', () => { const secretHash = await computeMessageSecretHash(secret); expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); expect( - await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(), + (await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait()).status, ).toEqual(TxStatus.MINED); expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); @@ -114,7 +114,7 @@ describe('guides/dapp/testing', () => { const secretHash = await computeMessageSecretHash(secret); expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); expect( - await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(), + (await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait()).status, ).toEqual(TxStatus.MINED); expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); @@ -170,8 +170,8 @@ describe('guides/dapp/testing', () => { // docs:start:calc-slot cheats = await CheatCodes.create(ETHEREUM_HOST, rpc); - // The balances mapping is defined on storage slot 1 and is indexed by user address - ownerSlot = cheats.aztec.computeSlotInMap(1n, owner.getAddress()); + // The balances mapping is defined on storage slot 3 and is indexed by user address + ownerSlot = cheats.aztec.computeSlotInMap(3n, owner.getAddress()); // docs:end:calc-slot }, 30_000); @@ -187,7 +187,7 @@ describe('guides/dapp/testing', () => { it('checks public storage', async () => { // docs:start:public-storage await token.methods.mint_public({ address: owner.getAddress() }, 100n).send().wait(); - const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(4n, owner.getAddress()); + const ownerPublicBalanceSlot = cheats.aztec.computeSlotInMap(6n, owner.getAddress()); const balance = await rpc.getPublicStorageAt(token.address, ownerPublicBalanceSlot); expect(toBigIntBE(balance!)).toEqual(100n); // docs:end:public-storage @@ -257,7 +257,7 @@ describe('guides/dapp/testing', () => { 1000n, 0, ); - await expect(call.simulate()).rejects.toThrowError(/Balance too low/); + await expect(call.simulate()).rejects.toThrowError(/Underflow/); // docs:end:local-pub-fails }); From ab181a0e996cdd9740c247384f615c9cd6dc8ab4 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Wed, 13 Sep 2023 16:52:55 +0000 Subject: [PATCH 3/9] fix: wrong order of parameters --- yarn-project/end-to-end/src/guides/dapp_testing.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index be1fcd5475b..c250e839fdf 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -163,7 +163,7 @@ describe('guides/dapp/testing', () => { expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( TxStatus.MINED, ); - expect((await token.methods.mint_private(secretHash, 100n).send().wait()).status).toEqual(TxStatus.MINED); + expect((await token.methods.mint_private(100n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); expect( (await token.methods.redeem_shield({ address: owner.getAddress() }, 100n, secretHash).send().wait()).status, ).toEqual(TxStatus.MINED); From 6c086f2c60ffcdb72873461e673e43a4a4dcc006 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 14 Sep 2023 08:42:50 +0000 Subject: [PATCH 4/9] fix: using secretHash instead of secret --- yarn-project/end-to-end/src/guides/dapp_testing.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index c250e839fdf..c70692a4814 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -165,7 +165,7 @@ describe('guides/dapp/testing', () => { ); expect((await token.methods.mint_private(100n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); expect( - (await token.methods.redeem_shield({ address: owner.getAddress() }, 100n, secretHash).send().wait()).status, + (await token.methods.redeem_shield({ address: owner.getAddress() }, 100n, secret).send().wait()).status, ).toEqual(TxStatus.MINED); // docs:start:calc-slot From f73a742ac2ec768b9a691f7bde7ba7d8bc35e1b7 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 14 Sep 2023 13:07:15 +0000 Subject: [PATCH 5/9] fix: add unencrypted log check --- .../src/guides/dapp_testing.test.ts | 16 ++++++------- .../contracts/test_contract/src/interface.nr | 24 +++++++++++++++++++ .../src/contracts/test_contract/src/main.nr | 11 +++++++++ 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index c70692a4814..2498394c3f3 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -146,6 +146,7 @@ describe('guides/dapp/testing', () => { let rpc: AztecRPC; let owner: AccountWallet; let recipient: AccountWallet; + let testContract: TestContract; let token: TokenContract; let cheats: CheatCodes; let ownerSlot: Fr; @@ -154,15 +155,13 @@ describe('guides/dapp/testing', () => { rpc = createAztecRpcClient(SANDBOX_URL); owner = await createAccount(rpc); recipient = await createAccount(rpc); + testContract = await TestContract.deploy(owner).send().deployed(); token = await TokenContract.deploy(owner).send().deployed(); expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( TxStatus.MINED, ); const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); - expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( - TxStatus.MINED, - ); expect((await token.methods.mint_private(100n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); expect( (await token.methods.redeem_shield({ address: owner.getAddress() }, 100n, secret).send().wait()).status, @@ -173,7 +172,7 @@ describe('guides/dapp/testing', () => { // The balances mapping is defined on storage slot 3 and is indexed by user address ownerSlot = cheats.aztec.computeSlotInMap(3n, owner.getAddress()); // docs:end:calc-slot - }, 30_000); + }, 60_000); it('checks private storage', async () => { // docs:start:private-storage @@ -193,12 +192,13 @@ describe('guides/dapp/testing', () => { // docs:end:public-storage }); - it('checks unencrypted logs', async () => { + it('checks unencrypted logs, [Kinda broken with current implementation]', async () => { // docs:start:unencrypted-logs - const tx = await token.methods.mint_public({ address: owner.getAddress() }, 100n).send().wait(); + const value = Fr.fromString('ef'); // Only 1 bytes will make its way in there :( so no larger stuff + const tx = await testContract.methods.emit_unencrypted(value).send().wait(); const logs = await rpc.getUnencryptedLogs(tx.blockNumber!, 1); - const textLogs = L2BlockL2Logs.unrollLogs(logs).map(log => log.toString('ascii')); - expect(textLogs).toEqual(['Coins minted']); + const log = L2BlockL2Logs.unrollLogs(logs)[0]; + expect(Fr.fromBuffer(log)).toEqual(value); // docs:end:unencrypted-logs }); diff --git a/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr b/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr index 6d03ba23bd0..1a11db0ac4f 100644 --- a/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr +++ b/yarn-project/noir-contracts/src/contracts/test_contract/src/interface.nr @@ -67,6 +67,18 @@ impl TestPrivateContextInterface { } + fn emit_unencrypted( + self, + context: &mut PrivateContext, + value: Field + ) { + let mut serialised_args = [0; 1]; + serialised_args[0] = value; + + context.call_public_function(self.address, 0x817a64cb, serialised_args) + } + + fn getPortalContractAddress( self, context: &mut PrivateContext, @@ -200,6 +212,18 @@ impl TestPublicContextInterface { } + fn emit_unencrypted( + self, + context: PublicContext, + value: Field + ) -> [Field; RETURN_VALUES_LENGTH] { + let mut serialised_args = [0; 1]; + serialised_args[0] = value; + + context.call_public_function(self.address, 0x817a64cb, serialised_args) + } + + fn isTimeEqual( self, context: PublicContext, 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 d2c29d6c6e8..1cfbb34248e 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 @@ -8,6 +8,7 @@ contract Test { get_public_key::get_public_key, context::get_portal_address, rand::rand, + logs::emit_unencrypted_log }, types::vec::BoundedVec, }; @@ -114,6 +115,16 @@ contract Test { } // docs:end:is-time-equal + // docs:start:emit_unencrypted_log + #[aztec(public)] + fn emit_unencrypted( + value: Field + ) -> Field { + let _hash = emit_unencrypted_log(value); + _hash[0] + } + // docs:end:emit_unencrypted_log + // Purely exists for testing unconstrained fn getRandom( kindaSeed: Field From dbae7f1f6f6714239f7351de796dce1910d9dc48 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 14 Sep 2023 14:19:59 +0000 Subject: [PATCH 6/9] fix: update refs in docs --- docs/docs/dev_docs/dapps/testing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/dev_docs/dapps/testing.md b/docs/docs/dev_docs/dapps/testing.md index ec59f1d7516..3551a435e69 100644 --- a/docs/docs/dev_docs/dapps/testing.md +++ b/docs/docs/dev_docs/dapps/testing.md @@ -97,7 +97,7 @@ We can have private transactions that work fine locally, but are dropped by the #### A public call fails locally -Public function calls can be caught failing locally similar to how we catch private function calls. For this example, we use a [`NativeTokenContract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr) instead of a private one. +Public function calls can be caught failing locally similar to how we catch private function calls. For this example, we use a [`TokenContract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr) instead of a private one. :::info Keep in mind that public function calls behave as in EVM blockchains, in that they are executed by the sequencer and not locally. Local simulation helps alert the user of a potential failure, but the actual execution path of a public function call will depend on when it gets mined. @@ -141,7 +141,7 @@ We can query the RPC server for all notes encrypted for a given user in a contra #### Querying public state -[Public state](../../concepts/foundation/state_model.md#public-state) behaves as a key-value store, much like in the EVM. This scenario is much more straightforward, in that we can directly query the target slot and get the result back as a buffer. Note that we use the [`NativeTokenContract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr) in this example, which defines a mapping of public balances on slot 4. +[Public state](../../concepts/foundation/state_model.md#public-state) behaves as a key-value store, much like in the EVM. This scenario is much more straightforward, in that we can directly query the target slot and get the result back as a buffer. Note that we use the [`TokenContract`](https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr) in this example, which defines a mapping of public balances on slot 6. #include_code public-storage /yarn-project/end-to-end/src/guides/dapp_testing.test.ts typescript From 371a6b2ea9fb24b6b477a3d3504cd9d146c45b24 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 14 Sep 2023 14:20:48 +0000 Subject: [PATCH 7/9] fix: delete native_token_contract --- yarn-project/noir-contracts/Nargo.toml | 1 - .../native_token_contract/Nargo.toml | 10 - .../native_token_contract/src/main.nr | 452 ------------------ 3 files changed, 463 deletions(-) delete mode 100644 yarn-project/noir-contracts/src/contracts/native_token_contract/Nargo.toml delete mode 100644 yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr diff --git a/yarn-project/noir-contracts/Nargo.toml b/yarn-project/noir-contracts/Nargo.toml index 9f6306f3b9a..6fd6bc4df17 100644 --- a/yarn-project/noir-contracts/Nargo.toml +++ b/yarn-project/noir-contracts/Nargo.toml @@ -9,7 +9,6 @@ members = [ "src/contracts/import_test_contract", "src/contracts/lending_contract", "src/contracts/multi_transfer_contract", - "src/contracts/native_token_contract", "src/contracts/non_native_token_contract", "src/contracts/parent_contract", "src/contracts/pending_commitments_contract", diff --git a/yarn-project/noir-contracts/src/contracts/native_token_contract/Nargo.toml b/yarn-project/noir-contracts/src/contracts/native_token_contract/Nargo.toml deleted file mode 100644 index 05cf9a92810..00000000000 --- a/yarn-project/noir-contracts/src/contracts/native_token_contract/Nargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "native_token_contract" -authors = [""] -compiler_version = "0.1" -type = "contract" - -[dependencies] -aztec = { path = "../../../../aztec-nr/aztec" } -value_note = { path = "../../../../aztec-nr/value-note"} -non_native = { path = "../non_native_token_contract"} \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr deleted file mode 100644 index 31dc5e610e5..00000000000 --- a/yarn-project/noir-contracts/src/contracts/native_token_contract/src/main.nr +++ /dev/null @@ -1,452 +0,0 @@ -// Testing token that can be bridged in and out. -// TODOS: -// - Add role based access control to mint functions -// - Add function for managing roles -// - Add public self-burn function for users to burn their own tokens -contract NativeToken { - // Libs - use dep::std::option::Option; - use dep::value_note::{ - balance_utils, - utils::{increment, decrement}, - value_note::{VALUE_NOTE_LEN, ValueNote, ValueNoteMethods}, - }; - use dep::std; - use dep::aztec::{ - constants_gen::GENERATOR_INDEX__SIGNATURE_PAYLOAD, - context::{PrivateContext, PublicContext, Context}, - note::{ - note_header::NoteHeader, - utils as note_utils, - }, - oracle::{ - compute_selector::compute_selector, - logs::emit_unencrypted_log, - }, - state_vars::{ - map::Map, - public_state::PublicState, - set::Set, - }, - types::type_serialisation::field_serialisation::{ - FieldSerialisationMethods, - FIELD_SERIALISED_LEN, - }, - auth::assert_valid_message_for, - }; - use dep::non_native::{ - hash::{get_mint_content_hash, get_withdraw_content_hash}, - transparent_note::{ - TransparentNote, - TransparentNoteMethods, - TRANSPARENT_NOTE_LEN, - }, - }; - - struct Storage { - balances: Map>, - total_supply: PublicState, - pending_shields: Set, - public_balances: Map>, - public_allowances: Map>>, - } - - impl Storage { - fn init(context: Context) -> pub Self { - Storage { - balances: Map::new( - context, - 1, // Storage slot - |context, slot| { - Set::new(context, slot, ValueNoteMethods) - }, - ), - total_supply: PublicState::new( - context, - 2, - FieldSerialisationMethods, - ), - pending_shields: Set::new(context, 3, TransparentNoteMethods), - public_balances: Map::new( - context, - 4, - |context, slot| { - PublicState::new( - context, - slot, - FieldSerialisationMethods, - ) - }, - ), - public_allowances: Map::new( - context, - 5, - |context, s1| { - Map::new( - context, - s1, - |context, s2| { - PublicState::new( - context, - s2, - FieldSerialisationMethods, - ) - }, - ) - }, - ), - } - } - } - - #[aztec(private)] - fn constructor( - initial_supply: Field, - owner: Field, - ) { - let storage = Storage::init(Context::private(&mut context)); - - let balance = storage.balances.at(owner); - increment(balance, initial_supply, owner); - } - - #[aztec(public)] - fn owner_mint_pub( - to: Field, - amount: Field, - ) -> Field { - let storage = Storage::init(Context::public(&mut context)); - let new_balance = storage.public_balances.at(to).read() + amount; - storage.public_balances.at(to).write(new_balance); - storage.total_supply.write(storage.total_supply.read() + amount); - let _hash = emit_unencrypted_log("Coins minted"); - - 1 - } - - #[aztec(public)] - fn owner_mint_priv( - amount: Field, - secret_hash: Field, - ) -> Field { - let storage = Storage::init(Context::public(&mut context)); - let pending_shields = storage.pending_shields; - - let mut note = TransparentNote::new(amount, secret_hash); - pending_shields.insert_from_public(&mut note); - - storage.total_supply.write(storage.total_supply.read() + amount); - - 1 - } - - // Mint Private Function - // This mint function differs to the typical token mint function as it only allows minting - // upon consuming valid messages from a token portal contract - #[aztec(private)] - fn mint( - amount: Field, - owner: Field, - // This field should be hidden - msg_key: Field, - secret: Field, - canceller: Field, - ) { - let storage = Storage::init(Context::private(&mut context)); - - let content_hash = get_mint_content_hash(amount, owner, canceller); - - // Get the l1 message from an oracle call - context.consume_l1_to_l2_message(inputs, msg_key, content_hash, secret); - - let balance = storage.balances.at(owner); - increment(balance, amount, owner); - } - - // Withdraws using user's private balance. - // @dev Destroys 2 of user's notes and sends a message to the L1 portal contract. That message can then be consumed - // by calling the `withdraw` function on the L1 portal contract (assuming the args are set correctly). - #[aztec(private)] - fn withdraw( - amount: Field, - sender: Field, - recipient: Field, // ethereum address in the field - callerOnL1: Field, // ethereum address that can call this function on the L1 portal (0x0 if anyone can call) - ) { - let storage = Storage::init(Context::private(&mut context)); - - let sender_balance = storage.balances.at(sender); - decrement(sender_balance, amount, sender); - - let content = get_withdraw_content_hash(amount, recipient, callerOnL1); - context.message_portal(content); - } - - // Mint Public Function - // This mint function differs to the typical token mint function as it only allows minting - // upon consuming valid messages from a token portal contract - #[aztec(public)] - fn mintPublic( - amount: Field, - owner_address: Field, - // This field should be hidden - msg_key: Field, - secret: Field, - canceller: Field, - ) -> Field { - let storage = Storage::init(Context::public(&mut context)); - let public_balances = storage.public_balances; - - let content_hash = get_mint_content_hash(amount, owner_address, canceller); - - // Consume message and emit nullifier - context.consume_l1_to_l2_message(msg_key, content_hash, secret); - - // Update the public balance - let recipient_balance = public_balances.at(owner_address); - let new_balance = recipient_balance.read() + amount; - recipient_balance.write(new_balance); - - // Push the return value into the context - new_balance - } - - // Withdraws using user's public balance. - #[aztec(public)] - fn withdrawPublic( - amount: Field, - recipient: Field, - callerOnL1: Field, // ethereum address that can call this function on the L1 portal (0x0 if anyone can call) - ) { - let storage = Storage::init(Context::public(&mut context)); - let public_balances = storage.public_balances; - - let sender = context.msg_sender(); - let sender_balance = public_balances.at(sender); - - let current_sender_balance: Field = sender_balance.read(); - - assert(current_sender_balance as u120 >= amount as u120); - let content = get_withdraw_content_hash(amount, recipient, callerOnL1); - - // Emit the l2 to l1 message - context.message_portal(content); - } - - #[aztec(public)] - fn approve( - spender: Field, - allowance: Field, - ) { - let storage = Storage::init(Context::public(&mut context)); - storage.public_allowances.at(context.msg_sender()).at(spender).write(allowance); - } - - #[aztec(public)] - fn transfer_pub( - to: Field, - amount: Field, - ) { - let storage = Storage::init(Context::public(&mut context)); - - // Decrease user's balance. - let sender = context.msg_sender(); - let sender_balance = storage.public_balances.at(sender); - let current_sender_balance: Field = sender_balance.read(); - assert(current_sender_balance as u120 >= amount as u120, "Balance too low"); - - let to_balance = storage.public_balances.at(to); - let current_to_balance: Field = to_balance.read(); - - // User has sufficient balance so we decrement it by `amount` - sender_balance.write(current_sender_balance - amount); - to_balance.write(current_to_balance + amount); - - let _hash = emit_unencrypted_log("Coins transferred"); - } - - #[aztec(public)] - fn transfer_from_pub( - from: Field, - to: Field, - amount: Field, - ) { - let storage = Storage::init(Context::public(&mut context)); - - // Decrease allowance - let allowance = storage.public_allowances.at(from).at(context.msg_sender()); - let current_allowance: Field = allowance.read(); - assert(current_allowance as u120 >= amount as u120); - allowance.write(current_allowance - amount); - - // Decrease user's balance. - let sender_balance = storage.public_balances.at(from); - let current_sender_balance: Field = sender_balance.read(); - assert(current_sender_balance as u120 >= amount as u120); - - let to_balance = storage.public_balances.at(to); - let current_to_balance: Field = to_balance.read(); - - // User has sufficient balance so we decrement it by `amount` - sender_balance.write(current_sender_balance - amount); - to_balance.write(current_to_balance + amount); - } - - // Transfers `amount` of tokens from `sender`'s private balance to a `recipient`. - // Note: Copied from PrivateToken - #[aztec(private)] - fn transfer( - from: Field, - to: Field, - amount: Field, - ) { - let storage = Storage::init(Context::private(&mut context)); - - // Gets the set of sender's notes and picks 2 of those. - let sender_balance = storage.balances.at(from); - decrement(sender_balance, amount, from); - - let balance = storage.balances.at(to); - increment(balance, amount, to); - } - - // Shield creates a way for a user to move tokens from the public context into the private context. - #[aztec(public)] - fn shield( - amount: Field, - secretHash: Field, - ) { - let storage = Storage::init(Context::public(&mut context)); - let public_balances = storage.public_balances; - let pending_shields = storage.pending_shields; - - // Decrease user's balance. - let sender = context.msg_sender(); - let sender_balance = public_balances.at(sender); - let current_sender_balance: Field = sender_balance.read(); - - assert(current_sender_balance as u120 >= amount as u120); - - // User has sufficient balance so we decrement it by `amount` - let _void1 = sender_balance.write(current_sender_balance - amount); - - // Create a commitment to the "amount" using the "secretHash" - // and insert it into the set of "pending_shields" and therefore - // (eventually) the private data tree. - let mut note = TransparentNote::new(amount, secretHash); - pending_shields.insert_from_public(&mut note); - } - - // The shield function takes a public balance, and creates a commitment containing the amount of tokens - // in the private context. - #[aztec(private)] - fn redeemShield( - amount: Field, - secret: Field, - owner: Field, - ) { - let storage = Storage::init(Context::private(&mut context)); - let pending_shields = storage.pending_shields; - - let mut public_note = TransparentNote::new_from_secret(amount, secret); - - // Ensure that the note exists in the tree and remove it. - pending_shields.assert_contains_and_remove_publicly_created(&mut public_note); - - // Mint the tokens - let balance = storage.balances.at(owner); - increment(balance, amount, owner); - } - - #[aztec(private)] - fn unshieldTokens( - from: Field, - to: Field, - amount: Field, - ) { - let storage = Storage::init(Context::private(&mut context)); - - // If `from != sender` then we use the is_valid function to check that the message is approved. - if (from != context.msg_sender()) { - // Compute the message hash, should follow eip-712 more here. - // @todo @lherskind, probably need a separate generator index and address of the - // @todo @lherskind Currently this can be used multiple times since it is not nullified. - // We can do a simple nullifier to handle that in here. Spends only 32 bytes onchain. - // @todo @LHerskind Is to be solved as part of https://github.com/AztecProtocol/aztec-packages/issues/1743 - let message_field: Field = std::hash::pedersen_with_separator([ - compute_selector("unshieldTokens(Field,Field,Field)"), - from, - to, - amount - ], - GENERATOR_INDEX__SIGNATURE_PAYLOAD - )[0]; - - assert_valid_message_for(&mut context, from, message_field); - } - - // Reduce user balance - let sender_balance = storage.balances.at(from); - decrement(sender_balance, amount, from); - - // enqueue a public function to perform the public state update. - let thisAddress = context.this_address(); - - let addUnshieldedBalance = compute_selector("addUnshieldedBalance(Field,Field)"); - let _callStackItem1 = context.call_public_function(thisAddress, addUnshieldedBalance, [amount, to]); - } - - #[aztec(public)] - internal fn addUnshieldedBalance( - amount: Field, - to: Field, - ) { - let storage = Storage::init(Context::public(&mut context)); - - let to_balance = storage.public_balances.at(to); - let current_balance = to_balance.read(); - let new_balance = current_balance + amount; - to_balance.write(new_balance); - } - - unconstrained fn balance_of( - owner: Field, - ) -> Field { - let storage = Storage::init(Context::none()); - let owner_balance = storage.balances.at(owner); - - 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 { contract_address, nonce, storage_slot }; - if (storage_slot == 3) { - note_utils::compute_note_hash_and_nullifier(TransparentNoteMethods, note_header, preimage) - } else { - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } - } - - unconstrained fn total_supply() -> Field { - let storage = Storage::init(Context::none()); - storage.total_supply.read() - } - - unconstrained fn public_balance_of( - owner: Field, - ) -> Field { - let storage = Storage::init(Context::none()); - storage.public_balances.at(owner).read() - } - - unconstrained fn public_allowance( - owner: Field, - spender: Field, - ) -> Field { - let storage = Storage::init(Context::none()); - storage.public_allowances.at(owner).at(spender).read() - } -} From 20ee877ab35b0abe8ef897e2bcc23457c2127abc Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 14 Sep 2023 14:39:31 +0000 Subject: [PATCH 8/9] fix: remove native token abi from cli_docs_sandbox --- yarn-project/end-to-end/src/cli_docs_sandbox.test.ts | 1 - 1 file changed, 1 deletion(-) 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 204e163fa53..61c241ba411 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 @@ -90,7 +90,6 @@ EscrowContractAbi ImportTestContractAbi LendingContractAbi MultiTransferContractAbi -NativeTokenContractAbi NonNativeTokenContractAbi ParentContractAbi PendingCommitmentsContractAbi From e284b9449c2da341c649dfbec52cab049544b505 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 15 Sep 2023 11:36:33 +0000 Subject: [PATCH 9/9] refactor: remove expects in dapp_teesting for status --- .../src/guides/dapp_testing.test.ts | 41 ++++++------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index 2498394c3f3..f0aaf2d38d2 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -5,7 +5,6 @@ import { CheatCodes, Fr, L2BlockL2Logs, - TxStatus, computeMessageSecretHash, createAccount, createAztecRpcClient, @@ -33,9 +32,7 @@ describe('guides/dapp/testing', () => { owner = await createAccount(rpc); recipient = await createAccount(rpc); token = await TokenContract.deploy(owner).send().deployed(); - expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( - TxStatus.MINED, - ); + await token.methods._initialize({ address: owner.getAddress() }).send().wait(); }, 60_000); // docs:start:stop-in-proc-sandbox @@ -46,10 +43,8 @@ describe('guides/dapp/testing', () => { expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(0n); const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); - expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); - expect( - (await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait()).status, - ).toEqual(TxStatus.MINED); + await token.methods.mint_private(20n, secretHash).send().wait(); + await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(); expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); }); @@ -73,19 +68,15 @@ describe('guides/dapp/testing', () => { owner = await createAccount(rpc); recipient = await createAccount(rpc); token = await TokenContract.deploy(owner).send().deployed(); - expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( - TxStatus.MINED, - ); + await token.methods._initialize({ address: owner.getAddress() }).send().wait(); }, 30_000); it('increases recipient funds on mint', async () => { expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(0n); const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); - expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); - expect( - (await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait()).status, - ).toEqual(TxStatus.MINED); + await token.methods.mint_private(20n, secretHash).send().wait(); + await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(); expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); }); @@ -102,9 +93,7 @@ describe('guides/dapp/testing', () => { rpc = createAztecRpcClient(SANDBOX_URL); [owner, recipient] = await getSandboxAccountsWallets(rpc); token = await TokenContract.deploy(owner).send().deployed(); - expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( - TxStatus.MINED, - ); + await token.methods._initialize({ address: owner.getAddress() }).send().wait(); // docs:end:use-existing-wallets }, 30_000); @@ -112,10 +101,8 @@ describe('guides/dapp/testing', () => { expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(0n); const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); - expect((await token.methods.mint_private(20n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); - expect( - (await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait()).status, - ).toEqual(TxStatus.MINED); + await token.methods.mint_private(20n, secretHash).send().wait(); + await token.methods.redeem_shield({ address: recipient.getAddress() }, 20n, secret).send().wait(); expect(await token.methods.balance_of_private({ address: recipient.getAddress() }).view()).toEqual(20n); }); }); @@ -157,15 +144,11 @@ describe('guides/dapp/testing', () => { recipient = await createAccount(rpc); testContract = await TestContract.deploy(owner).send().deployed(); token = await TokenContract.deploy(owner).send().deployed(); - expect((await token.methods._initialize({ address: owner.getAddress() }).send().wait()).status).toEqual( - TxStatus.MINED, - ); + await token.methods._initialize({ address: owner.getAddress() }).send().wait(); const secret = Fr.random(); const secretHash = await computeMessageSecretHash(secret); - expect((await token.methods.mint_private(100n, secretHash).send().wait()).status).toEqual(TxStatus.MINED); - expect( - (await token.methods.redeem_shield({ address: owner.getAddress() }, 100n, secret).send().wait()).status, - ).toEqual(TxStatus.MINED); + await token.methods.mint_private(100n, secretHash).send().wait(); + await token.methods.redeem_shield({ address: owner.getAddress() }, 100n, secret).send().wait(); // docs:start:calc-slot cheats = await CheatCodes.create(ETHEREUM_HOST, rpc);