From 22fb8fe0281379bf23836e1be33766b4f38a1813 Mon Sep 17 00:00:00 2001 From: David Banks <47112877+dbanks12@users.noreply.github.com> Date: Mon, 7 Aug 2023 12:47:35 -0400 Subject: [PATCH] chore(noir-libs): TransparentNote rework (#1412) Closes https://github.com/AztecProtocol/aztec-packages/issues/1194 Closes https://github.com/AztecProtocol/aztec-packages/issues/1363 Will likely rename getCommitment alongside all instances of "commitment" as a part of this other issue: https://github.com/AztecProtocol/aztec-packages/issues/1408 # Checklist: Remove the checklist to signal you've completed it. Enable auto-merge if the PR is ready to merge. - [x] If the pull request requires a cryptography review (e.g. cryptographic algorithm implementations) I have added the 'crypto' tag. - [x] I have reviewed my diff in github, line by line and removed unexpected formatting changes, testing logs, or commented-out code. - [x] Every change is related to the PR description. - [x] I have [linked](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) this pull request to relevant issues (if any exist). --------- Co-authored-by: Michael Connor --- .../src/client/client_execution_context.ts | 4 + .../src/client/private_execution.test.ts | 18 ++- .../acir-simulator/src/public/index.test.ts | 6 +- .../e2e_public_cross_chain_messaging.test.ts | 2 +- .../e2e_public_to_private_messaging.test.ts | 2 +- .../non_native_token_contract/src/main.nr | 65 +++++--- .../non_native_token_contract/src/storage.nr | 29 ++-- .../src/transparent_note.nr | 141 +++++++++++++----- .../noir-aztec/src/note/lifecycle.nr | 21 ++- .../noir-aztec/src/note/note_getter.nr | 49 ++++++ .../noir-aztec/src/state_vars/set.nr | 20 ++- .../src/simulator/public_executor.ts | 12 +- 12 files changed, 275 insertions(+), 94 deletions(-) diff --git a/yarn-project/acir-simulator/src/client/client_execution_context.ts b/yarn-project/acir-simulator/src/client/client_execution_context.ts index d2de01d5124..6b744fa5673 100644 --- a/yarn-project/acir-simulator/src/client/client_execution_context.ts +++ b/yarn-project/acir-simulator/src/client/client_execution_context.ts @@ -208,6 +208,10 @@ export class ClientTxExecutionContext { * @returns The commitment data. */ public async getCommitment(contractAddress: AztecAddress, commitment: ACVMField) { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): only works + // for noteHashes/commitments created by public functions! Once public kernel or + // base rollup circuit injects nonces, this can be used with commitments created by + // private functions as well. const commitmentInputs = await this.db.getCommitmentOracle(contractAddress, fromACVMField(commitment)); // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1029): support pending commitments here this.readRequestPartialWitnesses.push(ReadRequestMembershipWitness.empty(commitmentInputs.index)); diff --git a/yarn-project/acir-simulator/src/client/private_execution.test.ts b/yarn-project/acir-simulator/src/client/private_execution.test.ts index 0479739d780..d4b778b3093 100644 --- a/yarn-project/acir-simulator/src/client/private_execution.test.ts +++ b/yarn-project/acir-simulator/src/client/private_execution.test.ts @@ -492,17 +492,23 @@ describe('Private Execution test suite', () => { const wasm = await CircuitsWasm.get(); const secret = new Fr(1n); const secretHash = computeSecretMessageHash(wasm, secret); - const commitment = Fr.fromBuffer(hash([toBufferBE(amount, 32), secretHash.toBuffer()])); - const siloedCommitment = siloCommitment(wasm, contractAddress, commitment); + const preimage = [toBufferBE(amount, 32), secretHash.toBuffer()]; + const noteHash = Fr.fromBuffer(hash(preimage)); + const storageSlot = new Fr(2); + const innerNoteHash = hash([storageSlot.toBuffer(), noteHash.toBuffer()]); + const siloedNoteHash = siloCommitment(wasm, contractAddress, Fr.fromBuffer(innerNoteHash)); + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): should insert + // uniqueSiloedNoteHash in tree and that should be what is expected in Noir + //const uniqueSiloedNoteHash = computeUniqueCommitment(wasm, nonce, Fr.fromBuffer(innerNoteHash)); - const tree = await insertLeaves([siloedCommitment]); + const tree = await insertLeaves([siloedNoteHash]); oracle.getCommitmentOracle.mockImplementation(async () => { // Check the calculated commitment is correct return Promise.resolve({ - commitment: siloedCommitment, - index: 0n, + commitment: siloedNoteHash, siblingPath: (await tree.getSiblingPath(0n, false)).toFieldArray(), + index: 0n, }); }); @@ -519,7 +525,7 @@ describe('Private Execution test suite', () => { // Check the commitment read request was created successfully. const readRequests = result.callStackItem.publicInputs.readRequests.filter(field => !field.equals(Fr.ZERO)); expect(readRequests).toHaveLength(1); - expect(readRequests[0]).toEqual(siloedCommitment); + expect(readRequests[0]).toEqual(siloedNoteHash); }); }); diff --git a/yarn-project/acir-simulator/src/public/index.test.ts b/yarn-project/acir-simulator/src/public/index.test.ts index a243a3185e7..2e190ec4ca8 100644 --- a/yarn-project/acir-simulator/src/public/index.test.ts +++ b/yarn-project/acir-simulator/src/public/index.test.ts @@ -297,11 +297,13 @@ describe('ACIR public execution simulator', () => { // Assert the commitment was created expect(result.newCommitments.length).toEqual(1); - const expectedNewCommitmentValue = pedersenPlookupCommitInputs( + const expectedNoteHash = pedersenPlookupCommitInputs( wasm, params.map(a => a.toBuffer()), ); - expect(result.newCommitments[0].toBuffer()).toEqual(expectedNewCommitmentValue); + const storageSlot = new Fr(2); // matches storage.nr + const expectedInnerNoteHash = pedersenPlookupCommitInputs(wasm, [storageSlot.toBuffer(), expectedNoteHash]); + expect(result.newCommitments[0].toBuffer()).toEqual(expectedInnerNoteHash); }); it('Should be able to create a L2 to L1 message from the public context', async () => { diff --git a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts index 8117b913fcf..f238c6d276c 100644 --- a/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_cross_chain_messaging.test.ts @@ -82,7 +82,7 @@ describe('e2e_public_cross_chain_messaging', () => { // Generate a claim secret using pedersen const l1TokenBalance = 1000000n; const bridgeAmount = 100n; - const publicBalanceSlot = 2n; + const publicBalanceSlot = 3n; // check contract's storage.nr for slot assignment const [secret, secretHash] = await crossChainTestHarness.generateClaimSecret(); diff --git a/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts b/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts index 191a2f59775..2dbb4e62965 100644 --- a/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts +++ b/yarn-project/end-to-end/src/e2e_public_to_private_messaging.test.ts @@ -65,7 +65,7 @@ describe('e2e_public_to_private_messaging', () => { const l1TokenBalance = 1000000n; const bridgeAmount = 100n; const shieldAmount = 50n; - const publicBalanceSlot = 2n; + const publicBalanceSlot = 3n; // check contract's storage.nr for slot assignment const [secret, secretHash] = await crossChainTestHarness.generateClaimSecret(); diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr index ab590d6e41d..2662f816e29 100644 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/main.nr @@ -24,7 +24,11 @@ contract NonNativeToken { value_note::{VALUE_NOTE_LEN, ValueNoteMethods}, }; - use crate::transparent_note::TransparentNote; + use crate::transparent_note::{ + TransparentNote, + TransparentNoteMethods, + TRANSPARENT_NOTE_LEN, + }; use crate::storage::Storage; use crate::hash::{get_mint_content_hash, get_withdraw_content_hash}; @@ -59,7 +63,7 @@ contract NonNativeToken { // Should eventually be hidden: inputs: PrivateContextInputs, //*********************************/ - initial_supply: Field, + initial_supply: Field, owner: Field, ) -> distinct pub abi::PrivateCircuitPublicInputs { let storage = Storage::init(); @@ -74,14 +78,14 @@ contract NonNativeToken { } // Mint Private Function - // This mint function differs to the typical token mint function as it only allows minting + // This mint function differs to the typical token mint function as it only allows minting // upon consuming valid messages from a token portal contract fn mint( //*********************************/ // Should eventually be hidden: inputs: PrivateContextInputs, //*********************************/ - amount: Field, + amount: Field, owner: Field, // This field should be hidden msg_key: Field, @@ -114,8 +118,8 @@ contract NonNativeToken { // Should eventually be hidden: inputs: PrivateContextInputs, //*********************************/ - amount: Field, - sender: Field, + 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) ) -> distinct pub abi::PrivateCircuitPublicInputs { @@ -135,14 +139,14 @@ contract NonNativeToken { } // Mint Public Function - // This mint function differs to the typical token mint function as it only allows minting + // This mint function differs to the typical token mint function as it only allows minting // upon consuming valid messages from a token portal contract open fn mintPublic( //*********************************/ // Should eventually be hidden: inputs: PublicContextInputs, //*********************************/ - amount: Field, + amount: Field, owner_address: Field, // This field should be hidden msg_key: Field, @@ -177,7 +181,7 @@ contract NonNativeToken { ) { let storage = Storage::init(); let public_balances = storage.public_balances; - + let sender = inputs.call_context.msg_sender; let sender_balance = public_balances.at(sender); @@ -186,7 +190,7 @@ contract NonNativeToken { if (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); - } + } // TODO: Revert if there is not enough balance let content = get_withdraw_content_hash(amount, recipient, callerOnL1); @@ -203,8 +207,8 @@ contract NonNativeToken { // Should eventually be hidden: inputs: PrivateContextInputs, //*********************************/ - amount: Field, - sender: Field, + amount: Field, + sender: Field, recipient: Field, ) -> distinct pub abi::PrivateCircuitPublicInputs { let storage = Storage::init(); @@ -231,6 +235,7 @@ contract NonNativeToken { ) { let storage = Storage::init(); let public_balances = storage.public_balances; + let pending_shields = storage.pending_shields; // Decrease user's balance. let sender = inputs.call_context.msg_sender; @@ -241,16 +246,16 @@ contract NonNativeToken { // 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 - let note = TransparentNote::new(amount, secretHash); - - // Public oracle call to emit new commitment. - create_commitment(note.get_commitment()); + + // 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(inputs, &mut note); } // The shield function takes a public balance, and creates a commitment containing the amount of tokens - // in the private context. + // in the private context. fn redeemShield( inputs: PrivateContextInputs, amount: Field, @@ -258,14 +263,22 @@ contract NonNativeToken { owner: Field, ) -> distinct pub abi::PrivateCircuitPublicInputs { let storage = Storage::init(); + let pending_shields = storage.pending_shields; let mut context = Context::new(inputs, abi::hash_args([ amount, secret, owner ])); // Assert that the note exists within the tree - let public_note = TransparentNote::new_from_secret(amount, secret); - public_note.consume_in_secret(&mut context, inputs.roots.private_data_tree_root, secret); + let mut public_note = TransparentNote::new_from_secret(amount, secret); + + // Ensure that the note exists in the tree + pending_shields.assert_contains_note_hash(&mut context, &mut public_note); + // The above call to `assert_contains()` also returns a modified note with + // the header which is necessary for the next step (remove). + + // Consume note in secret! + pending_shields.remove(&mut context, public_note); // Mint the tokens let balance = storage.balances.at(owner); @@ -291,7 +304,7 @@ contract NonNativeToken { // enqueue a public function to perform the public state update. let thisAddress = inputs.call_context.storage_contract_address; - + // addUnshieldedBalance selector (in decimal) // recompute by: `cast keccak addUnshieldedBalance(field,field)` -> convert to decimal let pubEntryPointSelector = 753269941; @@ -318,7 +331,7 @@ contract NonNativeToken { ) -> Field { let storage = Storage::init(); let owner_balance = storage.balances.at(owner); - + balance_utils::get_balance(owner_balance.storage_slot) } @@ -327,6 +340,10 @@ contract NonNativeToken { // 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) + if (storage_slot == 2) { + note_utils::compute_note_hash_and_nullifier(TransparentNoteMethods, note_header, preimage) + } else { + note_utils::compute_note_hash_and_nullifier(ValueNoteMethods, note_header, preimage) + } } } diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/storage.nr b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/storage.nr index fba7386ea66..5b5e0329dd6 100644 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/storage.nr +++ b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/storage.nr @@ -1,11 +1,9 @@ -use dep::aztec::state_vars::{ - map::Map, - set::Set, - public_state::PublicState, - type_serialisation::field_serialisation::{ - FIELD_SERIALISED_LEN, - FieldSerialisationMethods, - }, +mod transparent_note; + +use crate::transparent_note::{ + TransparentNote, + TransparentNoteMethods, + TRANSPARENT_NOTE_LEN, }; use dep::value_note::value_note::{ @@ -14,17 +12,28 @@ use dep::value_note::value_note::{ VALUE_NOTE_LEN, }; +use dep::aztec::state_vars::{ + map::Map, + set::Set, + public_state::PublicState, +}; +use dep::aztec::state_vars::type_serialisation::field_serialisation::FieldSerialisationMethods; +use dep::aztec::state_vars::type_serialisation::field_serialisation::FIELD_SERIALISED_LEN; + struct Storage { balances: Map>, + pending_shields: Set, + public_balances: Map>, } impl Storage { fn init() -> Self { Storage { - balances: Map::new(1, |slot| Set::new(slot, ValueNoteMethods)), - public_balances: Map::new(2, |slot| PublicState::new(slot, FieldSerialisationMethods)), + balances: Map::new(1, |s| Set::new(s, ValueNoteMethods)), + pending_shields: Set::new(2, TransparentNoteMethods), + public_balances: Map::new(3, |s| PublicState::new(s, FieldSerialisationMethods)), } } } \ No newline at end of file diff --git a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/transparent_note.nr b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/transparent_note.nr index 9da37a4d38d..4ba940aa7e8 100644 --- a/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/transparent_note.nr +++ b/yarn-project/noir-contracts/src/contracts/non_native_token_contract/src/transparent_note.nr @@ -1,60 +1,97 @@ use dep::std::hash::pedersen; use dep::std::hash::pedersen_with_separator; -use dep::aztec::context::Context; -use dep::aztec::messaging::get_commitment_getter_data::make_commitment_getter_data; +use dep::aztec::note::{ + note_header::NoteHeader, + note_interface::NoteInterface, + utils::compute_siloed_note_hash, +}; use dep::aztec::oracle::{ - create_commitment::create_commitment, create_l2_to_l1_message::create_l2_to_l1_message, - get_commitment::get_commitment }; use dep::aztec::constants_gen::GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET; +global TRANSPARENT_NOTE_LEN: Field = 2; -// Transparent note represents a note that is created in the clear (public execution), -// but can only be spent by those that know the preimage of the "secretHash" +// Transparent note represents a note that is created in the clear (public execution), +// but can only be spent by those that know the preimage of the "secret_hash" struct TransparentNote { amount: Field, - secretHash: Field, + secret_hash: Field, + // the fields below are not serialised/deserialised + secret: Field, + header: NoteHeader, } impl TransparentNote { - fn new(amount: Field, secretHash: Field) -> Self { - Self { amount, secretHash } + + // CONSTRUCTORS + + fn new(amount: Field, secret_hash: Field) -> Self { + TransparentNote { + amount: amount, + secret_hash: secret_hash, + secret: 0, + header: NoteHeader::empty(), + } } + // new oracle call primitive + // get me the secret corresponding to this hash fn new_from_secret(amount: Field, secret: Field) -> Self { - let secretHash = TransparentNote::compute_secret_hash(secret); TransparentNote { - amount, - secretHash, + amount: amount, + secret_hash: TransparentNote::compute_secret_hash(secret), + secret: secret, + header: NoteHeader::empty(), } } - // Gets the value of the commitment - // TODO(maddiaa): this will need to be hashed with a slot to keep it unique, which slot to pick? https://github.com/AztecProtocol/aztec-packages/issues/847 - fn get_commitment(self: Self) -> Field { - pedersen([self.amount, self.secretHash])[0] + + // STANDARD NOTE_INTERFACE FUNCTIONS + + fn serialise(self) -> [Field; TRANSPARENT_NOTE_LEN] { + [self.amount, self.secret_hash] } - fn consume_in_secret(self: Self, context: &mut Context, root: Field, secret: Field) { - // Get the commitment value (before silo) - let innerCommitment = self.get_commitment(); + fn deserialise(preimage: [Field; TRANSPARENT_NOTE_LEN]) -> Self { + TransparentNote { + amount: preimage[0], + secret_hash: preimage[1], + secret: 0, + header: NoteHeader::empty(), + } + } - // Get the commitment data, (where it is in the db) - let commitment_oracle_call = get_commitment(innerCommitment); - let commitment_data = make_commitment_getter_data(commitment_oracle_call, 0); - let siloed_commitment = commitment_data.message; // oracle call returns siloed commitment + fn compute_note_hash(self) -> Field { + dep::std::hash::pedersen([ + self.amount, + self.secret_hash, + ])[0] + } - // Let the kernel perform the read. - context.push_read_request(siloed_commitment); + fn compute_nullifier(self) -> Field { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): should use + // `compute_note_hash_for_read_or_nullify` once public functions inject nonce! + let siloed_note_hash = compute_siloed_note_hash(TransparentNoteMethods, self); + pedersen([self.secret, siloed_note_hash])[0] + } - // Do we still need to do this with read requests? - assert(root == commitment_data.root); + fn dummy() -> Self { + TransparentNote { + amount: 0, + secret_hash: 0, + secret: 0, + header: NoteHeader::empty(), + } + } - // Calculate the nullifier - self.emit_nullifier(context, secret, siloed_commitment, commitment_data.leaf_index); + fn set_header(&mut self, header: NoteHeader) { + self.header = header; } + + // CUSTOM FUNCTIONS FOR THIS NOTE TYPE + fn compute_secret_hash(secret: Field) -> Field { // TODO(#1205) This is probably not the right index to use pedersen_with_separator([secret], GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET)[0] @@ -62,14 +99,44 @@ impl TransparentNote { fn knows_secret(self, secret: Field) { let hash = TransparentNote::compute_secret_hash(secret); - assert(self.secretHash == hash); + assert(self.secret_hash == hash); } +} - fn emit_nullifier(_self: Self, context: &mut Context, secret: Field, siloed_commitment: Field, index: Field) { - // Create a nullifier for the message based on its index in the tree - - let nullifier = pedersen([secret, siloed_commitment, index])[0]; - context.push_new_nullifier(nullifier, siloed_commitment) - } +fn deserialise(preimage: [Field; TRANSPARENT_NOTE_LEN]) -> TransparentNote { + TransparentNote::deserialise(preimage) +} + +fn serialise(note: TransparentNote) -> [Field; TRANSPARENT_NOTE_LEN] { + note.serialise() +} + +fn compute_note_hash(note: TransparentNote) -> Field { + note.compute_note_hash() +} + +fn compute_nullifier(note: TransparentNote) -> Field { + note.compute_nullifier() +} + +fn dummy() -> TransparentNote { + TransparentNote::dummy() +} + +fn get_header(note: TransparentNote) -> NoteHeader { + note.header +} + +fn set_header(note: &mut TransparentNote, header: NoteHeader) { + note.set_header(header) +} -} \ No newline at end of file +global TransparentNoteMethods = NoteInterface { + deserialise, + serialise, + compute_note_hash, + compute_nullifier, + dummy, + get_header, + set_header, +}; \ No newline at end of file diff --git a/yarn-project/noir-libs/noir-aztec/src/note/lifecycle.nr b/yarn-project/noir-libs/noir-aztec/src/note/lifecycle.nr index 87aa8047ffb..ee725e479ac 100644 --- a/yarn-project/noir-libs/noir-aztec/src/note/lifecycle.nr +++ b/yarn-project/noir-libs/noir-aztec/src/note/lifecycle.nr @@ -1,9 +1,11 @@ +use crate::abi::PublicContextInputs; use crate::context::Context; use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, utils::compute_inner_note_hash, }; +use crate::oracle::create_commitment::create_commitment; use crate::oracle::notes::{notify_created_note, notify_nullified_note}; use crate::constants_gen::EMPTY_NULLIFIED_COMMITMENT; use crate::types::option::Option; @@ -14,13 +16,12 @@ fn create_note( note: &mut Note, note_interface: NoteInterface, ) { - let mut inner_note_hash = 0; let contract_address = context.inputs.call_context.storage_contract_address; let header = NoteHeader { contract_address, storage_slot, nonce: 0 }; let set_header = note_interface.set_header; set_header(note, header); - inner_note_hash = compute_inner_note_hash(note_interface, *note); + let inner_note_hash = compute_inner_note_hash(note_interface, *note); let serialise = note_interface.serialise; let preimage = serialise(*note); @@ -29,6 +30,22 @@ fn create_note( context.push_new_note_hash(inner_note_hash); } +fn create_note_hash_from_public( + inputs: PublicContextInputs, + storage_slot: Field, + note: &mut Note, + note_interface: NoteInterface, +) { + let contract_address = inputs.call_context.storage_contract_address; + + let header = NoteHeader { contract_address, storage_slot, nonce: 0 }; + let set_header = note_interface.set_header; + set_header(note, header); + let inner_note_hash = compute_inner_note_hash(note_interface, *note); + + create_commitment(inner_note_hash); +} + fn destroy_note( context: &mut Context, storage_slot: Field, diff --git a/yarn-project/noir-libs/noir-aztec/src/note/note_getter.nr b/yarn-project/noir-libs/noir-aztec/src/note/note_getter.nr index 3a6c243c4d0..af17c68ceb2 100644 --- a/yarn-project/noir-libs/noir-aztec/src/note/note_getter.nr +++ b/yarn-project/noir-libs/noir-aztec/src/note/note_getter.nr @@ -9,9 +9,13 @@ use crate::context::Context; use crate::note::{ note_getter_options::NoteGetterOptions, note_interface::NoteInterface, + note_header::NoteHeader, utils::compute_note_hash_for_read_or_nullify, utils::compute_unique_siloed_note_hash, + utils::compute_inner_note_hash, + utils::compute_siloed_note_hash, }; +use crate::messaging::get_commitment_getter_data::make_commitment_getter_data; use crate::oracle; use crate::types::option::Option; @@ -48,6 +52,51 @@ fn ensure_note_exists( context.push_read_request(note_hash_for_read_request); } +// Ensure a note's hash exists in the tree without retrieving the entire +// notes via the oracle. +// Modifies the note by populating it with header info. +fn ensure_note_hash_exists( + context: &mut Context, + storage_slot: Field, + note_interface: NoteInterface, + note: &mut Note, +) { + // Initialize header of note. Must be done before computing note hashes as it initializes the: + // - storage slot (used in inner note hash) + // - the contract address (used in siloed note hash) + // - and the nonce (used in the unique siloed note hash) + let set_header = note_interface.set_header; + let note_header = NoteHeader { + contract_address: context.inputs.call_context.storage_contract_address, + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): should be + // real nonce (once public kernel applies nonces). + nonce: 0, + storage_slot + }; + set_header(note, note_header); + + // Get a note from oracle and early out if it doesn't exist. + let inner_note_hash = compute_inner_note_hash(note_interface, *note); + + let raw_oracle_ret = oracle::get_commitment::get_commitment(inner_note_hash); + let deserialized_oracle_ret = make_commitment_getter_data(raw_oracle_ret, 0); + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): should be + // unique_siloed_note_hash once public kernel applies nonces + let saved_siloed_note_hash = deserialized_oracle_ret.message; + + assert(saved_siloed_note_hash != 0); // TODO(dbanks12): necessary? + + check_note_header(*context, storage_slot, note_interface, *note); + + // Ensure that the note hash retrieved from oracle matches the one computed from note. + let computed_siloed_note_hash = compute_siloed_note_hash(note_interface, *note); + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): should be + // compute_note_hash_for_read_or_nullify once public kernel applies nonces + assert(computed_siloed_note_hash == saved_siloed_note_hash); + + context.push_read_request(computed_siloed_note_hash); +} + fn get_note( context: &mut Context, storage_slot: Field, diff --git a/yarn-project/noir-libs/noir-aztec/src/state_vars/set.nr b/yarn-project/noir-libs/noir-aztec/src/state_vars/set.nr index 2f5066ceb0c..28e9f3ccd38 100644 --- a/yarn-project/noir-libs/noir-aztec/src/state_vars/set.nr +++ b/yarn-project/noir-libs/noir-aztec/src/state_vars/set.nr @@ -1,11 +1,12 @@ +use crate::abi::PublicContextInputs; use crate::context::Context; -use crate::note::lifecycle::{create_note, destroy_note}; +use crate::note::lifecycle::{create_note, create_note_hash_from_public, destroy_note}; use crate::note::{ - note_getter::{get_notes, ensure_note_exists}, + note_getter::{get_notes, ensure_note_exists, ensure_note_hash_exists}, note_getter_options::NoteGetterOptions, note_interface::NoteInterface, + utils::compute_inner_note_hash, }; -use crate::oracle::create_commitment::create_commitment; use crate::types::option::Option; struct Set { @@ -22,16 +23,21 @@ impl Set { create_note(context, self.storage_slot, note, self.note_interface); } - fn insert_from_public(self, note: Note) { - let compute_note_hash = self.note_interface.compute_note_hash; - let note_hash = compute_note_hash(note); - create_commitment(note_hash); + fn insert_from_public(self, inputs: PublicContextInputs, note: &mut Note) { + create_note_hash_from_public(inputs, self.storage_slot, note, self.note_interface); } fn assert_contains(self, context: &mut Context, note: &mut Note) { ensure_note_exists(context, self.storage_slot, self.note_interface, note); } + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): rename to + // `assert_contains` and replace function above ^ once public kernel injects + // nonces to note hashes. + fn assert_contains_note_hash(self, context: &mut Context, note: &mut Note) { + ensure_note_hash_exists(context, self.storage_slot, self.note_interface, note) + } + fn remove(self, context: &mut Context, note: Note) { destroy_note(context, self.storage_slot, note, self.note_interface); } diff --git a/yarn-project/sequencer-client/src/simulator/public_executor.ts b/yarn-project/sequencer-client/src/simulator/public_executor.ts index 1949f13b9c3..bec33a138a3 100644 --- a/yarn-project/sequencer-client/src/simulator/public_executor.ts +++ b/yarn-project/sequencer-client/src/simulator/public_executor.ts @@ -93,20 +93,24 @@ export class WorldStateDB implements CommitmentsDB { return { message: message.toFieldArray(), - index, siblingPath: siblingPath.toFieldArray(), + index, }; } - public async getCommitmentOracle(address: AztecAddress, commitment: Fr): Promise { - const siloedCommitment = siloCommitment(await CircuitsWasm.get(), address, commitment); + public async getCommitmentOracle(address: AztecAddress, innerCommitment: Fr): Promise { + const siloedCommitment = siloCommitment(await CircuitsWasm.get(), address, innerCommitment); + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386): shoild be + // unique commitment that exists in tree (should be siloed and then made unique via + // nonce). Once public kernel or base rollup circuit injects nonces, this can be updated + // to use uniqueSiloedCommitment. const index = (await this.db.findLeafIndex(MerkleTreeId.PRIVATE_DATA_TREE, siloedCommitment.toBuffer()))!; const siblingPath = await this.db.getSiblingPath(MerkleTreeId.PRIVATE_DATA_TREE, index); return { commitment: siloedCommitment, - index, siblingPath: siblingPath.toFieldArray(), + index, }; }