From 91829b377d18d1ca7e8a27f4f6569d4ecfafbaa5 Mon Sep 17 00:00:00 2001 From: benesjan Date: Mon, 9 Mar 2026 07:21:05 +0000 Subject: [PATCH] refactor: deduplicate MembershipWitness by reusing protocol-circuits definition Remove the duplicate `MembershipWitness` struct from the aztec-nr oracle module and reuse the canonical one from `noir-protocol-circuits/types/merkle_tree/membership`. This also drops the unnecessary `M` generic parameter that was a workaround for a Noir type-system limitation that no longer exists. Co-Authored-By: Claude Opus 4.6 --- .../aztec-nr/aztec/src/history/note.nr | 2 +- .../aztec-nr/aztec/src/oracle/block_header.nr | 2 +- .../src/oracle/get_membership_witness.nr | 24 +++++-------------- .../get_nullifier_membership_witness.nr | 13 ++++++---- .../types/src/merkle_tree/membership.nr | 4 ++-- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/history/note.nr b/noir-projects/aztec-nr/aztec/src/history/note.nr index de62753b9248..8239eb85c65d 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note.nr @@ -36,7 +36,7 @@ where // don't even care _where_ in the tree it is stored. This is because entries in the note hash tree are unique. assert_eq( block_header.state.partial.note_hash_tree.root, - root_from_sibling_path(unique_note_hash, witness.index, witness.path), + root_from_sibling_path(unique_note_hash, witness.leaf_index, witness.sibling_path), "Proving note inclusion failed", ); diff --git a/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr b/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr index ea2afd317baa..d1f7b81243e2 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/block_header.nr @@ -61,7 +61,7 @@ fn constrain_get_block_header_at_internal( // 3) Check that the block is in the archive (i.e. the witness is valid) assert_eq( anchor_block_header.last_archive.root, - root_from_sibling_path(block_hash, witness.index, witness.path), + root_from_sibling_path(block_hash, witness.leaf_index, witness.sibling_path), "Proving membership of a block in archive failed", ); diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_membership_witness.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_membership_witness.nr index 90f83ad6f9df..8c1783f085b3 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_membership_witness.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_membership_witness.nr @@ -1,33 +1,21 @@ use crate::protocol::{ abis::block_header::BlockHeader, constants::{ARCHIVE_HEIGHT, NOTE_HASH_TREE_HEIGHT}, - traits::{Deserialize, Hash, Serialize}, + merkle_tree::MembershipWitness, + traits::Hash, }; -// Note: We have M here because we need to somehow set it when calling get_membership_witness function and one way to -// do it is to set M here and then set type of the return param, e.g.: -// -// `let witness: MembershipWitness = get_membership_witness(...);` -// -// Another way to do it would be to add "type_hint: [Field; T]" as argument to `get_membership_witness` but that's a -// bit too boilerplatey for my taste. -#[derive(Deserialize, Eq, Serialize)] -pub struct MembershipWitness { - pub index: Field, - pub path: [Field; N], -} - #[oracle(aztec_utl_getNoteHashMembershipWitness)] unconstrained fn get_note_hash_membership_witness_oracle( anchor_block_hash: Field, note_hash: Field, -) -> MembershipWitness {} +) -> MembershipWitness {} #[oracle(aztec_utl_getBlockHashMembershipWitness)] unconstrained fn get_block_hash_membership_witness_oracle( anchor_block_hash: Field, block_hash: Field, -) -> MembershipWitness {} +) -> MembershipWitness {} // Note: get_nullifier_membership_witness function is implemented in get_nullifier_membership_witness.nr @@ -36,7 +24,7 @@ unconstrained fn get_block_hash_membership_witness_oracle( pub unconstrained fn get_note_hash_membership_witness( anchor_block_header: BlockHeader, note_hash: Field, -) -> MembershipWitness { +) -> MembershipWitness { let anchor_block_hash = anchor_block_header.hash(); get_note_hash_membership_witness_oracle(anchor_block_hash, note_hash) } @@ -48,7 +36,7 @@ pub unconstrained fn get_note_hash_membership_witness( pub unconstrained fn get_block_hash_membership_witness( anchor_block_header: BlockHeader, block_hash: Field, -) -> MembershipWitness { +) -> MembershipWitness { let anchor_block_hash = anchor_block_header.hash(); get_block_hash_membership_witness_oracle(anchor_block_hash, block_hash) } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr b/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr index c6712ba82416..f7b6002f89ff 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/get_nullifier_membership_witness.nr @@ -17,8 +17,12 @@ unconstrained fn get_low_nullifier_membership_witness_oracle( _nullifier: Field, ) -> NullifierMembershipWitness {} -// Nullifier here refers to the nullifier we are looking to get non-inclusion proof for (by proving that a lower -// nullifier's next_value is bigger than the nullifier) +/// Returns a membership witness for the low nullifier of `nullifier` in the nullifier tree whose root is defined in +/// `block_header`. +/// +/// The low nullifier is the leaf with the largest value that is still smaller than `nullifier`. This is used to prove +/// non-inclusion: if the low nullifier's `next_value` is greater than `nullifier`, then `nullifier` is not in the +/// tree. pub unconstrained fn get_low_nullifier_membership_witness( block_header: BlockHeader, nullifier: Field, @@ -33,8 +37,9 @@ unconstrained fn get_nullifier_membership_witness_oracle( _nullifier: Field, ) -> NullifierMembershipWitness {} -// Nullifier here refers to the nullifier we are looking to get non-inclusion proof for (by proving that a lower -// nullifier's next_value is bigger than the nullifier) +/// Returns a membership witness for `nullifier` in the nullifier tree whose root is defined in `block_header`. +/// +/// This is used to prove that a nullifier exists in the tree (inclusion proof). pub unconstrained fn get_nullifier_membership_witness( block_header: BlockHeader, nullifier: Field, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr index 22f45f274ada..effc0aa23b6f 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/merkle_tree/membership.nr @@ -4,10 +4,10 @@ use crate::{ leaf_preimage::IndexedTreeLeafPreimage, root::{root_from_sibling_path, root_from_sibling_path_with_hasher}, }, - traits::Empty, + traits::{Deserialize, Empty, Serialize}, }; -#[derive(Eq)] +#[derive(Deserialize, Eq, Serialize)] pub struct MembershipWitness { pub leaf_index: Field, pub sibling_path: [Field; N],