diff --git a/yarn-project/aztec-nr/aztec/src/history.nr b/yarn-project/aztec-nr/aztec/src/history.nr new file mode 100644 index 00000000000..c36332d365a --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/history.nr @@ -0,0 +1,5 @@ +mod note_inclusion; +mod note_validity; +mod nullifier_inclusion; +mod nullifier_non_inclusion; +mod public_value_inclusion; \ No newline at end of file diff --git a/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr new file mode 100644 index 00000000000..df05ce24529 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/history/note_inclusion.nr @@ -0,0 +1,48 @@ +use dep::protocol_types::constants::NOTE_HASH_TREE_HEIGHT; +use dep::std::merkle::compute_merkle_root; + +use crate::{ + context::PrivateContext, + note::{ + utils::compute_unique_siloed_note_hash, + note_header::NoteHeader, + note_interface::NoteInterface, + }, + oracle::get_membership_witness::{ + get_membership_witness, + MembershipWitness, + }, +}; + +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 block_header = context.get_block_header(block_number); + + // 2) Get the membership witness of the note in the note hash tree + let note_hash_tree_id = 2; // TODO(#3443) + let witness: MembershipWitness = + get_membership_witness(block_number, note_hash_tree_id, note_commitment); + + // 3) Prove that the commitment is in the note hash tree + assert( + block_header.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_interface: NoteInterface, + note_with_header: Note, + block_number: u32, // The block at which we'll prove that the note exists + context: PrivateContext +) { + let note_commitment = compute_unique_siloed_note_hash(note_interface, note_with_header); + + prove_note_commitment_inclusion(note_commitment, block_number, context); +} \ No newline at end of file diff --git a/yarn-project/aztec-nr/aztec/src/history/note_validity.nr b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr new file mode 100644 index 00000000000..47a45547e40 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/history/note_validity.nr @@ -0,0 +1,19 @@ +use crate::{ + context::PrivateContext, + history::{ + note_inclusion::prove_note_inclusion, + nullifier_non_inclusion::prove_note_not_nullified, + }, + 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_interface: NoteInterface, + note_with_header: Note, + block_number: u32, // The block at which we'll prove that the note exists + context: PrivateContext +) { + prove_note_inclusion(note_interface, note_with_header, block_number, context); + prove_note_not_nullified(note_interface, note_with_header, block_number, context); +} \ No newline at end of file diff --git a/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr new file mode 100644 index 00000000000..f6f5185cace --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -0,0 +1,33 @@ +use dep::std::merkle::compute_merkle_root; + +use crate::{ + context::PrivateContext, + 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 block_header = context.get_block_header(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_data.value == nullifier, "Nullifier does not match value in witness"); + + // 4) Compute the nullifier tree leaf + let nullifier_leaf = witness.leaf_data.hash(); + + // 5) Prove that the nullifier is in the nullifier tree + assert( + block_header.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. +} \ No newline at end of file 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 new file mode 100644 index 00000000000..f491e55a2c1 --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -0,0 +1,63 @@ +use dep::std::merkle::compute_merkle_root; + +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, + utils::{ + full_field_less_than, + full_field_greater_than, + }, +}; + +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 block_header = context.get_block_header(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_data.hash(); + assert( + block_header.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_data.value, 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_data.next_value, nullifier) | (witness.leaf_data.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. +} + +pub fn prove_note_not_nullified( + note_interface: NoteInterface, + note_with_header: Note, + block_number: u32, // The block at which we'll prove that the note was not nullified + context: PrivateContext +) { + let nullifier = compute_siloed_nullifier(note_interface, note_with_header); + + prove_nullifier_non_inclusion(nullifier, block_number, context); +} \ No newline at end of file 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 new file mode 100644 index 00000000000..ec666cb69cb --- /dev/null +++ b/yarn-project/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -0,0 +1,44 @@ +use dep::protocol_types::constants::{ + PUBLIC_DATA_TREE_HEIGHT, + GENERATOR_INDEX__PUBLIC_LEAF_INDEX, +}; +use dep::std::merkle::compute_merkle_root; + +use crate::{ + context::PrivateContext, + hash::pedersen_hash, + oracle::get_sibling_path::get_sibling_path, +}; + +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 block_header = context.get_block_header(block_number); + + // 2) Compute the public value leaf index. + // We have to compute the leaf index here because unlike in the case of note commitments, public values are + // not siloed with contract address so an oracle could cheat and give us a membership witness for arbitrary + // value in the public data tree. + let value_leaf_index = pedersen_hash( + [context.this_address(), storage_slot], + GENERATOR_INDEX__PUBLIC_LEAF_INDEX + ); + + // 3) Get the sibling path of the value leaf index in the public data tree at block `block_number`. + let public_data_tree_id = 3; // TODO(#3443) + let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = + get_sibling_path(block_number, public_data_tree_id, value_leaf_index); + + // 4) Prove that the value provided on input is in the public data tree at the given storage slot. + assert( + block_header.public_data_tree_root == compute_merkle_root(value, value_leaf_index, 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. +} \ No newline at end of file diff --git a/yarn-project/aztec-nr/aztec/src/lib.nr b/yarn-project/aztec-nr/aztec/src/lib.nr index 99c087b0075..badfa9c0f05 100644 --- a/yarn-project/aztec-nr/aztec/src/lib.nr +++ b/yarn-project/aztec-nr/aztec/src/lib.nr @@ -2,6 +2,7 @@ mod abi; mod address; mod context; mod hash; +mod history; mod log; mod messaging; mod note; diff --git a/yarn-project/aztec-nr/aztec/src/utils.nr b/yarn-project/aztec-nr/aztec/src/utils.nr index 205b19fd49a..a9a766c6b7a 100644 --- a/yarn-project/aztec-nr/aztec/src/utils.nr +++ b/yarn-project/aztec-nr/aztec/src/utils.nr @@ -20,3 +20,15 @@ pub fn field_from_bytes(bytes: [u8; N], big_endian: bool) -> Field { as_field } + +// TODO(#3470): Copied over from https://github.com/AztecProtocol/aztec-packages/blob/a07c4bd47313be6aa604a63f37857eb0136b41ba/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr#L599 +// move to a shared place? + +// TODO to radix returns u8, so we cannot use bigger radixes. It'd be ideal to use a radix of the maximum range-constrained integer noir supports +pub fn full_field_less_than(lhs: Field, rhs: Field) -> bool { + dep::std::eddsa::lt_bytes32(lhs, rhs) +} + +pub fn full_field_greater_than(lhs: Field, rhs: Field) -> bool { + dep::std::eddsa::lt_bytes32(rhs, lhs) +} \ No newline at end of file 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 4240a863566..298ff392724 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 @@ -54,7 +54,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.proveNoteInclusion(owner, noteCreationBlockNumber, ignoredCommitment).send().wait(); + await contract.methods.test_note_inclusion_proof(owner, noteCreationBlockNumber, ignoredCommitment).send().wait(); } { @@ -63,12 +63,12 @@ describe('e2e_inclusion_proofs_contract', () => { // 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.proveNullifierNonInclusion(owner, blockNumber, ignoredNullifier).send().wait(); + await contract.methods.test_nullifier_non_inclusion_proof(owner, blockNumber, ignoredNullifier).send().wait(); } { // We test the failure case now --> The proof should fail when the nullifier already exists - const receipt = await contract.methods.nullifyNote(owner).send().wait({ debug: true }); + const receipt = await contract.methods.nullify_note(owner).send().wait({ debug: true }); const { newNullifiers } = receipt.debugInfo!; expect(newNullifiers.length).toBe(2); @@ -78,13 +78,38 @@ describe('e2e_inclusion_proofs_contract', () => { // 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.proveNullifierNonInclusion(owner, blockNumber, nullifier).send().wait(), + 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('proves note validity (note commitment inclusion and nullifier non-inclusion)', async () => { + // Owner of a note + const owner = accounts[0].address; + let noteCreationBlockNumber: number; + { + // 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!; + + 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()); + } + + { + // Prove note validity + await contract.methods.test_note_validity_proof(owner, noteCreationBlockNumber).send().wait(); + } + }); + it('note existence failure case', async () => { // Owner of a note const owner = AztecAddress.random(); @@ -92,7 +117,7 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await pxe.getBlockNumber(); const randomNoteCommitment = Fr.random(); await expect( - contract.methods.proveNoteInclusion(owner, blockNumber, randomNoteCommitment).send().wait(), + contract.methods.test_note_inclusion_proof(owner, blockNumber, randomNoteCommitment).send().wait(), ).rejects.toThrow(/Leaf value: 0x[0-9a-fA-F]+ not found in NOTE_HASH_TREE/); }); @@ -100,7 +125,7 @@ describe('e2e_inclusion_proofs_contract', () => { // Chose random block number between deployment and current block number to test archival node const blockNumber = await getRandomBlockNumberSinceDeployment(); - await contract.methods.provePublicValueInclusion(publicValue, blockNumber).send().wait(); + await contract.methods.test_public_value_inclusion_proof(publicValue, blockNumber).send().wait(); }); it('public value existence failure case', async () => { @@ -109,7 +134,7 @@ describe('e2e_inclusion_proofs_contract', () => { const randomPublicValue = Fr.random(); await expect( - contract.methods.provePublicValueInclusion(randomPublicValue, blockNumber).send().wait(), + contract.methods.test_public_value_inclusion_proof(randomPublicValue, blockNumber).send().wait(), ).rejects.toThrow(/Proving public value inclusion failed/); }); @@ -120,7 +145,7 @@ describe('e2e_inclusion_proofs_contract', () => { const block = await pxe.getBlock(blockNumber); const nullifier = block?.newNullifiers[0]; - await contract.methods.proveNullifierInclusion(nullifier!, blockNumber).send().wait(); + await contract.methods.test_nullifier_inclusion_proof(nullifier!, blockNumber).send().wait(); }); it('nullifier existence failure case', async () => { @@ -129,9 +154,9 @@ describe('e2e_inclusion_proofs_contract', () => { const blockNumber = await pxe.getBlockNumber(); const randomNullifier = Fr.random(); - await expect(contract.methods.proveNullifierInclusion(randomNullifier, blockNumber).send().wait()).rejects.toThrow( - /Low nullifier witness not found for nullifier 0x[0-9a-fA-F]+ at block/, - ); + await expect( + contract.methods.test_nullifier_inclusion_proof(randomNullifier, blockNumber).send().wait(), + ).rejects.toThrow(/Low nullifier witness not found for nullifier 0x[0-9a-fA-F]+ at block/); }); const getRandomBlockNumberSinceDeployment = async () => { diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr index a06c832cd50..3509134e7cf 100644 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr +++ b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/main.nr @@ -1,13 +1,5 @@ -mod utils; - // A demonstration of inclusion and non-inclusion proofs. contract InclusionProofs { - use dep::protocol_types::constants::{ - NOTE_HASH_TREE_HEIGHT, - PUBLIC_DATA_TREE_HEIGHT, - GENERATOR_INDEX__PUBLIC_LEAF_INDEX, - }; - use dep::std::merkle::compute_merkle_root; use dep::aztec::{ state_vars::{ map::Map, @@ -26,27 +18,28 @@ contract InclusionProofs { utils as note_utils, }, - oracle::{ - get_membership_witness::{ - get_membership_witness, - MembershipWitness, + history::{ + note_inclusion::{ + prove_note_commitment_inclusion, + prove_note_inclusion, + }, + note_validity::{ + prove_note_validity, + }, + nullifier_inclusion::{ + prove_nullifier_inclusion, + }, + nullifier_non_inclusion::{ + prove_nullifier_non_inclusion, + prove_note_not_nullified, }, - get_sibling_path::get_sibling_path, - get_nullifier_membership_witness::{ - get_low_nullifier_membership_witness, - get_nullifier_membership_witness, - NullifierMembershipWitness, + public_value_inclusion::{ + prove_public_value_inclusion, }, }, - hash::pedersen_hash, }; use dep::value_note::value_note::{ValueNote, ValueNoteMethods, VALUE_NOTE_LEN}; - use crate::utils::{ - full_field_less_than, - full_field_greater_than, - }; - struct Storage { private_values: Map>, public_value: PublicState, @@ -96,54 +89,33 @@ contract InclusionProofs { // Proves that the owner owned a ValueNote at block `block_number`. #[aztec(private)] - fn proveNoteInclusion( + fn test_note_inclusion_proof( owner: AztecAddress, block_number: u32, // The block at which we'll prove that the note exists spare_commitment: Field, // This is only used when the note is not found --> used to test the failure case ) { - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree - // root. - let block_header = context.get_block_header(block_number); - - // 2) Get the note from PXE. + // 1) Get the note from PXE. let private_values = storage.private_values.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); let notes = private_values.get_notes(options); let maybe_note = notes[0]; - // 3) Compute the commitment from the note - let note_commitment = if maybe_note.is_some() { - note_utils::compute_unique_siloed_note_hash(ValueNoteMethods, maybe_note.unwrap_unchecked()) + // 2) Prove the note inclusion + if maybe_note.is_some() { + prove_note_inclusion(ValueNoteMethods, maybe_note.unwrap_unchecked(), block_number, context); } else { - // Note was not found so we will use the spare commitment - spare_commitment + // Note was not found so we will prove inclusion of the spare commitment + prove_note_commitment_inclusion(spare_commitment, block_number, context); }; - - // 4) Get the membership witness of the note in the note hash tree - let note_hash_tree_id = 2; // TODO(#3443) - let witness: MembershipWitness = - get_membership_witness(block_number, note_hash_tree_id, note_commitment); - - // 5) Prove that the commitment is in the note hash tree - assert( - block_header.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 blocks tree root. } // Proves that the note was not yet nullified at block `block_number`. #[aztec(private)] - fn proveNullifierNonInclusion( + fn test_nullifier_non_inclusion_proof( owner: AztecAddress, block_number: u32, // The block at which we'll prove that the nullifier does not exists spare_nullifier: Field, // This is only used when the note is not found --> used to test the failure case ) { - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree - // root. - let block_header = context.get_block_header(block_number); - // 2) Get the note from PXE let private_values = storage.private_values.at(owner.address); let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); @@ -151,44 +123,31 @@ contract InclusionProofs { let maybe_note = notes[0]; // 3) Compute the nullifier from the note - let nullifier = if maybe_note.is_some() { - note_utils::compute_siloed_nullifier(ValueNoteMethods, maybe_note.unwrap_unchecked()) + if maybe_note.is_some() { + prove_note_not_nullified(ValueNoteMethods, maybe_note.unwrap_unchecked(), block_number, context); } else { // Note was not found so we will use the spare nullifier - spare_nullifier + prove_nullifier_non_inclusion(spare_nullifier, block_number, context); }; + } - // 4) Get the membership witness of a low nullifier of the nullifier - let witness = get_low_nullifier_membership_witness(block_number, nullifier); - - // 5) Prove that the nullifier is not included in the nullifier tree - - // 5.a) Compute the low nullifier leaf and prove that it is in the nullifier tree - let low_nullifier_leaf = witness.leaf_data.hash(); - assert( - block_header.nullifier_tree_root == compute_merkle_root(low_nullifier_leaf, witness.index, witness.path), - "Proving nullifier non-inclusion failed: Could not prove low nullifier inclusion" - ); - - // 5.b) Prove that the low nullifier is smaller than the nullifier - assert( - full_field_less_than(witness.leaf_data.value, nullifier), - "Proving nullifier non-inclusion failed: low_nullifier.value < nullifier.value check failed" - ); - - // 5.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_data.next_value, nullifier) | (witness.leaf_data.next_index == 0), - "Proving nullifier non-inclusion failed: low_nullifier.next_value > nullifier.value check failed" - ); + #[aztec(private)] + fn test_note_validity_proof( + owner: AztecAddress, + block_number: u32, // The block at which we'll prove that the note exists and is not nullified + ) { + // 1) Get the note from PXE. + let private_values = storage.private_values.at(owner.address); + let options = NoteGetterOptions::new().select(1, owner.address).set_limit(1); + let notes = private_values.get_notes(options); + let note = notes[0].unwrap(); - // --> Now we have traversed the trees all the way up to blocks tree root and verified that the nullifier - // was not yet included in the nullifier tree. + // 2) Prove the note validity + prove_note_validity(ValueNoteMethods, note, block_number, context); } #[aztec(private)] - fn nullifyNote( + fn nullify_note( owner: AztecAddress, ) { let private_values = storage.private_values.at(owner.address); @@ -203,64 +162,19 @@ 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 proveNullifierInclusion( + fn test_nullifier_inclusion_proof( nullifier: Field, block_number: u32, // The block at which we'll prove that the nullifier not exists in the tree ) { - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree - // root. - let block_header = context.get_block_header(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_data.value == nullifier, "Nullifier does not match value in witness"); - - // 4) Compute the nullifier tree leaf - let nullifier_leaf = witness.leaf_data.hash(); - - // 5) Prove that the nullifier is in the nullifier tree - assert( - block_header.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 blocks tree root and verified that the nullifier - // was not yet included in the nullifier tree. + prove_nullifier_inclusion(nullifier, block_number, context); } #[aztec(private)] - fn provePublicValueInclusion( + fn test_public_value_inclusion_proof( public_value: Field, block_number: u32, // The block at which we'll prove that the public value exists ) { - // 1) Get block header from oracle and ensure that the block hash is included in the current blocks tree - // root. - let block_header = context.get_block_header(block_number); - - // 2) Compute the public value leaf index. - // We have to compute the leaf index here because unlike in the case of note commitments, public values are - // not siloed with contract address so an oracle could cheat and give us a membership witness for arbitrary - // value in the public data tree. - let public_value_leaf_index = pedersen_hash( - [context.this_address(), storage.public_value.storage_slot], - GENERATOR_INDEX__PUBLIC_LEAF_INDEX - ); - - // 3) Get the sibling path of the public value leaf index in the public data tree at block `block_number`. - let public_data_tree_id = 3; // TODO(#3443) - let path: [Field; PUBLIC_DATA_TREE_HEIGHT] = - get_sibling_path(block_number, public_data_tree_id, public_value_leaf_index); - - // 4) Prove that the public value provided on input is in the public data tree - assert( - block_header.public_data_tree_root == compute_merkle_root(public_value, public_value_leaf_index, path), - "Proving public value inclusion failed" - ); - - // --> Now we have traversed the trees all the way up to blocks tree root and that way verified that - // a specific `public_value` was really set in a given contract storage slot at block `block_number`. + prove_public_value_inclusion(public_value, storage.public_value.storage_slot, block_number, context); } // Computes note hash and nullifier. diff --git a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr b/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr deleted file mode 100644 index e67cf2fd776..00000000000 --- a/yarn-project/noir-contracts/src/contracts/inclusion_proofs_contract/src/utils.nr +++ /dev/null @@ -1,11 +0,0 @@ -// TODO(#3470): Copied over from https://github.com/AztecProtocol/aztec-packages/blob/a07c4bd47313be6aa604a63f37857eb0136b41ba/yarn-project/noir-protocol-circuits/src/crates/rollup-lib/src/base/base_rollup_inputs.nr#L599 -// move to a shared place? - -// TODO to radix returns u8, so we cannot use bigger radixes. It'd be ideal to use a radix of the maximum range-constrained integer noir supports -pub fn full_field_less_than(lhs: Field, rhs: Field) -> bool { - dep::std::eddsa::lt_bytes32(lhs, rhs) -} - -pub fn full_field_greater_than(lhs: Field, rhs: Field) -> bool { - dep::std::eddsa::lt_bytes32(rhs, lhs) -} \ No newline at end of file