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 055a6222ebe..5b4469b20f5 100644 --- a/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/contract_inclusion.nr @@ -27,11 +27,10 @@ pub fn prove_contract_inclusion( contract_class_id: ContractClassId, 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); + // 1) Get block header from context + // let block_header = context.historical_header; // 2) Compute the contract address let contract_address = AztecAddress::compute_from_public_key( @@ -64,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_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr index facb1261ba5..599cb0cc727 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -1,40 +1,38 @@ use dep::std::merkle::compute_merkle_root; +use dep::protocol_types::header::Header; 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, }; -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); +fn _note_inclusion(note: Note, header: Header) where Note: NoteInterface { + // 1) Compute note_hash + let note_hash = compute_note_hash_for_consumption(note); // 2) Get the membership witness of the note in the note hash tree - let witness = get_note_hash_membership_witness(block_number, note_commitment); + let witness = get_note_hash_membership_witness(header.global_variables.block_number as u32, note_hash); // 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" + assert_eq( + header.state.partial.note_hash_tree.root, compute_merkle_root(note_hash, 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, +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); + let header = context.get_header_at(block_number); - prove_note_commitment_inclusion(note_commitment, block_number, context); + _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 fb5ed5b830b..192c4c985d8 100644 --- a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -2,17 +2,24 @@ 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, }; +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( - note_with_header: Note, - block_number: u32, // The block at which we'll prove that the note exists +pub fn prove_note_validity_at( + note: Note, + block_number: u32, 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_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 1d61313825e..6a1a28d6ac5 100644 --- a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -1,28 +1,26 @@ 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_interface::NoteInterface, + }, }; -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); +fn _nullifier_inclusion(nullifier: Field, header: Header) { + // 1) Get the membership witness of the nullifier + let witness = get_nullifier_membership_witness(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 == compute_merkle_root(nullifier_leaf, witness.index, witness.path), "Proving nullifier inclusion failed" @@ -30,3 +28,37 @@ pub fn prove_nullifier_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_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 +) { + let header = context.get_header_at(block_number); + + _nullifier_inclusion(nullifier, header); +} + +pub fn prove_note_is_nullified( + note: Note, + context: &mut PrivateContext +) where Note: NoteInterface { + let nullifier = compute_siloed_nullifier(note, context); + + _nullifier_inclusion(nullifier, context.historical_header); +} + +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 acc0d362f47..09c030da43e 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,10 +1,10 @@ use dep::std::merkle::compute_merkle_root; +use dep::protocol_types::header::Header; 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, @@ -14,16 +14,9 @@ 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); - - // 2) Get the membership witness of a low nullifier of the nullifier - let witness = get_low_nullifier_membership_witness(block_number, 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 @@ -49,12 +42,32 @@ pub fn prove_nullifier_non_inclusion( // 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, + note: Note, + context: &mut PrivateContext +) 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: 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); - prove_nullifier_non_inclusion(nullifier, block_number, *context); + _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 ad14fb77cb3..e1ca53711c5 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,10 @@ use dep::protocol_types::{ constants::GENERATOR_INDEX__PUBLIC_LEAF_INDEX, + header::Header, hash::pedersen_hash, + address::{ + AztecAddress + }, }; use dep::std::merkle::compute_merkle_root; @@ -8,7 +12,6 @@ use crate::{ context::PrivateContext, oracle::get_public_data_witness::{ get_public_data_witness, - PublicDataWitness, }, utils::{ full_field_less_than, @@ -18,20 +21,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 +) { + _public_value_inclusion( + value, + storage_slot, + contract_address, + context.historical_header + ); +} + +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 af4925f47ff..e42b107bbd7 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 @@ -50,19 +50,15 @@ 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) // 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], self.storage_slot + i, - block_number as u32, + (*private_context).this_address(), (*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 87821e95d11..459fddef998 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,59 +52,101 @@ 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', () => { 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. - const ignoredCommitment = 0; // Not ignored only when the note doesn't exist - await contract.methods - .test_note_inclusion_proof(owner, noteCreationBlockNumber, ignoredCommitment) - .send() - .wait(); - } + await contract.methods.test_note_inclusion(owner, true, noteCreationBlockNumber, false).send().wait(); - { - // Prove that the note has not been nullified + 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 // 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(); + }); + + 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; // 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(); + + const { newNullifiers } = receipt!.debugInfo!; + expect(newNullifiers.length).toBe(2); + // const nullifier = newNullifiers[1]; + }); - const blockNumber = await pxe.getBlockNumber(); - 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 // on low_nullifier.value < nullifier.value check. - await expect( - contract.methods.test_nullifier_non_inclusion_proof(owner, blockNumber, nullifier).send().wait(), - ).rejects.toThrowError( - /Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed/, - ); - } + 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 () => { + 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 () => { @@ -128,21 +170,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, false).send().wait(); + await contract.methods.test_note_validity(owner, false, 0n, false).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, blockNumber, 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 +198,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 +207,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 +227,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,8 +237,12 @@ 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 +277,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 +298,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 c3adf017e34..a02bc09740f 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,23 +26,27 @@ contract InclusionProofs { history::{ contract_inclusion::{ prove_contract_inclusion, + prove_contract_inclusion_at, }, note_inclusion::{ - prove_note_commitment_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_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 @@ -76,78 +81,100 @@ 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, + use_block_number: bool, 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). - spare_commitment: Field + 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 // 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); - // docs:end: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); + } + } + + #[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 { - // Note was not found so we will prove inclusion of the spare commitment - prove_note_commitment_inclusion(spare_commitment, block_number, context); - }; + 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 - prove_note_not_nullified(maybe_note.unwrap_unchecked(), block_number, &mut context); - // docs:end: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 { - // 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 - }; + prove_note_not_nullified(maybe_note.unwrap_unchecked(), &mut context); + } + // docs:end:prove_note_not_nullified } #[aztec(private)] - fn test_note_validity_proof( + fn test_note_validity( owner: AztecAddress, - block_number: u32 // The block at which we'll prove that the note exists and is not nullified + use_block_number: bool, + 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(); // 2) Prove the note validity // docs:start:prove_note_validity - prove_note_validity(note, block_number, &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 } @@ -167,37 +194,54 @@ 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, block_number, 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( + prove_public_value_inclusion_at( 0, storage.public_unused_value.storage_slot, + context.this_address(), 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, - block_number, - context - ); + if (use_block_number) { + 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.this_address(), + context + ); + } } // Proves that a contract exists at block `block_number`. @@ -209,7 +253,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, @@ -217,7 +261,7 @@ 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,