From e0de7c662b10b30c0ef926964660b5640b23ad93 Mon Sep 17 00:00:00 2001 From: Rahul Kothari Date: Mon, 10 Jul 2023 17:12:13 +0000 Subject: [PATCH 01/14] add multi transfer (initial version) simplify test. name fix. Use non-deterministic unenc log in zk token contract Add multi-transfer test to cci. Fix e2e build but multi transfer fails. Fix batchTx call. Co-authored-by: Jean M fix multi test compilation contracts compile. Fix e2e multi compilation. minor syntax change. minor syntax change. contracts compile. Fix e2e test compilation. fix multi contract. Fix e2e multi test compilation. Remove files that must be gitignored. --- .circleci/config.yml | 13 ++ .../end-to-end/src/e2e_multi_transfer.test.ts | 109 ++++++++++++ .../multi_transfer_contract/Nargo.toml | 9 + .../multi_transfer_contract/src/main.nr | 117 +++++++++++++ .../src/main.nr | 52 +++++- .../src/examples/multi_transfer_contract.json | 93 ++++++++++ .../src/note/note_getter_options.nr | 13 ++ .../noir-libs/value-note/src/filter.nr | 4 + .../noir-libs/value-note/src/utils.nr | 163 ++++++++++++++++++ 9 files changed, 572 insertions(+), 1 deletion(-) create mode 100644 yarn-project/end-to-end/src/e2e_multi_transfer.test.ts create mode 100644 yarn-project/noir-contracts/src/contracts/multi_transfer_contract/Nargo.toml create mode 100644 yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr create mode 100644 yarn-project/noir-contracts/src/examples/multi_transfer_contract.json diff --git a/.circleci/config.yml b/.circleci/config.yml index 7d83ab35e400..e64f5d7df927 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -726,6 +726,17 @@ jobs: command: ./scripts/cond_run_script end-to-end $JOB_NAME ./scripts/run_tests_local e2e_private_token_contract.test.ts working_directory: yarn-project/end-to-end + e2e-multi-transfer-contract: + docker: + - image: aztecprotocol/alpine-build-image + resource_class: small + steps: + - *checkout + - *setup_env + - run: + name: "Test" + command: cond_spot_run_tests end-to-end e2e_multi_transfer.test.ts + e2e-block-building: machine: image: ubuntu-2004:202010-01 @@ -1312,6 +1323,7 @@ workflows: - e2e-deploy-contract: *e2e_test - e2e-lending-contract: *e2e_test - e2e-zk-token-contract: *e2e_test + - e2e-multi-transfer-contract: *e2e_test - e2e-block-building: *e2e_test - e2e-nested-contract: *e2e_test - e2e-non-contract-account: *e2e_test @@ -1339,6 +1351,7 @@ workflows: - e2e-deploy-contract - e2e-lending-contract - e2e-zk-token-contract + - e2e-multi-transfer-contract - e2e-block-building - e2e-nested-contract - e2e-non-contract-account 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 new file mode 100644 index 000000000000..6783e32b9390 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -0,0 +1,109 @@ +import { AztecNodeService } from '@aztec/aztec-node'; +import { AztecRPCServer } from '@aztec/aztec-rpc'; +import { AztecAddress, Contract, Fr, Wallet } from '@aztec/aztec.js'; +import { DebugLogger } from '@aztec/foundation/log'; +import { PrivateTokenAirdropContract } from '@aztec/noir-contracts/types'; +import { MultiTransferContract } from '@aztec/noir-contracts/types'; +import { AztecRPC } from '@aztec/types'; + +import { + expectUnencryptedLogsFromLastBlockToBe, + expectsNumOfEncryptedLogsInTheLastBlockToBe, + setup, +} from './fixtures/utils.js'; + +describe('multi-transfer payments', () => { + const numberOfAccounts = 6; + + let aztecNode: AztecNodeService | undefined; + let aztecRpcServer: AztecRPC; + let wallet: Wallet; + let accounts: AztecAddress[]; + let logger: DebugLogger; + let ownerAddress: AztecAddress; + const recipients: AztecAddress[] = []; + + let zkTokenContract: PrivateTokenAirdropContract; + let multiTransferContract: MultiTransferContract; + + beforeEach(async () => { + ({ aztecNode, aztecRpcServer, accounts, logger, wallet } = await setup(numberOfAccounts + 1)); // 1st being the `owner` + ownerAddress = accounts[0]; + + for (let i = 1; i < accounts.length; i++) { + const account = accounts[i]; + recipients.push(account); + } + }, 100_000); + + afterEach(async () => { + await aztecNode?.stop(); + if (aztecRpcServer instanceof AztecRPCServer) { + await aztecRpcServer?.stop(); + } + }); + + 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); + }; + + it('12 transfers per transactions should work', async () => { + const initialNote = 1000n; + + logger(`Deploying zk token contract...`); + await deployZkTokenContract(initialNote, ownerAddress); + logger(`Deploying multi-transfer contract...`); + await deployMultiTransferContract(); + + logger(`self batchTransfer()`); + const batchTransferTx = zkTokenContract.methods + .batchTransfer(ownerAddress, [400n, 300n, 200n], [ownerAddress, ownerAddress, ownerAddress], 0, 0) + .send({ origin: ownerAddress }); + await batchTransferTx.isMined(); + const batchTransferTxReceipt = await batchTransferTx.getReceipt(); + logger(`consumption Receipt status: ${batchTransferTxReceipt.status}`); + await expectBalance(zkTokenContract, ownerAddress, initialNote); + await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1); + await expectUnencryptedLogsFromLastBlockToBe(aztecNode, ['Balance set in constructor']); + + const amounts: bigint[] = [50n, 50n, 50n, 20n, 20n, 20n, 15n, 15n, 15n, 30n, 30n, 30n]; + const amountSum = amounts.reduce((a, b) => a + b, 0n); + + logger(`multiTransfer()...`); + const multiTransferTx = multiTransferContract.methods + .multiTransfer( + zkTokenContract.address.toField(), + recipients.concat(recipients), + amounts, + ownerAddress, + Fr.fromBuffer(zkTokenContract.methods.batchTransfer.selector), + ) + .send({ origin: ownerAddress }); + await multiTransferTx.isMined(); + const multiTransferTxReceipt = await multiTransferTx.getReceipt(); + logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); + await expectBalance(zkTokenContract, ownerAddress, initialNote - amountSum); + await expectBalance(zkTokenContract, recipients[0], amounts[0] + amounts[numberOfAccounts]); + await expectBalance(zkTokenContract, recipients[1], amounts[1] + amounts[numberOfAccounts + 1]); + await expectBalance(zkTokenContract, recipients[2], amounts[2] + amounts[numberOfAccounts + 2]); + await expectBalance(zkTokenContract, recipients[3], amounts[3] + amounts[numberOfAccounts + 3]); + await expectBalance(zkTokenContract, recipients[4], amounts[4] + amounts[numberOfAccounts + 4]); + await expectBalance(zkTokenContract, recipients[5], amounts[5] + amounts[numberOfAccounts + 5]); + await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1); + await expectUnencryptedLogsFromLastBlockToBe(aztecNode, ['Balance set in constructor']); + }, 240_000); +}); 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 new file mode 100644 index 000000000000..8d56210c1897 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "multi_transfer_contract" +authors = [""] +compiler_version = "0.1" +type = "bin" + +[dependencies] +aztec = { path = "../../../../noir-libs/noir-aztec" } +value_note = { path = "../../../../noir-libs/value-note"} 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 new file mode 100644 index 000000000000..574bca962260 --- /dev/null +++ b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/src/main.nr @@ -0,0 +1,117 @@ +// Demonstrates how to send a message to a portal contract on L1. We use Uniswap here as it's the most typical example. +contract MultiTransfer { + use dep::aztec::abi; + use dep::aztec::abi::PrivateContextInputs; + use dep::aztec::abi::PublicContextInputs; + use dep::aztec::context::Context; + use dep::aztec::oracle::public_call; + use dep::aztec::private_call_stack_item::PrivateCallStackItem; + use dep::aztec::public_call_stack_item::PublicCallStackItem; + use dep::aztec::types::point::Point; + fn constructor( + inputs: PrivateContextInputs + ) -> distinct pub abi::PrivateCircuitPublicInputs { + Context::new(inputs, 0).finish() + } + + fn multiTransfer( + inputs: PrivateContextInputs, + asset: Field, // Asset to distribute + addresses: [Field; 12], // Addresses to distribute to + amounts: [Field; 12], // Amounts to distribute + owner: Field, // Owner of the asset + batch_transfer_selector: Field, // Function selector for transfer + ) -> distinct pub abi::PrivateCircuitPublicInputs { + let mut context = Context::new(inputs, abi::hash_args([ + asset, + addresses[0], + addresses[1], + addresses[2], + addresses[3], + addresses[4], + addresses[5], + addresses[6], + addresses[7], + addresses[8], + addresses[9], + addresses[10], + addresses[11], + amounts[0], + amounts[1], + amounts[2], + amounts[3], + amounts[4], + amounts[5], + amounts[6], + amounts[7], + amounts[8], + amounts[9], + amounts[10], + amounts[11], + owner, + batch_transfer_selector + ])); + + // First batch transfer call + let return_values_1 = context.call_private_function(asset, batch_transfer_selector, [ + owner, + amounts[0], + amounts[1], + amounts[2], + addresses[0], + addresses[1], + addresses[2], + 0 as Field, + 0 as Field, + ]); + let result1 = return_values_1[0]; + context.return_values.push(result1); + + // Second batch transfer call + let return_values_2 = context.call_private_function(asset, batch_transfer_selector, [ + owner, + amounts[3], + amounts[4], + amounts[5], + addresses[3], + addresses[4], + addresses[5], + 1 as Field, + 1 as Field, + ]); + let result2 = return_values_2[0]; + context.return_values.push(result2); + + // Third batch transfer call + let return_values_3 = context.call_private_function(asset, batch_transfer_selector, [ + owner, + amounts[6], + amounts[7], + amounts[8], + addresses[6], + addresses[7], + addresses[8], + 2 as Field, + 2 as Field, + ]); + let result3 = return_values_3[0]; + context.return_values.push(result3); + + // Fourth batch transfer call + let return_values_4 = context.call_private_function(asset, batch_transfer_selector, [ + owner, + amounts[9], + amounts[10], + amounts[11], + addresses[9], + addresses[10], + addresses[11], + 3 as Field, + 3 as Field, + ]); + let result4 = return_values_4[0]; + context.return_values.push(result4); + + context.finish() + } +} \ No newline at end of file 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 index 6f8427ff41fb..bebee77e1ec2 100644 --- 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 @@ -5,7 +5,7 @@ contract PrivateTokenAirdrop { // Libs use dep::value_note::{ balance_utils, - utils::{send_note, spend_notes}, + utils::{send_note, spend_notes, send_notes, spend_one_note}, value_note::{VALUE_NOTE_LEN, ValueNoteMethods}, }; @@ -17,6 +17,7 @@ contract PrivateTokenAirdrop { utils as note_utils, }; use dep::aztec::log::emit_unencrypted_log; + use dep::aztec::oracle::debug_log::debug_log_format; use crate::storage::Storage; use crate::claim_note::{ClaimNote, ClaimNoteMethods}; @@ -148,6 +149,55 @@ contract PrivateTokenAirdrop { context.finish() } + // Transfers `amounts` of tokens from `sender` to 3 `recipients`. Aztec only requires 4 notes per tx - 1 new note for sender's new balance and 1 note each for 3 recipients (for their new balance) + fn batchTransfer( + //*********************************/ + // Should eventually be hidden: + inputs: PrivateContextInputs, + //*********************************/ + sender: Field, + amounts: [Field; 3], + recipients: [Field; 3], + spend_note_offset: Field, + spend_note_index: Field, + ) -> distinct pub abi::PrivateCircuitPublicInputs { + let storage = Storage::init(); + let mut context = Context::new(inputs, abi::hash_args([ + sender, + amounts[0], + amounts[1], + amounts[2], + recipients[0], + recipients[1], + recipients[2], + spend_note_offset, + spend_note_index + ])); + + assert(spend_note_index as u64 < 4); + + // Gets the set of sender's notes and picks first 4 of those. + let sender_balance = storage.balances.at(sender); + let total = amounts[0] + amounts[1] + amounts[2]; + spend_one_note(&mut context, sender_balance, total, sender, spend_note_offset, spend_note_index); + + // Creates new note for the recipient. + let recipient1_balance = storage.balances.at(recipients[0]); + let recipient2_balance = storage.balances.at(recipients[1]); + let recipient3_balance = storage.balances.at(recipients[2]); + + send_notes(&mut context, [recipient1_balance, recipient2_balance, recipient3_balance], amounts, recipients); + + // 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(&mut context, context.new_commitments.storage[0]); + + // Return private circuit public inputs. All private functions need to return this as it is part of the input of the private kernel.. + context.finish() + } + // Helper function to get the balance of a user ("unconstrained" is a Noir alternative of Solidity's "view" function). unconstrained fn getBalance( owner: Field, diff --git a/yarn-project/noir-contracts/src/examples/multi_transfer_contract.json b/yarn-project/noir-contracts/src/examples/multi_transfer_contract.json new file mode 100644 index 000000000000..9c280a3c5d8a --- /dev/null +++ b/yarn-project/noir-contracts/src/examples/multi_transfer_contract.json @@ -0,0 +1,93 @@ +{ + "name": "MultiTransfer", + "functions": [ + { + "name": "constructor", + "functionType": "secret", + "parameters": [], + "returnTypes": [], + "bytecode": "H4sIAAAAAAAA/9XaV0/jQBiF4ZAFthd6773PxHZib2XZ3ivbC2Q3/P+fQI5IJMQtJxfvSJbtm+g8UWLPfPP9K5VK+6WT0dU8ys2ju3Xdvu85c9/buj492ve7rXMSqmnaqFUaMYmHoVLU8yykWb2axzxmefa/kidJI0/zWlEvaqGIadKIR1mRHIWT0Xfqs8I5Rydz9kNyDkByDkJyDkFyDkNyjkByjkJyjkFyjkNyTkByTkJyTkFyTkNyzkByzkJyzkFyzkNyLkByLkJyLkFyLkNyrkByrkJyrkFyrkNybkBybkJybkFybkNy7kByBkjO2KGc5TM5w/lG7DKaKxBz2WhOIOYLRnMKMXcbzRnE3GM0VyHmXqO5BjH3Gc05xNxvNBcQ84DRfBtiHjSa70DMQ0bzXYh52Gi+BzGPGM33IeZRo/kBxDxmNO9CzONG80OIecJo3oOYJ43mRxDzlNH8GGKeNpqfQMwzRvNTiHnWaH4GMc8Zzc8h5nmj+QXEvGA0v4SYF43mVxDzktH8GmJeNprfQMwrRvNbiHnVaH4HMa8Zze8h5nWj+QPEvGE0f4SYN43mTxDzltH8GWLeNpr3IeYdo/kLxByM5q8QczSav0HMF43m7xDzJaP5B8R82Wj+CTFfMZp/QcxXjebfEPM1o/kPxHzdaP4LMd8wmg8g5ptG8yHEfMtornfAHFtn9YarV1q9w+qlVW+pei3Ve6hePPWmqVdLvUvq5VFvS7vXQ70Ae81De8XaO9VeovbWtNekvRftRag2r1q1areqZaq2p1qXaj+qhag2oLWy1o5aS2ltobm25p6ai2luone13l16luvZpv/6QfPQb0HfzTEa8Kz8bEUAAA==", + "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f" + }, + { + "name": "multiTransfer", + "functionType": "secret", + "parameters": [ + { + "name": "asset", + "type": { + "kind": "field" + }, + "visibility": "private" + }, + { + "name": "addresses", + "type": { + "kind": "array", + "length": 12, + "type": { + "kind": "struct", + "fields": [ + { + "name": "x", + "type": { + "kind": "field" + } + }, + { + "name": "y", + "type": { + "kind": "field" + } + } + ] + } + }, + "visibility": "private" + }, + { + "name": "amounts", + "type": { + "kind": "array", + "length": 12, + "type": { + "kind": "field" + } + }, + "visibility": "private" + }, + { + "name": "owner", + "type": { + "kind": "struct", + "fields": [ + { + "name": "x", + "type": { + "kind": "field" + } + }, + { + "name": "y", + "type": { + "kind": "field" + } + } + ] + }, + "visibility": "private" + }, + { + "name": "batchTransferFnSelector", + "type": { + "kind": "field" + }, + "visibility": "private" + } + ], + "returnTypes": [], + "bytecode": "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", + "verificationKey": "0000000200000800000000740000000f00000003515f3109623eb3c25aa5b16a1a79fd558bac7a7ce62c4560a8c537c77ce80dd339128d1d37b6582ee9e6df9567efb64313471dfa18f520f9ce53161b50dbf7731bc5f900000003515f322bc4cce83a486a92c92fd59bd84e0f92595baa639fc2ed86b00ffa0dfded2a092a669a3bdb7a273a015eda494457cc7ed5236f26cee330c290d45a33b9daa94800000003515f332729426c008c085a81bd34d8ef12dd31e80130339ef99d50013a89e4558eee6d0fa4ffe2ee7b7b62eb92608b2251ac31396a718f9b34978888789042b790a30100000003515f342be6b6824a913eb7a57b03cb1ee7bfb4de02f2f65fe8a4e97baa7766ddb353a82a8a25c49dc63778cd9fe96173f12a2bc77f3682f4c4448f98f1df82c75234a100000003515f351f85760d6ab567465aadc2f180af9eae3800e6958fec96aef53fd8a7b195d7c000c6267a0dd5cfc22b3fe804f53e266069c0e36f51885baec1e7e67650c62e170000000c515f41524954484d455449430d9d0f8ece2aa12012fa21e6e5c859e97bd5704e5c122064a66051294bc5e04213f61f54a0ebdf6fee4d4a6ecf693478191de0c2899bcd8e86a636c8d3eff43400000003515f43224a99d02c86336737c8dd5b746c40d2be6aead8393889a76a18d664029096e90f7fe81adcc92a74350eada9622ac453f49ebac24a066a1f83b394df54dfa0130000000c515f46495845445f42415345060e8a013ed289c2f9fd7473b04f6594b138ddb4b4cf6b901622a14088f04b8d2c83ff74fce56e3d5573b99c7b26d85d5046ce0c6559506acb7a675e7713eb3a00000007515f4c4f4749430721a91cb8da4b917e054f72147e1760cfe0ef3d45090ac0f4961d84ec1996961a25e787b26bd8b50b1a99450f77a424a83513c2b33af268cd253b0587ff50c700000003515f4d05dbd8623b8652511e1eb38d38887a69eceb082f807514f09e127237c5213b401b9325b48c6c225968002318095f89d0ef9cf629b2b7f0172e03bc39aacf6ed800000007515f52414e474504b57a3805e41df328f5ca9aefa40fad5917391543b7b65c6476e60b8f72e9ad07c92f3b3e11c8feae96dedc4b14a6226ef3201244f37cfc1ee5b96781f48d2b000000075349474d415f3125001d1954a18571eaa007144c5a567bb0d2be4def08a8be918b8c05e3b27d312c59ed41e09e144eab5de77ca89a2fd783be702a47c951d3112e3de02ce6e47c000000075349474d415f3223994e6a23618e60fa01c449a7ab88378709197e186d48d604bfb6931ffb15ad11c5ec7a0700570f80088fd5198ab5d5c227f2ad2a455a6edeec024156bb7beb000000075349474d415f3300cda5845f23468a13275d18bddae27c6bb189cf9aa95b6a03a0cb6688c7e8d829639b45cf8607c525cc400b55ebf90205f2f378626dc3406cc59b2d1b474fba000000075349474d415f342d299e7928496ea2d37f10b43afd6a80c90a33b483090d18069ffa275eedb2fc2f82121e8de43dc036d99b478b6227ceef34248939987a19011f065d8b5cef5c0000000010000000000000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f" + } + ] +} diff --git a/yarn-project/noir-libs/noir-aztec/src/note/note_getter_options.nr b/yarn-project/noir-libs/noir-aztec/src/note/note_getter_options.nr index 6ed9d5ac9722..4599633f6b1e 100644 --- a/yarn-project/noir-libs/noir-aztec/src/note/note_getter_options.nr +++ b/yarn-project/noir-libs/noir-aztec/src/note/note_getter_options.nr @@ -61,4 +61,17 @@ impl NoteGetterOptions { filter_args, } } + + fn with_filter_and_offset( + filter: fn ([Option; MAX_READ_REQUESTS_PER_CALL], P) -> [Option; S], + filter_args: P, + offset: u32, + ) -> Self { + NoteGetterOptions { + sort_by: [Sort::nada(); N], + offset, + filter, + filter_args, + } + } } diff --git a/yarn-project/noir-libs/value-note/src/filter.nr b/yarn-project/noir-libs/value-note/src/filter.nr index 04c7488dd321..27007cce1914 100644 --- a/yarn-project/noir-libs/value-note/src/filter.nr +++ b/yarn-project/noir-libs/value-note/src/filter.nr @@ -6,6 +6,10 @@ fn get_2_notes

(notes: [Option; MAX_READ_REQUESTS_PER_CALL], _x: P) [notes[0], notes[1]] } +fn get_4_notes

(notes: [Option; MAX_READ_REQUESTS_PER_CALL], _x: P) -> [Option; 4] { + [notes[0], notes[1], notes[2], notes[3]] +} + fn get_all_notes

(notes: [Option; MAX_READ_REQUESTS_PER_CALL], _x: P) -> [Option; MAX_READ_REQUESTS_PER_CALL] { notes } \ No newline at end of file diff --git a/yarn-project/noir-libs/value-note/src/utils.nr b/yarn-project/noir-libs/value-note/src/utils.nr index 974ae8d8c5e2..b1d30d1a6415 100644 --- a/yarn-project/noir-libs/value-note/src/utils.nr +++ b/yarn-project/noir-libs/value-note/src/utils.nr @@ -6,8 +6,10 @@ use dep::aztec::state_vars::set::Set; use dep::aztec::types::point::Point; use crate::{ filter::get_2_notes, + filter::get_4_notes, value_note::{ValueNote, VALUE_NOTE_LEN}, }; +use dep::aztec::oracle::debug_log::debug_log_format; fn spend_notes( context: &mut PrivateContext, @@ -57,6 +59,100 @@ fn spend_notes( ); } +fn spend_one_note( + context: &mut Context, + balance: Set, + amount: Field, + owner: Field, + note_offset: Field, + spend_note_index: Field, +) { + let options = NoteGetterOptions::with_filter_and_offset(get_4_notes, 0, note_offset as u32); + let maybe_notes = balance.get_notes(context, options); + + let note0 = maybe_notes[0].unwrap_or(ValueNote::dummy()); + let note1 = maybe_notes[1].unwrap_or(ValueNote::dummy()); + let note2 = maybe_notes[2].unwrap_or(ValueNote::dummy()); + let note3 = maybe_notes[3].unwrap_or(ValueNote::dummy()); + + assert(spend_note_index as u64 < 4); + + // Ensure the notes are actually owned by the owner (to prevent user from generating a valid proof while + // spending someone else's notes). + if maybe_notes[0].is_some() { + assert(owner == note0.owner); + } + if maybe_notes[1].is_some() { + assert(owner == note1.owner); + } + if maybe_notes[2].is_some() { + assert(owner == note2.owner); + } + if maybe_notes[3].is_some() { + assert(owner == note3.owner); + } + + debug_log_format("i1: v = {0}, o = {1}", [note0.value, note0.owner]); + debug_log_format("i2: v = {0}, o = {1}", [note1.value, note1.owner]); + debug_log_format("i3: v = {0}, o = {1}", [note2.value, note2.owner]); + debug_log_format("i4: v = {0}, o = {1}", [note3.value, note3.owner]); + + // Checks that the first notes is greater than or equal to the total amount being transferred. + let mut note_value = 0; + if spend_note_index == 0 { + note_value = note0.value; + } else if spend_note_index == 1 { + note_value = note1.value; + } else if spend_note_index == 2 { + note_value = note2.value; + } else { + note_value = note3.value; + } + + debug_log_format("spend note value {0}", [note_value]); + + // Assert that the note chosen to spend has enough funds. + assert(note_value as u64 >= amount as u64); + + // Removes only the note that is meant to be actually spent from the owner's notes. + if spend_note_index == 0 { + balance.remove(context, note0); + } else if spend_note_index == 1 { + balance.remove(context, note1); + } else if spend_note_index == 2 { + balance.remove(context, note2); + } else { + balance.remove(context, note3); + } + + // Creates change note for the owner. + let change_value = note_value - amount; + let mut change_note = ValueNote::new(change_value, owner); + + debug_log_format("change: v = {0}, o = {1}", [change_note.value, change_note.owner]); + + // Insert the change note to the owner's sets of notes. + balance.insert(context, &mut change_note); + + // Emit the newly created encrypted note preimages via oracle calls. + + // Emit the newly created encrypted note preimages via oracle calls. + let mut encrypted_data = [0; VALUE_NOTE_LEN]; + if change_value != 0 { + encrypted_data = change_note.serialise(); + }; + + let encryption_pub_key = get_public_key(owner); + emit_encrypted_log( + context, + context.inputs.call_context.storage_contract_address, + balance.storage_slot, + owner, + encryption_pub_key, + encrypted_data, + ); +} + fn send_note( context: &mut PrivateContext, balance: Set, @@ -78,4 +174,71 @@ fn send_note( encryption_pub_key, note.serialise(), ); +} + +fn send_notes( + context: &mut Context, + recipient_balances: [Set; 3], + amounts: [Field; 3], + recipients: [Field; 3], +) { + + // Creates (4 - 1 = 3) new notes for the recipients. + let mut recipient0_note = ValueNote::new(amounts[0], recipients[0]); + let mut recipient1_note = ValueNote::new(amounts[1], recipients[1]); + let mut recipient2_note = ValueNote::new(amounts[2], recipients[2]); + + debug_log_format("o1: v = {0}, o = {1}", [recipient0_note.value, recipient0_note.owner]); + debug_log_format("o2: v = {0}, o = {1}", [recipient1_note.value, recipient1_note.owner]); + debug_log_format("o3: v = {0}, o = {1}", [recipient2_note.value, recipient2_note.owner]); + + // Insert the new notes to the recipient's sets of notes. + recipient_balances[0].insert(context, &mut recipient0_note); + recipient_balances[1].insert(context, &mut recipient1_note); + recipient_balances[2].insert(context, &mut recipient2_note); + + // Get recipient encryption keys. + let recipient0_encryption_pub_key = get_public_key(recipients[0]); + let recipient1_encryption_pub_key = get_public_key(recipients[1]); + let recipient2_encryption_pub_key = get_public_key(recipients[2]); + + // Emit the newly created encrypted note preimages via oracle calls. + let mut recipient0_encrypted_data = [0; VALUE_NOTE_LEN]; + if recipient0_note.value != 0 { + recipient0_encrypted_data = recipient0_note.serialise(); + }; + emit_encrypted_log( + context, + context.inputs.call_context.storage_contract_address, + recipient_balances[0].storage_slot, + recipients[0], + recipient0_encryption_pub_key, + recipient0_encrypted_data, + ); + + let mut recipient1_encrypted_data = [0; VALUE_NOTE_LEN]; + if recipient1_note.value != 0 { + recipient1_encrypted_data = recipient1_note.serialise(); + }; + emit_encrypted_log( + context, + context.inputs.call_context.storage_contract_address, + recipient_balances[1].storage_slot, + recipients[1], + recipient1_encryption_pub_key, + recipient1_encrypted_data, + ); + + let mut recipient2_encrypted_data = [0; VALUE_NOTE_LEN]; + if recipient2_note.value != 0 { + recipient2_encrypted_data = recipient2_note.serialise(); + }; + emit_encrypted_log( + context, + context.inputs.call_context.storage_contract_address, + recipient_balances[1].storage_slot, + recipients[1], + recipient2_encryption_pub_key, + recipient2_encrypted_data, + ); } \ No newline at end of file From 90b68f9293c727f3770f0151c57cff9e886fdf2c Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Wed, 9 Aug 2023 19:22:29 +0000 Subject: [PATCH 02/14] Fix note filtering based on pending nullifers. --- .../end-to-end/src/e2e_multi_transfer.test.ts | 20 +++++-------- .../multi_transfer_contract/src/main.nr | 30 +++++++++++++++---- 2 files changed, 31 insertions(+), 19 deletions(-) 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 index 6783e32b9390..19b645d17152 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -6,11 +6,7 @@ import { PrivateTokenAirdropContract } from '@aztec/noir-contracts/types'; import { MultiTransferContract } from '@aztec/noir-contracts/types'; import { AztecRPC } from '@aztec/types'; -import { - expectUnencryptedLogsFromLastBlockToBe, - expectsNumOfEncryptedLogsInTheLastBlockToBe, - setup, -} from './fixtures/utils.js'; +import { expectsNumOfEncryptedLogsInTheLastBlockToBe, setup } from './fixtures/utils.js'; describe('multi-transfer payments', () => { const numberOfAccounts = 6; @@ -41,7 +37,7 @@ describe('multi-transfer payments', () => { if (aztecRpcServer instanceof AztecRPCServer) { await aztecRpcServer?.stop(); } - }); + }, 500_000); const deployZkTokenContract = async (initialBalance: bigint, owner: AztecAddress) => { logger(`Deploying zk token contract...`); @@ -71,16 +67,15 @@ describe('multi-transfer payments', () => { logger(`self batchTransfer()`); const batchTransferTx = zkTokenContract.methods - .batchTransfer(ownerAddress, [400n, 300n, 200n], [ownerAddress, ownerAddress, ownerAddress], 0, 0) + .batchTransfer(ownerAddress, [200n, 300n, 400n], [ownerAddress, ownerAddress, ownerAddress], 0, 0) .send({ origin: ownerAddress }); await batchTransferTx.isMined(); const batchTransferTxReceipt = await batchTransferTx.getReceipt(); logger(`consumption Receipt status: ${batchTransferTxReceipt.status}`); await expectBalance(zkTokenContract, ownerAddress, initialNote); - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1); - await expectUnencryptedLogsFromLastBlockToBe(aztecNode, ['Balance set in constructor']); + await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 4); - const amounts: bigint[] = [50n, 50n, 50n, 20n, 20n, 20n, 15n, 15n, 15n, 30n, 30n, 30n]; + const amounts: bigint[] = [20n, 20n, 20n, 50n, 50n, 50n, 80n, 80n, 80n, 100n, 100n, 100n]; const amountSum = amounts.reduce((a, b) => a + b, 0n); logger(`multiTransfer()...`); @@ -103,7 +98,6 @@ describe('multi-transfer payments', () => { await expectBalance(zkTokenContract, recipients[3], amounts[3] + amounts[numberOfAccounts + 3]); await expectBalance(zkTokenContract, recipients[4], amounts[4] + amounts[numberOfAccounts + 4]); await expectBalance(zkTokenContract, recipients[5], amounts[5] + amounts[numberOfAccounts + 5]); - await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 1); - await expectUnencryptedLogsFromLastBlockToBe(aztecNode, ['Balance set in constructor']); - }, 240_000); + await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); + }, 540_000); }); 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 index 574bca962260..c1b9ebe0d49b 100644 --- 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 @@ -8,6 +8,16 @@ contract MultiTransfer { use dep::aztec::private_call_stack_item::PrivateCallStackItem; use dep::aztec::public_call_stack_item::PublicCallStackItem; use dep::aztec::types::point::Point; + + // Libs + use dep::value_note::{ + value_note::{VALUE_NOTE_LEN, ValueNoteMethods}, + }; + use dep::aztec::note::{ + note_header::NoteHeader, + utils as note_utils, + }; + fn constructor( inputs: PrivateContextInputs ) -> distinct pub abi::PrivateCircuitPublicInputs { @@ -76,8 +86,8 @@ contract MultiTransfer { addresses[3], addresses[4], addresses[5], - 1 as Field, - 1 as Field, + 0 as Field, + 0 as Field, ]); let result2 = return_values_2[0]; context.return_values.push(result2); @@ -91,8 +101,8 @@ contract MultiTransfer { addresses[6], addresses[7], addresses[8], - 2 as Field, - 2 as Field, + 0 as Field, + 0 as Field, ]); let result3 = return_values_3[0]; context.return_values.push(result3); @@ -106,12 +116,20 @@ contract MultiTransfer { addresses[9], addresses[10], addresses[11], - 3 as Field, - 3 as Field, + 0 as Field, + 0 as Field, ]); let result4 = return_values_4[0]; context.return_values.push(result4); context.finish() } + + // 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 }; + note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) + } } \ No newline at end of file From fd75e4f0a77e2cd1da4edfde0aab5266bf4e29f6 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Thu, 10 Aug 2023 18:53:38 +0000 Subject: [PATCH 03/14] Fix ctrlv mistake. --- yarn-project/end-to-end/src/e2e_multi_transfer.test.ts | 2 +- yarn-project/noir-libs/value-note/src/utils.nr | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 index 19b645d17152..83fd7c8ac282 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -99,5 +99,5 @@ describe('multi-transfer payments', () => { await expectBalance(zkTokenContract, recipients[4], amounts[4] + amounts[numberOfAccounts + 4]); await expectBalance(zkTokenContract, recipients[5], amounts[5] + amounts[numberOfAccounts + 5]); await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); - }, 540_000); + }, 650_000); }); diff --git a/yarn-project/noir-libs/value-note/src/utils.nr b/yarn-project/noir-libs/value-note/src/utils.nr index b1d30d1a6415..4aced8bae00a 100644 --- a/yarn-project/noir-libs/value-note/src/utils.nr +++ b/yarn-project/noir-libs/value-note/src/utils.nr @@ -236,8 +236,8 @@ fn send_notes( emit_encrypted_log( context, context.inputs.call_context.storage_contract_address, - recipient_balances[1].storage_slot, - recipients[1], + recipient_balances[2].storage_slot, + recipients[2], recipient2_encryption_pub_key, recipient2_encrypted_data, ); From edc0ff1ca5ba4e383015b75dae1230befec4b557 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 11 Aug 2023 20:50:18 +0000 Subject: [PATCH 04/14] Fix isMined timeout :/:/:/ --- yarn-project/end-to-end/src/e2e_multi_transfer.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 index 83fd7c8ac282..5a454773bd85 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -37,7 +37,7 @@ describe('multi-transfer payments', () => { if (aztecRpcServer instanceof AztecRPCServer) { await aztecRpcServer?.stop(); } - }, 500_000); + }); const deployZkTokenContract = async (initialBalance: bigint, owner: AztecAddress) => { logger(`Deploying zk token contract...`); @@ -88,7 +88,7 @@ describe('multi-transfer payments', () => { Fr.fromBuffer(zkTokenContract.methods.batchTransfer.selector), ) .send({ origin: ownerAddress }); - await multiTransferTx.isMined(); + await multiTransferTx.isMined({ timeout: 1000 }); const multiTransferTxReceipt = await multiTransferTx.getReceipt(); logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); await expectBalance(zkTokenContract, ownerAddress, initialNote - amountSum); From 1badb307d2b605fb2cd7b4d4d7246ef62493ab02 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 11 Aug 2023 21:44:09 +0000 Subject: [PATCH 05/14] Add comments. --- .../contracts/multi_transfer_contract/src/main.nr | 7 ++++++- .../private_token_airdrop_contract/src/main.nr | 4 +++- yarn-project/noir-libs/value-note/src/utils.nr | 15 ++++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) 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 index c1b9ebe0d49b..547a71573305 100644 --- 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 @@ -1,4 +1,4 @@ -// Demonstrates how to send a message to a portal contract on L1. We use Uniswap here as it's the most typical example. +// Demonstrates how to perform 4 x 4 = 16 transfers in one transaction. Uses the private airdrop contract in the backend. contract MultiTransfer { use dep::aztec::abi; use dep::aztec::abi::PrivateContextInputs; @@ -24,6 +24,11 @@ contract MultiTransfer { Context::new(inputs, 0).finish() } + // 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. fn multiTransfer( inputs: PrivateContextInputs, asset: Field, // Asset to distribute 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 index bebee77e1ec2..8501a4e68594 100644 --- 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 @@ -149,7 +149,9 @@ contract PrivateTokenAirdrop { context.finish() } - // Transfers `amounts` of tokens from `sender` to 3 `recipients`. Aztec only requires 4 notes per tx - 1 new note for sender's new balance and 1 note each for 3 recipients (for their new balance) + // 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) fn batchTransfer( //*********************************/ // Should eventually be hidden: diff --git a/yarn-project/noir-libs/value-note/src/utils.nr b/yarn-project/noir-libs/value-note/src/utils.nr index 4aced8bae00a..ba99b43255b4 100644 --- a/yarn-project/noir-libs/value-note/src/utils.nr +++ b/yarn-project/noir-libs/value-note/src/utils.nr @@ -59,6 +59,13 @@ fn spend_notes( ); } +/* + Spends one note from a set of 4 notes. + Details: Reads 4 notes from the user's `balance`: [n_{o}, n_{o+1}, n_{o+2}, n_{o+3}] + where "o" is the note_offset. Then, spends the note: [n_{o + i}] + where "i" is the spend_note_index. This gives more control to the user on which + note is to be spent. +*/ fn spend_one_note( context: &mut Context, balance: Set, @@ -134,8 +141,6 @@ fn spend_one_note( // Insert the change note to the owner's sets of notes. balance.insert(context, &mut change_note); - // Emit the newly created encrypted note preimages via oracle calls. - // Emit the newly created encrypted note preimages via oracle calls. let mut encrypted_data = [0; VALUE_NOTE_LEN]; if change_value != 0 { @@ -176,13 +181,17 @@ fn send_note( ); } +/* + Sends three amounts to three recipients. + Why three? Because one private call currently allows `MAX_NEW_COMMITMENTS_PER_CALL = 4` output commitments. + So we split the output notes as: 3 to recipients + 1 to the owner (the change note). +*/ fn send_notes( context: &mut Context, recipient_balances: [Set; 3], amounts: [Field; 3], recipients: [Field; 3], ) { - // Creates (4 - 1 = 3) new notes for the recipients. let mut recipient0_note = ValueNote::new(amounts[0], recipients[0]); let mut recipient1_note = ValueNote::new(amounts[1], recipients[1]); From c3af44d0d23d0e46e59e9dae19cb0bef938ca91b Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 11 Aug 2023 21:59:29 +0000 Subject: [PATCH 06/14] Remove unnecessary fn. --- .../src/contracts/multi_transfer_contract/src/main.nr | 8 -------- 1 file changed, 8 deletions(-) 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 index 547a71573305..4b1df41de2a1 100644 --- 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 @@ -129,12 +129,4 @@ contract MultiTransfer { context.finish() } - - // 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 }; - note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) - } } \ No newline at end of file From fa1f817594d92fe8678c218657ce1bd6a98dde43 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 11 Aug 2023 22:16:07 +0000 Subject: [PATCH 07/14] Simple comments. bump timeouts. bump multi test timeout. --- .../end-to-end/src/e2e_multi_transfer.test.ts | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) 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 index 5a454773bd85..b83256a76aa9 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -37,7 +37,7 @@ describe('multi-transfer payments', () => { if (aztecRpcServer instanceof AztecRPCServer) { await aztecRpcServer?.stop(); } - }); + }, 30_000); const deployZkTokenContract = async (initialBalance: bigint, owner: AztecAddress) => { logger(`Deploying zk token contract...`); @@ -62,9 +62,11 @@ describe('multi-transfer payments', () => { logger(`Deploying zk token contract...`); await deployZkTokenContract(initialNote, ownerAddress); + logger(`Deploying multi-transfer contract...`); await deployMultiTransferContract(); + // owner: 1000 => [100, 200, 300, 400] logger(`self batchTransfer()`); const batchTransferTx = zkTokenContract.methods .batchTransfer(ownerAddress, [200n, 300n, 400n], [ownerAddress, ownerAddress, ownerAddress], 0, 0) @@ -75,9 +77,22 @@ describe('multi-transfer payments', () => { await expectBalance(zkTokenContract, ownerAddress, initialNote); await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 4); - const amounts: bigint[] = [20n, 20n, 20n, 50n, 50n, 50n, 80n, 80n, 80n, 100n, 100n, 100n]; + const amounts: bigint[] = [20n, 25n, 30n, 40n, 50n, 60n, 75n, 80n, 85n, 100n, 120n, 130n]; const amountSum = amounts.reduce((a, b) => a + b, 0n); + /** + * owner: [100, 200, 300, 400] + * | | | | + * | | | [50 (o), 100, 120, 130] batchTx + * | | | + * | | [60 (o), 75, 80, 85] batchTx + * | | + * | [50 (o), 40, 50, 60] batchTx + * | + * [25 (o), 20, 25, 30] batchTx + * + * o = owner + */ logger(`multiTransfer()...`); const multiTransferTx = multiTransferContract.methods .multiTransfer( @@ -88,9 +103,10 @@ describe('multi-transfer payments', () => { Fr.fromBuffer(zkTokenContract.methods.batchTransfer.selector), ) .send({ origin: ownerAddress }); - await multiTransferTx.isMined({ timeout: 1000 }); + await multiTransferTx.isMined({ timeout: 1000 }); // mining timeout ≥ time needed for the test to finish. const multiTransferTxReceipt = await multiTransferTx.getReceipt(); logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); + await expectBalance(zkTokenContract, ownerAddress, initialNote - amountSum); await expectBalance(zkTokenContract, recipients[0], amounts[0] + amounts[numberOfAccounts]); await expectBalance(zkTokenContract, recipients[1], amounts[1] + amounts[numberOfAccounts + 1]); @@ -99,5 +115,5 @@ describe('multi-transfer payments', () => { await expectBalance(zkTokenContract, recipients[4], amounts[4] + amounts[numberOfAccounts + 4]); await expectBalance(zkTokenContract, recipients[5], amounts[5] + amounts[numberOfAccounts + 5]); await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); - }, 650_000); + }, 850_000); }); From 045d2d4c73bc1eb558c6cfaeeaedae52b6878a96 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Tue, 15 Aug 2023 11:45:17 +0000 Subject: [PATCH 08/14] remove debug statements in nr. --- .../private_token_airdrop_contract/src/main.nr | 1 - yarn-project/noir-libs/value-note/src/utils.nr | 14 -------------- 2 files changed, 15 deletions(-) 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 index 8501a4e68594..25b14482362e 100644 --- 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 @@ -17,7 +17,6 @@ contract PrivateTokenAirdrop { utils as note_utils, }; use dep::aztec::log::emit_unencrypted_log; - use dep::aztec::oracle::debug_log::debug_log_format; use crate::storage::Storage; use crate::claim_note::{ClaimNote, ClaimNoteMethods}; diff --git a/yarn-project/noir-libs/value-note/src/utils.nr b/yarn-project/noir-libs/value-note/src/utils.nr index ba99b43255b4..d7ccc0169b22 100644 --- a/yarn-project/noir-libs/value-note/src/utils.nr +++ b/yarn-project/noir-libs/value-note/src/utils.nr @@ -9,7 +9,6 @@ use crate::{ filter::get_4_notes, value_note::{ValueNote, VALUE_NOTE_LEN}, }; -use dep::aztec::oracle::debug_log::debug_log_format; fn spend_notes( context: &mut PrivateContext, @@ -99,11 +98,6 @@ fn spend_one_note( assert(owner == note3.owner); } - debug_log_format("i1: v = {0}, o = {1}", [note0.value, note0.owner]); - debug_log_format("i2: v = {0}, o = {1}", [note1.value, note1.owner]); - debug_log_format("i3: v = {0}, o = {1}", [note2.value, note2.owner]); - debug_log_format("i4: v = {0}, o = {1}", [note3.value, note3.owner]); - // Checks that the first notes is greater than or equal to the total amount being transferred. let mut note_value = 0; if spend_note_index == 0 { @@ -115,8 +109,6 @@ fn spend_one_note( } else { note_value = note3.value; } - - debug_log_format("spend note value {0}", [note_value]); // Assert that the note chosen to spend has enough funds. assert(note_value as u64 >= amount as u64); @@ -136,8 +128,6 @@ fn spend_one_note( let change_value = note_value - amount; let mut change_note = ValueNote::new(change_value, owner); - debug_log_format("change: v = {0}, o = {1}", [change_note.value, change_note.owner]); - // Insert the change note to the owner's sets of notes. balance.insert(context, &mut change_note); @@ -197,10 +187,6 @@ fn send_notes( let mut recipient1_note = ValueNote::new(amounts[1], recipients[1]); let mut recipient2_note = ValueNote::new(amounts[2], recipients[2]); - debug_log_format("o1: v = {0}, o = {1}", [recipient0_note.value, recipient0_note.owner]); - debug_log_format("o2: v = {0}, o = {1}", [recipient1_note.value, recipient1_note.owner]); - debug_log_format("o3: v = {0}, o = {1}", [recipient2_note.value, recipient2_note.owner]); - // Insert the new notes to the recipient's sets of notes. recipient_balances[0].insert(context, &mut recipient0_note); recipient_balances[1].insert(context, &mut recipient1_note); From 3ff9501aa3c60b19e4cc899bb838fd48e1c1c371 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Tue, 15 Aug 2023 12:29:16 +0000 Subject: [PATCH 09/14] rearrange (rahul's suggestion) --- .../noir-libs/value-note/src/utils.nr | 127 ++++++------------ 1 file changed, 44 insertions(+), 83 deletions(-) diff --git a/yarn-project/noir-libs/value-note/src/utils.nr b/yarn-project/noir-libs/value-note/src/utils.nr index d7ccc0169b22..0bef051a1c8c 100644 --- a/yarn-project/noir-libs/value-note/src/utils.nr +++ b/yarn-project/noir-libs/value-note/src/utils.nr @@ -82,48 +82,39 @@ fn spend_one_note( let note3 = maybe_notes[3].unwrap_or(ValueNote::dummy()); assert(spend_note_index as u64 < 4); - - // Ensure the notes are actually owned by the owner (to prevent user from generating a valid proof while - // spending someone else's notes). - if maybe_notes[0].is_some() { - assert(owner == note0.owner); - } - if maybe_notes[1].is_some() { - assert(owner == note1.owner); - } - if maybe_notes[2].is_some() { - assert(owner == note2.owner); - } - if maybe_notes[3].is_some() { - assert(owner == note3.owner); - } - - // Checks that the first notes is greater than or equal to the total amount being transferred. + + // Check that the note being spent is actually owned by the owner. + // Removes only the note that is meant to be actually spent from the owner's notes. let mut note_value = 0; if spend_note_index == 0 { + if maybe_notes[0].is_some() { + assert(owner == note0.owner); + } note_value = note0.value; + balance.remove(context, note0); } else if spend_note_index == 1 { + if maybe_notes[1].is_some() { + assert(owner == note1.owner); + } note_value = note1.value; + balance.remove(context, note1); } else if spend_note_index == 2 { + if maybe_notes[2].is_some() { + assert(owner == note2.owner); + } note_value = note2.value; + balance.remove(context, note2); } else { + if maybe_notes[3].is_some() { + assert(owner == note3.owner); + } note_value = note3.value; + balance.remove(context, note3); } // Assert that the note chosen to spend has enough funds. assert(note_value as u64 >= amount as u64); - - // Removes only the note that is meant to be actually spent from the owner's notes. - if spend_note_index == 0 { - balance.remove(context, note0); - } else if spend_note_index == 1 { - balance.remove(context, note1); - } else if spend_note_index == 2 { - balance.remove(context, note2); - } else { - balance.remove(context, note3); - } - + // Creates change note for the owner. let change_value = note_value - amount; let mut change_note = ValueNote::new(change_value, owner); @@ -182,58 +173,28 @@ fn send_notes( amounts: [Field; 3], recipients: [Field; 3], ) { - // Creates (4 - 1 = 3) new notes for the recipients. - let mut recipient0_note = ValueNote::new(amounts[0], recipients[0]); - let mut recipient1_note = ValueNote::new(amounts[1], recipients[1]); - let mut recipient2_note = ValueNote::new(amounts[2], recipients[2]); - - // Insert the new notes to the recipient's sets of notes. - recipient_balances[0].insert(context, &mut recipient0_note); - recipient_balances[1].insert(context, &mut recipient1_note); - recipient_balances[2].insert(context, &mut recipient2_note); - - // Get recipient encryption keys. - let recipient0_encryption_pub_key = get_public_key(recipients[0]); - let recipient1_encryption_pub_key = get_public_key(recipients[1]); - let recipient2_encryption_pub_key = get_public_key(recipients[2]); - - // Emit the newly created encrypted note preimages via oracle calls. - let mut recipient0_encrypted_data = [0; VALUE_NOTE_LEN]; - if recipient0_note.value != 0 { - recipient0_encrypted_data = recipient0_note.serialise(); - }; - emit_encrypted_log( - context, - context.inputs.call_context.storage_contract_address, - recipient_balances[0].storage_slot, - recipients[0], - recipient0_encryption_pub_key, - recipient0_encrypted_data, - ); - - let mut recipient1_encrypted_data = [0; VALUE_NOTE_LEN]; - if recipient1_note.value != 0 { - recipient1_encrypted_data = recipient1_note.serialise(); - }; - emit_encrypted_log( - context, - context.inputs.call_context.storage_contract_address, - recipient_balances[1].storage_slot, - recipients[1], - recipient1_encryption_pub_key, - recipient1_encrypted_data, - ); - - let mut recipient2_encrypted_data = [0; VALUE_NOTE_LEN]; - if recipient2_note.value != 0 { - recipient2_encrypted_data = recipient2_note.serialise(); - }; - emit_encrypted_log( - context, - context.inputs.call_context.storage_contract_address, - recipient_balances[2].storage_slot, - recipients[2], - recipient2_encryption_pub_key, - recipient2_encrypted_data, - ); + for i in 0..3 { + // Creates a new note for the i-th recipients + let mut recipient_note = ValueNote::new(amounts[i], recipients[i]); + + // Insert the new notes to the i-th recipient's sets of notes. + recipient_balances[i].insert(context, &mut recipient_note); + + // Get recipient encryption keys. + let recipient_encryption_pub_key = get_public_key(recipients[i]); + + // Emit the newly created encrypted note preimages via oracle calls. + let mut recipient_encrypted_data = [0; VALUE_NOTE_LEN]; + if recipient_note.value != 0 { + recipient_encrypted_data = recipient_note.serialise(); + }; + emit_encrypted_log( + context, + context.inputs.call_context.storage_contract_address, + recipient_balances[i].storage_slot, + recipients[i], + recipient_encryption_pub_key, + recipient_encrypted_data, + ); + } } \ No newline at end of file From 5e8cadba5d90dfb66ae6fcacdd2993406d6cdf9a Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Wed, 16 Aug 2023 11:58:59 +0000 Subject: [PATCH 10/14] Remove `spend_note_index`. --- .../end-to-end/src/e2e_multi_transfer.test.ts | 88 ++++++++++++------- .../multi_transfer_contract/src/main.nr | 19 ++-- .../src/main.nr | 9 +- 3 files changed, 68 insertions(+), 48 deletions(-) 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 index b83256a76aa9..80dbc30fddba 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -8,8 +8,13 @@ import { AztecRPC } 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 = 6; + const numberOfAccounts = 12; let aztecNode: AztecNodeService | undefined; let aztecRpcServer: AztecRPC; @@ -18,6 +23,7 @@ describe('multi-transfer payments', () => { let logger: DebugLogger; let ownerAddress: AztecAddress; const recipients: AztecAddress[] = []; + let initialBalance: bigint; let zkTokenContract: PrivateTokenAirdropContract; let multiTransferContract: MultiTransferContract; @@ -30,6 +36,13 @@ describe('multi-transfer payments', () => { const account = accounts[i]; recipients.push(account); } + + logger(`Deploying zk token contract...`); + initialBalance = 1000n; + await deployZkTokenContract(initialBalance, ownerAddress); + + logger(`Deploying multi-transfer contract...`); + await deployMultiTransferContract(); }, 100_000); afterEach(async () => { @@ -57,63 +70,72 @@ describe('multi-transfer payments', () => { expect(balance).toBe(expectedBalance); }; + /** + * 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 () => { - const initialNote = 1000n; - - logger(`Deploying zk token contract...`); - await deployZkTokenContract(initialNote, ownerAddress); - - logger(`Deploying multi-transfer contract...`); - await deployMultiTransferContract(); - - // owner: 1000 => [100, 200, 300, 400] + // Transaction 1 logger(`self batchTransfer()`); const batchTransferTx = zkTokenContract.methods - .batchTransfer(ownerAddress, [200n, 300n, 400n], [ownerAddress, ownerAddress, ownerAddress], 0, 0) + .batchTransfer(ownerAddress, [200n, 300n, 400n], [ownerAddress, ownerAddress, ownerAddress], 0) .send({ origin: ownerAddress }); await batchTransferTx.isMined(); const batchTransferTxReceipt = await batchTransferTx.getReceipt(); logger(`consumption Receipt status: ${batchTransferTxReceipt.status}`); - await expectBalance(zkTokenContract, ownerAddress, initialNote); + 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]; - /** - * owner: [100, 200, 300, 400] - * | | | | - * | | | [50 (o), 100, 120, 130] batchTx - * | | | - * | | [60 (o), 75, 80, 85] batchTx - * | | - * | [50 (o), 40, 50, 60] batchTx - * | - * [25 (o), 20, 25, 30] batchTx - * - * o = owner - */ + // Transaction 2 logger(`multiTransfer()...`); const multiTransferTx = multiTransferContract.methods .multiTransfer( zkTokenContract.address.toField(), - recipients.concat(recipients), + recipients, amounts, ownerAddress, Fr.fromBuffer(zkTokenContract.methods.batchTransfer.selector), + noteOffsets, ) .send({ origin: ownerAddress }); await multiTransferTx.isMined({ timeout: 1000 }); // mining timeout ≥ time needed for the test to finish. const multiTransferTxReceipt = await multiTransferTx.getReceipt(); logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); - await expectBalance(zkTokenContract, ownerAddress, initialNote - amountSum); - await expectBalance(zkTokenContract, recipients[0], amounts[0] + amounts[numberOfAccounts]); - await expectBalance(zkTokenContract, recipients[1], amounts[1] + amounts[numberOfAccounts + 1]); - await expectBalance(zkTokenContract, recipients[2], amounts[2] + amounts[numberOfAccounts + 2]); - await expectBalance(zkTokenContract, recipients[3], amounts[3] + amounts[numberOfAccounts + 3]); - await expectBalance(zkTokenContract, recipients[4], amounts[4] + amounts[numberOfAccounts + 4]); - await expectBalance(zkTokenContract, recipients[5], amounts[5] + amounts[numberOfAccounts + 5]); + await expectBalance(zkTokenContract, ownerAddress, initialBalance - amountSum); + for (let index = 0; index < numberOfAccounts; index++) { + await expectBalance(zkTokenContract, recipients[index], amounts[index]); + } await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); }, 850_000); }); 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 index 4b1df41de2a1..d303fb091c83 100644 --- 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 @@ -36,6 +36,7 @@ contract MultiTransfer { amounts: [Field; 12], // Amounts to distribute owner: Field, // Owner of the asset batch_transfer_selector: Field, // Function selector for transfer + note_offsets: [Field; 4], // Offsets from which 4 notes of the owner would be read. ) -> distinct pub abi::PrivateCircuitPublicInputs { let mut context = Context::new(inputs, abi::hash_args([ asset, @@ -64,7 +65,11 @@ contract MultiTransfer { amounts[10], amounts[11], owner, - batch_transfer_selector + batch_transfer_selector, + note_offsets[0], + note_offsets[1], + note_offsets[2], + note_offsets[3], ])); // First batch transfer call @@ -76,8 +81,7 @@ contract MultiTransfer { addresses[0], addresses[1], addresses[2], - 0 as Field, - 0 as Field, + note_offsets[0], ]); let result1 = return_values_1[0]; context.return_values.push(result1); @@ -91,8 +95,7 @@ contract MultiTransfer { addresses[3], addresses[4], addresses[5], - 0 as Field, - 0 as Field, + note_offsets[1], ]); let result2 = return_values_2[0]; context.return_values.push(result2); @@ -106,8 +109,7 @@ contract MultiTransfer { addresses[6], addresses[7], addresses[8], - 0 as Field, - 0 as Field, + note_offsets[2], ]); let result3 = return_values_3[0]; context.return_values.push(result3); @@ -121,8 +123,7 @@ contract MultiTransfer { addresses[9], addresses[10], addresses[11], - 0 as Field, - 0 as Field, + note_offsets[3], ]); let result4 = return_values_4[0]; context.return_values.push(result4); 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 index 25b14482362e..7d071289d3b8 100644 --- 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 @@ -160,7 +160,6 @@ contract PrivateTokenAirdrop { amounts: [Field; 3], recipients: [Field; 3], spend_note_offset: Field, - spend_note_index: Field, ) -> distinct pub abi::PrivateCircuitPublicInputs { let storage = Storage::init(); let mut context = Context::new(inputs, abi::hash_args([ @@ -172,15 +171,13 @@ contract PrivateTokenAirdrop { recipients[1], recipients[2], spend_note_offset, - spend_note_index ])); - assert(spend_note_index as u64 < 4); - - // Gets the set of sender's notes and picks first 4 of those. + // 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]; - spend_one_note(&mut context, sender_balance, total, sender, spend_note_offset, spend_note_index); + spend_one_note(&mut context, sender_balance, total, sender, spend_note_offset, 0); // Creates new note for the recipient. let recipient1_balance = storage.balances.at(recipients[0]); From bc2d61a3aa7b4dddea131c1f9bff41850bc8116f Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 18 Aug 2023 08:47:47 +0000 Subject: [PATCH 11/14] add note-split test. --- .../src/note_processor/note_processor.ts | 8 +-- .../end-to-end/src/e2e_multi_transfer.test.ts | 51 +++++++++++++++++++ .../noir-libs/value-note/src/balance_utils.nr | 10 ++-- 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/yarn-project/aztec-rpc/src/note_processor/note_processor.ts b/yarn-project/aztec-rpc/src/note_processor/note_processor.ts index 023379c83ddc..9ca4f4f416b2 100644 --- a/yarn-project/aztec-rpc/src/note_processor/note_processor.ts +++ b/yarn-project/aztec-rpc/src/note_processor/note_processor.ts @@ -109,6 +109,10 @@ export class NoteProcessor { indexOfTxInABlock * MAX_NEW_COMMITMENTS_PER_TX, (indexOfTxInABlock + 1) * MAX_NEW_COMMITMENTS_PER_TX, ); + const newNullifiers = block.newNullifiers.slice( + indexOfTxInABlock * MAX_NEW_NULLIFIERS_PER_TX, + (indexOfTxInABlock + 1) * MAX_NEW_NULLIFIERS_PER_TX, + ); // Note: Each tx generates a `TxL2Logs` object and for this reason we can rely on its index corresponding // to the index of a tx in a block. const txFunctionLogs = txLogs[indexOfTxInABlock].functionLogs; @@ -117,10 +121,6 @@ export class NoteProcessor { const noteSpendingInfo = NoteSpendingInfo.fromEncryptedBuffer(logs, privateKey, curve); if (noteSpendingInfo) { // We have successfully decrypted the data. - const newNullifiers = block.newNullifiers.slice( - indexOfTxInABlock * MAX_NEW_NULLIFIERS_PER_TX, - (indexOfTxInABlock + 1) * MAX_NEW_NULLIFIERS_PER_TX, - ); try { const { index, nonce, siloedNullifier } = await this.findNoteIndexAndNullifier( dataStartIndexForTx, 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 index 80dbc30fddba..f3957821300b 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -138,4 +138,55 @@ describe('multi-transfer payments', () => { } await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); }, 850_000); + + /** + * + * 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] + */ + it('create 12 small notes out of 1 large note', async () => { + // Transaction 1 + const amounts: bigint[] = [50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n, 50n]; + const noteOffsets: bigint[] = [0n, 0n, 3n, 6n]; + const repeatedSelfAdddress: AztecAddress[] = Array(12).fill(ownerAddress); + + logger(`split multiTransfer()...`); + const multiTransferTx = multiTransferContract.methods + .multiTransfer( + zkTokenContract.address.toField(), + repeatedSelfAdddress, + amounts, + ownerAddress, + Fr.fromBuffer(zkTokenContract.methods.batchTransfer.selector), + noteOffsets, + ) + .send({ origin: ownerAddress }); + await multiTransferTx.isMined({ timeout: 1000 }); // mining timeout ≥ time needed for the test to finish. + const multiTransferTxReceipt = await multiTransferTx.getReceipt(); + logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); + + await expectBalance(zkTokenContract, ownerAddress, initialBalance); + await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); + }, 850_000); }); diff --git a/yarn-project/noir-libs/value-note/src/balance_utils.nr b/yarn-project/noir-libs/value-note/src/balance_utils.nr index 8d3f486b9bdf..bb50827ea72b 100644 --- a/yarn-project/noir-libs/value-note/src/balance_utils.nr +++ b/yarn-project/noir-libs/value-note/src/balance_utils.nr @@ -5,9 +5,6 @@ unconstrained fn get_balance(storage_slot: Field) -> Field { get_balance_internal(storage_slot, 10, 0) } -// TODO(1.5) We don't support yet more than one page of notes -// noir is having issues with loop handling in unconstrained functions -// rewrite to unconstrained and integrate into noir-aztec when fixed unconstrained fn get_balance_internal(storage_slot: Field, limit: u32, offset: u32) -> Field { let mut balance = 0; @@ -17,10 +14,9 @@ unconstrained fn get_balance_internal(storage_slot: Field, limit: u32, offset: u for i in 0..len { balance += opt_notes[i].unwrap_or(dummy()).value; } - // TODO - // if (notes[len - 1].is_dummy()) { - // balance += get_balance(storage_slot, limit, offset + limit); - // } + if (opt_notes[len - 1].is_some()) { + balance += get_balance_internal(storage_slot, limit, offset + limit); + } balance } \ No newline at end of file From 3afc86dc53b801afae453500172618dd943cb484 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 18 Aug 2023 09:39:30 +0000 Subject: [PATCH 12/14] Fix errors due to new nargo version. --- .../end-to-end/src/e2e_multi_transfer.test.ts | 11 ++++++----- .../src/contracts/multi_transfer_contract/Nargo.toml | 2 +- .../src/contracts/multi_transfer_contract/src/main.nr | 6 +++--- .../private_token_airdrop_contract/src/main.nr | 2 +- yarn-project/noir-libs/value-note/src/utils.nr | 6 ++---- 5 files changed, 13 insertions(+), 14 deletions(-) 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 index f3957821300b..e403f66cd653 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -4,7 +4,7 @@ import { AztecAddress, Contract, Fr, Wallet } from '@aztec/aztec.js'; import { DebugLogger } from '@aztec/foundation/log'; import { PrivateTokenAirdropContract } from '@aztec/noir-contracts/types'; import { MultiTransferContract } from '@aztec/noir-contracts/types'; -import { AztecRPC } from '@aztec/types'; +import { AztecRPC, CompleteAddress } from '@aztec/types'; import { expectsNumOfEncryptedLogsInTheLastBlockToBe, setup } from './fixtures/utils.js'; @@ -19,7 +19,7 @@ describe('multi-transfer payments', () => { let aztecNode: AztecNodeService | undefined; let aztecRpcServer: AztecRPC; let wallet: Wallet; - let accounts: AztecAddress[]; + // let accountAddresses: AztecAddress[]; let logger: DebugLogger; let ownerAddress: AztecAddress; const recipients: AztecAddress[] = []; @@ -29,11 +29,12 @@ describe('multi-transfer payments', () => { let multiTransferContract: MultiTransferContract; beforeEach(async () => { + let accounts: CompleteAddress[]; ({ aztecNode, aztecRpcServer, accounts, logger, wallet } = await setup(numberOfAccounts + 1)); // 1st being the `owner` - ownerAddress = accounts[0]; + ownerAddress = accounts[0].address; for (let i = 1; i < accounts.length; i++) { - const account = accounts[i]; + const account = accounts[i].address; recipients.push(account); } @@ -65,7 +66,7 @@ describe('multi-transfer payments', () => { }; const expectBalance = async (tokenContract: Contract, owner: AztecAddress, expectedBalance: bigint) => { - const [balance] = await tokenContract.methods.getBalance(owner).view({ from: owner }); + const balance = await tokenContract.methods.getBalance(owner).view({ from: owner }); logger(`Account ${owner} balance: ${balance}`); expect(balance).toBe(expectedBalance); }; 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 index 8d56210c1897..d6220bfb1eb0 100644 --- a/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/Nargo.toml +++ b/yarn-project/noir-contracts/src/contracts/multi_transfer_contract/Nargo.toml @@ -2,7 +2,7 @@ name = "multi_transfer_contract" authors = [""] compiler_version = "0.1" -type = "bin" +type = "contract" [dependencies] aztec = { path = "../../../../noir-libs/noir-aztec" } 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 index d303fb091c83..f064541da195 100644 --- 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 @@ -3,7 +3,7 @@ contract MultiTransfer { use dep::aztec::abi; use dep::aztec::abi::PrivateContextInputs; use dep::aztec::abi::PublicContextInputs; - use dep::aztec::context::Context; + use dep::aztec::context::PrivateContext; use dep::aztec::oracle::public_call; use dep::aztec::private_call_stack_item::PrivateCallStackItem; use dep::aztec::public_call_stack_item::PublicCallStackItem; @@ -21,7 +21,7 @@ contract MultiTransfer { fn constructor( inputs: PrivateContextInputs ) -> distinct pub abi::PrivateCircuitPublicInputs { - Context::new(inputs, 0).finish() + PrivateContext::new(inputs, 0).finish() } // Transfers 12 amounts to 12 recipients. @@ -38,7 +38,7 @@ contract MultiTransfer { batch_transfer_selector: Field, // Function selector for transfer note_offsets: [Field; 4], // Offsets from which 4 notes of the owner would be read. ) -> distinct pub abi::PrivateCircuitPublicInputs { - let mut context = Context::new(inputs, abi::hash_args([ + let mut context = PrivateContext::new(inputs, abi::hash_args([ asset, addresses[0], addresses[1], 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 index 7d071289d3b8..47b28f2ce389 100644 --- 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 @@ -162,7 +162,7 @@ contract PrivateTokenAirdrop { spend_note_offset: Field, ) -> distinct pub abi::PrivateCircuitPublicInputs { let storage = Storage::init(); - let mut context = Context::new(inputs, abi::hash_args([ + let mut context = PrivateContext::new(inputs, abi::hash_args([ sender, amounts[0], amounts[1], diff --git a/yarn-project/noir-libs/value-note/src/utils.nr b/yarn-project/noir-libs/value-note/src/utils.nr index 0bef051a1c8c..a493881ef5de 100644 --- a/yarn-project/noir-libs/value-note/src/utils.nr +++ b/yarn-project/noir-libs/value-note/src/utils.nr @@ -66,7 +66,7 @@ fn spend_notes( note is to be spent. */ fn spend_one_note( - context: &mut Context, + context: &mut PrivateContext, balance: Set, amount: Field, owner: Field, @@ -133,7 +133,6 @@ fn spend_one_note( context, context.inputs.call_context.storage_contract_address, balance.storage_slot, - owner, encryption_pub_key, encrypted_data, ); @@ -168,7 +167,7 @@ fn send_note( So we split the output notes as: 3 to recipients + 1 to the owner (the change note). */ fn send_notes( - context: &mut Context, + context: &mut PrivateContext, recipient_balances: [Set; 3], amounts: [Field; 3], recipients: [Field; 3], @@ -192,7 +191,6 @@ fn send_notes( context, context.inputs.call_context.storage_contract_address, recipient_balances[i].storage_slot, - recipients[i], recipient_encryption_pub_key, recipient_encrypted_data, ); From e4c88827d50dc8bf67dcdc32e35dcd86f574829e Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 18 Aug 2023 10:05:47 +0000 Subject: [PATCH 13/14] reduce time limits. --- yarn-project/end-to-end/src/e2e_multi_transfer.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 index e403f66cd653..96bb6b2d1b77 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -72,6 +72,8 @@ describe('multi-transfer payments', () => { }; /** + * Payroll example + * * Transaction 1: * The sender first splits 1000 to create new notes (for himself) with values 100, 200, 300, 400: * 0: sender: [1000] @@ -138,9 +140,10 @@ describe('multi-transfer payments', () => { await expectBalance(zkTokenContract, recipients[index], amounts[index]); } await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); - }, 850_000); + }, 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] @@ -183,11 +186,11 @@ describe('multi-transfer payments', () => { noteOffsets, ) .send({ origin: ownerAddress }); - await multiTransferTx.isMined({ timeout: 1000 }); // mining timeout ≥ time needed for the test to finish. + await multiTransferTx.isMined({ timeout: 100 }); // mining timeout ≥ time needed for the test to finish. const multiTransferTxReceipt = await multiTransferTx.getReceipt(); logger(`Consumption Receipt status: ${multiTransferTxReceipt.status}`); await expectBalance(zkTokenContract, ownerAddress, initialBalance); await expectsNumOfEncryptedLogsInTheLastBlockToBe(aztecNode, 16); - }, 850_000); + }, 100_000); }); From 21793c0eb98e80781afeaa8a5949a1b067815602 Mon Sep 17 00:00:00 2001 From: Suyash Bagad Date: Fri, 18 Aug 2023 11:46:20 +0000 Subject: [PATCH 14/14] remove unused accountAddresses. --- yarn-project/end-to-end/src/e2e_multi_transfer.test.ts | 1 - 1 file changed, 1 deletion(-) 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 index 96bb6b2d1b77..72da34ab962b 100644 --- a/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts +++ b/yarn-project/end-to-end/src/e2e_multi_transfer.test.ts @@ -19,7 +19,6 @@ describe('multi-transfer payments', () => { let aztecNode: AztecNodeService | undefined; let aztecRpcServer: AztecRPC; let wallet: Wallet; - // let accountAddresses: AztecAddress[]; let logger: DebugLogger; let ownerAddress: AztecAddress; const recipients: AztecAddress[] = [];