diff --git a/address-note/src/address_note.nr b/address-note/src/address_note.nr index 93251b6..01991d5 100644 --- a/address-note/src/address_note.nr +++ b/address-note/src/address_note.nr @@ -1,64 +1,17 @@ use dep::aztec::{ - context::PrivateContext, - keys::getters::{get_nsk_app, get_public_keys}, macros::notes::note, - note::{ - note_interface::NullifiableNote, note_metadata::SettledNoteMetadata, - retrieved_note::RetrievedNote, utils::compute_note_hash_for_nullify, - }, oracle::random::random, - protocol_types::{ - address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, traits::Serialize, - }, + protocol_types::{address::AztecAddress, traits::Serialize}, }; // docs:start:address_note_def -// docs:start:address_note_struct -// Stores an address #[note] -#[derive(Eq, Serialize)] +#[derive(Eq)] pub struct AddressNote { address: AztecAddress, owner: AztecAddress, randomness: Field, } -// docs:end:address_note_struct - -impl NullifiableNote for AddressNote { - fn compute_nullifier( - self, - context: &mut PrivateContext, - note_hash_for_nullify: Field, - ) -> Field { - let owner_npk_m_hash = get_public_keys(self.owner).npk_m.hash(); - let secret = context.request_nsk_app(owner_npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } - - 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); - let owner_npk_m_hash = get_public_keys(self.owner).npk_m.hash(); - let secret = get_nsk_app(owner_npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } -} impl AddressNote { pub fn new(address: AztecAddress, owner: AztecAddress) -> Self { @@ -69,5 +22,5 @@ impl AddressNote { let randomness = unsafe { random() }; AddressNote { address, owner, randomness } } - // docs:end:address_note_def } +// docs:end:address_note_def diff --git a/aztec/src/discovery/mod.nr b/aztec/src/discovery/mod.nr index 08a06c0..23e11b0 100644 --- a/aztec/src/discovery/mod.nr +++ b/aztec/src/discovery/mod.nr @@ -17,7 +17,7 @@ global NOTE_PRIVATE_LOG_RESERVED_FIELDS: u32 = 2; pub global MAX_NOTE_PACKED_LEN: u32 = PRIVATE_LOG_SIZE_IN_FIELDS - NOTE_PRIVATE_LOG_RESERVED_FIELDS; pub struct NoteHashAndNullifier { - /// The result of NoteInterface::compute_note_hash + /// The result of NoteHash::compute_note_hash pub note_hash: Field, /// The result of NullifiableNote::compute_nullifier_without_context pub inner_nullifier: Field, @@ -33,7 +33,7 @@ pub struct NoteHashAndNullifier { /// /// ``` /// |packed_note_content, contract_address, nonce, storage_slot, note_type_id| { -/// if note_type_id == MyNoteType::get_note_type_id() { +/// if note_type_id == MyNoteType::get_id() { /// assert(packed_note_content.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH); /// /// let note = MyNoteType::unpack(aztec::utils::array::subarray(packed_note.storage(), 0)); @@ -46,7 +46,7 @@ pub struct NoteHashAndNullifier { /// note_hash, inner_nullifier /// } /// ) -/// } else if note_type_id == MyOtherNoteType::get_note_type_id() { +/// } else if note_type_id == MyOtherNoteType::get_id() { /// ... // Similar to above but calling MyOtherNoteType::unpack_content /// } else { /// Option::none() // Unknown note type ID diff --git a/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr b/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr index ad150a3..7143c54 100644 --- a/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr +++ b/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr @@ -5,7 +5,7 @@ use crate::{ ecdh_shared_secret::derive_ecdh_shared_secret_using_aztec_address, ephemeral::generate_ephemeral_key_pair, }, - note::{note_emission::NoteEmission, note_interface::NoteInterface}, + note::{note_emission::NoteEmission, note_interface::NoteType}, oracle::{ notes::{get_app_tag_as_sender, increment_app_tagging_secret_index_as_sender}, random::random, @@ -218,14 +218,14 @@ fn compute_note_plaintext_for_this_strategy( storage_slot: Field, ) -> [u8; N * 32 + 64] where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { let packed_note = note.pack(); let storage_slot_bytes: [u8; 32] = storage_slot.to_be_bytes(); // TODO(#10952): The following can be reduced to 7 bits - let note_type_id_bytes: [u8; 32] = Note::get_note_type_id().to_be_bytes(); + let note_type_id_bytes: [u8; 32] = Note::get_id().to_be_bytes(); // We combine all the bytes into plaintext_bytes: let mut plaintext_bytes: [u8; N * 32 + 64] = [0; N * 32 + 64]; @@ -252,7 +252,7 @@ fn compute_log( sender: AztecAddress, ) -> [Field; PRIVATE_LOG_SIZE_IN_FIELDS] where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { // ***************************************************************************** // Compute the shared secret @@ -416,7 +416,7 @@ unconstrained fn compute_log_unconstrained( sender: AztecAddress, ) -> [Field; PRIVATE_LOG_SIZE_IN_FIELDS] where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { compute_log(context, note, storage_slot, recipient, sender) } @@ -431,7 +431,7 @@ pub fn encode_and_encrypt_note( sender: AztecAddress, ) -> fn[(&mut PrivateContext, AztecAddress, AztecAddress)](NoteEmission) -> () where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { |e: NoteEmission| { let note = e.note; @@ -454,7 +454,7 @@ pub fn encode_and_encrypt_note_unconstrained( sender: AztecAddress, ) -> fn[(&mut PrivateContext, AztecAddress, AztecAddress)](NoteEmission) -> () where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { |e: NoteEmission| { let note = e.note; diff --git a/aztec/src/history/note_inclusion.nr b/aztec/src/history/note_inclusion.nr index 6382577..cf2cb4d 100644 --- a/aztec/src/history/note_inclusion.nr +++ b/aztec/src/history/note_inclusion.nr @@ -3,8 +3,7 @@ use dep::protocol_types::merkle_tree::root::root_from_sibling_path; use crate::{ note::{ - note_interface::{NoteInterface, NullifiableNote}, - retrieved_note::RetrievedNote, + note_interface::NoteHash, retrieved_note::RetrievedNote, utils::compute_note_hash_for_nullify, }, oracle::get_membership_witness::get_note_hash_membership_witness, @@ -17,13 +16,13 @@ trait ProveNoteInclusion { storage_slot: Field, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteHash; } impl ProveNoteInclusion for BlockHeader { fn prove_note_inclusion(self, retrieved_note: RetrievedNote, storage_slot: Field) where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { let note_hash = compute_note_hash_for_nullify(retrieved_note, storage_slot); diff --git a/aztec/src/history/note_validity.nr b/aztec/src/history/note_validity.nr index 8911bf5..50e0be5 100644 --- a/aztec/src/history/note_validity.nr +++ b/aztec/src/history/note_validity.nr @@ -1,6 +1,6 @@ use crate::{ context::PrivateContext, - note::{note_interface::{NoteInterface, NullifiableNote}, retrieved_note::RetrievedNote}, + note::{note_interface::NoteHash, retrieved_note::RetrievedNote}, }; use dep::protocol_types::block_header::BlockHeader; @@ -13,7 +13,7 @@ trait ProveNoteValidity { context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteHash; } impl ProveNoteValidity for BlockHeader { @@ -24,7 +24,7 @@ impl ProveNoteValidity for BlockHeader { context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { self.prove_note_inclusion(retrieved_note, storage_slot); self.prove_note_not_nullified(retrieved_note, storage_slot, context); diff --git a/aztec/src/history/nullifier_inclusion.nr b/aztec/src/history/nullifier_inclusion.nr index 0a4df51..5d14bf6 100644 --- a/aztec/src/history/nullifier_inclusion.nr +++ b/aztec/src/history/nullifier_inclusion.nr @@ -4,8 +4,7 @@ use dep::protocol_types::merkle_tree::root::root_from_sibling_path; use crate::{ context::PrivateContext, note::{ - note_interface::{NoteInterface, NullifiableNote}, - retrieved_note::RetrievedNote, + note_interface::NoteHash, retrieved_note::RetrievedNote, utils::compute_siloed_note_nullifier, }, oracle::get_nullifier_membership_witness::get_nullifier_membership_witness, @@ -50,7 +49,7 @@ trait ProveNoteIsNullified { context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteHash; } impl ProveNoteIsNullified for BlockHeader { @@ -62,7 +61,7 @@ impl ProveNoteIsNullified for BlockHeader { context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { let nullifier = compute_siloed_note_nullifier(retrieved_note, storage_slot, context); diff --git a/aztec/src/history/nullifier_non_inclusion.nr b/aztec/src/history/nullifier_non_inclusion.nr index 1e68f7f..2c2e88a 100644 --- a/aztec/src/history/nullifier_non_inclusion.nr +++ b/aztec/src/history/nullifier_non_inclusion.nr @@ -1,8 +1,7 @@ use crate::{ context::PrivateContext, note::{ - note_interface::{NoteInterface, NullifiableNote}, - retrieved_note::RetrievedNote, + note_interface::NoteHash, retrieved_note::RetrievedNote, utils::compute_siloed_note_nullifier, }, oracle::get_nullifier_membership_witness::get_low_nullifier_membership_witness, @@ -63,7 +62,7 @@ trait ProveNoteNotNullified { context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteHash; } impl ProveNoteNotNullified for BlockHeader { @@ -75,7 +74,7 @@ impl ProveNoteNotNullified for BlockHeader { context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { let nullifier = compute_siloed_note_nullifier(retrieved_note, storage_slot, context); diff --git a/aztec/src/macros/functions/call_interface_stubs.nr b/aztec/src/macros/functions/call_interface_stubs.nr index 4b2cf4a..16154d0 100644 --- a/aztec/src/macros/functions/call_interface_stubs.nr +++ b/aztec/src/macros/functions/call_interface_stubs.nr @@ -1,6 +1,5 @@ use crate::macros::utils::{ - add_to_field_slice, AsStrQuote, compute_fn_selector, get_trait_impl_method, is_fn_private, - is_fn_view, + add_to_field_slice, AsStrQuote, compute_fn_selector, is_fn_private, is_fn_view, }; use std::meta::{type_of, unquote}; diff --git a/aztec/src/macros/mod.nr b/aztec/src/macros/mod.nr index 97877ad..624fc27 100644 --- a/aztec/src/macros/mod.nr +++ b/aztec/src/macros/mod.nr @@ -147,8 +147,8 @@ comptime fn generate_contract_library_method_compute_note_hash_and_nullifier() - let get_note_type_id = get_trait_impl_method( typ, - quote { crate::note::note_interface::NoteInterface }, - quote { get_note_type_id }, + quote { crate::note::note_interface::NoteType }, + quote { get_id }, ); let unpack = get_trait_impl_method( typ, diff --git a/aztec/src/macros/notes/mod.nr b/aztec/src/macros/notes/mod.nr index cfb3821..90c9ca7 100644 --- a/aztec/src/macros/notes/mod.nr +++ b/aztec/src/macros/notes/mod.nr @@ -50,14 +50,10 @@ comptime fn derive_packable_if_not_implemented_and_get_len(s: StructDefinition) } } -/// Generates default `NoteInterface` implementation for a given note struct `s` and returns it as a quote. +/// Generates default `NoteType` implementation for a given note struct `s` and returns it as a quote. /// -/// impl NoteInterface for NoteStruct { -/// fn get_note_type_id() -> Field { -/// ... -/// } -/// -/// fn compute_note_hash(self, storage_slot: Field) -> Field { +/// impl NoteType for NoteStruct { +/// fn get_id() -> Field { /// ... /// } /// } @@ -65,32 +61,105 @@ comptime fn generate_note_interface(s: StructDefinition, note_type_id: Field) -> let name = s.name(); quote { - impl aztec::note::note_interface::NoteInterface for $name { - fn get_note_type_id() -> Field { + impl aztec::note::note_interface::NoteType for $name { + fn get_id() -> Field { $note_type_id } + } + } +} + +/// Generates default `NoteHash` trait implementation for a given note struct `s` and returns it as a quote. +/// +/// # Generated Implementation +/// ``` +/// impl NoteHash for NoteStruct { +/// fn compute_note_hash(self, storage_slot: Field) -> 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 { ... } +/// } +/// ``` +comptime fn generate_note_hash_trait_impl(s: StructDefinition) -> Quoted { + let name = s.name(); + quote { + impl aztec::note::note_interface::NoteHash for $name { fn compute_note_hash(self, storage_slot: Field) -> Field { let inputs = aztec::protocol_types::utils::arrays::array_concat(self.pack(), [storage_slot]); aztec::protocol_types::hash::poseidon2_hash_with_separator(inputs, aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HASH) } + + fn compute_nullifier( + self, + context: &mut aztec::prelude::PrivateContext, + note_hash_for_nullify: Field, + ) -> Field { + 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. + let owner_npk_m_hash = aztec::protocol_types::traits::Hash::hash(owner_npk_m); + let secret = context.request_nsk_app(owner_npk_m_hash); + aztec::protocol_types::hash::poseidon2_hash_with_separator( + [note_hash_for_nullify, secret], + aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_NULLIFIER as Field, + ) + } + + unconstrained fn compute_nullifier_without_context( + self, + storage_slot: Field, + contract_address: aztec::prelude::AztecAddress, + note_nonce: 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. + let owner_npk_m_hash = aztec::protocol_types::traits::Hash::hash(owner_npk_m); + let secret = aztec::keys::getters::get_nsk_app(owner_npk_m_hash); + aztec::protocol_types::hash::poseidon2_hash_with_separator( + [note_hash_for_nullify, secret], + aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_NULLIFIER as Field, + ) + } } } } -/// Generates default `NoteInterface` implementation for a given partial note struct `s` and returns it as a quote. +/// Generates default `NoteHash` implementation for a given partial note struct `s` and returns it as a quote. /// -/// impl NoteInterface for NoteStruct { -/// fn get_note_type_id() -> Field { +/// impl NoteHash for NoteStruct { +/// fn compute_note_hash(self, storage_slot: Field) -> Field { /// ... /// } /// -/// fn compute_note_hash(self, storage_slot: Field) -> 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 { /// ... /// } /// } /// -/// # On differences from `generate_note_interface` +/// # On differences from `generate_note_hash_trait_impl` /// We use multi-scalar multiplication (MSM) instead of Poseidon2 here since this is a partial note and therefore /// does require MSM's additive homomorphism property (the property is used to add to the commitment in public). /// We don't use this implementation for standard notes as well because Poseidon2 is significantly cheaper @@ -108,9 +177,8 @@ comptime fn generate_note_interface(s: StructDefinition, note_type_id: Field) -> /// /// Since -l would be -3 (an extraordinarily large number that cannot be a valid preimage length), /// including the length protects against these collisions. -comptime fn generate_note_interface_for_partial_note( +comptime fn generate_note_hash_trait_impl_for_partial_note( s: StructDefinition, - note_type_id: Field, indexed_fixed_fields: [(Quoted, Type, u32)], indexed_nullable_fields: [(Quoted, Type, u32)], ) -> Quoted { @@ -136,11 +204,7 @@ comptime fn generate_note_interface_for_partial_note( .join(quote {,}); quote { - impl aztec::note::note_interface::NoteInterface for $name { - fn get_note_type_id() -> Field { - $note_type_id - } - + impl aztec::note::note_interface::NoteHash for $name { fn compute_note_hash(self, storage_slot: Field) -> Field { $new_aux_vars let point = std::embedded_curve_ops::multi_scalar_mul( @@ -149,6 +213,45 @@ comptime fn generate_note_interface_for_partial_note( ); point.x } + + fn compute_nullifier( + self, + context: &mut aztec::prelude::PrivateContext, + note_hash_for_nullify: Field, + ) -> Field { + 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. + let owner_npk_m_hash = aztec::protocol_types::traits::Hash::hash(owner_npk_m); + let secret = context.request_nsk_app(owner_npk_m_hash); + aztec::protocol_types::hash::poseidon2_hash_with_separator( + [note_hash_for_nullify, secret], + aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_NULLIFIER as Field, + ) + } + + unconstrained fn compute_nullifier_without_context( + self, + storage_slot: Field, + contract_address: aztec::prelude::AztecAddress, + note_nonce: 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. + let owner_npk_m_hash = aztec::protocol_types::traits::Hash::hash(owner_npk_m); + let secret = aztec::keys::getters::get_nsk_app(owner_npk_m_hash); + aztec::protocol_types::hash::poseidon2_hash_with_separator( + [note_hash_for_nullify, secret], + aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_NULLIFIER as Field, + ) + } } } } @@ -406,7 +509,7 @@ comptime fn generate_multi_scalar_mul( /// ); /// /// let let storage_slot_bytes = storage_slot.to_be_bytes(); -/// let let note_type_id_bytes = TokenNote::get_note_type_id().to_be_bytes(); +/// let let note_type_id_bytes = TokenNote::get_id().to_be_bytes(); /// /// for i in 0..32 { /// log_plaintext[i] = storage_slot_bytes[i]; @@ -511,7 +614,7 @@ comptime fn generate_setup_payload( } } - pub fn encrypt_log(self, context: &mut PrivateContext, recipient: aztec::protocol_types::address::AztecAddress, sender: aztec::protocol_types::address::AztecAddress) -> [Field; $encrypted_log_fields_length] { + pub fn encrypt_log(self, context: &mut aztec::prelude::PrivateContext, recipient: aztec::protocol_types::address::AztecAddress, sender: aztec::protocol_types::address::AztecAddress) -> [Field; $encrypted_log_fields_length] { aztec::encrypted_logs::log_assembly_strategies::default_aes128::partial_note::compute_partial_public_log_payload( context.this_address(), self.log_plaintext, @@ -561,7 +664,7 @@ comptime fn get_setup_log_plaintext_body( let mut log_plaintext: [u8; $log_plaintext_length] = [0; $log_plaintext_length]; let storage_slot_bytes: [u8; 32] = storage_slot.to_be_bytes(); - let note_type_id_bytes: [u8; 32] = $name::get_note_type_id().to_be_bytes(); + let note_type_id_bytes: [u8; 32] = $name::get_id().to_be_bytes(); for i in 0..32 { log_plaintext[i] = storage_slot_bytes[i]; @@ -902,8 +1005,8 @@ comptime fn index_note_fields( /// - SetupPayload /// - FinalizationPayload /// - PartialNote trait implementation -/// - NoteExport -/// - NoteInterface trait implementation +/// - NoteType trait implementation +/// - NoteHash trait implementation /// - Packable implementation /// /// Registers the note in the global `NOTES` map. @@ -914,6 +1017,8 @@ comptime fn index_note_fields( /// in the partial note are nullable. #[varargs] pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) -> Quoted { + assert_has_owner(s); + // We separate struct members into fixed ones and nullable ones and we store info about the start index of each // member in the packed note array. let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, nullable_fields); @@ -924,9 +1029,9 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) -> generate_setup_payload(s, indexed_fixed_fields, indexed_nullable_fields); let (finalization_payload_impl, finalization_payload_name) = generate_finalization_payload(s, indexed_fixed_fields, indexed_nullable_fields); - let note_interface_impl = generate_note_interface_for_partial_note( + let note_interface_impl = generate_note_interface(s, note_type_id); + let note_hash_impl = generate_note_hash_trait_impl_for_partial_note( s, - note_type_id, indexed_fixed_fields, indexed_nullable_fields, ); @@ -947,6 +1052,7 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) -> $setup_payload_impl $finalization_payload_impl $note_interface_impl + $note_hash_impl $partial_note_impl $packable_impl } @@ -954,18 +1060,22 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) -> /// Generates the following: /// - NoteTypeProperties -/// - NoteInterface trait implementation +/// - NoteType trait implementation +/// - NoteHash trait implementation /// - Packable implementation /// /// Registers the note in the global `NOTES` map. /// /// For more details on the generated code, see the individual functions. pub comptime fn note(s: StructDefinition) -> Quoted { + assert_has_owner(s); + let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, &[]); let note_properties = generate_note_properties(s); let note_type_id = get_next_note_type_id(); let note_interface_impl = generate_note_interface(s, note_type_id); + let note_hash_impl = generate_note_hash_trait_impl(s); let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s); register_note( @@ -979,20 +1089,50 @@ pub comptime fn note(s: StructDefinition) -> Quoted { quote { $note_properties $note_interface_impl + $note_hash_impl $packable_impl } } -/// Generates the following: -/// - NoteTypeProperties -/// - Packable implementation +/// Generates code for a custom note implementation that requires specialized note hash or nullifier computation. /// -/// Registers the note in the global `NOTES` map. +/// # Generated Code +/// - NoteTypeProperties: Defines the structure and properties of note fields +/// - NoteType trait implementation: Provides the note type ID +/// - Packable implementation: Enables serialization/deserialization of the note /// -/// For more details on the generated code, see the individual functions. -pub comptime fn note_custom_interface(s: StructDefinition) -> Quoted { +/// # Registration +/// Registers the note in the global `NOTES` map with: +/// - Note type ID +/// - Packed length +/// - Field indices and nullability +/// +/// # Use Cases +/// Use this macro when implementing a note that needs custom: +/// - Note hash computation logic +/// - Nullifier computation logic +/// +/// The macro omits generating default NoteHash trait implementation, allowing you to provide your own. +/// +/// # Example +/// ``` +/// #[custom_note] +/// struct CustomNote { +/// value: Field, +/// metadata: Field +/// } +/// +/// impl NoteHash for CustomNote { +/// // Custom note hash computation... +/// fn compute_note_hash(...) -> Field { ... } +/// +/// // Custom nullifier computation... +/// fn compute_nullifier(...) -> Field { ... } +/// fn compute_nullifier_without_context(...) -> Field { ... } +/// } +/// ``` +pub comptime fn custom_note(s: StructDefinition) -> Quoted { let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s); - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/12012): This is broken let note_type_id = get_next_note_type_id(); let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, &[]); @@ -1005,9 +1145,31 @@ pub comptime fn note_custom_interface(s: StructDefinition) -> Quoted { ); let note_properties = generate_note_properties(s); + let note_interface_impl = generate_note_interface(s, note_type_id); quote { $note_properties + $note_interface_impl $packable_impl } } + +/// Asserts that the note has an 'owner' field. +/// +/// We require notes implemented with #[note] macro and #[partial_note] macro to have an 'owner' field because our +/// auto-generated nullifier functions expect it. This requirement is most likely only temporary. +comptime fn assert_has_owner(note: StructDefinition) { + let fields = note.fields_as_written(); + let mut has_owner = false; + for i in 0..fields.len() { + let (field_name, _) = fields[i]; + if field_name == quote { owner } { + has_owner = true; + break; + } + } + assert( + has_owner, + "Note must have an 'owner' field. If your notes have no owner, use #[custom_note] insteadof #[note] and implement the NoteHashing trait manually.", + ); +} diff --git a/aztec/src/macros/utils.nr b/aztec/src/macros/utils.nr index aabd6be..ecd4205 100644 --- a/aztec/src/macros/utils.nr +++ b/aztec/src/macros/utils.nr @@ -252,7 +252,7 @@ pub(crate) comptime fn is_note(typ: Type) -> bool { let (def, _) = struc; def.has_named_attribute("note") | def.has_named_attribute("partial_note") - | def.has_named_attribute("note_custom_interface") + | def.has_named_attribute("custom_note") }) } @@ -266,7 +266,7 @@ pub(crate) comptime fn is_note(typ: Type) -> bool { /// A copy of this function exists in `noir-protocol-circuits/crates/types/src/meta/mod.nr`. We maintain separate /// copies because importing it here from there would cause the `target_trait` to be interpreted in the context /// of the protocol circuits types crate, making it impossible to compile code for traits from this crate -/// (e.g. NoteInterface). +/// (e.g. NoteType). pub(crate) comptime fn get_trait_impl_method( typ: Type, target_trait: Quoted, diff --git a/aztec/src/note/lifecycle.nr b/aztec/src/note/lifecycle.nr index 2e9a7c4..f4569bc 100644 --- a/aztec/src/note/lifecycle.nr +++ b/aztec/src/note/lifecycle.nr @@ -1,7 +1,7 @@ use crate::context::PrivateContext; use crate::note::{ note_emission::NoteEmission, - note_interface::{NoteInterface, NullifiableNote}, + note_interface::{NoteHash, NoteType}, retrieved_note::RetrievedNote, utils::{compute_note_hash_for_nullify_from_read_request, compute_note_hash_for_read_request}, }; @@ -14,7 +14,7 @@ pub fn create_note( note: Note, ) -> NoteEmission where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { let note_hash_counter = context.side_effect_counter; @@ -23,7 +23,7 @@ where let packed_note = Note::pack(note); notify_created_note( storage_slot, - Note::get_note_type_id(), + Note::get_id(), packed_note, note_hash, note_hash_counter, @@ -41,7 +41,7 @@ pub fn destroy_note( storage_slot: Field, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { let note_hash_for_read_request = compute_note_hash_for_read_request(retrieved_note, storage_slot); @@ -55,7 +55,7 @@ pub fn destroy_note_unsafe( note_hash_for_read_request: Field, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { let note_hash_for_nullify = compute_note_hash_for_nullify_from_read_request(retrieved_note, note_hash_for_read_request); diff --git a/aztec/src/note/note_getter/mod.nr b/aztec/src/note/note_getter/mod.nr index 0814379..54d8e8b 100644 --- a/aztec/src/note/note_getter/mod.nr +++ b/aztec/src/note/note_getter/mod.nr @@ -2,7 +2,7 @@ use crate::context::PrivateContext; use crate::note::{ constants::{GET_NOTE_ORACLE_RETURN_LENGTH, VIEW_NOTE_ORACLE_RETURN_LENGTH}, note_getter_options::{NoteGetterOptions, NoteStatus, PropertySelector, Select, Sort, SortOrder}, - note_interface::{NoteInterface, NullifiableNote}, + note_interface::{NoteHash, NoteType}, note_viewer_options::NoteViewerOptions, retrieved_note::RetrievedNote, utils::compute_note_hash_for_read_request, @@ -76,7 +76,7 @@ pub fn get_note( storage_slot: Field, ) -> (RetrievedNote, Field) where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { // Safety: Constraining that we got a valid note from the oracle is fairly straightforward: all we need to do // is check that the metadata is correct, and that the note exists. @@ -105,7 +105,7 @@ pub fn get_notes( options: NoteGetterOptions, ) -> (BoundedVec, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL>, BoundedVec) where - Note: NoteInterface + NullifiableNote + Eq + Packable, + Note: NoteType + NoteHash + Eq + Packable, { // Safety: The notes are constrained below. let opt_notes = unsafe { get_notes_internal(storage_slot, options) }; @@ -130,7 +130,7 @@ fn constrain_get_notes_internal, ) -> (BoundedVec, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL>, BoundedVec) where - Note: NoteInterface + NullifiableNote + Eq + Packable, + Note: NoteType + NoteHash + Eq + Packable, { // The filter is applied first to avoid pushing note read requests for notes we're not interested in. Note that // while the filter function can technically mutate the notes (as opposed to simply removing some), the private @@ -183,7 +183,7 @@ where unconstrained fn get_note_internal(storage_slot: Field) -> RetrievedNote where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { let placeholder_note = [Option::none()]; let placeholder_fields = [0; GET_NOTE_ORACLE_RETURN_LENGTH]; @@ -215,7 +215,7 @@ unconstrained fn get_notes_internal, ) -> [Option>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { // This function simply performs some transformations from NoteGetterOptions into the types required by the oracle. let (num_selects, select_by_indexes, select_by_offsets, select_by_lengths, select_values, select_comparators, sort_by_indexes, sort_by_offsets, sort_by_lengths, sort_order) = @@ -252,7 +252,7 @@ pub unconstrained fn view_notes( options: NoteViewerOptions, ) -> BoundedVec where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { let (num_selects, select_by_indexes, select_by_offsets, select_by_lengths, select_values, select_comparators, sort_by_indexes, sort_by_offsets, sort_by_lengths, sort_order) = flatten_options(options.selects, options.sorts); diff --git a/aztec/src/note/note_getter_options.nr b/aztec/src/note/note_getter_options.nr index 66432e4..e161da9 100644 --- a/aztec/src/note/note_getter_options.nr +++ b/aztec/src/note/note_getter_options.nr @@ -1,4 +1,4 @@ -use crate::note::{note_interface::NoteInterface, retrieved_note::RetrievedNote}; +use crate::note::{note_interface::NoteType, retrieved_note::RetrievedNote}; use dep::protocol_types::{ constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, traits::{Packable, ToField}, @@ -137,7 +137,7 @@ impl NoteGetterOptions NoteGetterOptions where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { // This function initializes a NoteGetterOptions that simply returns the maximum number of notes allowed in a call. pub fn new() -> Self { @@ -157,7 +157,7 @@ where impl NoteGetterOptions where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { // This function initializes a NoteGetterOptions with a preprocessor, which takes the notes returned from // the database and preprocessor_args as its parameters. @@ -182,7 +182,7 @@ where impl NoteGetterOptions where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { // This function initializes a NoteGetterOptions with a filter, which takes // the notes returned from the database and filter_args as its parameters. diff --git a/aztec/src/note/note_interface.nr b/aztec/src/note/note_interface.nr index 4d58d12..7338d9e 100644 --- a/aztec/src/note/note_interface.nr +++ b/aztec/src/note/note_interface.nr @@ -1,21 +1,22 @@ use crate::context::PrivateContext; use dep::protocol_types::address::AztecAddress; use dep::protocol_types::traits::Empty; -pub trait NoteProperties { - fn properties() -> T; -} - -pub trait PartialNote -where - S: Empty, - F: Empty, -{ - fn setup_payload() -> S; - fn finalization_payload() -> F; +// Autogenerated by the #[note] macro +// docs:start:note_interfaces +pub trait NoteType { + /// Returns the unique identifier for the note type. This is typically used when processing note logs. + fn get_id() -> Field; } -pub trait NullifiableNote { +pub trait NoteHash { + /// Returns the non-siloed note hash, i.e. the inner hash computed by the contract during private execution. Note + /// hashes are later siloed by contract address and nonce by the kernels before being committed to the state tree. + /// + /// This should be a commitment to the packed note, including the storage slot (for indexing) and some random + /// value (to prevent brute force trial-hashing attacks). + fn compute_note_hash(self, storage_slot: Field) -> Field; + /// Returns the non-siloed nullifier (also called inner-nullifier), which will be later siloed by contract address /// by the kernels before being committed to the state tree. /// @@ -47,17 +48,18 @@ pub trait NullifiableNote { note_nonce: Field, ) -> Field; } +// docs:end:note_interfaces -// docs:start:note_interface -// Autogenerated by the #[note] macro -pub trait NoteInterface { - fn get_note_type_id() -> Field; +pub trait NoteProperties { + fn properties() -> T; +} - /// Returns the non-siloed note hash, i.e. the inner hash computed by the contract during private execution. Note - /// hashes are later siloed by contract address and nonce by the kernels before being committed to the state tree. - /// - /// This should be a commitment to the packed note, including the storage slot (for indexing) and some random - /// value (to prevent brute force trial-hashing attacks). - fn compute_note_hash(self, storage_slot: Field) -> Field; +pub trait PartialNote +where + S: Empty, + F: Empty, +{ + fn setup_payload() -> S; + + fn finalization_payload() -> F; } -// docs:end:note_interface diff --git a/aztec/src/note/note_viewer_options.nr b/aztec/src/note/note_viewer_options.nr index fb93b8d..5150e52 100644 --- a/aztec/src/note/note_viewer_options.nr +++ b/aztec/src/note/note_viewer_options.nr @@ -1,6 +1,6 @@ use crate::note::constants::MAX_NOTES_PER_PAGE; use crate::note::note_getter_options::{NoteStatus, PropertySelector, Select, Sort}; -use crate::note::note_interface::NoteInterface; +use crate::note::note_interface::NoteType; use dep::protocol_types::traits::{Packable, ToField}; use std::option::Option; @@ -17,7 +17,7 @@ pub struct NoteViewerOptions { impl NoteViewerOptions { pub fn new() -> NoteViewerOptions where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { NoteViewerOptions { selects: BoundedVec::new(), diff --git a/aztec/src/note/utils.nr b/aztec/src/note/utils.nr index 9b67cf8..a8bc843 100644 --- a/aztec/src/note/utils.nr +++ b/aztec/src/note/utils.nr @@ -1,6 +1,6 @@ use crate::{ context::PrivateContext, - note::{note_interface::{NoteInterface, NullifiableNote}, retrieved_note::RetrievedNote}, + note::{note_interface::NoteHash, retrieved_note::RetrievedNote}, }; use dep::protocol_types::hash::{ @@ -13,7 +13,7 @@ pub fn compute_note_hash_for_read_request( storage_slot: Field, ) -> Field where - Note: NoteInterface, + Note: NoteHash, { let note_hash = retrieved_note.note.compute_note_hash(storage_slot); @@ -39,7 +39,7 @@ pub fn compute_note_hash_for_nullify( storage_slot: Field, ) -> Field where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { compute_note_hash_for_nullify_from_read_request( retrieved_note, @@ -57,7 +57,7 @@ pub fn compute_note_hash_for_nullify_from_read_request( // There is just one instance in which the note hash for nullification does not match the note hash used for a read // request, which is when dealing with pending previous phase notes. These had their existence proven using their // non-siloed note hash along with the note hash counter (like all pending notes), but since they will be - // uncondtionally inserted in the note hash tree (since they cannot be squashed) they must be nullified using the + // unconditionally inserted in the note hash tree (since they cannot be squashed) they must be nullified using the // *unique* note hash. // If we didn't, it'd be possible to emit a second different nullifier for the same note in a follow up transaction, // once the note is settled, resulting in a double spend. @@ -80,7 +80,7 @@ pub fn compute_siloed_note_nullifier( context: &mut PrivateContext, ) -> Field where - Note: NoteInterface + NullifiableNote, + Note: NoteHash, { let note_hash_for_nullify = compute_note_hash_for_nullify(retrieved_note, storage_slot); let inner_nullifier = retrieved_note.note.compute_nullifier(context, note_hash_for_nullify); diff --git a/aztec/src/oracle/note_discovery.nr b/aztec/src/oracle/note_discovery.nr index 3a7d257..35d34e3 100644 --- a/aztec/src/oracle/note_discovery.nr +++ b/aztec/src/oracle/note_discovery.nr @@ -18,8 +18,8 @@ unconstrained fn sync_notes_oracle() {} /// /// The packed note is what `getNotes` will later return. PXE indexes notes by `storage_slot`, so this value /// is typically used to filter notes that correspond to different state variables. `note_hash` and `nullifier` are -/// the inner hashes, i.e. the raw hashes returned by `NoteInterface::compute_note_hash` and -/// `NullifiableNote::compute_nullifier`. PXE will verify that the siloed unique note hash was inserted into the tree +/// the inner hashes, i.e. the raw hashes returned by `NoteHash::compute_note_hash` and +/// `NoteHash::compute_nullifier`. PXE will verify that the siloed unique note hash was inserted into the tree /// at `tx_hash`, and will store the nullifier to later check for nullification. /// /// `recipient` is the account to which the note was sent to. Other accounts will not be able to access this note (e.g. diff --git a/aztec/src/oracle/notes.nr b/aztec/src/oracle/notes.nr index b8c319e..ec91033 100644 --- a/aztec/src/oracle/notes.nr +++ b/aztec/src/oracle/notes.nr @@ -1,7 +1,5 @@ use crate::{ - note::{ - note_interface::NoteInterface, note_metadata::NoteMetadata, retrieved_note::RetrievedNote, - }, + note::{note_interface::NoteType, note_metadata::NoteMetadata, retrieved_note::RetrievedNote}, utils::array, }; @@ -168,7 +166,7 @@ pub unconstrained fn get_notes [Option>; S] where - Note: NoteInterface + Packable, + Note: NoteType + Packable, { let fields = get_notes_oracle_wrapper( storage_slot, diff --git a/aztec/src/prelude.nr b/aztec/src/prelude.nr index b62e61c..5cc3a14 100644 --- a/aztec/src/prelude.nr +++ b/aztec/src/prelude.nr @@ -3,7 +3,7 @@ pub use crate::{ context::{PrivateContext, PublicContext, ReturnsHash}, note::{ note_getter_options::NoteGetterOptions, - note_interface::{NoteInterface, NullifiableNote}, + note_interface::{NoteHash, NoteType}, note_viewer_options::NoteViewerOptions, retrieved_note::RetrievedNote, }, diff --git a/aztec/src/state_vars/private_immutable.nr b/aztec/src/state_vars/private_immutable.nr index dab695e..da9da82 100644 --- a/aztec/src/state_vars/private_immutable.nr +++ b/aztec/src/state_vars/private_immutable.nr @@ -8,7 +8,7 @@ use crate::note::{ lifecycle::create_note, note_emission::NoteEmission, note_getter::{get_note, view_notes}, - note_interface::{NoteInterface, NullifiableNote}, + note_interface::{NoteHash, NoteType}, note_viewer_options::NoteViewerOptions, }; use crate::oracle::notes::check_nullifier_exists; @@ -56,7 +56,7 @@ impl PrivateImmutable { // docs:start:initialize pub fn initialize(self, note: Note) -> NoteEmission where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { // Nullify the storage slot. let nullifier = self.compute_initialization_nullifier(); @@ -69,7 +69,7 @@ impl PrivateImmutable { // docs:start:get_note pub fn get_note(self) -> Note where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { let storage_slot = self.storage_slot; let retrieved_note = get_note(self.context, storage_slot).0; @@ -94,7 +94,7 @@ impl PrivateImmutable { // docs:start:view_note pub unconstrained fn view_note(self) -> Note where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { let mut options = NoteViewerOptions::new(); view_notes(self.storage_slot, options.set_limit(1)).get(0) diff --git a/aztec/src/state_vars/private_mutable.nr b/aztec/src/state_vars/private_mutable.nr index f0164ae..01a2d40 100644 --- a/aztec/src/state_vars/private_mutable.nr +++ b/aztec/src/state_vars/private_mutable.nr @@ -8,7 +8,7 @@ use crate::note::{ lifecycle::{create_note, destroy_note_unsafe}, note_emission::NoteEmission, note_getter::{get_note, view_notes}, - note_interface::{NoteInterface, NullifiableNote}, + note_interface::{NoteHash, NoteType}, note_viewer_options::NoteViewerOptions, }; use crate::note::retrieved_note::RetrievedNote; @@ -59,7 +59,7 @@ impl PrivateMutable { impl PrivateMutable where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { // docs:start:initialize pub fn initialize(self, note: Note) -> NoteEmission { @@ -127,7 +127,7 @@ where impl PrivateMutable where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { pub unconstrained fn is_initialized(self) -> bool { let nullifier = self.compute_initialization_nullifier(); diff --git a/aztec/src/state_vars/private_set.nr b/aztec/src/state_vars/private_set.nr index 33946f1..7188f4a 100644 --- a/aztec/src/state_vars/private_set.nr +++ b/aztec/src/state_vars/private_set.nr @@ -5,7 +5,7 @@ use crate::note::{ note_emission::NoteEmission, note_getter::{get_notes, view_notes}, note_getter_options::NoteGetterOptions, - note_interface::{NoteInterface, NullifiableNote}, + note_interface::{NoteHash, NoteType}, note_viewer_options::NoteViewerOptions, utils::compute_note_hash_for_read_request, }; @@ -43,7 +43,7 @@ impl PrivateSet { impl PrivateSet where - Note: NoteInterface + NullifiableNote + Eq + Packable, + Note: NoteType + NoteHash + Eq + Packable, { // docs:start:insert pub fn insert(self, note: Note) -> NoteEmission { @@ -97,7 +97,7 @@ where impl PrivateSet where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { // docs:start:view_notes pub unconstrained fn view_notes( diff --git a/aztec/src/test/helpers/test_environment.nr b/aztec/src/test/helpers/test_environment.nr index 712ecd9..85a32ee 100644 --- a/aztec/src/test/helpers/test_environment.nr +++ b/aztec/src/test/helpers/test_environment.nr @@ -6,7 +6,7 @@ use crate::context::{ use crate::hash::hash_args; use crate::test::helpers::{cheatcodes, utils::Deployer}; -use crate::note::note_interface::{NoteInterface, NullifiableNote}; +use crate::note::note_interface::{NoteHash, NoteType}; use crate::oracle::{ execution::{get_block_number, get_contract_address}, notes::notify_created_note, @@ -158,7 +158,7 @@ impl TestEnvironment { contract_address: AztecAddress, ) where - Note: NoteInterface + NullifiableNote + Packable, + Note: NoteType + NoteHash + Packable, { let original_contract_address = get_contract_address(); cheatcodes::set_contract_address(contract_address); @@ -168,7 +168,7 @@ impl TestEnvironment { let packed_note = Note::pack(note); notify_created_note( storage_slot, - Note::get_note_type_id(), + Note::get_id(), packed_note, note_hash, note_hash_counter, diff --git a/aztec/src/test/mocks/mock_note.nr b/aztec/src/test/mocks/mock_note.nr index c71d1a6..8e3390a 100644 --- a/aztec/src/test/mocks/mock_note.nr +++ b/aztec/src/test/mocks/mock_note.nr @@ -1,12 +1,13 @@ use crate::{ context::PrivateContext, note::{ - note_interface::NoteInterface, note_metadata::SettledNoteMetadata, - retrieved_note::RetrievedNote, utils::compute_note_hash_for_nullify, + note_interface::{NoteHash, NoteType}, + note_metadata::SettledNoteMetadata, + retrieved_note::RetrievedNote, + utils::compute_note_hash_for_nullify, }, }; -use crate::note::note_interface::NullifiableNote; use dep::protocol_types::{ address::AztecAddress, constants::{GENERATOR_INDEX__NOTE_HASH, GENERATOR_INDEX__NOTE_NULLIFIER}, @@ -20,7 +21,19 @@ pub(crate) struct MockNote { pub(crate) value: Field, } -impl NullifiableNote for MockNote { +impl NoteType for MockNote { + fn get_id() -> Field { + // randomly chosen note type id --> has to fit within 7 bits + 76 + } +} + +impl NoteHash for MockNote { + fn compute_note_hash(self: Self, storage_slot: Field) -> Field { + let input = array_concat(self.pack(), [storage_slot]); + poseidon2_hash_with_separator(input, GENERATOR_INDEX__NOTE_HASH) + } + fn compute_nullifier( _self: Self, _context: &mut PrivateContext, @@ -55,21 +68,6 @@ impl NullifiableNote for MockNote { } } -impl NoteInterface for MockNote { - fn get_note_type_id() -> Field { - // randomly chosen note type id --> has to fit within 7 bits - 76 - } - - fn compute_note_hash(self: Self, storage_slot: Field) -> Field { - // We use Poseidon2 instead of multi-scalar multiplication (MSM) here since this is not a partial note - // and therefore does not require MSM's additive homomorphism property. Additionally, Poseidon2 uses fewer - // constraints. - let input = array_concat(self.pack(), [storage_slot]); - poseidon2_hash_with_separator(input, GENERATOR_INDEX__NOTE_HASH) - } -} - pub(crate) struct MockNoteBuilder { value: Field, contract_address: Option, diff --git a/uint-note/src/uint_note.nr b/uint-note/src/uint_note.nr index 923a29d..3e0517a 100644 --- a/uint-note/src/uint_note.nr +++ b/uint-note/src/uint_note.nr @@ -1,16 +1,11 @@ use dep::aztec::{ - keys::getters::{get_nsk_app, get_public_keys}, macros::notes::partial_note, - note::{note_metadata::SettledNoteMetadata, utils::compute_note_hash_for_nullify}, oracle::random::random, - prelude::{NullifiableNote, PrivateContext, RetrievedNote}, - protocol_types::{ - address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, traits::Serialize, - }, + protocol_types::{address::AztecAddress, traits::Serialize}, }; // docs:start:UintNote +// We derive the Serialize trait because in some cases notes are used as parameters to contract functions. #[partial_note(quote {value})] #[derive(Eq, Serialize)] pub struct UintNote { @@ -21,44 +16,6 @@ pub struct UintNote { randomness: Field, } // docs:end:UintNote - -impl NullifiableNote for UintNote { - // docs:start:nullifier - fn compute_nullifier( - self, - context: &mut PrivateContext, - note_hash_for_nullify: Field, - ) -> Field { - let owner_npk_m_hash = get_public_keys(self.owner).npk_m.hash(); - let secret = context.request_nsk_app(owner_npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } - // docs:end:nullifier - - 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); - let owner_npk_m_hash = get_public_keys(self.owner).npk_m.hash(); - let secret = get_nsk_app(owner_npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER, - ) - } -} - impl UintNote { pub fn new(value: U128, owner: AztecAddress) -> Self { // Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, diff --git a/value-note/src/utils.nr b/value-note/src/utils.nr index 98c3544..0ab3e48 100644 --- a/value-note/src/utils.nr +++ b/value-note/src/utils.nr @@ -1,13 +1,20 @@ -use crate::{filter::filter_notes_min_sum, value_note::{VALUE_NOTE_LEN, ValueNote}}; -use dep::aztec::encrypted_logs::log_assembly_strategies::default_aes128::note::encode_and_encrypt_note; -use dep::aztec::note::note_getter_options::SortOrder; -use dep::aztec::prelude::{AztecAddress, NoteGetterOptions, PrivateContext, PrivateSet}; +use crate::{filter::filter_notes_min_sum, value_note::ValueNote}; + +use aztec::{ + encrypted_logs::log_assembly_strategies::default_aes128::note::encode_and_encrypt_note, + note::note_getter_options::SortOrder, + prelude::{AztecAddress, NoteGetterOptions, PrivateContext, PrivateSet}, + protocol_types::traits::Packable, +}; // Sort the note values (0th field) in descending order. // Pick the fewest notes whose sum is equal to or greater than `amount`. -pub fn create_note_getter_options_for_decreasing_balance( +pub fn create_note_getter_options_for_decreasing_balance( amount: Field, -) -> NoteGetterOptions { +) -> NoteGetterOptions +where + ValueNote: Packable, +{ NoteGetterOptions::with_filter(filter_notes_min_sum, amount).sort( ValueNote::properties().value, SortOrder.DESC, diff --git a/value-note/src/value_note.nr b/value-note/src/value_note.nr index ba5bdf4..408081f 100644 --- a/value-note/src/value_note.nr +++ b/value-note/src/value_note.nr @@ -1,19 +1,4 @@ -use dep::aztec::{ - context::PrivateContext, - keys::getters::{get_nsk_app, get_public_keys}, - macros::notes::note, - note::{ - note_interface::NullifiableNote, note_metadata::SettledNoteMetadata, - retrieved_note::RetrievedNote, utils::compute_note_hash_for_nullify, - }, - oracle::random::random, - protocol_types::{ - address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, - }, -}; - -pub(crate) global VALUE_NOTE_LEN: u32 = 3; // 3 plus a header. +use aztec::{macros::notes::note, oracle::random::random, protocol_types::address::AztecAddress}; // docs:start:value-note-def #[note] @@ -25,45 +10,6 @@ pub struct ValueNote { } // docs:end:value-note-def -impl NullifiableNote for ValueNote { - // docs:start:nullifier - - fn compute_nullifier( - self, - context: &mut PrivateContext, - note_hash_for_nullify: Field, - ) -> Field { - let owner_npk_m_hash: Field = get_public_keys(self.owner).npk_m.hash(); - let secret = context.request_nsk_app(owner_npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } - - // docs:end:nullifier - - 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); - let owner_npk_m_hash: Field = get_public_keys(self.owner).npk_m.hash(); - let secret = get_nsk_app(owner_npk_m_hash); - poseidon2_hash_with_separator( - [note_hash_for_nullify, secret], - GENERATOR_INDEX__NOTE_NULLIFIER as Field, - ) - } -} - impl ValueNote { pub fn new(value: Field, owner: AztecAddress) -> Self { // Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing,