diff --git a/docs/docs/aztec/smart_contracts/functions/attributes.md b/docs/docs/aztec/smart_contracts/functions/attributes.md index fa274fd7037a..10d80f24df2a 100644 --- a/docs/docs/aztec/smart_contracts/functions/attributes.md +++ b/docs/docs/aztec/smart_contracts/functions/attributes.md @@ -236,7 +236,7 @@ impl NoteHash for CustomNote { ) } - unconstrained fn compute_nullifier_without_context(self, storage_slot: Field, contract_address: AztecAddress, note_nonce: Field) -> Field { + unconstrained fn compute_nullifier_unconstrained(self, storage_slot: Field, contract_address: AztecAddress, note_nonce: Field) -> Field { // We set the note_hash_counter to 0 as the note is not transient and the concept of transient note does // not make sense in an unconstrained context. let retrieved_note = RetrievedNote { note: self, contract_address, nonce: note_nonce, note_hash_counter: 0 }; diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index ca535d3a95a2..788df4b9a506 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -8,6 +8,19 @@ Aztec is in full-speed development. Literally every version breaks compatibility ## TBD +### [aztec-nr] `compute_nullifier_without_context` renamed + +The `compute_nullifier_without_context` function from `NoteHash` (ex `NoteInterface`) is now called `compute_nullifier_unconstrained`, and instead of taking storage slot, contract address and nonce it takes a note hash for nullification (same as `compute_note_hash`). This makes writing this +function simpler: + +```diff +- unconstrained fn compute_nullifier_without_context(self, storage_slot: Field, contract_address: AztecAddress, nonce: Field) -> Field { +- let note_hash_for_nullify = ...; ++ unconstrained fn compute_nullifier_unconstrained(self, note_hash_for_nullify: Field) -> Field { + ... + } +``` + ### `U128` type replaced with native `u128` The `U128` type has been replaced with the native `u128` type. This means that you can no longer use the `U128` type in your code. Instead, you should use the `u128` type. diff --git a/noir-projects/aztec-nr/aztec/src/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/discovery/mod.nr index 23e11b0bc147..c03a695c75ba 100644 --- a/noir-projects/aztec-nr/aztec/src/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/discovery/mod.nr @@ -19,7 +19,7 @@ pub global MAX_NOTE_PACKED_LEN: u32 = PRIVATE_LOG_SIZE_IN_FIELDS - NOTE_PRIVATE_ pub struct NoteHashAndNullifier { /// The result of NoteHash::compute_note_hash pub note_hash: Field, - /// The result of NullifiableNote::compute_nullifier_without_context + /// The result of NoteHash::compute_nullifier_unconstrained (since all of note discovery is unconstrained) pub inner_nullifier: Field, } @@ -39,7 +39,12 @@ pub struct NoteHashAndNullifier { /// let note = MyNoteType::unpack(aztec::utils::array::subarray(packed_note.storage(), 0)); /// /// let note_hash = note.compute_note_hash(storage_slot); -/// let inner_nullifier = note.compute_nullifier_without_context(storage_slot, contract_address, nonce); +/// let note_hash_for_nullify = aztec::note::utils::compute_note_hash_for_nullify( +/// RetrievedNote{ note, contract_address, metadata: SettledNoteMetadata::new(nonce).into() }, +/// storage_slot +/// ); +/// +/// let inner_nullifier = note.compute_nullifier_unconstrained(note_hash_for_nullify); /// /// Option::some( /// aztec::discovery::NoteHashAndNullifier { diff --git a/noir-projects/aztec-nr/aztec/src/macros/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/mod.nr index 624fc275b53b..062d82d5e38c 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/mod.nr @@ -156,6 +156,18 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() - quote { unpack }, ); + let compute_note_hash = get_trait_impl_method( + typ, + quote { crate::note::note_interface::NoteHash }, + quote { compute_note_hash }, + ); + + let compute_nullifier_unconstrained = get_trait_impl_method( + typ, + quote { crate::note::note_interface::NoteHash }, + quote { compute_nullifier_unconstrained }, + ); + let if_or_else_if = if i == 0 { quote { if } } else { @@ -177,8 +189,21 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() - let note = $unpack(aztec::utils::array::subarray(packed_note.storage(), 0)); - let note_hash = note.compute_note_hash(storage_slot); - let inner_nullifier = note.compute_nullifier_without_context(storage_slot, contract_address, nonce); + let note_hash = $compute_note_hash(note, storage_slot); + + // The note discovery process finds settled notes, that is, notes that were created in prior + // transactions and are therefore already part of the note hash tree. We therefore compute the + // nullification note hash by treating the note as a settled note with the provided nonce. + let note_hash_for_nullify = aztec::note::utils::compute_note_hash_for_nullify( + aztec::note::retrieved_note::RetrievedNote{ + note, + contract_address, + metadata: aztec::note::note_metadata::SettledNoteMetadata::new(nonce).into() + }, + storage_slot, + ); + + let inner_nullifier = $compute_nullifier_unconstrained(note, note_hash_for_nullify); Option::some( aztec::discovery::NoteHashAndNullifier { diff --git a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr index 39cf5d01d7d3..8fd18fd7c8e3 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -78,12 +78,7 @@ comptime fn generate_note_interface(s: StructDefinition, note_type_id: Field) -> /// /// fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field { ... } /// -/// unconstrained fn compute_nullifier_without_context( -/// self, -/// storage_slot: Field, -/// contract_address: AztecAddress, -/// note_nonce: Field, -/// ) -> Field { ... } +/// unconstrained fn compute_nullifier_unconstrained(note_hash_for_nullify: Field) -> Field { ... } /// } /// ``` comptime fn generate_note_hash_trait_impl(s: StructDefinition) -> Quoted { @@ -112,18 +107,10 @@ comptime fn generate_note_hash_trait_impl(s: StructDefinition) -> Quoted { ) } - unconstrained fn compute_nullifier_without_context( + unconstrained fn compute_nullifier_unconstrained( self, - storage_slot: Field, - contract_address: aztec::prelude::AztecAddress, - note_nonce: Field, + note_hash_for_nullify: Field, ) -> Field { - let retrieved_note = aztec::prelude::RetrievedNote { - note: self, - contract_address, - metadata: aztec::note::note_metadata::SettledNoteMetadata::new(note_nonce).into(), - }; - let note_hash_for_nullify = aztec::note::utils::compute_note_hash_for_nullify(retrieved_note, storage_slot); let owner_npk_m = aztec::keys::getters::get_public_keys(self.owner).npk_m; // We invoke hash as a static trait function rather than calling owner_npk_m.hash() directly // in the quote to avoid "trait not in scope" compiler warnings. @@ -149,14 +136,7 @@ comptime fn generate_note_hash_trait_impl(s: StructDefinition) -> Quoted { /// ... /// } /// -/// unconstrained fn compute_nullifier_without_context( -/// self, -/// storage_slot: Field, -/// contract_address: AztecAddress, -/// note_nonce: Field, -/// ) -> Field { -/// ... -/// } +/// unconstrained fn compute_nullifier_unconstrained(note_hash_for_nullify: Field) -> Field { ... } /// } /// /// # On differences from `generate_note_hash_trait_impl` @@ -230,18 +210,10 @@ comptime fn generate_note_hash_trait_impl_for_partial_note( ) } - unconstrained fn compute_nullifier_without_context( + unconstrained fn compute_nullifier_unconstrained( self, - storage_slot: Field, - contract_address: aztec::prelude::AztecAddress, - note_nonce: Field, + note_hash_for_nullify: Field, ) -> Field { - let retrieved_note = aztec::prelude::RetrievedNote { - note: self, - contract_address, - metadata: aztec::note::note_metadata::SettledNoteMetadata::new(note_nonce).into(), - }; - let note_hash_for_nullify = aztec::note::utils::compute_note_hash_for_nullify(retrieved_note, storage_slot); let owner_npk_m = aztec::keys::getters::get_public_keys(self.owner).npk_m; // We invoke hash as a static trait function rather than calling owner_npk_m.hash() directly in // the quote to avoid "trait not in scope" compiler warnings. @@ -1127,7 +1099,7 @@ pub comptime fn note(s: StructDefinition) -> Quoted { /// /// // Custom nullifier computation... /// fn compute_nullifier(...) -> Field { ... } -/// fn compute_nullifier_without_context(...) -> Field { ... } +/// fn compute_nullifier_unconstrained(...) -> Field { ... } /// } /// ``` pub comptime fn custom_note(s: StructDefinition) -> Quoted { diff --git a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr index 7338d9e1a1f8..bdce61d880d3 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr @@ -30,23 +30,10 @@ pub trait NoteHash { /// circuits. fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field; - /// Like `compute_nullifier`, this function also computes a note's nullifier, but there are some key differences. - /// - /// First and most importantly, this function is unconstrained: there are no guarantees on the returned value being - /// correct. Because of that it doesn't need to take a context (since it won't perform any kernel key validation - /// requests). - /// - /// Second, it always computes the nullifier for a **settled** note, i.e. a note that has been created in a previous - /// transaction, which therefore has a nonce. This is typically fine, since this function will mostly be used in - /// unconstrained execution contexts, which exist outside of any transaction and therefore have no concept of - /// pending notes, only settled. This also causes this function to not need to take a note hash for nullification, - /// since it will just compute the unique note hash internally using the provided nonce. - unconstrained fn compute_nullifier_without_context( - self, - storage_slot: Field, - contract_address: AztecAddress, - note_nonce: Field, - ) -> Field; + /// Like `compute_nullifier`, except this variant is unconstrained: there are no guarantees on the returned value + /// being correct. Because of that it doesn't need to take a context (since it won't perform any kernel key + /// validation requests). + unconstrained fn compute_nullifier_unconstrained(self, note_hash_for_nullify: Field) -> Field; } // docs:end:note_interfaces diff --git a/noir-projects/aztec-nr/aztec/src/note/utils.nr b/noir-projects/aztec-nr/aztec/src/note/utils.nr index a8bc843dfd9a..1a3ee92305a5 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -33,7 +33,8 @@ where } } -/// Returns the note hash that must be used to compute a note's nullifier. +/// Returns the note hash that must be used to compute a note's nullifier when calling `NoteHash::compute_nullifier` or +/// `NoteHash::compute_nullifier_unconstrained`. pub fn compute_note_hash_for_nullify( retrieved_note: RetrievedNote, storage_slot: Field, diff --git a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr index 8e3390a4943c..6c70b7de64cb 100644 --- a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr +++ b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr @@ -1,11 +1,6 @@ use crate::{ context::PrivateContext, - note::{ - note_interface::{NoteHash, NoteType}, - note_metadata::SettledNoteMetadata, - retrieved_note::RetrievedNote, - utils::compute_note_hash_for_nullify, - }, + note::{note_interface::{NoteHash, NoteType}, retrieved_note::RetrievedNote}, }; use dep::protocol_types::{ @@ -47,20 +42,9 @@ impl NoteHash for MockNote { ) } - unconstrained fn compute_nullifier_without_context( - self, - storage_slot: Field, - contract_address: AztecAddress, - note_nonce: Field, - ) -> Field { - let retrieved_note = RetrievedNote { - note: self, - contract_address, - metadata: SettledNoteMetadata::new(note_nonce).into(), - }; + unconstrained fn compute_nullifier_unconstrained(self, note_hash_for_nullify: Field) -> Field { // We don't use any kind of secret here since this is only a mock note and having it here would make tests // more cumbersome - let note_hash_for_nullify = compute_note_hash_for_nullify(retrieved_note, storage_slot); poseidon2_hash_with_separator( [note_hash_for_nullify], GENERATOR_INDEX__NOTE_NULLIFIER as Field, diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr b/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr index c40e23275b1b..b1a6e3bd6be0 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/test_note.nr @@ -3,8 +3,8 @@ use dep::aztec::{ macros::notes::custom_note, note::note_interface::NoteHash, protocol_types::{ - address::AztecAddress, constants::GENERATOR_INDEX__NOTE_HASH, - hash::poseidon2_hash_with_separator, traits::Packable, utils::arrays::array_concat, + constants::GENERATOR_INDEX__NOTE_HASH, hash::poseidon2_hash_with_separator, + traits::Packable, utils::arrays::array_concat, }, }; @@ -27,17 +27,17 @@ impl NoteHash for TestNote { _context: &mut PrivateContext, _note_hash_for_nullify: Field, ) -> Field { - // This note is never nullified so we don't care about having a real implementation here. + // This note's nullifier is never used for any meaningful purpose so we don't care about having a real + // implementation here. 0 } - unconstrained fn compute_nullifier_without_context( + unconstrained fn compute_nullifier_unconstrained( _self: Self, - _storage_slot: Field, - _contract_address: AztecAddress, - _note_nonce: Field, + _note_hash_for_nullify: Field, ) -> Field { - // This note is never nullified so we don't care about having a real implementation here. + // This note's nullifier is never used for any meaningful purpose so we don't care about having a real + // implementation here. 0 } } diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr index 20820383f31f..d40f83feb22c 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/transparent_note.nr @@ -1,9 +1,7 @@ use dep::aztec::{ macros::notes::custom_note, - note::{note_metadata::SettledNoteMetadata, utils::compute_note_hash_for_nullify}, - prelude::{NoteHash, PrivateContext, RetrievedNote}, + prelude::{NoteHash, PrivateContext}, protocol_types::{ - address::AztecAddress, constants::{GENERATOR_INDEX__NOTE_HASH, GENERATOR_INDEX__NOTE_NULLIFIER}, hash::poseidon2_hash_with_separator, traits::Packable, @@ -48,18 +46,7 @@ impl NoteHash for TransparentNote { ) } - unconstrained fn compute_nullifier_without_context( - self, - storage_slot: Field, - contract_address: AztecAddress, - note_nonce: Field, - ) -> Field { - let retrieved_note = RetrievedNote { - note: self, - contract_address, - metadata: SettledNoteMetadata::new(note_nonce).into(), - }; - let note_hash_for_nullify = compute_note_hash_for_nullify(retrieved_note, storage_slot); + unconstrained fn compute_nullifier_unconstrained(self, note_hash_for_nullify: Field) -> Field { // compute_nullifier ignores context so we can reuse it here self.compute_nullifier(zeroed(), note_hash_for_nullify) }