From cf1ee1bf35adaab5a6f70c87290d6181c2e9ad90 Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 1 Feb 2024 15:12:31 +0100 Subject: [PATCH 01/11] fix --- .../aztec/src/history/contract_inclusion.nr | 3 +- .../aztec/src/history/note_inclusion.nr | 32 ++++++---------- .../aztec/src/history/note_validity.nr | 5 +-- .../aztec/src/history/nullifier_inclusion.nr | 24 +++++------- .../src/history/nullifier_non_inclusion.nr | 29 +++++--------- .../src/history/public_value_inclusion.nr | 19 +++++----- .../src/state_vars/stable_public_state.nr | 4 -- .../src/e2e_inclusion_proofs_contract.test.ts | 4 +- .../inclusion_proofs_contract/src/main.nr | 38 ++++--------------- 9 files changed, 53 insertions(+), 105 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr index 38ecf9e4d24a..6d2f88e48d98 100644 --- a/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr @@ -26,11 +26,10 @@ pub fn prove_contract_inclusion( contract_class_id: Field, initialization_hash: Field, portal_contract_address: EthAddress, - block_number: u32, // The block at which we'll prove that the public value exists context: PrivateContext ) -> AztecAddress { // 1) Get block header from oracle and ensure that the block is included in the archive. - // let block_header = context.get_header.at(block_number); + // let block_header = context.historical_header; // 2) Compute the contract address let contract_address = AztecAddress::compute_from_public_key( diff --git a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr index 15a2acc44edf..a8e9040a853a 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -10,31 +10,21 @@ use crate::{ oracle::get_membership_witness::get_note_hash_membership_witness, }; -pub fn prove_note_commitment_inclusion( - note_commitment: Field, - block_number: u32, // The block at which we'll prove that the note exists - context: PrivateContext -) { - // 1) Get block header from oracle and ensure that the block is included in the archive. - let header = context.get_header_at(block_number); - - // 2) Get the membership witness of the note in the note hash tree - let witness = get_note_hash_membership_witness(block_number, note_commitment); - - // 3) Prove that the commitment is in the note hash tree - assert( - header.state.partial.note_hash_tree.root - == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving note inclusion failed" - ); - // --> Now we have traversed the trees all the way up to archive root. -} - pub fn prove_note_inclusion( note_with_header: Note, - block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext ) where Note: NoteInterface { let note_commitment = compute_note_hash_for_read_or_nullify(note_with_header); - prove_note_commitment_inclusion(note_commitment, block_number, context); + // 1) Get the membership witness of the note in the note hash tree + let witness = get_note_hash_membership_witness( + context.historical_header.global_variables.block_number as u32, + note_commitment + ); + + // 2) Prove that the commitment is in the note hash tree + assert( + context.historical_header.state.partial.note_hash_tree.root + == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving note inclusion failed" + ); } diff --git a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr index 6742771d705f..23aed15df2c0 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -10,9 +10,8 @@ use crate::{ // A helper function that proves that a note is valid at the given block number pub fn prove_note_validity( note_with_header: Note, - block_number: u32, // The block at which we'll prove that the note exists context: &mut PrivateContext ) where Note: NoteInterface { - prove_note_inclusion(note_with_header, block_number, *context); - prove_note_not_nullified(note_with_header, block_number, context); + prove_note_inclusion(note_with_header, *context); + prove_note_not_nullified(note_with_header, context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr index 1d61313825e8..118ef5093896 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -5,26 +5,22 @@ use crate::{ oracle::get_nullifier_membership_witness::get_nullifier_membership_witness, }; -pub fn prove_nullifier_inclusion( - nullifier: Field, - block_number: u32, // The block at which we'll prove that the note exists - context: PrivateContext -) { - // 1) Get block header from oracle and ensure that the block hash is included in the archive. - let header = context.get_header_at(block_number); - - // 2) Get the membership witness of the nullifier - let witness = get_nullifier_membership_witness(block_number, nullifier); +pub fn prove_nullifier_inclusion(nullifier: Field, context: PrivateContext) { + // 1) Get the membership witness of the nullifier + let witness = get_nullifier_membership_witness( + context.historical_header.global_variables.block_number as u32, + nullifier + ); - // 3) Check that the witness we obtained matches the nullifier + // 2) Check that the witness we obtained matches the nullifier assert(witness.leaf_preimage.nullifier == nullifier, "Nullifier does not match value in witness"); - // 4) Compute the nullifier tree leaf + // 3) Compute the nullifier tree leaf let nullifier_leaf = witness.leaf_preimage.hash(); - // 5) Prove that the nullifier is in the nullifier tree + // 4) Prove that the nullifier is in the nullifier tree assert( - header.state.partial.nullifier_tree.root + context.historical_header.state.partial.nullifier_tree.root == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving nullifier inclusion failed" ); // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index 0c40d4d648b8..98212bcc6367 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -14,23 +14,24 @@ use crate::{ }, }; -pub fn prove_nullifier_non_inclusion( - nullifier: Field, - block_number: u32, // The block at which we'll prove that the nullifier does not exists - context: PrivateContext -) { - // 1) Get block header from oracle and ensure that the block is included in the archive. - let header = context.get_header_at(block_number); +pub fn prove_note_not_nullified( + note_with_header: Note, + context: &mut PrivateContext +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note_with_header, context); // 2) Get the membership witness of a low nullifier of the nullifier - let witness = get_low_nullifier_membership_witness(block_number, nullifier); + let witness = get_low_nullifier_membership_witness( + context.historical_header.global_variables.block_number as u32, + nullifier + ); // 3) Prove that the nullifier is not included in the nullifier tree // 3.a) Compute the low nullifier leaf and prove that it is in the nullifier tree let low_nullifier_leaf = witness.leaf_preimage.hash(); assert( - header.state.partial.nullifier_tree.root + context.historical_header.state.partial.nullifier_tree.root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" ); @@ -48,13 +49,3 @@ pub fn prove_nullifier_non_inclusion( // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier // was not yet included in the nullifier tree. } - -pub fn prove_note_not_nullified( - note_with_header: Note, - block_number: u32, // The block at which we'll prove that the note was not nullified - context: &mut PrivateContext -) where Note: NoteInterface { - let nullifier = compute_siloed_nullifier(note_with_header, context); - - prove_nullifier_non_inclusion(nullifier, block_number, *context); -} diff --git a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr index ad14fb77cb38..06202f1b475d 100644 --- a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -18,22 +18,21 @@ use crate::{ pub fn prove_public_value_inclusion( value: Field, // The value that we want to prove is in the public data tree storage_slot: Field, // The storage slot in which the value is stored - block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext ) { - // 1) Get block header from oracle and ensure that the block hash is included in the archive. - let header = context.get_header_at(block_number); - - // 2) Compute the leaf slot by siloing the storage slot with our own address + // 1) Compute the leaf slot by siloing the storage slot with our own address let public_value_leaf_slot = pedersen_hash( [context.this_address().to_field(), storage_slot], GENERATOR_INDEX__PUBLIC_LEAF_INDEX ); - // 3) Get the membership witness of the slot - let witness = get_public_data_witness(block_number, public_value_leaf_slot); + // 2) Get the membership witness of the slot + let witness = get_public_data_witness( + context.historical_header.global_variables.block_number as u32, + public_value_leaf_slot + ); - // 4) Check that the witness matches the corresponding public_value + // 3) Check that the witness matches the corresponding public_value let preimage = witness.leaf_preimage; // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` @@ -51,9 +50,9 @@ pub fn prove_public_value_inclusion( assert_eq(preimage.value, value, "Public value does not match the witness"); } - // 5) Prove that the leaf we validated is in the public data tree + // 4) Prove that the leaf we validated is in the public data tree assert( - header.state.partial.public_data_tree.root + context.historical_header.state.partial.public_data_tree.root == compute_merkle_root(preimage.hash(), witness.index, witness.path), "Proving public value inclusion failed" ); // --> Now we have traversed the trees all the way up to archive root and that way verified that a specific diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr index 013f059aef08..972ca889d877 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr @@ -47,9 +47,6 @@ impl StablePublicState { // Read the value from storage (using the public tree) let fields = storage_read(self.storage_slot); - // TODO: The block_number here can be removed when using the current header in the membership proof. - let block_number = private_context.get_header().global_variables.block_number; - // Loop over the fields and prove their inclusion in the public tree for i in 0..fields.len() { // TODO: Update membership proofs to use current header (Requires #4179) @@ -59,7 +56,6 @@ impl StablePublicState { prove_public_value_inclusion( fields[i], self.storage_slot + i, - block_number as u32, (*private_context), ) } diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 87821e95d11c..960089cb99b7 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -74,7 +74,7 @@ describe('e2e_inclusion_proofs_contract', () => { // Prove note inclusion in a given block. const ignoredCommitment = 0; // Not ignored only when the note doesn't exist await contract.methods - .test_note_inclusion_proof(owner, noteCreationBlockNumber, ignoredCommitment) + .test_note_inclusion_proof(owner, ignoredCommitment) .send() .wait(); } @@ -141,7 +141,7 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await getRandomBlockNumberSinceDeployment(); const randomNoteCommitment = Fr.random(); await expect( - contract.methods.test_note_inclusion_proof(owner, blockNumber, randomNoteCommitment).send().wait(), + contract.methods.test_note_inclusion_proof(owner, randomNoteCommitment).send().wait(), ).rejects.toThrow(`Leaf value: ${randomNoteCommitment.toString()} not found in NOTE_HASH_TREE`); }); }); diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 37517063ad35..aa21c63f098d 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -26,7 +26,6 @@ contract InclusionProofs { prove_contract_inclusion, }, note_inclusion::{ - prove_note_commitment_inclusion, prove_note_inclusion, }, note_validity::{ @@ -36,7 +35,6 @@ contract InclusionProofs { prove_nullifier_inclusion, }, nullifier_non_inclusion::{ - prove_nullifier_non_inclusion, prove_note_not_nullified, }, public_value_inclusion::{ @@ -101,7 +99,6 @@ contract InclusionProofs { #[aztec(private)] fn test_note_inclusion_proof( owner: AztecAddress, - block_number: u32, // The block at which we'll prove that the note exists // Value below is only used when the note is not found --> used to test the note inclusion failure case (it // allows me to pass in random value of note nullifier - I cannot add and fetch a random note from PXE because // PXE performs note commitment inclusion check when you add a new note). @@ -118,12 +115,9 @@ contract InclusionProofs { // 2) Prove the note inclusion if maybe_note.is_some() { // docs:start:prove_note_inclusion - prove_note_inclusion(maybe_note.unwrap_unchecked(), block_number, context); + prove_note_inclusion(maybe_note.unwrap_unchecked(), context); // docs:end:prove_note_inclusion - } else { - // Note was not found so we will prove inclusion of the spare commitment - prove_note_commitment_inclusion(spare_commitment, block_number, context); - }; + } } // Proves that the note was not yet nullified at block `block_number`. @@ -145,14 +139,9 @@ contract InclusionProofs { // 3) Compute the nullifier from the note if maybe_note.is_some() { // docs:start:prove_note_not_nullified - prove_note_not_nullified(maybe_note.unwrap_unchecked(), block_number, &mut context); + prove_note_not_nullified(maybe_note.unwrap_unchecked(), &mut context); // docs:end:prove_note_not_nullified - } else { - // Note was not found so we will use the spare nullifier - // docs:start:prove_nullifier_non_inclusion - prove_nullifier_non_inclusion(spare_nullifier, block_number, context); - // docs:end:prove_nullifier_non_inclusion - }; + } } #[aztec(private)] @@ -168,7 +157,7 @@ contract InclusionProofs { // 2) Prove the note validity // docs:start:prove_note_validity - prove_note_validity(note, block_number, &mut context); + prove_note_validity(note, &mut context); // docs:end:prove_note_validity } @@ -193,19 +182,14 @@ contract InclusionProofs { block_number: u32 // The block at which we'll prove that the nullifier not exists in the tree ) { // docs:start:prove_nullifier_inclusion - prove_nullifier_inclusion(nullifier, block_number, context); + prove_nullifier_inclusion(nullifier, context); // docs:end:prove_nullifier_inclusion } #[aztec(private)] fn test_public_unused_value_inclusion_proof(block_number: u32 // The block at which we'll prove that the public value exists ) { - prove_public_value_inclusion( - 0, - storage.public_unused_value.storage_slot, - block_number, - context - ); + prove_public_value_inclusion(0, storage.public_unused_value.storage_slot, context); } #[aztec(private)] @@ -213,12 +197,7 @@ contract InclusionProofs { public_value: Field, block_number: u32 // The block at which we'll prove that the public value exists ) { - prove_public_value_inclusion( - public_value, - storage.public_value.storage_slot, - block_number, - context - ); + prove_public_value_inclusion(public_value, storage.public_value.storage_slot, context); } // Proves that a contract exists at block `block_number`. @@ -244,7 +223,6 @@ contract InclusionProofs { contract_class_id, initialization_hash, portal_contract_address, - block_number, context ); // Here typically the factory would add the contract address to its internal map of deployed contracts. From 76ed29757ea11353401845aadc385e6add386bf9 Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 1 Feb 2024 15:50:14 +0100 Subject: [PATCH 02/11] fix --- .../aztec/src/history/note_inclusion.nr | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr index a8e9040a853a..8af849829523 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -3,7 +3,7 @@ use dep::std::merkle::compute_merkle_root; use crate::{ context::PrivateContext, note::{ - utils::compute_note_hash_for_read_or_nullify, + utils::compute_note_hash_for_consumption, note_header::NoteHeader, note_interface::NoteInterface, }, @@ -14,7 +14,7 @@ pub fn prove_note_inclusion( note_with_header: Note, context: PrivateContext ) where Note: NoteInterface { - let note_commitment = compute_note_hash_for_read_or_nullify(note_with_header); + let note_commitment = compute_note_hash_for_consumption(note_with_header); // 1) Get the membership witness of the note in the note hash tree let witness = get_note_hash_membership_witness( @@ -28,3 +28,24 @@ pub fn prove_note_inclusion( == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving note inclusion failed" ); } + +pub fn prove_note_inclusion_at( + note_with_header: Note, + block_number: u32, // The block at which we'll prove that the note exists + context: PrivateContext +) where Note: NoteInterface { + let note_commitment = compute_note_hash_for_consumption(note_with_header); + + // 1) Get block header from oracle and ensure that the block is included in the archive. + let header = context.get_header_at(block_number); + + // 2) Get the membership witness of the note in the note hash tree + let witness = get_note_hash_membership_witness(block_number, note_commitment); + + // 3) Prove that the commitment is in the note hash tree + assert( + header.state.partial.note_hash_tree.root + == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving note inclusion failed" + ); + // --> Now we have traversed the trees all the way up to archive root. +} From 69571858802053c5202392989414453835a48f66 Mon Sep 17 00:00:00 2001 From: Esau Date: Thu, 1 Feb 2024 17:02:03 +0100 Subject: [PATCH 03/11] next fix nullifier non incl test done tests --- .../aztec/src/history/contract_inclusion.nr | 28 ++++- .../aztec/src/history/note_validity.nr | 13 ++- .../aztec/src/history/nullifier_inclusion.nr | 26 +++++ .../src/history/nullifier_non_inclusion.nr | 37 +++++++ .../src/history/public_value_inclusion.nr | 45 ++++++++ .../src/state_vars/stable_public_state.nr | 1 - .../src/e2e_inclusion_proofs_contract.test.ts | 82 ++++++++++---- .../inclusion_proofs_contract/src/main.nr | 101 +++++++++++++----- 8 files changed, 285 insertions(+), 48 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr index 3f3910f27188..5b4469b20f51 100644 --- a/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr @@ -29,7 +29,7 @@ pub fn prove_contract_inclusion( portal_contract_address: EthAddress, context: PrivateContext ) -> AztecAddress { - // 1) Get block header from oracle and ensure that the block is included in the archive. + // 1) Get block header from context // let block_header = context.historical_header; // 2) Compute the contract address @@ -63,3 +63,29 @@ pub fn prove_contract_inclusion( contract_address } + +pub fn prove_contract_inclusion_at( + public_key: GrumpkinPoint, + contract_address_salt: Field, + contract_class_id: ContractClassId, + initialization_hash: Field, + portal_contract_address: EthAddress, + block_number: u32, + context: PrivateContext +) -> AztecAddress { + // 1) Get block header from oracle and ensure that the block is included in the archive. + let header = context.get_header_at(block_number); + + // 2) Compute the contract address + let contract_address = AztecAddress::compute_from_public_key( + public_key, + contract_class_id, + contract_address_salt, + initialization_hash, + portal_contract_address + ); + + // TODO(@spalladino): See above func to impl + + contract_address +} diff --git a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr index 23aed15df2c0..8f1a0edc4e49 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -2,12 +2,13 @@ use crate::{ context::PrivateContext, history::{ note_inclusion::prove_note_inclusion, + note_inclusion::prove_note_inclusion_at, nullifier_non_inclusion::prove_note_not_nullified, + nullifier_non_inclusion::prove_note_not_nullified_at, }, note::note_interface::NoteInterface, }; -// A helper function that proves that a note is valid at the given block number pub fn prove_note_validity( note_with_header: Note, context: &mut PrivateContext @@ -15,3 +16,13 @@ pub fn prove_note_validity( prove_note_inclusion(note_with_header, *context); prove_note_not_nullified(note_with_header, context); } + +// A helper function that proves that a note is valid at the given block number +pub fn prove_note_validity_at( + note_with_header: Note, + block_number: u32, + context: &mut PrivateContext +) where Note: NoteInterface { + prove_note_inclusion_at(note_with_header, block_number, *context); + prove_note_not_nullified_at(note_with_header, block_number, context); +} diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr index 118ef5093896..510445cfc1eb 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -26,3 +26,29 @@ pub fn prove_nullifier_inclusion(nullifier: Field, context: PrivateContext) { // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier // was not yet included in the nullifier tree. } + +pub fn prove_nullifier_inclusion_at( + nullifier: Field, + block_number: u32, // The block at which we'll prove that the note exists + context: PrivateContext +) { + // 1) Get block header from oracle and ensure that the block hash is included in the archive. + let header = context.get_header_at(block_number); + + // 2) Get the membership witness of the nullifier + let witness = get_nullifier_membership_witness(block_number, nullifier); + + // 3) Check that the witness we obtained matches the nullifier + assert(witness.leaf_preimage.nullifier == nullifier, "Nullifier does not match value in witness"); + + // 4) Compute the nullifier tree leaf + let nullifier_leaf = witness.leaf_preimage.hash(); + + // 5) Prove that the nullifier is in the nullifier tree + assert( + header.state.partial.nullifier_tree.root + == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving nullifier inclusion failed" + ); + // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier + // was not yet included in the nullifier tree. +} diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index 98212bcc6367..25e5b4352c96 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -49,3 +49,40 @@ pub fn prove_note_not_nullified( // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier // was not yet included in the nullifier tree. } + +pub fn prove_note_not_nullified_at( + note_with_header: Note, + block_number: u32, // The block at which we'll prove that the note was not nullified + context: &mut PrivateContext +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note_with_header, context); + + // 1) Get block header from oracle and ensure that the block is included in the archive. + let header = context.get_header_at(block_number); + + // 2) Get the membership witness of a low nullifier of the nullifier + let witness = get_low_nullifier_membership_witness(block_number, nullifier); + + // 3) Prove that the nullifier is not included in the nullifier tree + + // 3.a) Compute the low nullifier leaf and prove that it is in the nullifier tree + let low_nullifier_leaf = witness.leaf_preimage.hash(); + assert( + header.state.partial.nullifier_tree.root + == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" + ); + + // 3.b) Prove that the low nullifier is smaller than the nullifier + assert( + full_field_less_than(witness.leaf_preimage.nullifier, nullifier), "Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed" + ); + + // 3.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not + // included in the nullifier tree (or to 0 if the to-be-inserted nullifier is the largest of all) + assert( + full_field_greater_than(witness.leaf_preimage.next_nullifier, nullifier) + | (witness.leaf_preimage.next_index == 0), "Proving nullifier non-inclusion failed: low_nullifier.next_value > nullifier.value check failed" + ); + // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier + // was not yet included in the nullifier tree. +} diff --git a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr index 06202f1b475d..1854ce14a484 100644 --- a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -58,3 +58,48 @@ pub fn prove_public_value_inclusion( // --> Now we have traversed the trees all the way up to archive root and that way verified that a specific // `value` was really set in a given contract storage slot at block `block_number` in public data tree. } + +pub fn prove_public_value_inclusion_at( + value: Field, // The value that we want to prove is in the public data tree + storage_slot: Field, // The storage slot in which the value is stored + block_number: u32, // The block at which we'll prove that the note exists + context: PrivateContext +) { + // 1) Get block header from oracle and ensure that the block hash is included in the archive. + let header = context.get_header_at(block_number); + + // 2) Compute the leaf slot by siloing the storage slot with our own address + let public_value_leaf_slot = pedersen_hash( + [context.this_address().to_field(), storage_slot], + GENERATOR_INDEX__PUBLIC_LEAF_INDEX + ); + + // 3) Get the membership witness of the slot + let witness = get_public_data_witness(block_number, public_value_leaf_slot); + + // 4) Check that the witness matches the corresponding public_value + let preimage = witness.leaf_preimage; + + // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` + // 1. The value is the same as the one in the witness + // 2. The value was never initialized and is zero + let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); + let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); + let is_max = ((preimage.next_index == 0) & (preimage.next_slot == 0)); + let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); + + if is_in_range { + assert_eq(value, 0, "Non-existant public data leaf value is non-zero"); + } else { + assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot don't match witness"); + assert_eq(preimage.value, value, "Public value does not match the witness"); + } + + // 5) Prove that the leaf we validated is in the public data tree + assert( + header.state.partial.public_data_tree.root + == compute_merkle_root(preimage.hash(), witness.index, witness.path), "Proving public value inclusion failed" + ); + // --> Now we have traversed the trees all the way up to archive root and that way verified that a specific + // `value` was really set in a given contract storage slot at block `block_number` in public data tree. +} diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr index 972ca889d877..77a70a4ae531 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr @@ -51,7 +51,6 @@ impl StablePublicState { for i in 0..fields.len() { // TODO: Update membership proofs to use current header (Requires #4179) // Currently executing unnecessary computation: - // - a membership proof of the header(block_number) in the history // - a membership proof of the value in the public tree of the header prove_public_value_inclusion( fields[i], diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 960089cb99b7..c9e6025d935e 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -72,20 +72,39 @@ describe('e2e_inclusion_proofs_contract', () => { { // Prove note inclusion in a given block. - const ignoredCommitment = 0; // Not ignored only when the note doesn't exist await contract.methods - .test_note_inclusion_proof(owner, ignoredCommitment) + .test_note_inclusion(owner, true, noteCreationBlockNumber) .send() .wait(); + + await contract.methods + .test_note_inclusion(owner, false, 0n) + .send() + .wait(); + } + + { + // Prove that the note has not been nullified with block_number + // TODO(#3535): Prove the nullifier non-inclusion at older block to test archival node. This is currently not + // possible because of issue https://github.com/AztecProtocol/aztec-packages/issues/3535 + const blockNumber = await pxe.getBlockNumber(); + await contract.methods.test_note_not_nullified(owner, true, blockNumber, false).send().wait(); + await contract.methods.test_note_not_nullified(owner, false, 0n, false).send().wait(); + } + + { + const blockNumber = await pxe.getBlockNumber(); + await contract.methods.test_note_validity(owner, true, blockNumber).send().wait(); + await contract.methods.test_note_validity(owner, false, 0n).send().wait(); } { - // Prove that the note has not been nullified + // Prove that the note has not been nullified with block_number // TODO(#3535): Prove the nullifier non-inclusion at older block to test archival node. This is currently not // possible because of issue https://github.com/AztecProtocol/aztec-packages/issues/3535 const blockNumber = await pxe.getBlockNumber(); - const ignoredNullifier = 0; // Not ignored only when the note doesn't exist - await contract.methods.test_nullifier_non_inclusion_proof(owner, blockNumber, ignoredNullifier).send().wait(); + await contract.methods.test_note_not_nullified(owner, true, blockNumber, false).send().wait(); + await contract.methods.test_note_not_nullified(owner, false, 0n, false).send().wait(); } { @@ -94,16 +113,25 @@ describe('e2e_inclusion_proofs_contract', () => { const { newNullifiers } = receipt.debugInfo!; expect(newNullifiers.length).toBe(2); - const blockNumber = await pxe.getBlockNumber(); + const currentBlockNumber = await pxe.getBlockNumber(); const nullifier = newNullifiers[1]; + const randomBlockNumberSinceDeployment = await getRandomBlockNumberSinceDeployment(); + // Note: getLowNullifierMembershipWitness returns the membership witness of the nullifier itself and not // the low nullifier when the nullifier already exists in the tree and for this reason the execution fails // on low_nullifier.value < nullifier.value check. await expect( - contract.methods.test_nullifier_non_inclusion_proof(owner, blockNumber, nullifier).send().wait(), - ).rejects.toThrowError( + contract.methods.test_note_not_nullified(owner, true, currentBlockNumber, true).send().wait(), + ).rejects.toThrow( /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, ); + await expect( + contract.methods.test_note_not_nullified(owner, false, 0n, true).send().wait(), + ).rejects.toThrow( + /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + ); + // This does not throw because we are checking nullified note before the note was nullified + await contract.methods.test_note_not_nullified(owner, true, randomBlockNumberSinceDeployment, true).send().wait(); } }); @@ -128,21 +156,26 @@ describe('e2e_inclusion_proofs_contract', () => { { // Prove note validity - await contract.methods.test_note_validity_proof(owner, noteCreationBlockNumber).send().wait(); + await contract.methods.test_note_validity(owner, true, noteCreationBlockNumber).send().wait(); + await contract.methods.test_note_validity(owner, false, 0n).send().wait(); } }); it('note existence failure case', async () => { // Owner of a note - ignored in the contract since the note won't be found and the spare random note commitment // will be used instead - const owner = AztecAddress.random(); + const owner = AztecAddress.fromField(new Fr(88n)); // Choose random block number between deployment and current block number to test archival node const blockNumber = await getRandomBlockNumberSinceDeployment(); - const randomNoteCommitment = Fr.random(); + await expect( - contract.methods.test_note_inclusion_proof(owner, randomNoteCommitment).send().wait(), - ).rejects.toThrow(`Leaf value: ${randomNoteCommitment.toString()} not found in NOTE_HASH_TREE`); + contract.methods.test_note_inclusion_fail_case(owner, true, blockNumber).send().wait(), + ).rejects.toThrow(/Leaf value: .* not found in NOTE_HASH_TREE/); + + await expect( + contract.methods.test_note_inclusion_fail_case(owner, false, 0n).send().wait(), + ).rejects.toThrow(/Leaf value: .* not found in NOTE_HASH_TREE/); }); }); @@ -151,7 +184,8 @@ describe('e2e_inclusion_proofs_contract', () => { // Choose random block number between deployment and current block number to test archival node const blockNumber = await getRandomBlockNumberSinceDeployment(); - await contract.methods.test_public_value_inclusion_proof(publicValue, blockNumber).send().wait(); + await contract.methods.test_public_value_inclusion(publicValue, true, blockNumber).send().wait(); + await contract.methods.test_public_value_inclusion(publicValue, false, 0n).send().wait(); }); it('public value existence failure case', async () => { @@ -159,13 +193,16 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await getRandomBlockNumber(); const randomPublicValue = Fr.random(); await expect( - contract.methods.test_public_value_inclusion_proof(randomPublicValue, blockNumber).send().wait(), + contract.methods.test_public_value_inclusion(randomPublicValue, true, blockNumber).send().wait(), + ).rejects.toThrow('Public value does not match the witness'); + await expect( + contract.methods.test_public_value_inclusion(randomPublicValue, false, 0n).send().wait(), ).rejects.toThrow('Public value does not match the witness'); }); it('proves existence of uninitialized public value', async () => { const blockNumber = await getRandomBlockNumber(); - await contract.methods.test_public_unused_value_inclusion_proof(blockNumber).send().wait(); + await contract.methods.test_public_unused_value_inclusion(blockNumber).send().wait(); }); }); @@ -176,7 +213,8 @@ describe('e2e_inclusion_proofs_contract', () => { const block = await pxe.getBlock(blockNumber); const nullifier = block?.newNullifiers[0]; - await contract.methods.test_nullifier_inclusion_proof(nullifier!, blockNumber).send().wait(); + await contract.methods.test_nullifier_inclusion(nullifier!, true, blockNumber).send().wait(); + await contract.methods.test_nullifier_inclusion(nullifier!, false, 0n).send().wait(); }); it('nullifier existence failure case', async () => { @@ -185,7 +223,11 @@ describe('e2e_inclusion_proofs_contract', () => { const randomNullifier = Fr.random(); await expect( - contract.methods.test_nullifier_inclusion_proof(randomNullifier, blockNumber).send().wait(), + contract.methods.test_nullifier_inclusion(randomNullifier, true, blockNumber).send().wait(), + ).rejects.toThrow(`Low nullifier witness not found for nullifier ${randomNullifier.toString()} at block`); + + await expect( + contract.methods.test_nullifier_inclusion(randomNullifier, false, 0n).send().wait(), ).rejects.toThrow(`Low nullifier witness not found for nullifier ${randomNullifier.toString()} at block`); }); }); @@ -221,7 +263,7 @@ describe('e2e_inclusion_proofs_contract', () => { // Note: We pass in preimage of AztecAddress instead of just AztecAddress in order for the contract to be able to // test that the contract was deployed with correct constructor parameters. await contract.methods - .test_contract_inclusion_proof( + .test_contract_inclusion( publicKey, contractAddressSalt, contractClassId, @@ -242,7 +284,7 @@ describe('e2e_inclusion_proofs_contract', () => { await expect( contract.methods - .test_contract_inclusion_proof( + .test_contract_inclusion( publicKey, contractAddressSalt, contractClassId, diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 073b3f6e303b..8a21e960a5b3 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -18,6 +18,7 @@ contract InclusionProofs { context::Context, note::{ note_getter_options::NoteGetterOptions, + note_getter_options::NoteStatus, note_header::NoteHeader, utils as note_utils, }, @@ -25,21 +26,27 @@ contract InclusionProofs { history::{ contract_inclusion::{ prove_contract_inclusion, + prove_contract_inclusion_at, }, note_inclusion::{ prove_note_inclusion, + prove_note_inclusion_at, }, note_validity::{ prove_note_validity, + prove_note_validity_at, }, nullifier_inclusion::{ prove_nullifier_inclusion, + prove_nullifier_inclusion_at, }, nullifier_non_inclusion::{ prove_note_not_nullified, + prove_note_not_nullified_at, }, public_value_inclusion::{ prove_public_value_inclusion, + prove_public_value_inclusion_at, }, }, // docs:end:imports @@ -96,14 +103,11 @@ contract InclusionProofs { } // docs:end:create_note - // Proves that the owner owned a ValueNote at block `block_number`. #[aztec(private)] - fn test_note_inclusion_proof( + fn test_note_inclusion( owner: AztecAddress, - // Value below is only used when the note is not found --> used to test the note inclusion failure case (it - // allows me to pass in random value of note nullifier - I cannot add and fetch a random note from PXE because - // PXE performs note commitment inclusion check when you add a new note). - spare_commitment: Field + use_block_number: bool, + block_number: u32 // The block at which we'll prove that the note exists ) { // docs:start:get_note_from_pxe // 1) Get the note from PXE. @@ -114,40 +118,62 @@ contract InclusionProofs { // docs:end:get_note_from_pxe // 2) Prove the note inclusion - if maybe_note.is_some() { - // docs:start:prove_note_inclusion + if (use_block_number) { + prove_note_inclusion_at(maybe_note.unwrap_unchecked(), block_number, context); + } else { prove_note_inclusion(maybe_note.unwrap_unchecked(), context); - // docs:end:prove_note_inclusion + } + } + + #[aztec(private)] + fn test_note_inclusion_fail_case( + owner: AztecAddress, + use_block_number: bool, + block_number: u32 // The block at which we'll prove that the note exists + ) { + let mut note = ValueNote::new(1, owner); + + if (use_block_number) { + prove_note_inclusion_at(note, block_number, context); + } else { + prove_note_inclusion(note, context); } } // Proves that the note was not yet nullified at block `block_number`. #[aztec(private)] - fn test_nullifier_non_inclusion_proof( + fn test_note_not_nullified( owner: AztecAddress, + use_block_number: bool, block_number: u32, // The block at which we'll prove that the nullifier does not exists // Value below is only used when the note is not found --> used to test the nullifier non-inclusion failure // case (it allows me to pass in random value of note nullifier - I cannot add and fetch a random note from PXE // because PXE performs note commitment inclusion check when you add a new note). - spare_nullifier: Field + fail_case: bool ) { // 2) Get the note from PXE let private_values = storage.private_values.at(owner); - let options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); + let mut options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); + if (fail_case) { + options = options.set_status(NoteStatus.ACTIVE_OR_NULLIFIED); + } let notes = private_values.get_notes(options); let maybe_note = notes[0]; // 3) Compute the nullifier from the note - if maybe_note.is_some() { - // docs:start:prove_note_not_nullified + // docs:start:prove_note_not_nullified + if (use_block_number) { + prove_note_not_nullified_at(maybe_note.unwrap_unchecked(), block_number, &mut context); + } else { prove_note_not_nullified(maybe_note.unwrap_unchecked(), &mut context); - // docs:end:prove_note_not_nullified } + // docs:end:prove_note_not_nullified } #[aztec(private)] - fn test_note_validity_proof( + fn test_note_validity( owner: AztecAddress, + use_block_number: bool, block_number: u32 // The block at which we'll prove that the note exists and is not nullified ) { // 1) Get the note from PXE. @@ -158,7 +184,11 @@ contract InclusionProofs { // 2) Prove the note validity // docs:start:prove_note_validity - prove_note_validity(note, &mut context); + if (use_block_number) { + prove_note_validity_at(note, block_number, &mut context); + } else { + prove_note_validity(note, &mut context); + } // docs:end:prove_note_validity } @@ -178,27 +208,47 @@ contract InclusionProofs { // Note: I am not getting a nullifier of the note that was created in this contract in this function because it is // currently not possible to obtain a nullified note from PXE. #[aztec(private)] - fn test_nullifier_inclusion_proof( + fn test_nullifier_inclusion( nullifier: Field, + use_block_number: bool, block_number: u32 // The block at which we'll prove that the nullifier not exists in the tree ) { // docs:start:prove_nullifier_inclusion - prove_nullifier_inclusion(nullifier, context); + if (use_block_number) { + prove_nullifier_inclusion_at(nullifier, block_number, context); + } else { + prove_nullifier_inclusion(nullifier, context); + } // docs:end:prove_nullifier_inclusion } #[aztec(private)] - fn test_public_unused_value_inclusion_proof(block_number: u32 // The block at which we'll prove that the public value exists + fn test_public_unused_value_inclusion(block_number: u32 // The block at which we'll prove that the public value exists ) { - prove_public_value_inclusion(0, storage.public_unused_value.storage_slot, context); + prove_public_value_inclusion_at( + 0, + storage.public_unused_value.storage_slot, + block_number, + context + ); } #[aztec(private)] - fn test_public_value_inclusion_proof( + fn test_public_value_inclusion( public_value: Field, + use_block_number: bool, block_number: u32 // The block at which we'll prove that the public value exists ) { - prove_public_value_inclusion(public_value, storage.public_value.storage_slot, context); + if (use_block_number) { + prove_public_value_inclusion_at( + public_value, + storage.public_value.storage_slot, + block_number, + context + ); + } else { + prove_public_value_inclusion(public_value, storage.public_value.storage_slot, context); + } } // Proves that a contract exists at block `block_number`. @@ -210,7 +260,7 @@ contract InclusionProofs { // that it is what it expects. The constructor param check is the reason of why we pass in the preimage of // contract's aztec address instead of just the address. #[aztec(private)] - fn test_contract_inclusion_proof( + fn test_contract_inclusion( public_key: GrumpkinPoint, contract_address_salt: Field, contract_class_id: ContractClassId, @@ -218,12 +268,13 @@ contract InclusionProofs { portal_contract_address: EthAddress, block_number: u32 // The block at which we'll prove that the public value exists ) { - let proven_contract_address = prove_contract_inclusion( + let proven_contract_address = prove_contract_inclusion_at( public_key, contract_address_salt, contract_class_id, initialization_hash, portal_contract_address, + block_number, context ); // Here typically the factory would add the contract address to its internal map of deployed contracts. From df85ca2cc68c3f79aad93610e226a2b25a621432 Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 2 Feb 2024 21:47:56 +0100 Subject: [PATCH 04/11] address comments nr --- .../aztec/src/history/note_inclusion.nr | 44 +++++------- .../aztec/src/history/note_validity.nr | 17 ++--- .../aztec/src/history/nullifier_inclusion.nr | 51 ++++++++------ .../src/history/nullifier_non_inclusion.nr | 64 +++++++----------- .../src/history/public_value_inclusion.nr | 67 ++++++++----------- .../src/state_vars/stable_public_state.nr | 1 + .../inclusion_proofs_contract/src/main.nr | 9 ++- 7 files changed, 115 insertions(+), 138 deletions(-) diff --git a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr index 8af849829523..a2da086e82e0 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -1,4 +1,5 @@ use dep::std::merkle::compute_merkle_root; +use dep::protocol_types::header::Header; use crate::{ context::PrivateContext, @@ -10,42 +11,29 @@ use crate::{ oracle::get_membership_witness::get_note_hash_membership_witness, }; -pub fn prove_note_inclusion( - note_with_header: Note, - context: PrivateContext -) where Note: NoteInterface { - let note_commitment = compute_note_hash_for_consumption(note_with_header); +fn _note_inclusion(note: Note, header: Header) where Note: NoteInterface { + // 1) Compute note_hash + let note_hash = compute_note_hash_for_consumption(note); - // 1) Get the membership witness of the note in the note hash tree - let witness = get_note_hash_membership_witness( - context.historical_header.global_variables.block_number as u32, - note_commitment - ); + // 2) Get the membership witness of the note in the note hash tree + let witness = get_note_hash_membership_witness(header.global_variables.block_number as u32, note_hash); - // 2) Prove that the commitment is in the note hash tree - assert( - context.historical_header.state.partial.note_hash_tree.root - == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving note inclusion failed" + // 3) Prove that the commitment is in the note hash tree + assert_eq( + header.state.partial.note_hash_tree.root, compute_merkle_root(note_hash, witness.index, witness.path), "Proving note inclusion failed" ); } -pub fn prove_note_inclusion_at( - note_with_header: Note, +pub fn prove_note_inclusion(note: Note, context: PrivateContext) where Note: NoteInterface { + _note_inclusion(note, context.historical_header); +} + +pub fn prove_note_inclusion_at( + note: Note, block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext ) where Note: NoteInterface { - let note_commitment = compute_note_hash_for_consumption(note_with_header); - - // 1) Get block header from oracle and ensure that the block is included in the archive. let header = context.get_header_at(block_number); - // 2) Get the membership witness of the note in the note hash tree - let witness = get_note_hash_membership_witness(block_number, note_commitment); - - // 3) Prove that the commitment is in the note hash tree - assert( - header.state.partial.note_hash_tree.root - == compute_merkle_root(note_commitment, witness.index, witness.path), "Proving note inclusion failed" - ); - // --> Now we have traversed the trees all the way up to archive root. + _note_inclusion(note, header); } diff --git a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr index 8f1a0edc4e49..df4f998cb6c5 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -9,20 +9,17 @@ use crate::{ note::note_interface::NoteInterface, }; -pub fn prove_note_validity( - note_with_header: Note, - context: &mut PrivateContext -) where Note: NoteInterface { - prove_note_inclusion(note_with_header, *context); - prove_note_not_nullified(note_with_header, context); +pub fn prove_note_validity(note: Note, context: &mut PrivateContext) where Note: NoteInterface { + prove_note_inclusion(note, *context); + prove_note_not_nullified(note, context); } // A helper function that proves that a note is valid at the given block number -pub fn prove_note_validity_at( - note_with_header: Note, +pub fn prove_note_validity_at( + note: Note, block_number: u32, context: &mut PrivateContext ) where Note: NoteInterface { - prove_note_inclusion_at(note_with_header, block_number, *context); - prove_note_not_nullified_at(note_with_header, block_number, context); + prove_note_inclusion_at(note, block_number, *context); + prove_note_not_nullified_at(note, block_number, context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr index 510445cfc1eb..0a4b7f986c74 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -1,16 +1,19 @@ use dep::std::merkle::compute_merkle_root; +use dep::protocol_types::header::Header; use crate::{ context::PrivateContext, oracle::get_nullifier_membership_witness::get_nullifier_membership_witness, + note::{ + utils::compute_siloed_nullifier, + note_header::NoteHeader, + note_interface::NoteInterface, + }, }; -pub fn prove_nullifier_inclusion(nullifier: Field, context: PrivateContext) { +fn _nullifier_inclusion(nullifier: Field, header: Header) { // 1) Get the membership witness of the nullifier - let witness = get_nullifier_membership_witness( - context.historical_header.global_variables.block_number as u32, - nullifier - ); + let witness = get_nullifier_membership_witness(header.global_variables.block_number as u32, nullifier); // 2) Check that the witness we obtained matches the nullifier assert(witness.leaf_preimage.nullifier == nullifier, "Nullifier does not match value in witness"); @@ -20,35 +23,43 @@ pub fn prove_nullifier_inclusion(nullifier: Field, context: PrivateContext) { // 4) Prove that the nullifier is in the nullifier tree assert( - context.historical_header.state.partial.nullifier_tree.root + header.state.partial.nullifier_tree.root == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving nullifier inclusion failed" ); // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier // was not yet included in the nullifier tree. } +pub fn prove_nullifier_inclusion(nullifier: Field, context: PrivateContext) { + _nullifier_inclusion(nullifier, context.historical_header); +} + pub fn prove_nullifier_inclusion_at( nullifier: Field, block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext ) { - // 1) Get block header from oracle and ensure that the block hash is included in the archive. let header = context.get_header_at(block_number); - // 2) Get the membership witness of the nullifier - let witness = get_nullifier_membership_witness(block_number, nullifier); + _nullifier_inclusion(nullifier, header); +} - // 3) Check that the witness we obtained matches the nullifier - assert(witness.leaf_preimage.nullifier == nullifier, "Nullifier does not match value in witness"); +pub fn prove_note_is_nullified( + note: Note, + context: &mut PrivateContext +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note, context); - // 4) Compute the nullifier tree leaf - let nullifier_leaf = witness.leaf_preimage.hash(); + _nullifier_inclusion(nullifier, context.historical_header); +} - // 5) Prove that the nullifier is in the nullifier tree - assert( - header.state.partial.nullifier_tree.root - == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving nullifier inclusion failed" - ); - // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier - // was not yet included in the nullifier tree. +pub fn prove_note_is_nullified_at( + note: Note, + block_number: u32, + context: &mut PrivateContext +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note, context); + let header = context.get_header_at(block_number); + + _nullifier_inclusion(nullifier, header); } diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index 25e5b4352c96..2f7319c8a9c5 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -1,4 +1,5 @@ use dep::std::merkle::compute_merkle_root; +use dep::protocol_types::header::Header; use crate::{ context::PrivateContext, @@ -14,24 +15,16 @@ use crate::{ }, }; -pub fn prove_note_not_nullified( - note_with_header: Note, - context: &mut PrivateContext -) where Note: NoteInterface { - let nullifier = compute_siloed_nullifier(note_with_header, context); - - // 2) Get the membership witness of a low nullifier of the nullifier - let witness = get_low_nullifier_membership_witness( - context.historical_header.global_variables.block_number as u32, - nullifier - ); +fn _nullifier_non_inclusion(nullifier: Field, header: Header) { + // 1) Get the membership witness of a low nullifier of the nullifier + let witness = get_low_nullifier_membership_witness(header.global_variables.block_number as u32, nullifier); // 3) Prove that the nullifier is not included in the nullifier tree // 3.a) Compute the low nullifier leaf and prove that it is in the nullifier tree let low_nullifier_leaf = witness.leaf_preimage.hash(); assert( - context.historical_header.state.partial.nullifier_tree.root + header.state.partial.nullifier_tree.root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" ); @@ -50,39 +43,32 @@ pub fn prove_note_not_nullified( // was not yet included in the nullifier tree. } +pub fn prove_nullifier_not_included(nullifier: Field, context: PrivateContext) { + _nullifier_non_inclusion(nullifier, context.historical_header); +} + +pub fn prove_nullifier_not_included_at(nullifier: Field, block_number: u32, context: PrivateContext) { + let header = context.get_header_at(block_number); + + _nullifier_non_inclusion(nullifier, header); +} + +pub fn prove_note_not_nullified( + note_with_header: Note, + context: &mut PrivateContext +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note_with_header, context); + + _nullifier_non_inclusion(nullifier, context.historical_header); +} + pub fn prove_note_not_nullified_at( note_with_header: Note, block_number: u32, // The block at which we'll prove that the note was not nullified context: &mut PrivateContext ) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note_with_header, context); - - // 1) Get block header from oracle and ensure that the block is included in the archive. let header = context.get_header_at(block_number); - // 2) Get the membership witness of a low nullifier of the nullifier - let witness = get_low_nullifier_membership_witness(block_number, nullifier); - - // 3) Prove that the nullifier is not included in the nullifier tree - - // 3.a) Compute the low nullifier leaf and prove that it is in the nullifier tree - let low_nullifier_leaf = witness.leaf_preimage.hash(); - assert( - header.state.partial.nullifier_tree.root - == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" - ); - - // 3.b) Prove that the low nullifier is smaller than the nullifier - assert( - full_field_less_than(witness.leaf_preimage.nullifier, nullifier), "Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed" - ); - - // 3.c) Prove that the low nullifier is pointing "over" the nullifier to prove that the nullifier is not - // included in the nullifier tree (or to 0 if the to-be-inserted nullifier is the largest of all) - assert( - full_field_greater_than(witness.leaf_preimage.next_nullifier, nullifier) - | (witness.leaf_preimage.next_index == 0), "Proving nullifier non-inclusion failed: low_nullifier.next_value > nullifier.value check failed" - ); - // --> Now we have traversed the trees all the way up to archive root and verified that the nullifier - // was not yet included in the nullifier tree. + _nullifier_non_inclusion(nullifier, header); } diff --git a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr index 1854ce14a484..1a6ecb2f117f 100644 --- a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -1,6 +1,11 @@ use dep::protocol_types::{ constants::GENERATOR_INDEX__PUBLIC_LEAF_INDEX, + header::Header, hash::pedersen_hash, + address::{ + AztecAddress, + EthAddress, + }, }; use dep::std::merkle::compute_merkle_root; @@ -18,64 +23,46 @@ use crate::{ pub fn prove_public_value_inclusion( value: Field, // The value that we want to prove is in the public data tree storage_slot: Field, // The storage slot in which the value is stored + contract_address: AztecAddress, // The contract we want to look into context: PrivateContext ) { - // 1) Compute the leaf slot by siloing the storage slot with our own address - let public_value_leaf_slot = pedersen_hash( - [context.this_address().to_field(), storage_slot], - GENERATOR_INDEX__PUBLIC_LEAF_INDEX - ); - - // 2) Get the membership witness of the slot - let witness = get_public_data_witness( - context.historical_header.global_variables.block_number as u32, - public_value_leaf_slot - ); - - // 3) Check that the witness matches the corresponding public_value - let preimage = witness.leaf_preimage; - - // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` - // 1. The value is the same as the one in the witness - // 2. The value was never initialized and is zero - let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); - let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); - let is_max = ((preimage.next_index == 0) & (preimage.next_slot == 0)); - let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); - - if is_in_range { - assert_eq(value, 0, "Non-existant public data leaf value is non-zero"); - } else { - assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot don't match witness"); - assert_eq(preimage.value, value, "Public value does not match the witness"); - } - - // 4) Prove that the leaf we validated is in the public data tree - assert( - context.historical_header.state.partial.public_data_tree.root - == compute_merkle_root(preimage.hash(), witness.index, witness.path), "Proving public value inclusion failed" + _public_value_inclusion( + value, + storage_slot, + contract_address, + context.historical_header ); - // --> Now we have traversed the trees all the way up to archive root and that way verified that a specific - // `value` was really set in a given contract storage slot at block `block_number` in public data tree. } pub fn prove_public_value_inclusion_at( value: Field, // The value that we want to prove is in the public data tree storage_slot: Field, // The storage slot in which the value is stored + contract_address: AztecAddress, // The contract we want to look into block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext ) { - // 1) Get block header from oracle and ensure that the block hash is included in the archive. let header = context.get_header_at(block_number); - // 2) Compute the leaf slot by siloing the storage slot with our own address + _public_value_inclusion(value, storage_slot, contract_address, header); +} + +fn _public_value_inclusion( + value: Field, + storage_slot: Field, + contract_address: AztecAddress, + header: Header +) { + // 1) Compute the leaf slot by siloing the storage slot with the contract address let public_value_leaf_slot = pedersen_hash( - [context.this_address().to_field(), storage_slot], + [contract_address.to_field(), storage_slot], GENERATOR_INDEX__PUBLIC_LEAF_INDEX ); // 3) Get the membership witness of the slot - let witness = get_public_data_witness(block_number, public_value_leaf_slot); + let witness = get_public_data_witness( + header.global_variables.block_number as u32, + public_value_leaf_slot + ); // 4) Check that the witness matches the corresponding public_value let preimage = witness.leaf_preimage; diff --git a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr index b0ea0b3887e3..e42b107bbd74 100644 --- a/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr +++ b/yarn-project/aztec-nr/aztec/src/state_vars/stable_public_state.nr @@ -58,6 +58,7 @@ impl StablePublicState { prove_public_value_inclusion( fields[i], self.storage_slot + i, + (*private_context).this_address(), (*private_context), ) } diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index fa454ad35612..5bbec45a0bbf 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -206,6 +206,7 @@ contract InclusionProofs { prove_public_value_inclusion_at( 0, storage.public_unused_value.storage_slot, + context.this_address(), block_number, context ); @@ -221,11 +222,17 @@ contract InclusionProofs { prove_public_value_inclusion_at( public_value, storage.public_value.storage_slot, + context.this_address(), block_number, context ); } else { - prove_public_value_inclusion(public_value, storage.public_value.storage_slot, context); + prove_public_value_inclusion( + public_value, + storage.public_value.storage_slot, + context.this_address(), + context + ); } } From c82cbc9525286f1e860ed85a6d8e3c919b1d0acb Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 2 Feb 2024 23:14:25 +0100 Subject: [PATCH 05/11] fix tests --- .../src/e2e_inclusion_proofs_contract.test.ts | 134 +++++++++++------- .../inclusion_proofs_contract/src/main.nr | 16 ++- 2 files changed, 97 insertions(+), 53 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index c9e6025d935e..257b355e0470 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -52,87 +52,123 @@ describe('e2e_inclusion_proofs_contract', () => { owner = accounts[0].address; }); - it('proves note existence and its nullifier non-existence and nullifier non-existence failure case', async () => { + describe('proves note existence and its nullifier non-existence and nullifier non-existence failure case', () => { // Owner of a note let noteCreationBlockNumber: number; - { + let newCommitments, visibleNotes: any; + const value = 100n; + let validNoteBlockNumber: any; + + it('should return the correct values for creating a note', async () => { // Create a note - const value = 100n; const receipt = await contract.methods.create_note(owner, value).send().wait({ debug: true }); noteCreationBlockNumber = receipt.blockNumber!; - const { newCommitments, visibleNotes } = receipt.debugInfo!; + ({ newCommitments, visibleNotes } = receipt.debugInfo!); + }) + + it('should return the correct values for creating a note', async () => { expect(newCommitments.length).toBe(1); expect(visibleNotes.length).toBe(1); const [receivedValue, receivedOwner, _randomness] = visibleNotes[0].note.items; expect(receivedValue.toBigInt()).toBe(value); expect(receivedOwner).toEqual(owner.toField()); - } + }) - { + + it('should not throw because the note is included', async () => { // Prove note inclusion in a given block. await contract.methods - .test_note_inclusion(owner, true, noteCreationBlockNumber) + .test_note_inclusion(owner, true, noteCreationBlockNumber, false) .send() .wait(); await contract.methods - .test_note_inclusion(owner, false, 0n) + .test_note_inclusion(owner, false, 0n, false) .send() .wait(); - } - - { - // Prove that the note has not been nullified with block_number - // TODO(#3535): Prove the nullifier non-inclusion at older block to test archival node. This is currently not - // possible because of issue https://github.com/AztecProtocol/aztec-packages/issues/3535 - const blockNumber = await pxe.getBlockNumber(); - await contract.methods.test_note_not_nullified(owner, true, blockNumber, false).send().wait(); - await contract.methods.test_note_not_nullified(owner, false, 0n, false).send().wait(); - } - - { - const blockNumber = await pxe.getBlockNumber(); - await contract.methods.test_note_validity(owner, true, blockNumber).send().wait(); - await contract.methods.test_note_validity(owner, false, 0n).send().wait(); - } + }) - { + it('should not throw because the note is not nullified', async () => { // Prove that the note has not been nullified with block_number // TODO(#3535): Prove the nullifier non-inclusion at older block to test archival node. This is currently not // possible because of issue https://github.com/AztecProtocol/aztec-packages/issues/3535 const blockNumber = await pxe.getBlockNumber(); await contract.methods.test_note_not_nullified(owner, true, blockNumber, false).send().wait(); await contract.methods.test_note_not_nullified(owner, false, 0n, false).send().wait(); - } - - { + }) + + it('should not throw because is both included, not nullified, and therefore valid', async () => { + validNoteBlockNumber = await pxe.getBlockNumber(); + await contract.methods.test_note_validity(owner, true, validNoteBlockNumber, false).send().wait(); + await contract.methods.test_note_validity(owner, false, 0n, false).send().wait(); + }) + + describe('we will test the vailure case by nullifying a note', () => { + let receipt: any; + let currentBlockNumber: any; + let randomBlockNumberSinceDeployment: any; // We test the failure case now --> The proof should fail when the nullifier already exists - const receipt = await contract.methods.nullify_note(owner).send().wait({ debug: true }); - const { newNullifiers } = receipt.debugInfo!; - expect(newNullifiers.length).toBe(2); + it('nullifies a note and grabs block number', async () => { + receipt = await contract.methods.nullify_note(owner).send().wait({ debug: true }); + currentBlockNumber = await pxe.getBlockNumber(); + randomBlockNumberSinceDeployment = await getRandomBlockNumberSinceDeployment(); + + const { newNullifiers } = receipt!.debugInfo!; + expect(newNullifiers.length).toBe(2); + const nullifier = newNullifiers[1]; + }) - const currentBlockNumber = await pxe.getBlockNumber(); - const nullifier = newNullifiers[1]; - const randomBlockNumberSinceDeployment = await getRandomBlockNumberSinceDeployment(); // Note: getLowNullifierMembershipWitness returns the membership witness of the nullifier itself and not // the low nullifier when the nullifier already exists in the tree and for this reason the execution fails // on low_nullifier.value < nullifier.value check. - await expect( - contract.methods.test_note_not_nullified(owner, true, currentBlockNumber, true).send().wait(), - ).rejects.toThrow( - /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, - ); - await expect( - contract.methods.test_note_not_nullified(owner, false, 0n, true).send().wait(), - ).rejects.toThrow( - /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, - ); - // This does not throw because we are checking nullified note before the note was nullified - await contract.methods.test_note_not_nullified(owner, true, randomBlockNumberSinceDeployment, true).send().wait(); - } + it('should throw when testing if note is not nullified at the current block', async () => { + await expect( + contract.methods.test_note_not_nullified(owner, true, currentBlockNumber, true).send().wait(), + ).rejects.toThrow( + /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + ); + await expect( + contract.methods.test_note_not_nullified(owner, false, 0n, true).send().wait(), + ).rejects.toThrow( + /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + ); + }) + + it('should not throw when we test inclusion of nullified note', async () => { + const blockNumber = await pxe.getBlockNumber(); + await contract.methods + .test_note_inclusion(owner, true, noteCreationBlockNumber, true) + .send() + .wait(); + + await contract.methods + .test_note_inclusion(owner, false, 0n, true) + .send() + .wait(); + }) + + it('should throw when we test validity', async () => { + const blockNumber = await pxe.getBlockNumber(); + await expect( + contract.methods.test_note_validity(owner, true, blockNumber, true).send().wait(), + ).rejects.toThrow( + /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + ); + await expect( + contract.methods.test_note_validity(owner, false, 0n, true).send().wait(), + ).rejects.toThrow( + /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, + ); + }) + + it('should not throw because the note was not nullified yet at validNoteBlockNumber', async () => { + await contract.methods.test_note_not_nullified(owner, true, validNoteBlockNumber, true).send().wait(); + await contract.methods.test_note_validity(owner, true, validNoteBlockNumber, true).send().wait(); + }) + }) }); it('proves note validity (note commitment inclusion and nullifier non-inclusion)', async () => { @@ -156,8 +192,8 @@ describe('e2e_inclusion_proofs_contract', () => { { // Prove note validity - await contract.methods.test_note_validity(owner, true, noteCreationBlockNumber).send().wait(); - await contract.methods.test_note_validity(owner, false, 0n).send().wait(); + await contract.methods.test_note_validity(owner, true, noteCreationBlockNumber, false).send().wait(); + await contract.methods.test_note_validity(owner, false, 0n, false).send().wait(); } }); diff --git a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 5bbec45a0bbf..4fd43f153056 100644 --- a/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -85,12 +85,16 @@ contract InclusionProofs { fn test_note_inclusion( owner: AztecAddress, use_block_number: bool, - block_number: u32 // The block at which we'll prove that the note exists + block_number: u32, // The block at which we'll prove that the note exists + nullified: bool ) { // docs:start:get_note_from_pxe // 1) Get the note from PXE. let private_values = storage.private_values.at(owner); - let options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); + let mut options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); + if (nullified) { + options = options.set_status(NoteStatus.ACTIVE_OR_NULLIFIED); + } let notes = private_values.get_notes(options); let maybe_note = notes[0]; // docs:end:get_note_from_pxe @@ -152,11 +156,15 @@ contract InclusionProofs { fn test_note_validity( owner: AztecAddress, use_block_number: bool, - block_number: u32 // The block at which we'll prove that the note exists and is not nullified + block_number: u32, // The block at which we'll prove that the note exists and is not nullified + nullified: bool ) { // 1) Get the note from PXE. let private_values = storage.private_values.at(owner); - let options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); + let mut options = NoteGetterOptions::new().select(1, owner.to_field(), Option::none()).set_limit(1); + if (nullified) { + options = options.set_status(NoteStatus.ACTIVE_OR_NULLIFIED); + } let notes = private_values.get_notes(options); let note = notes[0].unwrap(); From a0c9b3d0629b5ee696b4714012d919becf07f1ea Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 2 Feb 2024 23:14:47 +0100 Subject: [PATCH 06/11] format --- .../src/e2e_inclusion_proofs_contract.test.ts | 67 +++++++------------ 1 file changed, 24 insertions(+), 43 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 257b355e0470..355decc078e4 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -59,14 +59,13 @@ describe('e2e_inclusion_proofs_contract', () => { const value = 100n; let validNoteBlockNumber: any; - it('should return the correct values for creating a note', async () => { + it('should return the correct values for creating a note', async () => { // Create a note const receipt = await contract.methods.create_note(owner, value).send().wait({ debug: true }); noteCreationBlockNumber = receipt.blockNumber!; ({ newCommitments, visibleNotes } = receipt.debugInfo!); - }) - + }); it('should return the correct values for creating a note', async () => { expect(newCommitments.length).toBe(1); @@ -74,21 +73,14 @@ describe('e2e_inclusion_proofs_contract', () => { const [receivedValue, receivedOwner, _randomness] = visibleNotes[0].note.items; expect(receivedValue.toBigInt()).toBe(value); expect(receivedOwner).toEqual(owner.toField()); - }) - + }); it('should not throw because the note is included', async () => { // Prove note inclusion in a given block. - await contract.methods - .test_note_inclusion(owner, true, noteCreationBlockNumber, false) - .send() - .wait(); + await contract.methods.test_note_inclusion(owner, true, noteCreationBlockNumber, false).send().wait(); - await contract.methods - .test_note_inclusion(owner, false, 0n, false) - .send() - .wait(); - }) + await contract.methods.test_note_inclusion(owner, false, 0n, false).send().wait(); + }); it('should not throw because the note is not nullified', async () => { // Prove that the note has not been nullified with block_number @@ -97,13 +89,13 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await pxe.getBlockNumber(); await contract.methods.test_note_not_nullified(owner, true, blockNumber, false).send().wait(); await contract.methods.test_note_not_nullified(owner, false, 0n, false).send().wait(); - }) + }); it('should not throw because is both included, not nullified, and therefore valid', async () => { validNoteBlockNumber = await pxe.getBlockNumber(); await contract.methods.test_note_validity(owner, true, validNoteBlockNumber, false).send().wait(); await contract.methods.test_note_validity(owner, false, 0n, false).send().wait(); - }) + }); describe('we will test the vailure case by nullifying a note', () => { let receipt: any; @@ -118,8 +110,7 @@ describe('e2e_inclusion_proofs_contract', () => { const { newNullifiers } = receipt!.debugInfo!; expect(newNullifiers.length).toBe(2); const nullifier = newNullifiers[1]; - }) - + }); // Note: getLowNullifierMembershipWitness returns the membership witness of the nullifier itself and not // the low nullifier when the nullifier already exists in the tree and for this reason the execution fails @@ -130,25 +121,17 @@ describe('e2e_inclusion_proofs_contract', () => { ).rejects.toThrow( /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, ); - await expect( - contract.methods.test_note_not_nullified(owner, false, 0n, true).send().wait(), - ).rejects.toThrow( + await expect(contract.methods.test_note_not_nullified(owner, false, 0n, true).send().wait()).rejects.toThrow( /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, ); - }) + }); it('should not throw when we test inclusion of nullified note', async () => { const blockNumber = await pxe.getBlockNumber(); - await contract.methods - .test_note_inclusion(owner, true, noteCreationBlockNumber, true) - .send() - .wait(); + await contract.methods.test_note_inclusion(owner, true, noteCreationBlockNumber, true).send().wait(); - await contract.methods - .test_note_inclusion(owner, false, 0n, true) - .send() - .wait(); - }) + await contract.methods.test_note_inclusion(owner, false, 0n, true).send().wait(); + }); it('should throw when we test validity', async () => { const blockNumber = await pxe.getBlockNumber(); @@ -157,18 +140,16 @@ describe('e2e_inclusion_proofs_contract', () => { ).rejects.toThrow( /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, ); - await expect( - contract.methods.test_note_validity(owner, false, 0n, true).send().wait(), - ).rejects.toThrow( + await expect(contract.methods.test_note_validity(owner, false, 0n, true).send().wait()).rejects.toThrow( /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, ); - }) + }); it('should not throw because the note was not nullified yet at validNoteBlockNumber', async () => { await contract.methods.test_note_not_nullified(owner, true, validNoteBlockNumber, true).send().wait(); await contract.methods.test_note_validity(owner, true, validNoteBlockNumber, true).send().wait(); - }) - }) + }); + }); }); it('proves note validity (note commitment inclusion and nullifier non-inclusion)', async () => { @@ -209,9 +190,9 @@ describe('e2e_inclusion_proofs_contract', () => { contract.methods.test_note_inclusion_fail_case(owner, true, blockNumber).send().wait(), ).rejects.toThrow(/Leaf value: .* not found in NOTE_HASH_TREE/); - await expect( - contract.methods.test_note_inclusion_fail_case(owner, false, 0n).send().wait(), - ).rejects.toThrow(/Leaf value: .* not found in NOTE_HASH_TREE/); + await expect(contract.methods.test_note_inclusion_fail_case(owner, false, 0n).send().wait()).rejects.toThrow( + /Leaf value: .* not found in NOTE_HASH_TREE/, + ); }); }); @@ -262,9 +243,9 @@ describe('e2e_inclusion_proofs_contract', () => { contract.methods.test_nullifier_inclusion(randomNullifier, true, blockNumber).send().wait(), ).rejects.toThrow(`Low nullifier witness not found for nullifier ${randomNullifier.toString()} at block`); - await expect( - contract.methods.test_nullifier_inclusion(randomNullifier, false, 0n).send().wait(), - ).rejects.toThrow(`Low nullifier witness not found for nullifier ${randomNullifier.toString()} at block`); + await expect(contract.methods.test_nullifier_inclusion(randomNullifier, false, 0n).send().wait()).rejects.toThrow( + `Low nullifier witness not found for nullifier ${randomNullifier.toString()} at block`, + ); }); }); From 67a94b21cb3598082392ae13a49bccf6c617f805 Mon Sep 17 00:00:00 2001 From: Esau Date: Fri, 2 Feb 2024 23:52:25 +0100 Subject: [PATCH 07/11] fix format --- .../end-to-end/src/e2e_inclusion_proofs_contract.test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 355decc078e4..459fddef9983 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -67,7 +67,7 @@ describe('e2e_inclusion_proofs_contract', () => { ({ newCommitments, visibleNotes } = receipt.debugInfo!); }); - it('should return the correct values for creating a note', async () => { + it('should return the correct values for creating a note', () => { expect(newCommitments.length).toBe(1); expect(visibleNotes.length).toBe(1); const [receivedValue, receivedOwner, _randomness] = visibleNotes[0].note.items; @@ -100,16 +100,14 @@ describe('e2e_inclusion_proofs_contract', () => { describe('we will test the vailure case by nullifying a note', () => { let receipt: any; let currentBlockNumber: any; - let randomBlockNumberSinceDeployment: any; // We test the failure case now --> The proof should fail when the nullifier already exists it('nullifies a note and grabs block number', async () => { receipt = await contract.methods.nullify_note(owner).send().wait({ debug: true }); currentBlockNumber = await pxe.getBlockNumber(); - randomBlockNumberSinceDeployment = await getRandomBlockNumberSinceDeployment(); const { newNullifiers } = receipt!.debugInfo!; expect(newNullifiers.length).toBe(2); - const nullifier = newNullifiers[1]; + // const nullifier = newNullifiers[1]; }); // Note: getLowNullifierMembershipWitness returns the membership witness of the nullifier itself and not @@ -127,7 +125,6 @@ describe('e2e_inclusion_proofs_contract', () => { }); it('should not throw when we test inclusion of nullified note', async () => { - const blockNumber = await pxe.getBlockNumber(); await contract.methods.test_note_inclusion(owner, true, noteCreationBlockNumber, true).send().wait(); await contract.methods.test_note_inclusion(owner, false, 0n, true).send().wait(); From c289e232afc21fe2d7baddc275ff74ce39cb8f74 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 5 Feb 2024 19:56:31 +0100 Subject: [PATCH 08/11] Addressing comments and fixing merge problems --- l1-contracts/src/core/libraries/ConstantsGen.sol | 3 +-- .../aztec-nr/aztec/src/history/note_inclusion.nr | 9 ++++----- .../aztec-nr/aztec/src/history/note_validity.nr | 6 +++--- .../aztec-nr/aztec/src/history/nullifier_inclusion.nr | 9 ++++----- .../aztec/src/history/nullifier_non_inclusion.nr | 11 +++++------ .../aztec/src/history/public_value_inclusion.nr | 4 +--- 6 files changed, 18 insertions(+), 24 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index fac4c0b82080..454c173b6630 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -64,8 +64,7 @@ library Constants { uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 32; uint256 internal constant ARGS_HASH_CHUNK_COUNT = 32; uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 1000; - uint256 internal constant CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = - 0x6999d1e02b08a447a463563453cb36919c9dd7150336fc7c4d2b52f8; + uint256 internal constant CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = 0x6999d1e02b08a447a463563453cb36919c9dd7150336fc7c4d2b52f8; uint256 internal constant L1_TO_L2_MESSAGE_LENGTH = 8; uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 25; uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20; diff --git a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr index a2da086e82e0..599cb0cc727a 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -5,13 +5,12 @@ use crate::{ context::PrivateContext, note::{ utils::compute_note_hash_for_consumption, - note_header::NoteHeader, note_interface::NoteInterface, }, oracle::get_membership_witness::get_note_hash_membership_witness, }; -fn _note_inclusion(note: Note, header: Header) where Note: NoteInterface { +fn _note_inclusion(note: Note, header: Header) where Note: NoteInterface { // 1) Compute note_hash let note_hash = compute_note_hash_for_consumption(note); @@ -24,15 +23,15 @@ fn _note_inclusion(note: Note, header: Header) where Note: NoteInterface { ); } -pub fn prove_note_inclusion(note: Note, context: PrivateContext) where Note: NoteInterface { +pub fn prove_note_inclusion(note: Note, context: PrivateContext) where Note: NoteInterface { _note_inclusion(note, context.historical_header); } -pub fn prove_note_inclusion_at( +pub fn prove_note_inclusion_at( note: Note, block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { let header = context.get_header_at(block_number); _note_inclusion(note, header); diff --git a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr index df4f998cb6c5..192c4c985d8f 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -9,17 +9,17 @@ use crate::{ note::note_interface::NoteInterface, }; -pub fn prove_note_validity(note: Note, context: &mut PrivateContext) where Note: NoteInterface { +pub fn prove_note_validity(note: Note, context: &mut PrivateContext) where Note: NoteInterface { prove_note_inclusion(note, *context); prove_note_not_nullified(note, context); } // A helper function that proves that a note is valid at the given block number -pub fn prove_note_validity_at( +pub fn prove_note_validity_at( note: Note, block_number: u32, context: &mut PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { prove_note_inclusion_at(note, block_number, *context); prove_note_not_nullified_at(note, block_number, context); } diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr index 0a4b7f986c74..6a1a28d6ac5e 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -6,7 +6,6 @@ use crate::{ oracle::get_nullifier_membership_witness::get_nullifier_membership_witness, note::{ utils::compute_siloed_nullifier, - note_header::NoteHeader, note_interface::NoteInterface, }, }; @@ -44,20 +43,20 @@ pub fn prove_nullifier_inclusion_at( _nullifier_inclusion(nullifier, header); } -pub fn prove_note_is_nullified( +pub fn prove_note_is_nullified( note: Note, context: &mut PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note, context); _nullifier_inclusion(nullifier, context.historical_header); } -pub fn prove_note_is_nullified_at( +pub fn prove_note_is_nullified_at( note: Note, block_number: u32, context: &mut PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note, context); let header = context.get_header_at(block_number); diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index 9f1607a54dfb..09c030da43e7 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -5,7 +5,6 @@ use crate::{ context::PrivateContext, note::{ utils::compute_siloed_nullifier, - note_header::NoteHeader, note_interface::NoteInterface, }, oracle::get_nullifier_membership_witness::get_low_nullifier_membership_witness, @@ -54,20 +53,20 @@ pub fn prove_nullifier_not_included_at(nullifier: Field, block_number: u32, cont } pub fn prove_note_not_nullified( - note_with_header: Note, + note: Note, context: &mut PrivateContext -) where Note: NoteInterface { - let nullifier = compute_siloed_nullifier(note_with_header, context); +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note, context); _nullifier_non_inclusion(nullifier, context.historical_header); } pub fn prove_note_not_nullified_at( - note_with_header: Note, + note: Note, block_number: u32, // The block at which we'll prove that the note was not nullified context: &mut PrivateContext ) where Note: NoteInterface { - let nullifier = compute_siloed_nullifier(note_with_header, context); + let nullifier = compute_siloed_nullifier(note, context); let header = context.get_header_at(block_number); _nullifier_non_inclusion(nullifier, header); diff --git a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr index 1a6ecb2f117f..e1ca53711c5b 100644 --- a/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -3,8 +3,7 @@ use dep::protocol_types::{ header::Header, hash::pedersen_hash, address::{ - AztecAddress, - EthAddress, + AztecAddress }, }; use dep::std::merkle::compute_merkle_root; @@ -13,7 +12,6 @@ use crate::{ context::PrivateContext, oracle::get_public_data_witness::{ get_public_data_witness, - PublicDataWitness, }, utils::{ full_field_less_than, From f134042e460124a2464c383209fd089aeef8766c Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 5 Feb 2024 20:02:01 +0100 Subject: [PATCH 09/11] bump --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 95e9503fa2fc..c3b372f3f390 100644 --- a/README.md +++ b/README.md @@ -75,3 +75,5 @@ Recovering if the sync is not happening with basic pull commands: this needs to exist in the branch we push to, and have the same content as our base. This is similar to submodules, except instead of pointing to the final state of the module, it points to the last commit we have sync'd from, for purposes of commit replay. This can be fixed to match the commit in master after merges. - manually editing the parent variable in noir/.gitrepo: this is the parent of the last sync commit on aztec side. If you get errors with a commit not being found in the upstream repo, and the commit mentioned is not the commit variable above, it might indicate this is somehow incorrect. This can happen when commit content is ported without its history, e.g. squashes - use pull --force ONLY where you would use git reset. That is, if you really want to match some upstream noir for a purpose its fine, but you'll lose local changes (if any) + +bump ci \ No newline at end of file From f220fce105bcb0bd41be7a6a2b1796cc0170cd18 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 5 Feb 2024 20:02:13 +0100 Subject: [PATCH 10/11] Revert "bump" This reverts commit f134042e460124a2464c383209fd089aeef8766c. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index c3b372f3f390..95e9503fa2fc 100644 --- a/README.md +++ b/README.md @@ -75,5 +75,3 @@ Recovering if the sync is not happening with basic pull commands: this needs to exist in the branch we push to, and have the same content as our base. This is similar to submodules, except instead of pointing to the final state of the module, it points to the last commit we have sync'd from, for purposes of commit replay. This can be fixed to match the commit in master after merges. - manually editing the parent variable in noir/.gitrepo: this is the parent of the last sync commit on aztec side. If you get errors with a commit not being found in the upstream repo, and the commit mentioned is not the commit variable above, it might indicate this is somehow incorrect. This can happen when commit content is ported without its history, e.g. squashes - use pull --force ONLY where you would use git reset. That is, if you really want to match some upstream noir for a purpose its fine, but you'll lose local changes (if any) - -bump ci \ No newline at end of file From 80f2ed797b6fdee1f9f226e5715ba4a23cde2297 Mon Sep 17 00:00:00 2001 From: Esau Date: Mon, 5 Feb 2024 20:06:22 +0100 Subject: [PATCH 11/11] fix ? --- l1-contracts/src/core/libraries/ConstantsGen.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 454c173b6630..fac4c0b82080 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -64,7 +64,8 @@ library Constants { uint256 internal constant ARGS_HASH_CHUNK_LENGTH = 32; uint256 internal constant ARGS_HASH_CHUNK_COUNT = 32; uint256 internal constant MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 1000; - uint256 internal constant CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = 0x6999d1e02b08a447a463563453cb36919c9dd7150336fc7c4d2b52f8; + uint256 internal constant CONTRACT_CLASS_REGISTERED_MAGIC_VALUE = + 0x6999d1e02b08a447a463563453cb36919c9dd7150336fc7c4d2b52f8; uint256 internal constant L1_TO_L2_MESSAGE_LENGTH = 8; uint256 internal constant L1_TO_L2_MESSAGE_ORACLE_CALL_LENGTH = 25; uint256 internal constant MAX_NOTE_FIELDS_LENGTH = 20;