From 383e9614535b38d7c6a64a1a2999639e58f5ca7e Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 09:08:42 +0000 Subject: [PATCH 01/16] refactor: Notes implementing Packable --- .../default_aes128/note.nr | 17 +-- .../aztec/src/history/note_inclusion.nr | 12 +- .../aztec/src/history/note_validity.nr | 8 +- .../aztec/src/history/nullifier_inclusion.nr | 8 +- .../src/history/nullifier_non_inclusion.nr | 8 +- .../aztec-nr/aztec/src/macros/mod.nr | 28 ++-- .../aztec-nr/aztec/src/macros/notes/mod.nr | 138 ++++++++---------- .../aztec-nr/aztec/src/note/discovery/mod.nr | 17 +-- .../aztec-nr/aztec/src/note/lifecycle.nr | 15 +- .../aztec/src/note/note_getter/mod.nr | 30 ++-- .../aztec/src/note/note_getter_options.nr | 11 +- .../aztec-nr/aztec/src/note/note_interface.nr | 6 +- .../aztec/src/note/note_viewer_options.nr | 4 +- .../aztec-nr/aztec/src/note/utils.nr | 30 ++-- .../aztec-nr/aztec/src/oracle/notes.nr | 25 ++-- noir-projects/aztec-nr/aztec/src/prelude.nr | 1 - .../aztec/src/state_vars/private_immutable.nr | 6 +- .../aztec/src/state_vars/private_mutable.nr | 4 +- .../aztec/src/state_vars/private_set.nr | 4 +- .../src/test/helpers/test_environment.nr | 7 +- .../aztec/src/test/mocks/mock_note.nr | 17 +-- .../ecdsa_public_key_note/src/lib.nr | 100 ++++++------- .../contracts/token_contract/src/main.nr | 4 +- .../crates/types/src/meta/mod.nr | 20 ++- 24 files changed, 246 insertions(+), 274 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr index 2e8b8f9af301..f890cf646ce3 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr @@ -5,9 +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, retrieved_note::RetrievedNote, - }, + note::{note_emission::NoteEmission, note_interface::NoteInterface}, oracle::{ notes::{get_app_tag_as_sender, increment_app_tagging_secret_index_as_sender}, random::random, @@ -16,6 +14,7 @@ use crate::{ }; use dep::protocol_types::{ abis::note_hash::NoteHash, address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_FIELDS, + traits::Packable, }; use std::aes128::aes128_encrypt; @@ -219,9 +218,9 @@ fn compute_note_plaintext_for_this_strategy( storage_slot: Field, ) -> [u8; N * 32 + 64] where - Note: NoteInterface, + Note: NoteInterface + Packable, { - let packed_note = note.pack_content(); + let packed_note = note.pack(); let storage_slot_bytes: [u8; 32] = storage_slot.to_be_bytes(); @@ -253,7 +252,7 @@ fn compute_log( sender: AztecAddress, ) -> [Field; PRIVATE_LOG_SIZE_IN_FIELDS] where - Note: NoteInterface, + Note: NoteInterface + Packable, { // ***************************************************************************** // Compute the shared secret @@ -417,7 +416,7 @@ unconstrained fn compute_log_unconstrained( sender: AztecAddress, ) -> [Field; PRIVATE_LOG_SIZE_IN_FIELDS] where - Note: NoteInterface, + Note: NoteInterface + Packable, { compute_log(context, note, storage_slot, recipient, sender) } @@ -432,7 +431,7 @@ pub fn encode_and_encrypt_note( sender: AztecAddress, ) -> fn[(&mut PrivateContext, AztecAddress, AztecAddress)](NoteEmission) -> () where - Note: NoteInterface, + Note: NoteInterface + Packable, { |e: NoteEmission| { let note = e.note; @@ -455,7 +454,7 @@ pub fn encode_and_encrypt_note_unconstrained( sender: AztecAddress, ) -> fn[(&mut PrivateContext, AztecAddress, AztecAddress)](NoteEmission) -> () where - Note: NoteInterface, + Note: NoteInterface + Packable, { |e: NoteEmission| { let note = e.note; diff --git a/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr index 5be54699bffd..638257716598 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr @@ -11,23 +11,19 @@ use crate::{ }; trait ProveNoteInclusion { - fn prove_note_inclusion( + fn prove_note_inclusion( header: BlockHeader, retrieved_note: RetrievedNote, storage_slot: Field, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteInterface + NullifiableNote; } impl ProveNoteInclusion for BlockHeader { - fn prove_note_inclusion( - self, - retrieved_note: RetrievedNote, - storage_slot: Field, - ) + fn prove_note_inclusion(self, retrieved_note: RetrievedNote, storage_slot: Field) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { let note_hash = compute_note_hash_for_nullify(retrieved_note, storage_slot); diff --git a/noir-projects/aztec-nr/aztec/src/history/note_validity.nr b/noir-projects/aztec-nr/aztec/src/history/note_validity.nr index a8d738ff341a..2d58d06ee5fe 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note_validity.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note_validity.nr @@ -6,25 +6,25 @@ use crate::{ use dep::protocol_types::block_header::BlockHeader; trait ProveNoteValidity { - fn prove_note_validity( + fn prove_note_validity( header: BlockHeader, retrieved_note: RetrievedNote, storage_slot: Field, context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteInterface + NullifiableNote; } impl ProveNoteValidity for BlockHeader { - fn prove_note_validity( + fn prove_note_validity( self, retrieved_note: RetrievedNote, storage_slot: Field, context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { self.prove_note_inclusion(retrieved_note, storage_slot); self.prove_note_not_nullified(retrieved_note, storage_slot, context); diff --git a/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr index 3d47e2d5094e..0cb4bd36d129 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -43,26 +43,26 @@ impl ProveNullifierInclusion for BlockHeader { } trait ProveNoteIsNullified { - fn prove_note_is_nullified( + fn prove_note_is_nullified( header: BlockHeader, retrieved_note: RetrievedNote, storage_slot: Field, context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteInterface + NullifiableNote; } impl ProveNoteIsNullified for BlockHeader { // docs:start:prove_note_is_nullified - fn prove_note_is_nullified( + fn prove_note_is_nullified( self, retrieved_note: RetrievedNote, storage_slot: Field, context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { let nullifier = compute_siloed_nullifier(retrieved_note, storage_slot, context); diff --git a/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr index acc417a2c681..755f2ee63533 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier_non_inclusion.nr @@ -56,26 +56,26 @@ impl ProveNullifierNonInclusion for BlockHeader { } trait ProveNoteNotNullified { - fn prove_note_not_nullified( + fn prove_note_not_nullified( header: BlockHeader, retrieved_note: RetrievedNote, storage_slot: Field, context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote; + Note: NoteInterface + NullifiableNote; } impl ProveNoteNotNullified for BlockHeader { // docs:start:prove_note_not_nullified - fn prove_note_not_nullified( + fn prove_note_not_nullified( self, retrieved_note: RetrievedNote, storage_slot: Field, context: &mut PrivateContext, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { let nullifier = compute_siloed_nullifier(retrieved_note, storage_slot, context); diff --git a/noir-projects/aztec-nr/aztec/src/macros/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/mod.nr index 3511953e2641..7f5dcfacae11 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/mod.nr @@ -112,10 +112,10 @@ comptime fn generate_contract_interface(m: Module) -> Quoted { } comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted { - let mut max_note_content_length: u32 = 0; + let mut max_note_packed_len: u32 = 0; let notes = NOTES.entries(); let body = if notes.len() > 0 { - max_note_content_length = notes.fold( + max_note_packed_len = notes.fold( 0, |acc, (_, (_, len, _, _)): (Type, (StructDefinition, u32, Field, [(Quoted, u32, bool)]))| { if len > acc { @@ -138,7 +138,7 @@ comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted { if_statements_list = if_statements_list.push_back( quote { $if_or_else_if note_type_id == $typ::get_note_type_id() { - aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack_content, contract_address, nonce, compute_nullifier, storage_slot, packed_note_content) + aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack, contract_address, nonce, compute_nullifier, storage_slot, packed_note) } }, ); @@ -165,7 +165,7 @@ comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted { storage_slot: Field, note_type_id: Field, compute_nullifier: bool, - packed_note_content: [Field; $max_note_content_length], + packed_note: [Field; $max_note_packed_len], ) -> pub [Field; 4] { $body } @@ -183,16 +183,16 @@ comptime fn generate_process_log() -> Quoted { // A typical implementation of the lambda looks something like this: // ``` - // |packed_note_content: BoundedVec, contract_address: AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field| { + // |packed_note: BoundedVec, contract_address: AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field| { // let hashes = if note_type_id == MyNoteType::get_note_type_id() { - // assert(packed_note_content.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH); + // assert(packed_note.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH); // dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier( - // MyNoteType::unpack_content, + // MyNoteType::unpack, // contract_address, // nonce, // true, // storage_slot, - // packed_note_content.storage(), + // packed_note.storage(), // ) // } else { // panic(f"Unknown note type id {note_type_id}") @@ -214,7 +214,7 @@ comptime fn generate_process_log() -> Quoted { let mut if_note_type_id_match_statements_list = &[]; for i in 0..notes.len() { - let (typ, (_, packed_note_content_length, _, _)) = notes[i]; + let (typ, (_, packed_note_length, _, _)) = notes[i]; let if_or_else_if = if i == 0 { quote { if } @@ -225,17 +225,17 @@ comptime fn generate_process_log() -> Quoted { if_note_type_id_match_statements_list = if_note_type_id_match_statements_list.push_back( quote { $if_or_else_if note_type_id == $typ::get_note_type_id() { - // As an extra safety check we make sure that the packed_note_content bounded vec has the + // As an extra safety check we make sure that the packed_note bounded vec has the // expected length, to avoid scenarios in which compute_note_hash_and_optionally_a_nullifier // silently trims the end if the log were to be longer. - let expected_len = $packed_note_content_length; - let actual_len = packed_note_content.len(); + let expected_len = $packed_note_length; + let actual_len = packed_note.len(); assert( actual_len == expected_len, f"Expected note content of length {expected_len} but got {actual_len} for note type id {note_type_id}" ); - aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack_content, contract_address, nonce, true, storage_slot, packed_note_content.storage()) + aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack, contract_address, nonce, true, storage_slot, packed_note.storage()) } }, ); @@ -257,7 +257,7 @@ comptime fn generate_process_log() -> Quoted { unique_note_hashes_in_tx, first_nullifier_in_tx, recipient, - |packed_note_content: BoundedVec, contract_address, nonce, storage_slot, note_type_id| { + |packed_note: BoundedVec, contract_address, nonce, storage_slot, note_type_id| { let hashes = $if_note_type_id_match_statements else { panic(f"Unknown note type id {note_type_id}") 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 7098dc3c3002..cd79aaea678d 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -1,9 +1,9 @@ use crate::{note::note_getter_options::PropertySelector, prelude::Point}; -use protocol_types::meta::{generate_deserialize_from_fields, generate_serialize_to_fields}; +use protocol_types::meta::{derive_packable_and_get_packed_len, generate_serialize_to_fields}; use std::{ collections::umap::UHashMap, hash::{BuildHasherDefault, derive_generators, poseidon2::Poseidon2Hasher}, - meta::{typ::fresh_type_variable, type_of, unquote}, + meta::{type_of, unquote}, }; /// A map from note type to (note_struct_definition, note_packed_len, note_type_id, fields). @@ -28,18 +28,35 @@ comptime fn get_next_note_type_id() -> Field { note_type_id } -/// Generates default `NoteInterface` implementation for a given note struct `s` and returns it as quote along with -/// the length of the packed note. +/// Returns the packed length of a given note struct `s` wrapped in an Option if it implements `Packable`. Returns +/// `Option::none()` if not. +comptime fn get_packable_length(s: StructDefinition) -> Option { + let packed_len_typ = std::meta::typ::fresh_type_variable(); + + // We don't care about the result of the implements check. We just want the get the packed length. + let _ = s.as_type().implements( + quote { crate::protocol_types::traits::Packable<$packed_len_typ> }.as_trait_constraint(), + ); + packed_len_typ.as_constant() +} + +/// Generates a quote that implements `Packable` for a given note struct `s`. +/// If the note struct already implements `Packable`, we return an empty quote. +comptime fn derive_packable_if_not_implemented_and_get_len(s: StructDefinition) -> (Quoted, u32) { + let maybe_packed_length = get_packable_length(s); + + if maybe_packed_length.is_some() { + (quote {}, maybe_packed_length.unwrap()) + } else { + // We didn't manage to get the packed length which means the note struct doesn't implement `Packable` + // so we derive it and then get the packed length again. + derive_packable_and_get_packed_len(s) + } +} + +/// Generates default `NoteInterface` implementation for a given note struct `s` and returns it as a quote. /// /// impl NoteInterface for NoteStruct { -/// fn pack_content(self) -> [Field; N] { -/// ... -/// } -/// -/// fn unpack_content(packed_content: [Field; N]) -> Self { -/// ... -/// } -/// /// fn get_note_type_id() -> Field { /// ... /// } @@ -66,35 +83,8 @@ comptime fn generate_note_interface( note_type_id: Field, indexed_fixed_fields: [(Quoted, Type, u32)], indexed_nullable_fields: [(Quoted, Type, u32)], -) -> (Quoted, u32) { +) -> Quoted { let name = s.name(); - let typ = s.as_type(); - - // In notes we care about DA costs so we enable packing - let packing_enabled = true; - - // First we compute note content serialization. We do that by passing the whole note struct - // to the `generate_serialize_to_fields(...)`. - let (content_fields_list, content_aux_vars_list) = - generate_serialize_to_fields(quote { self }, typ, &[], packing_enabled); - - // If there are `aux_vars` we need to join them with `;` and add a trailing `;` to the joined string. - let content_aux_vars = if content_aux_vars_list.len() > 0 { - let joint = content_aux_vars_list.join(quote {;}); - quote { $joint; } - } else { - quote {} - }; - let content_fields = content_fields_list.join(quote {,}); - let content_len = content_fields_list.len(); - - let (unpacked_content, _) = generate_deserialize_from_fields( - quote {}, - typ, - quote { packed_content }, // "packed_content" is argument of NoteInterface::unpack_content - 0, - packing_enabled, - ); // Second we compute quotes for MSM // `compute_note_hash()` is computed over all the fields so we need to merge fixed and nullable. @@ -115,18 +105,8 @@ comptime fn generate_note_interface( .push_back(quote { std::hash::from_field_unsafe($merged_fields_len) }) .join(quote {,}); - ( - quote { - impl aztec::note::note_interface::NoteInterface<$content_len> for $name { - fn pack_content(self) -> [Field; $content_len] { - $content_aux_vars - [$content_fields] - } - - fn unpack_content(packed_content: [Field; $content_len]) -> Self { - $unpacked_content - } - + quote { + impl aztec::note::note_interface::NoteInterface for $name { fn get_note_type_id() -> Field { $note_type_id } @@ -140,9 +120,7 @@ comptime fn generate_note_interface( point.x } } - }, - content_len, - ) + } } /// Generates note properties struct for a given note struct `s`. @@ -405,10 +383,10 @@ comptime fn generate_multi_scalar_mul( /// log_plaintext[32 + i] = note_type_id_bytes[i]; /// } /// -/// let packed_note_content = [npk_m_hash as Field, randomness as Field]; +/// let packed_note = [npk_m_hash as Field, randomness as Field]; /// -/// for i in 0..packed_note_content.len() { -/// let bytes: [u8; 32] = packed_note_content[i].to_be_bytes(); +/// for i in 0..packed_note.len() { +/// let bytes: [u8; 32] = packed_note[i].to_be_bytes(); /// for j in 0..32 { /// log_plaintext[64 + i * 32 + j] = bytes[j]; /// } @@ -561,10 +539,10 @@ comptime fn get_setup_log_plaintext_body( } $aux_vars_for_serialization - let packed_note_content = [$fields]; + let packed_note = [$fields]; - for i in 0..packed_note_content.len() { - let bytes: [u8; 32] = packed_note_content[i].to_be_bytes(); + for i in 0..packed_note.len() { + let bytes: [u8; 32] = packed_note[i].to_be_bytes(); for j in 0..32 { log_plaintext[64 + i * 32 + j] = bytes[j]; } @@ -896,7 +874,9 @@ comptime fn index_note_fields( /// - PartialNote trait implementation /// - NoteExport /// - NoteInterface trait implementation -/// - Registers the note in the global `NOTES` map. +/// - Packable implementation +/// +/// Registers the note in the global `NOTES` map. /// /// For more details on the generated code, see the individual functions. /// @@ -914,7 +894,7 @@ 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, note_packed_len) = generate_note_interface( + let note_interface_impl = generate_note_interface( s, note_type_id, indexed_fixed_fields, @@ -922,6 +902,8 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) -> ); let partial_note_impl = generate_partial_note_impl(s, setup_payload_name, finalization_payload_name); + let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s); + register_note( s, note_packed_len, @@ -936,13 +918,16 @@ pub comptime fn partial_note(s: StructDefinition, nullable_fields: [Quoted]) -> $finalization_payload_impl $note_interface_impl $partial_note_impl + $packable_impl } } /// Generates the following: /// - NoteTypeProperties /// - NoteInterface trait implementation -/// - Registers the note in the global `NOTES` map. +/// - 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 { @@ -950,12 +935,14 @@ pub comptime fn note(s: StructDefinition) -> Quoted { let note_properties = generate_note_properties(s); let note_type_id = get_next_note_type_id(); - let (note_interface_impl, note_packed_len) = generate_note_interface( + let note_interface_impl = generate_note_interface( s, note_type_id, indexed_fixed_fields, indexed_nullable_fields, ); + let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s); + register_note( s, note_packed_len, @@ -967,28 +954,20 @@ pub comptime fn note(s: StructDefinition) -> Quoted { quote { $note_properties $note_interface_impl + $packable_impl } } /// Generates the following: /// - NoteTypeProperties +/// - 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_custom_interface(s: StructDefinition) -> Quoted { - let note_properties = generate_note_properties(s); + let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s); let note_type_id = get_next_note_type_id(); - let serialized_len_type = fresh_type_variable(); - let note_interface_impl = s.as_type().get_trait_impl( - quote { crate::note::note_interface::NoteInterface<$serialized_len_type> } - .as_trait_constraint(), - ); - let name = s.name(); - - let note_packed_len = note_interface_impl - .expect(f"Note {name} must implement NoteInterface trait") - .trait_generic_args()[0] - .as_constant() - .unwrap(); let (indexed_fixed_fields, indexed_nullable_fields) = index_note_fields(s, &[]); register_note( @@ -999,7 +978,10 @@ pub comptime fn note_custom_interface(s: StructDefinition) -> Quoted { indexed_nullable_fields, ); + let note_properties = generate_note_properties(s); + quote { $note_properties + $packable_impl } } diff --git a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr index 21c5574d2a38..2480c7f2fd1c 100644 --- a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr @@ -30,15 +30,15 @@ pub struct NoteHashesAndNullifier { /// note in the contract given their contents. A typical implementation of such a function would look like this: /// /// ``` -/// |packed_note_content, note_header, note_type_id| { +/// |packed_note, note_header, note_type_id| { /// let hashes = if note_type_id == MyNoteType::get_note_type_id() { -/// assert(packed_note_content.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH); +/// assert(packed_note.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH); /// dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier( /// MyNoteType::unpack_content, /// contract_address, /// nonce, /// storage_slot, -/// packed_note_content.storage(), +/// packed_note.storage(), /// ) /// } else { /// panic(f"Unknown note type id {note_type_id}") @@ -60,8 +60,7 @@ pub unconstrained fn do_process_log( recipient: AztecAddress, compute_note_hash_and_nullifier: fn[Env](BoundedVec, AztecAddress, Field, Field, Field) -> Option, ) { - let (storage_slot, note_type_id, packed_note_content) = - destructure_log_plaintext(log_plaintext); + let (storage_slot, note_type_id, packed_note) = destructure_log_plaintext(log_plaintext); // We need to find the note's nonce, which is the one that results in one of the unique note hashes from tx_hash for_each_in_bounded_vec( @@ -71,7 +70,7 @@ pub unconstrained fn do_process_log( // TODO(#11157): handle failed note_hash_and_nullifier computation let hashes = compute_note_hash_and_nullifier( - packed_note_content, + packed_note, context.this_address(), candidate_nonce, storage_slot, @@ -87,7 +86,7 @@ pub unconstrained fn do_process_log( context.this_address(), // TODO(#10727): allow other contracts to deliver notes storage_slot, candidate_nonce, - packed_note_content, + packed_note, hashes.note_hash, hashes.inner_nullifier, tx_hash, @@ -118,9 +117,9 @@ unconstrained fn destructure_log_plaintext( let storage_slot = log_plaintext.get(0); let note_type_id = log_plaintext.get(1); - let packed_note_content = array::subbvec(log_plaintext, NOTE_LOG_RESERVED_FIELDS); + let packed_note = array::subbvec(log_plaintext, NOTE_LOG_RESERVED_FIELDS); - (storage_slot, note_type_id, packed_note_content) + (storage_slot, note_type_id, packed_note) } fn for_each_in_bounded_vec( diff --git a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr index e17ea277aafd..b6db493b8642 100644 --- a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr +++ b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr @@ -6,6 +6,7 @@ use crate::note::{ utils::{compute_note_hash_for_nullify_internal, compute_note_hash_for_read_request}, }; use crate::oracle::notes::notify_created_note; +use protocol_types::traits::Packable; pub fn create_note( context: &mut PrivateContext, @@ -13,17 +14,17 @@ pub fn create_note( note: Note, ) -> NoteEmission where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote + Packable, { let note_hash_counter = context.side_effect_counter; let note_hash = note.compute_note_hash(storage_slot); - let packed_note_content = Note::pack_content(note); + let packed_note = Note::pack(note); notify_created_note( storage_slot, Note::get_note_type_id(), - packed_note_content, + packed_note, note_hash, note_hash_counter, ); @@ -34,13 +35,13 @@ where } // Note: This function is currently totally unused. -pub fn destroy_note( +pub fn destroy_note( context: &mut PrivateContext, retrieved_note: RetrievedNote, storage_slot: Field, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { let note_hash_for_read_request = compute_note_hash_for_read_request(retrieved_note, storage_slot); @@ -48,13 +49,13 @@ where destroy_note_unsafe(context, retrieved_note, note_hash_for_read_request) } -pub fn destroy_note_unsafe( +pub fn destroy_note_unsafe( context: &mut PrivateContext, retrieved_note: RetrievedNote, note_hash_for_read_request: Field, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { let note_hash_for_nullify = compute_note_hash_for_nullify_internal(retrieved_note, note_hash_for_read_request); diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr index 80496d877561..7c3cb3e38ff5 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr @@ -9,8 +9,9 @@ use crate::note::{ }; use crate::oracle; use crate::utils::comparison::compare; -use dep::protocol_types::constants::{ - GET_NOTES_ORACLE_RETURN_LENGTH, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, +use dep::protocol_types::{ + constants::{GET_NOTES_ORACLE_RETURN_LENGTH, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL}, + traits::Packable, }; pub use crate::note::constants::MAX_NOTES_PER_PAGE; @@ -18,13 +19,13 @@ pub use crate::note::constants::MAX_NOTES_PER_PAGE; mod test; fn extract_property_value_from_selector( - packed_note_content: [Field; N], + packed_note: [Field; N], selector: PropertySelector, ) -> Field { // Selectors use PropertySelectors in order to locate note properties inside the packed note. // This allows easier packing and custom (un)packing schemas. A note property is located // inside the packed note using the index inside the array, a byte offset and a length. - let value: [u8; 32] = packed_note_content[selector.index].to_be_bytes(); + let value: [u8; 32] = packed_note[selector.index].to_be_bytes(); let offset = selector.offset; let length = selector.length; let mut value_field = 0 as Field; @@ -38,14 +39,11 @@ fn extract_property_value_from_selector( value_field } -fn check_note_content( - packed_note_content: [Field; N], - selects: BoundedVec, N>, -) { +fn check_note_content(packed_note: [Field; N], selects: BoundedVec, N>) { for i in 0..selects.len() { let select = selects.get_unchecked(i).unwrap_unchecked(); let value_field = - extract_property_value_from_selector(packed_note_content, select.property_selector); + extract_property_value_from_selector(packed_note, select.property_selector); assert( compare(value_field, select.comparator, select.value.to_field()), @@ -78,7 +76,7 @@ pub fn get_note( storage_slot: Field, ) -> (RetrievedNote, Field) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote + 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. @@ -107,7 +105,7 @@ pub fn get_notes( options: NoteGetterOptions, ) -> (BoundedVec, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL>, BoundedVec) where - Note: NoteInterface + NullifiableNote + Eq, + Note: NoteInterface + NullifiableNote + Eq + Packable, { // Safety: The notes are constrained below. let opt_notes = unsafe { get_notes_internal(storage_slot, options) }; @@ -132,7 +130,7 @@ fn constrain_get_notes_internal, ) -> (BoundedVec, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL>, BoundedVec) where - Note: NoteInterface + NullifiableNote + Eq, + Note: NoteInterface + NullifiableNote + 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 contents of the notes (as opposed to simply removing some), @@ -164,7 +162,7 @@ where "Note contract address mismatch.", ); - let fields = retrieved_note.note.pack_content(); + let fields = retrieved_note.note.pack(); check_note_content(fields, options.selects); if i != 0 { check_notes_order(prev_fields, fields, options.sorts); @@ -183,7 +181,7 @@ where unconstrained fn get_note_internal(storage_slot: Field) -> RetrievedNote where - Note: NoteInterface, + Note: NoteInterface + Packable, { let placeholder_note = [Option::none()]; let placeholder_fields = [0; GET_NOTE_ORACLE_RETURN_LENGTH]; @@ -215,7 +213,7 @@ unconstrained fn get_notes_internal, ) -> [Option>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where - Note: NoteInterface, + Note: NoteInterface + 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 +250,7 @@ pub unconstrained fn view_notes( options: NoteViewerOptions, ) -> BoundedVec where - Note: NoteInterface, + Note: NoteInterface + 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/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr index 8ead18b196b0..66432e4f5555 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter_options.nr @@ -1,5 +1,8 @@ use crate::note::{note_interface::NoteInterface, retrieved_note::RetrievedNote}; -use dep::protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, traits::ToField}; +use dep::protocol_types::{ + constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + traits::{Packable, ToField}, +}; use std::option::Option; pub struct PropertySelector { @@ -134,7 +137,7 @@ impl NoteGetterOptions NoteGetterOptions where - Note: NoteInterface, + Note: NoteInterface + Packable, { // This function initializes a NoteGetterOptions that simply returns the maximum number of notes allowed in a call. pub fn new() -> Self { @@ -154,7 +157,7 @@ where impl NoteGetterOptions where - Note: NoteInterface, + Note: NoteInterface + Packable, { // This function initializes a NoteGetterOptions with a preprocessor, which takes the notes returned from // the database and preprocessor_args as its parameters. @@ -179,7 +182,7 @@ where impl NoteGetterOptions where - Note: NoteInterface, + Note: NoteInterface + 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/noir-projects/aztec-nr/aztec/src/note/note_interface.nr b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr index ecac68a9c168..e0d0d02b3533 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr @@ -41,11 +41,7 @@ pub trait NullifiableNote { // docs:start:note_interface // Autogenerated by the #[note] macro -pub trait NoteInterface { - fn pack_content(self) -> [Field; N]; - - fn unpack_content(fields: [Field; N]) -> Self; - +pub trait NoteInterface { fn get_note_type_id() -> Field; /// Returns the non-siloed note hash, i.e. the inner hash computed by the contract during private execution. Note diff --git a/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr b/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr index c131195d69ea..fb93b8d9498c 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_viewer_options.nr @@ -1,7 +1,7 @@ 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 dep::protocol_types::traits::ToField; +use dep::protocol_types::traits::{Packable, ToField}; use std::option::Option; // docs:start:NoteViewerOptions @@ -17,7 +17,7 @@ pub struct NoteViewerOptions { impl NoteViewerOptions { pub fn new() -> NoteViewerOptions where - Note: NoteInterface, + Note: NoteInterface + Packable, { NoteViewerOptions { selects: BoundedVec::new(), diff --git a/noir-projects/aztec-nr/aztec/src/note/utils.nr b/noir-projects/aztec-nr/aztec/src/note/utils.nr index 3a2762c3324c..62728efe37fb 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -11,15 +11,16 @@ use dep::protocol_types::{ compute_siloed_nullifier as compute_siloed_nullifier_from_preimage, compute_unique_note_hash, }, + traits::Packable, }; -pub fn compute_siloed_nullifier( +pub fn compute_siloed_nullifier( retrieved_note: RetrievedNote, storage_slot: Field, context: &mut PrivateContext, ) -> Field where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { 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); @@ -28,12 +29,12 @@ where } // TODO(#7775): make this not impossible to understand -pub fn compute_note_hash_for_read_request( +pub fn compute_note_hash_for_read_request( retrieved_note: RetrievedNote, storage_slot: Field, ) -> Field where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface, { let note_hash = retrieved_note.note.compute_note_hash(storage_slot); @@ -50,13 +51,10 @@ where } // TODO(#7775): make this not impossible to understand -pub fn compute_note_hash_for_nullify_internal( +pub fn compute_note_hash_for_nullify_internal( retrieved_note: RetrievedNote, note_hash_for_read_request: Field, -) -> Field -where - Note: NoteInterface + NullifiableNote, -{ +) -> Field { if (retrieved_note.note_hash_counter != 0) & (retrieved_note.nonce != 0) { // Non-revertible note, nullified by a revertible nullifier, we need to nullify the note hash that will reach the tree let siloed_note_hash = @@ -110,30 +108,30 @@ where // } // } -pub fn compute_note_hash_for_nullify( +pub fn compute_note_hash_for_nullify( retrieved_note: RetrievedNote, storage_slot: Field, ) -> Field where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { let note_hash_for_read_request = compute_note_hash_for_read_request(retrieved_note, storage_slot); compute_note_hash_for_nullify_internal(retrieved_note, note_hash_for_read_request) } -pub unconstrained fn compute_note_hash_and_optionally_a_nullifier( - unpack_content: fn([Field; N]) -> T, +pub unconstrained fn compute_note_hash_and_optionally_a_nullifier( + unpack_note: fn([Field; N]) -> Note, contract_address: AztecAddress, nonce: Field, compute_nullifier: bool, storage_slot: Field, - packed_note_content: [Field; S], + packed_note: [Field; N], ) -> [Field; 4] where - T: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote, { - let note = unpack_content(array::subarray(packed_note_content, 0)); + let note = unpack_note(array::subarray(packed_note, 0)); let note_hash = note.compute_note_hash(storage_slot); let siloed_note_hash = compute_siloed_note_hash(contract_address, note_hash); diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index fa36a763e465..c0fc57318a85 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -3,6 +3,7 @@ use crate::{note::{note_interface::NoteInterface, retrieved_note::RetrievedNote} use dep::protocol_types::{ address::AztecAddress, indexed_tagging_secret::{INDEXED_TAGGING_SECRET_LENGTH, IndexedTaggingSecret}, + traits::Packable, }; /// Notifies the simulator that a note has been created, so that it can be returned in future read requests in the same @@ -10,7 +11,7 @@ use dep::protocol_types::{ pub fn notify_created_note( storage_slot: Field, note_type_id: Field, - packed_note_content: [Field; N], + packed_note: [Field; N], note_hash: Field, counter: u32, ) { @@ -20,7 +21,7 @@ pub fn notify_created_note( notify_created_note_oracle_wrapper( storage_slot, note_type_id, - packed_note_content, + packed_note, note_hash, counter, ) @@ -45,24 +46,18 @@ pub fn notify_created_nullifier(nullifier: Field) { unconstrained fn notify_created_note_oracle_wrapper( storage_slot: Field, note_type_id: Field, - packed_note_content: [Field; N], + packed_note: [Field; N], note_hash: Field, counter: u32, ) { - let _ = notify_created_note_oracle( - storage_slot, - note_type_id, - packed_note_content, - note_hash, - counter, - ); + let _ = notify_created_note_oracle(storage_slot, note_type_id, packed_note, note_hash, counter); } #[oracle(notifyCreatedNote)] unconstrained fn notify_created_note_oracle( _storage_slot: Field, _note_type_id: Field, - _packed_note_content: [Field; N], + _packed_note: [Field; N], _note_hash: Field, _counter: u32, ) -> Field {} @@ -164,10 +159,10 @@ pub unconstrained fn get_notes>; S], // TODO: Remove it and use `limit` to initialize the note array. placeholder_fields: [Field; NS], // TODO: Remove it and use `limit` to initialize the note array. - _placeholder_note_length: [Field; N], // Turbofish hack? Compiler breaks calculating read_offset unless we add this parameter + _placeholder_note_length: [Field; N], // Turbofish hack? Compiler breaks calculating read_offset unless we add this parameter TODO(benesjan): try removing this. ) -> [Option>; S] where - Note: NoteInterface, + Note: NoteInterface + Packable, { sync_notes_oracle_wrapper(); let fields = get_notes_oracle_wrapper( @@ -198,9 +193,9 @@ where let nonce = fields[read_offset]; let note_hash_counter = fields[read_offset + 1] as u32; - let note_content = array::subarray(fields, read_offset + 2); + let packed_note = array::subarray(fields, read_offset + 2); - let note = Note::unpack_content(note_content); + let note = Note::unpack(packed_note); let retrieved_note = RetrievedNote { note, contract_address, nonce, note_hash_counter }; placeholder_opt_notes[i] = Option::some(retrieved_note); diff --git a/noir-projects/aztec-nr/aztec/src/prelude.nr b/noir-projects/aztec-nr/aztec/src/prelude.nr index bff301cdf44a..b62e61ca3588 100644 --- a/noir-projects/aztec-nr/aztec/src/prelude.nr +++ b/noir-projects/aztec-nr/aztec/src/prelude.nr @@ -6,7 +6,6 @@ pub use crate::{ note_interface::{NoteInterface, NullifiableNote}, note_viewer_options::NoteViewerOptions, retrieved_note::RetrievedNote, - utils::compute_note_hash_and_optionally_a_nullifier as utils_compute_note_hash_and_optionally_a_nullifier, }, state_vars::{ map::Map, private_immutable::PrivateImmutable, private_mutable::PrivateMutable, diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr index 58b932b12011..dab695e717c6 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_immutable.nr @@ -56,7 +56,7 @@ impl PrivateImmutable { // docs:start:initialize pub fn initialize(self, note: Note) -> NoteEmission where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote + 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, + Note: NoteInterface + NullifiableNote + 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, + Note: NoteInterface + NullifiableNote + Packable, { let mut options = NoteViewerOptions::new(); view_notes(self.storage_slot, options.set_limit(1)).get(0) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr index 496a7d677fab..f0164aee7ef8 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_mutable.nr @@ -59,7 +59,7 @@ impl PrivateMutable { impl PrivateMutable where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote + Packable, { // docs:start:initialize pub fn initialize(self, note: Note) -> NoteEmission { @@ -127,7 +127,7 @@ where impl PrivateMutable where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote + Packable, { pub unconstrained fn is_initialized(self) -> bool { let nullifier = self.compute_initialization_nullifier(); diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr index 96d7a51d7ec0..33946f1f2745 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/private_set.nr @@ -43,7 +43,7 @@ impl PrivateSet { impl PrivateSet where - Note: NoteInterface + NullifiableNote + Eq, + Note: NoteInterface + NullifiableNote + Eq + Packable, { // docs:start:insert pub fn insert(self, note: Note) -> NoteEmission { @@ -97,7 +97,7 @@ where impl PrivateSet where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote + Packable, { // docs:start:view_notes pub unconstrained fn view_notes( diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr index 6f806b185e63..941a2243bfae 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr @@ -12,6 +12,7 @@ use crate::oracle::{ notes::notify_created_note, }; use protocol_types::constants::PUBLIC_DISPATCH_SELECTOR; +use protocol_types::traits::Packable; pub struct TestEnvironment {} @@ -157,18 +158,18 @@ impl TestEnvironment { contract_address: AztecAddress, ) where - Note: NoteInterface + NullifiableNote, + Note: NoteInterface + NullifiableNote + Packable, { let original_contract_address = get_contract_address(); cheatcodes::set_contract_address(contract_address); let note_hash_counter = cheatcodes::get_side_effects_counter(); let note_hash = note.compute_note_hash(storage_slot); - let packed_content = Note::pack_content(note); + let packed_note = Note::pack(note); notify_created_note( storage_slot, Note::get_note_type_id(), - packed_content, + packed_note, note_hash, note_hash_counter, ); 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 fe19edfd5f39..e28dea8fc8ed 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 @@ -11,12 +11,11 @@ use dep::protocol_types::{ address::AztecAddress, constants::{GENERATOR_INDEX__NOTE_HASH, GENERATOR_INDEX__NOTE_NULLIFIER}, hash::poseidon2_hash_with_separator, + traits::Packable, utils::arrays::array_concat, }; -global MOCK_NOTE_LENGTH: u32 = 1; - -#[derive(Eq)] +#[derive(Eq, Packable)] pub(crate) struct MockNote { pub(crate) value: Field, } @@ -54,15 +53,7 @@ impl NullifiableNote for MockNote { } } -impl NoteInterface for MockNote { - fn pack_content(self) -> [Field; MOCK_NOTE_LENGTH] { - [self.value] - } - - fn unpack_content(fields: [Field; MOCK_NOTE_LENGTH]) -> Self { - Self { value: fields[0] } - } - +impl NoteInterface for MockNote { fn get_note_type_id() -> Field { // randomly chosen note type id --> has to fit within 7 bits 76 @@ -72,7 +63,7 @@ impl NoteInterface for MockNote { // 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_content(), [storage_slot]); + let input = array_concat(self.pack(), [storage_slot]); poseidon2_hash_with_separator(input, GENERATOR_INDEX__NOTE_HASH) } } diff --git a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr index 072b17bb8f5f..e9811f5b92d0 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr @@ -2,11 +2,11 @@ use dep::aztec::prelude::{NoteInterface, NullifiableNote, PrivateContext, Retrie use dep::aztec::{ note::utils::compute_note_hash_for_nullify, keys::getters::{get_nsk_app, get_public_keys}, - protocol_types::{address::AztecAddress, constants::{GENERATOR_INDEX__NOTE_NULLIFIER, GENERATOR_INDEX__NOTE_HASH}, hash::poseidon2_hash_with_separator, utils::arrays::array_concat}, + protocol_types::{address::AztecAddress, constants::{GENERATOR_INDEX__NOTE_NULLIFIER, GENERATOR_INDEX__NOTE_HASH}, hash::poseidon2_hash_with_separator, utils::arrays::array_concat, traits::Packable}, macros::notes::note_custom_interface }; -global ECDSA_PUBLIC_KEY_NOTE_LEN: u32 = 5; +global ECDSA_PUBLIC_KEY_NOTE_PCKD_LEN: u32 = 5; // Stores an ECDSA public key composed of two 32-byte elements // TODO: Do we need to include a nonce, in case we want to read/nullify/recreate with the same pubkey value? @@ -17,52 +17,7 @@ pub struct EcdsaPublicKeyNote { owner: AztecAddress, } -impl NoteInterface for EcdsaPublicKeyNote { - // Cannot use the automatic packing since x and y don't fit. Pack the note as 5 fields where: - // [0] = x[0..31] (upper bound excluded) - // [1] = x[31] - // [2] = y[0..31] - // [3] = y[31] - // [4] = owner - fn pack_content(self) -> [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] { - let mut x: Field = 0; - let mut y: Field = 0; - let mut mul: Field = 1; - - for i in 1..32 { - let byte_x: Field = self.x[31 - i] as Field; - x = x + (byte_x * mul); - let byte_y: Field = self.y[31 - i] as Field; - y = y + (byte_y * mul); - mul *= 256; - } - - let last_x = self.x[31] as Field; - let last_y = self.y[31] as Field; - - [x, last_x, y, last_y, self.owner.to_field()] - } - - // Cannot use the automatic unpacking for the aforementioned reasons - fn unpack_content(packed_content: [Field; ECDSA_PUBLIC_KEY_NOTE_LEN]) -> EcdsaPublicKeyNote { - let mut x: [u8; 32] = [0; 32]; - let mut y: [u8; 32] = [0; 32]; - - let part_x:[u8; 32] = packed_content[0].to_be_bytes(); - for i in 0..31 { - x[i] = part_x[i + 1]; - } - x[31] = packed_content[1].to_be_bytes::<32>()[31]; - - let part_y:[u8; 32] = packed_content[2].to_be_bytes(); - for i in 0..31 { - y[i] = part_y[i + 1]; - } - y[31] = packed_content[3].to_be_bytes::<32>()[31]; - - EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(packed_content[4]) } - } - +impl NoteInterface for EcdsaPublicKeyNote { fn get_note_type_id() -> Field { // randomly chosen note type id --> has to fit within 7 bits 76 @@ -72,7 +27,7 @@ impl NoteInterface for EcdsaPublicKeyNote { // 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 inputs = array_concat(self.pack_content(), [storage_slot]); + let inputs = array_concat(self.pack(), [storage_slot]); poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__NOTE_HASH) } } @@ -112,3 +67,50 @@ impl EcdsaPublicKeyNote { EcdsaPublicKeyNote { x, y, owner } } } + +impl Packable for EcdsaPublicKeyNote { + // Cannot use the automatic packing since x and y don't fit. Pack the note as 5 fields where: + // [0] = x[0..31] (upper bound excluded) + // [1] = x[31] + // [2] = y[0..31] + // [3] = y[31] + // [4] = owner + fn pack(self) -> [Field; ECDSA_PUBLIC_KEY_NOTE_PCKD_LEN] { + let mut x: Field = 0; + let mut y: Field = 0; + let mut mul: Field = 1; + + for i in 1..32 { + let byte_x: Field = self.x[31 - i] as Field; + x = x + (byte_x * mul); + let byte_y: Field = self.y[31 - i] as Field; + y = y + (byte_y * mul); + mul *= 256; + } + + let last_x = self.x[31] as Field; + let last_y = self.y[31] as Field; + + [x, last_x, y, last_y, self.owner.to_field()] + } + + // Cannot use the automatic unpacking for the aforementioned reasons + fn unpack(packed_content: [Field; ECDSA_PUBLIC_KEY_NOTE_PCKD_LEN]) -> EcdsaPublicKeyNote { + let mut x: [u8; 32] = [0; 32]; + let mut y: [u8; 32] = [0; 32]; + + let part_x:[u8; 32] = packed_content[0].to_be_bytes(); + for i in 0..31 { + x[i] = part_x[i + 1]; + } + x[31] = packed_content[1].to_be_bytes::<32>()[31]; + + let part_y:[u8; 32] = packed_content[2].to_be_bytes(); + for i in 0..31 { + y[i] = part_y[i + 1]; + } + y[31] = packed_content[3].to_be_bytes::<32>()[31]; + + EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(packed_content[4]) } + } +} \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr index 74a989931579..325ab8edf024 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/main.nr @@ -30,9 +30,7 @@ pub contract Token { storage::storage, }, oracle::random::random, - prelude::{ - AztecAddress, FunctionSelector, Map, PublicContext, PublicImmutable, PublicMutable, - }, + prelude::{AztecAddress, Map, PublicContext, PublicImmutable, PublicMutable}, protocol_types::{point::Point, traits::Serialize}, }; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr index 06c30b85bcfc..75c8de81fdf4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr @@ -453,7 +453,13 @@ pub(crate) comptime fn derive_deserialize(s: StructDefinition) -> Quoted { } } -pub(crate) comptime fn derive_packable(s: StructDefinition) -> Quoted { +/// Generates the `Packable` implementation for a given struct and returns the packed length. +/// +/// Note: We are having this function separate from `derive_packable` because we use this in the note macros to get +/// the packed length of a note as well as the `Packable` implementation. We need the length to be able to register +/// the note in the global `NOTES` map. There the length is used to generate +/// `compute_note_hash_and_optionally_a_nullifier` function on contracts. +pub comptime fn derive_packable_and_get_packed_len(s: StructDefinition) -> (Quoted, u32) { let packing_enabled = true; let typ = s.as_type(); @@ -471,7 +477,8 @@ pub(crate) comptime fn derive_packable(s: StructDefinition) -> Quoted { let field_packings = fields.join(quote {,}); let packed_len = fields.len(); - quote { + ( + quote { impl Packable<$packed_len> for $typ { fn pack(self) -> [Field; $packed_len] { $aux_vars_for_packing @@ -482,7 +489,14 @@ pub(crate) comptime fn derive_packable(s: StructDefinition) -> Quoted { $unpacked } } - } + }, + packed_len, + ) +} + +pub(crate) comptime fn derive_packable(s: StructDefinition) -> Quoted { + let (packable_impl, _) = derive_packable_and_get_packed_len(s); + packable_impl } #[derive(Packable, Serialize, Deserialize, Eq)] From 8922811732d2843056775632edca9ad32f484c8e Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 09:28:42 +0000 Subject: [PATCH 02/16] stale naming fix --- .../aztec-nr/aztec/src/macros/mod.nr | 4 ++-- .../aztec-nr/aztec/src/note/discovery/mod.nr | 8 +++---- .../aztec/src/oracle/note_discovery.nr | 24 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/mod.nr index 7f5dcfacae11..afff10527091 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/mod.nr @@ -183,9 +183,9 @@ comptime fn generate_process_log() -> Quoted { // A typical implementation of the lambda looks something like this: // ``` - // |packed_note: BoundedVec, contract_address: AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field| { + // |packed_note: BoundedVec, contract_address: AztecAddress, nonce: Field, storage_slot: Field, note_type_id: Field| { // let hashes = if note_type_id == MyNoteType::get_note_type_id() { - // assert(packed_note.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH); + // assert(packed_note.len() == MY_NOTE_TYPE_PACKED_LENGTH); // dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier( // MyNoteType::unpack, // contract_address, diff --git a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr index 2480c7f2fd1c..b40d7d324312 100644 --- a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr @@ -14,7 +14,7 @@ use dep::protocol_types::{ // We reserve two fields in the note log that are not part of the note content: one for the storage slot, and one for // the note type id. global NOTE_LOG_RESERVED_FIELDS: u32 = 2; -pub global MAX_NOTE_SERIALIZED_LEN: u32 = PRIVATE_LOG_SIZE_IN_FIELDS - NOTE_LOG_RESERVED_FIELDS; +pub global MAX_NOTE_PACKED_LEN: u32 = PRIVATE_LOG_SIZE_IN_FIELDS - NOTE_LOG_RESERVED_FIELDS; pub struct NoteHashesAndNullifier { pub note_hash: Field, @@ -32,7 +32,7 @@ pub struct NoteHashesAndNullifier { /// ``` /// |packed_note, note_header, note_type_id| { /// let hashes = if note_type_id == MyNoteType::get_note_type_id() { -/// assert(packed_note.len() == MY_NOTE_TYPE_SERIALIZATION_LENGTH); +/// assert(packed_note.len() == MY_NOTE_TYPE_PACKED_LENGTH); /// dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier( /// MyNoteType::unpack_content, /// contract_address, @@ -58,7 +58,7 @@ pub unconstrained fn do_process_log( unique_note_hashes_in_tx: BoundedVec, first_nullifier_in_tx: Field, recipient: AztecAddress, - compute_note_hash_and_nullifier: fn[Env](BoundedVec, AztecAddress, Field, Field, Field) -> Option, + compute_note_hash_and_nullifier: fn[Env](BoundedVec, AztecAddress, Field, Field, Field) -> Option, ) { let (storage_slot, note_type_id, packed_note) = destructure_log_plaintext(log_plaintext); @@ -105,7 +105,7 @@ pub unconstrained fn do_process_log( unconstrained fn destructure_log_plaintext( log_plaintext: BoundedVec, -) -> (Field, Field, BoundedVec) { +) -> (Field, Field, BoundedVec) { assert(log_plaintext.len() >= NOTE_LOG_RESERVED_FIELDS); // If NOTE_LOG_RESERVED_FIELDS is changed, causing the assertion below to fail, then the declarations for diff --git a/noir-projects/aztec-nr/aztec/src/oracle/note_discovery.nr b/noir-projects/aztec-nr/aztec/src/oracle/note_discovery.nr index 8d4c2848991b..5b1cb8bd248a 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/note_discovery.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/note_discovery.nr @@ -1,24 +1,24 @@ -use crate::note::discovery::MAX_NOTE_SERIALIZED_LEN; +use crate::note::discovery::MAX_NOTE_PACKED_LEN; use dep::protocol_types::address::AztecAddress; -/// Informs PXE of a note's existence so that it can later retrieved by the `getNotes` oracle. The note will be scoped -/// to `contract_address`, meaning other contracts will not be able to access it unless authorized. +/// Informs PXE of a note's existence so that it can later be retrieved by the `getNotes` oracle. The note will be +/// scoped to `contract_address`, meaning other contracts will not be able to access it unless authorized. /// -/// The note's `content` 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 at -/// `tx_hash`, and will store the nullifier to later check for nullification. +/// 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 +/// 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. /// other accounts will not be able to see one another's token balance notes, even in the same PXE) unless authorized. /// -/// Returns true if the note was sucessfully delivered and added to PXE's database. +/// Returns true if the note was successfully delivered and added to PXE's database. pub unconstrained fn deliver_note( contract_address: AztecAddress, storage_slot: Field, nonce: Field, - content: BoundedVec, + packed_note: BoundedVec, note_hash: Field, nullifier: Field, tx_hash: Field, @@ -28,7 +28,7 @@ pub unconstrained fn deliver_note( contract_address, storage_slot, nonce, - content, + packed_note, note_hash, nullifier, tx_hash, @@ -41,7 +41,7 @@ unconstrained fn deliver_note_oracle( contract_address: AztecAddress, storage_slot: Field, nonce: Field, - content: BoundedVec, + packed_note: BoundedVec, note_hash: Field, nullifier: Field, tx_hash: Field, From e66257d8b1c698d61ee25fca03cbda2749a0eab4 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 09:47:33 +0000 Subject: [PATCH 03/16] fix --- noir-projects/aztec-nr/aztec/src/macros/mod.nr | 8 ++++---- noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/mod.nr index afff10527091..423dc05cf50b 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/mod.nr @@ -207,8 +207,8 @@ comptime fn generate_process_log() -> Quoted { // ``` // // We create this implementation by iterating over the different note types, creating an `if` or `else if` clause - // for each of them and calling `compute_note_hash_and_optionally_a_nullifier` with the note's deserialization - // function, and finally produce the required `NoteHashesAndNullifier` object. + // for each of them and calling `compute_note_hash_and_optionally_a_nullifier` with the note's `unpack` function, + // and finally produce the required `NoteHashesAndNullifier` object. let notes = NOTES.entries(); @@ -232,7 +232,7 @@ comptime fn generate_process_log() -> Quoted { let actual_len = packed_note.len(); assert( actual_len == expected_len, - f"Expected note content of length {expected_len} but got {actual_len} for note type id {note_type_id}" + f"Expected packed note of length {expected_len} but got {actual_len} for note type id {note_type_id}" ); aztec::note::utils::compute_note_hash_and_optionally_a_nullifier($typ::unpack, contract_address, nonce, true, storage_slot, packed_note.storage()) @@ -294,7 +294,7 @@ comptime fn generate_process_log() -> Quoted { comptime fn generate_note_exports() -> Quoted { let notes = NOTES.values(); - // Second value in each tuple is `note_serialized_len` and that is ignored here because it's only used when + // Second value in each tuple is `note_packed_len` and that is ignored here because it's only used when // generating the `compute_note_hash_and_optionally_a_nullifier` function. notes .map(|(s, _, note_type_id, fields): (StructDefinition, u32, Field, [(Quoted, u32, bool)])| { diff --git a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr index b40d7d324312..2d5c4d973c99 100644 --- a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr @@ -112,7 +112,7 @@ unconstrained fn destructure_log_plaintext( // `storage_slot` and `note_type_id` must be updated as well. static_assert( NOTE_LOG_RESERVED_FIELDS == 2, - "unepxected value for NOTE_LOG_RESERVED_FIELDS", + "unexpected value for NOTE_LOG_RESERVED_FIELDS", ); let storage_slot = log_plaintext.get(0); let note_type_id = log_plaintext.get(1); From 42520dec962cf6328b6b52a44afc583fd1809f36 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 10:20:50 +0000 Subject: [PATCH 04/16] fix --- noir-projects/aztec-nr/aztec/src/note/utils.nr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/note/utils.nr b/noir-projects/aztec-nr/aztec/src/note/utils.nr index 62728efe37fb..5e17536373ee 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -11,7 +11,6 @@ use dep::protocol_types::{ compute_siloed_nullifier as compute_siloed_nullifier_from_preimage, compute_unique_note_hash, }, - traits::Packable, }; pub fn compute_siloed_nullifier( @@ -126,12 +125,13 @@ pub unconstrained fn compute_note_hash_and_optionally_a_nullifier [Field; 4] where Note: NoteInterface + NullifiableNote, { - let note = unpack_note(array::subarray(packed_note, 0)); + let packed_note = array::subarray(packed_note_with_padding, 0); + let note = unpack_note(packed_note); let note_hash = note.compute_note_hash(storage_slot); let siloed_note_hash = compute_siloed_note_hash(contract_address, note_hash); From b0eabb6f31badc40635c534755b23b74644567d2 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 10:47:16 +0000 Subject: [PATCH 05/16] fixes --- .../aztec-nr/aztec/src/macros/notes/mod.nr | 2 +- .../aztec-nr/aztec/src/note/utils.nr | 8 +++- .../ecdsa_public_key_note/src/lib.nr | 2 +- .../spam_contract/src/types/balance_set.nr | 43 +++++++------------ .../src/types/balances_map.nr | 29 +++++-------- 5 files changed, 35 insertions(+), 49 deletions(-) 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 cd79aaea678d..15158d937030 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -56,7 +56,7 @@ 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. /// -/// impl NoteInterface for NoteStruct { +/// impl NoteInterface for NoteStruct { /// fn get_note_type_id() -> Field { /// ... /// } diff --git a/noir-projects/aztec-nr/aztec/src/note/utils.nr b/noir-projects/aztec-nr/aztec/src/note/utils.nr index 5e17536373ee..000b187df1e9 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -66,7 +66,7 @@ pub fn compute_note_hash_for_nullify_internal( } // TODO(#7775): nuke this commented out code - kept it around as it contains comments which might be helpful when tackling #7775 -// pub fn compute_note_hash_for_nullify(note: Note) -> Field where Note: NoteInterface { +// pub fn compute_note_hash_for_nullify(note: Note) -> Field where Note: NoteInterface { // let header = note.get_header(); // // There are 3 cases for reading a note intended for consumption: // // 1. The note was inserted in this transaction, is revertible, or is not nullified by a revertible nullifier in @@ -119,6 +119,12 @@ where compute_note_hash_for_nullify_internal(retrieved_note, note_hash_for_read_request) } +/// Computes the note hash and optionally a nullifier for a given note. `N` is the length of the packed note, +/// `S` is the length of the packed note with its padding array. +/// +/// Note: `packed_note_with_padding` is typically constructed by calling the `storage()` method on a `BoundedVec`. This +/// function will then extract the relevant fields from the array using the `subarray` method and the actual packed +/// note length `N`. pub unconstrained fn compute_note_hash_and_optionally_a_nullifier( unpack_note: fn([Field; N]) -> Note, contract_address: AztecAddress, diff --git a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr index e9811f5b92d0..c02d76a041fa 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr @@ -17,7 +17,7 @@ pub struct EcdsaPublicKeyNote { owner: AztecAddress, } -impl NoteInterface for EcdsaPublicKeyNote { +impl NoteInterface for EcdsaPublicKeyNote { fn get_note_type_id() -> Field { // randomly chosen note type id --> has to fit within 7 bits 76 diff --git a/noir-projects/noir-contracts/contracts/spam_contract/src/types/balance_set.nr b/noir-projects/noir-contracts/contracts/spam_contract/src/types/balance_set.nr index 480e8e18fd5b..d18d9a99dd78 100644 --- a/noir-projects/noir-contracts/contracts/spam_contract/src/types/balance_set.nr +++ b/noir-projects/noir-contracts/contracts/spam_contract/src/types/balance_set.nr @@ -3,7 +3,9 @@ use crate::types::token_note::OwnedNote; use dep::aztec::{ context::{PrivateContext, UnconstrainedContext}, note::note_emission::OuterNoteEmission, - protocol_types::{address::AztecAddress, constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL}, + protocol_types::{ + address::AztecAddress, constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, traits::Packable, + }, }; use dep::aztec::prelude::{ NoteGetterOptions, NoteInterface, NoteViewerOptions, NullifiableNote, PrivateSet, RetrievedNote, @@ -21,19 +23,16 @@ impl BalanceSet { } impl BalanceSet { - pub unconstrained fn balance_of(self: Self) -> U128 + pub unconstrained fn balance_of(self: Self) -> U128 where - Note: NoteInterface + NullifiableNote + OwnedNote, + Note: NoteInterface + NullifiableNote + OwnedNote + Packable, { self.balance_of_with_offset(0) } - pub unconstrained fn balance_of_with_offset( - self: Self, - offset: u32, - ) -> U128 + pub unconstrained fn balance_of_with_offset(self: Self, offset: u32) -> U128 where - Note: NoteInterface + NullifiableNote + OwnedNote, + Note: NoteInterface + NullifiableNote + OwnedNote + Packable, { let mut balance = U128::from_integer(0); // docs:start:view_notes @@ -54,13 +53,9 @@ impl BalanceSet { } impl BalanceSet { - pub fn add( - self: Self, - owner: AztecAddress, - addend: U128, - ) -> OuterNoteEmission + pub fn add(self: Self, owner: AztecAddress, addend: U128) -> OuterNoteEmission where - Note: NoteInterface + NullifiableNote + OwnedNote + Eq, + Note: NoteInterface + NullifiableNote + OwnedNote + Eq + Packable, { if addend == U128::from_integer(0) { OuterNoteEmission::new(Option::none()) @@ -73,13 +68,9 @@ impl BalanceSet { } } - pub fn sub( - self: Self, - owner: AztecAddress, - amount: U128, - ) -> OuterNoteEmission + pub fn sub(self: Self, owner: AztecAddress, amount: U128) -> OuterNoteEmission where - Note: NoteInterface + NullifiableNote + OwnedNote + Eq, + Note: NoteInterface + NullifiableNote + OwnedNote + Eq + Packable, { let subtracted = self.try_sub(amount, MAX_NOTE_HASH_READ_REQUESTS_PER_CALL); @@ -98,13 +89,9 @@ impl BalanceSet { // The `max_notes` parameter is used to fine-tune the number of constraints created by this function. The gate count // scales relatively linearly with `max_notes`, but a lower `max_notes` parameter increases the likelihood of // `try_sub` subtracting an amount smaller than `target_amount`. - pub fn try_sub( - self: Self, - target_amount: U128, - max_notes: u32, - ) -> U128 + pub fn try_sub(self: Self, target_amount: U128, max_notes: u32) -> U128 where - Note: NoteInterface + NullifiableNote + OwnedNote + Eq, + Note: NoteInterface + NullifiableNote + OwnedNote + Eq + Packable, { // We are using a preprocessor here (filter applied in an unconstrained context) instead of a filter because // we do not need to prove correct execution of the preprocessor. @@ -132,12 +119,12 @@ impl BalanceSet { // The preprocessor (a filter applied in an unconstrained context) does not check if total sum is larger or equal to // 'min_sum' - all it does is remove extra notes if it does reach that value. // Note that proper usage of this preprocessor requires for notes to be sorted in descending order. -pub fn preprocess_notes_min_sum( +pub fn preprocess_notes_min_sum( notes: [Option>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], min_sum: U128, ) -> [Option>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where - Note: NoteInterface + NullifiableNote + OwnedNote, + Note: NoteInterface + NullifiableNote + OwnedNote, { let mut selected = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let mut sum = U128::from_integer(0); diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr index 5f051d4daaef..b646c3bf4fbc 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/balances_map.nr @@ -2,7 +2,7 @@ use crate::types::token_note::OwnedNote; use dep::aztec::{ context::{PrivateContext, UnconstrainedContext}, note::note_emission::OuterNoteEmission, - protocol_types::constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, + protocol_types::{constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, traits::Packable}, }; use dep::aztec::prelude::{ AztecAddress, Map, NoteGetterOptions, NoteInterface, NoteViewerOptions, NullifiableNote, @@ -27,23 +27,20 @@ impl BalancesMap { } impl BalancesMap { - pub unconstrained fn balance_of( - self: Self, - owner: AztecAddress, - ) -> U128 + pub unconstrained fn balance_of(self: Self, owner: AztecAddress) -> U128 where - Note: NoteInterface + NullifiableNote + OwnedNote, + Note: NoteInterface + NullifiableNote + OwnedNote + Packable, { self.balance_of_with_offset(owner, 0) } - pub unconstrained fn balance_of_with_offset( + pub unconstrained fn balance_of_with_offset( self: Self, owner: AztecAddress, offset: u32, ) -> U128 where - Note: NoteInterface + NullifiableNote + OwnedNote, + Note: NoteInterface + NullifiableNote + OwnedNote + Packable, { let mut balance = U128::from_integer(0); // docs:start:view_notes @@ -65,13 +62,9 @@ impl BalancesMap { impl BalancesMap { - pub fn add( - self: Self, - owner: AztecAddress, - addend: U128, - ) -> OuterNoteEmission + pub fn add(self: Self, owner: AztecAddress, addend: U128) -> OuterNoteEmission where - Note: NoteInterface + NullifiableNote + OwnedNote + Eq, + Note: NoteInterface + NullifiableNote + OwnedNote + Eq + Packable, { if addend == U128::from_integer(0) { OuterNoteEmission::new(Option::none()) @@ -84,13 +77,13 @@ impl BalancesMap { } } - pub fn sub( + pub fn sub( self: Self, owner: AztecAddress, subtrahend: U128, ) -> OuterNoteEmission where - Note: NoteInterface + NullifiableNote + OwnedNote + Eq, + Note: NoteInterface + NullifiableNote + OwnedNote + Eq + Packable, { let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend); let notes = self.map.at(owner).pop_notes(options); @@ -112,12 +105,12 @@ impl BalancesMap { } } -pub fn filter_notes_min_sum( +pub fn filter_notes_min_sum( retrieved_notes: [Option>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], min_sum: U128, ) -> [Option>; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where - Note: NoteInterface + OwnedNote, + Note: NoteInterface + OwnedNote, { let mut selected = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let mut sum = U128::from_integer(0); From b058280874578c1070e01db00b6f6108af07286d Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 12:03:07 +0000 Subject: [PATCH 06/16] replacing usage of note content with packed note --- .../advanced/storage/storage_slots.md | 4 +- .../smart_contracts/functions/attributes.md | 106 ++++++++++-------- .../functions/function_transforms.md | 4 +- .../common_patterns/index.md | 2 +- .../default_aes128/note.nr | 1 - .../aztec-nr/aztec/src/macros/mod.nr | 6 +- .../aztec-nr/aztec/src/note/discovery/mod.nr | 10 +- .../aztec/src/note/note_getter/mod.nr | 20 ++-- .../aztec-nr/aztec/src/note/note_interface.nr | 2 +- .../aztec-nr/uint-note/src/uint_note.nr | 2 +- .../src/subscription_note.nr | 2 +- .../ecdsa_public_key_note/src/lib.nr | 12 +- .../nft_contract/src/types/nft_note.nr | 2 +- .../spam_contract/src/types/token_note.nr | 2 +- .../src/types/token_note.nr | 2 +- yarn-project/pxe/src/database/note_dao.ts | 4 +- 16 files changed, 94 insertions(+), 87 deletions(-) diff --git a/docs/docs/aztec/concepts/advanced/storage/storage_slots.md b/docs/docs/aztec/concepts/advanced/storage/storage_slots.md index 2bfc1ef43b03..3b86951dea8f 100644 --- a/docs/docs/aztec/concepts/advanced/storage/storage_slots.md +++ b/docs/docs/aztec/concepts/advanced/storage/storage_slots.md @@ -32,10 +32,10 @@ Nevertheless, the concept of a storage slot is very useful when writing applicat If we include the storage slot, as part of the note whose commitment is stored in the note hashes tree, we can _logically link_ all the notes that make up the storage slot. For the case of a balance, we can say that the balance is the sum of all the notes that have the same storage slot - in the same way that your physical wallet balance is the sum of all the physical notes in your wallet. -Similarly to how we siloed the public storage slots, we can silo our private storage by hashing the logical storage slot together with the note content. +Similarly to how we siloed the public storage slots, we can silo our private storage by hashing the packed note together with the logical storage slot. ```rust -note_hash = H(logical_storage_slot, note_content_hash); +note_hash = H([...packed_note, logical_storage_slot]); ``` Note hash siloing is done in the application circuit, since it is not necessary for security of the network (but only the application). diff --git a/docs/docs/aztec/smart_contracts/functions/attributes.md b/docs/docs/aztec/smart_contracts/functions/attributes.md index 1de3b944a4ea..cf12a349dee7 100644 --- a/docs/docs/aztec/smart_contracts/functions/attributes.md +++ b/docs/docs/aztec/smart_contracts/functions/attributes.md @@ -218,67 +218,75 @@ struct CustomNote { ### After expansion ```rust -impl CustomNote { - fn pack_content(self: CustomNote) -> [Field; PACKED_NOTE_CONTENT_LEN] { - [self.data, self.owner.to_field()] - } - - fn unpack_content(packed_content: [Field; PACKED_NOTE_CONTENT_LEN]) -> Self { - CustomNote { - data: packed_content[0] as Field, - owner: Address::from_field(packed_content[1]), - header: NoteHeader::empty() - } - } - +impl NoteInterface for CustomNote { fn get_note_type_id() -> Field { - // Assigned by macros by incrementing a counter - 2 + 0 } - fn get_header(note: CustomNote) -> aztec::note::note_header::NoteHeader { - note.header + fn compute_note_hash(self, storage_slot: Field) -> Field { + let point = std::embedded_curve_ops::multi_scalar_mul( + [ + Point { x: 0x..., y: 0x... }, + Point { x: 0x..., y: 0x... }, + Point { x: 0x..., y: 0x... }, + Point { x: 0x..., y: 0x... } + ], + [ + std::hash::from_field_unsafe(self.data), + std::hash::from_field_unsafe(self.owner.to_field()), + std::hash::from_field_unsafe(storage_slot), + std::hash::from_field_unsafe(3) // Length of the rest of the preimage + ] + ); + point.x } +} - fn set_header(self: &mut CustomNote, header: aztec::note::note_header::NoteHeader) { - self.header = header; +impl NullifiableNote for CustomNote { + + 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 + ) } - fn compute_note_hiding_point(self: CustomNote) -> Point { - aztec::hash::pedersen_commitment( - self.serialize_content(), - aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HIDING_POINT + unconstrained fn compute_nullifier_without_context(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 }; + 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 ) } +} - fn to_be_bytes(self, storage_slot: Field) -> [u8; 128] { - assert(128 == 2 * 32 + 64, "Note byte length must be equal to (serialized_length * 32) + 64 bytes"); - let serialized_note = self.serialize_content(); - - let mut buffer: [u8; 128] = [0; 128]; - - let storage_slot_bytes = storage_slot.to_be_bytes(32); - let note_type_id_bytes = CustomNote::get_note_type_id().to_be_bytes(32); - - for i in 0..32 { - buffer[i] = storage_slot_bytes[i]; - buffer[32 + i] = note_type_id_bytes[i]; - } +impl CustomNote { + pub fn new(x: [u8; 32], y: [u8; 32], owner: AztecAddress) -> Self { + CustomNote { x, y, owner } + } +} - for i in 0..serialized_note.len() { - let bytes = serialized_note[i].to_be_bytes(32); - for j in 0..32 { - buffer[64 + i * 32 + j] = bytes[j]; - } - } - buffer - } +impl Packable<2> for CustomNote { + fn pack(self) -> [Field; 2] { + [self.data, self.owner.to_field()] + } - pub fn properties() -> CustomNoteProperties { - CustomNoteProperties { - data: aztec::note::note_getter_options::PropertySelector { index: 0, offset: 0, length: 32 }, - owner: aztec::note::note_getter_options::PropertySelector { index: 1, offset: 0, length: 32 } - } + // Cannot use the automatic unpacking for the aforementioned reasons + fn unpack(packed_content: [Field; 2]) -> CustomNote { + CustomNote { data: packed_content[0], owner: AztecAddress { inner: packed_content[1] } } } } diff --git a/docs/docs/aztec/smart_contracts/functions/function_transforms.md b/docs/docs/aztec/smart_contracts/functions/function_transforms.md index 67a22b36403b..cc82dcee1733 100644 --- a/docs/docs/aztec/smart_contracts/functions/function_transforms.md +++ b/docs/docs/aztec/smart_contracts/functions/function_transforms.md @@ -156,10 +156,10 @@ The function is automatically generated based on the note types defined in the c ```rust if (note_type_id == NoteType::get_note_type_id()) { aztec::note::utils::compute_note_hash_and_optionally_a_nullifier( - NoteType::unpack_content, + NoteType::unpack, note_header, compute_nullifier, - packed_note_content + packed_note ) } ``` diff --git a/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md b/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md index 5c35a86c5a4e..cd0c387db20b 100644 --- a/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md +++ b/docs/docs/developers/guides/smart_contracts/writing_contracts/common_patterns/index.md @@ -81,7 +81,7 @@ See [partial notes](../../../../../aztec/concepts/advanced/storage/partial_notes When you send someone a note, the note hash gets added to the note hash tree. To spend the note, the receiver needs to get the note itself (the note hash preimage). There are two ways you can get a hold of your notes: -1. When sending someone a note, emit the note contents to the recipient (the function encrypts the log in such a way that only a recipient can decrypt it). PXE then tries to decrypt all the encrypted logs, and stores the successfully decrypted one. [More info here](../how_to_emit_event.md) +1. When sending someone a note, emit the note log to the recipient (the function encrypts the log in such a way that only a recipient can decrypt it). PXE then tries to decrypt all the encrypted logs, and stores the successfully decrypted one. [More info here](../how_to_emit_event.md) 2. Manually using `pxe.addNote()` - If you choose to not emit logs to save gas or when creating a note in the public domain and want to consume it in private domain (`encrypt_and_emit_note` shouldn't be called in the public domain because everything is public), like in the previous section where we created a note in public that doesn't have a designated owner. #include_code pxe_add_note yarn-project/end-to-end/src/composed/e2e_persistence.test.ts typescript diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr index f890cf646ce3..ad150a36e6bf 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/log_assembly_strategies/default_aes128/note.nr @@ -463,7 +463,6 @@ where assert_note_exists(*context, note_hash_counter); - // Unconstrained logs have both their content and encryption unconstrained - it could occur that the // Unconstrained logs have both their content and encryption unconstrained - it could occur that the // recipient is unable to decrypt the payload. // Regarding the note hash counter, this is used for squashing. The kernel assumes that a given note can have diff --git a/noir-projects/aztec-nr/aztec/src/macros/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/mod.nr index 423dc05cf50b..0f52cd3e661c 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/mod.nr @@ -173,11 +173,11 @@ comptime fn generate_compute_note_hash_and_optionally_a_nullifier() -> Quoted { } comptime fn generate_process_log() -> Quoted { - // This mandatory function processes a log emitted by the contract. This is currently used to recover note contents - // and deliver the note to PXE. + // This mandatory function processes a log emitted by the contract. This is currently used to recover a note and + // deliver it to PXE. // The bulk of the work of this function is done by aztec::note::discovery::do_process_log, so all we need to do // is call that function. However, one of its parameters is a lambda function that computes note hash and nullifier - // given note contents and metadata (e.g. note type id), since this behavior is contract-specific (as it + // given packed note and metadata (e.g. note type id), since this behavior is contract-specific (as it // depends on the note types implemented by each contract). // The job of this macro is therefore to implement this lambda function and then call `do_process_log` with it. diff --git a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr index 2d5c4d973c99..1416fc82324f 100644 --- a/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/discovery/mod.nr @@ -11,7 +11,7 @@ use dep::protocol_types::{ hash::compute_note_hash_nonce, }; -// We reserve two fields in the note log that are not part of the note content: one for the storage slot, and one for +// We reserve two fields in the note log that are not part of the packed note: one for the storage slot, and one for // the note type id. global NOTE_LOG_RESERVED_FIELDS: u32 = 2; pub global MAX_NOTE_PACKED_LEN: u32 = PRIVATE_LOG_SIZE_IN_FIELDS - NOTE_LOG_RESERVED_FIELDS; @@ -27,14 +27,14 @@ pub struct NoteHashesAndNullifier { /// created, along with the list of unique note hashes in said transaction. /// /// Additionally, this requires a `compute_note_hash_and_nullifier` lambda that is able to compute these values for any -/// note in the contract given their contents. A typical implementation of such a function would look like this: +/// note type in the contract given the packed note. A typical implementation of such a function would look like this: /// /// ``` /// |packed_note, note_header, note_type_id| { /// let hashes = if note_type_id == MyNoteType::get_note_type_id() { /// assert(packed_note.len() == MY_NOTE_TYPE_PACKED_LENGTH); /// dep::aztec::note::utils::compute_note_hash_and_optionally_a_nullifier( -/// MyNoteType::unpack_content, +/// MyNoteType::unpack, /// contract_address, /// nonce, /// storage_slot, @@ -95,9 +95,9 @@ pub unconstrained fn do_process_log( "Failed to deliver note", ); - // We don't exit the loop - it is possible (though rare) for the exact same note content to be present + // We don't exit the loop - it is possible (though rare) for the exact same packed note to be present // multiple times in the same transaction with different nonces. This typically doesn't happen due to - // notes containing random values in order to hide their contents. + // notes containing random values in order to protect against note hash preimage attacks. } }, ); diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr index 7c3cb3e38ff5..3e3bd3a1850a 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter/mod.nr @@ -39,7 +39,7 @@ fn extract_property_value_from_selector( value_field } -fn check_note_content(packed_note: [Field; N], selects: BoundedVec, N>) { +fn check_packed_note(packed_note: [Field; N], selects: BoundedVec, N>) { for i in 0..selects.len() { let select = selects.get_unchecked(i).unwrap_unchecked(); let value_field = @@ -133,14 +133,14 @@ where Note: NoteInterface + NullifiableNote + 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 contents of the notes (as opposed to simply removing some), - // the private kernel will later validate that these note actually exist, so transformations would cause for that - // check to fail. + // while the filter function can technically mutate the notes (as opposed to simply removing some), the private + // kernel will later validate that these note actually exist, so transformations would cause for that check + // to fail. let filter_fn = options.filter; let filter_args = options.filter_args; let filtered_notes = filter_fn(opt_notes, filter_args); - let notes = crate::utils::array::collapse(filtered_notes); + let notes: BoundedVec, 16> = crate::utils::array::collapse(filtered_notes); let mut note_hashes: BoundedVec = BoundedVec::new(); @@ -149,7 +149,7 @@ where // for the runtime length, and can therefore have fewer loop iterations. assert(notes.len() <= options.limit, "Got more notes than limit."); - let mut prev_fields = [0; N]; + let mut prev_packed_note = [0; N]; for i in 0..options.limit { if i < notes.len() { let retrieved_note = notes.get_unchecked(i); @@ -162,12 +162,12 @@ where "Note contract address mismatch.", ); - let fields = retrieved_note.note.pack(); - check_note_content(fields, options.selects); + let packed_note = retrieved_note.note.pack(); + check_packed_note(packed_note, options.selects); if i != 0 { - check_notes_order(prev_fields, fields, options.sorts); + check_notes_order(prev_packed_note, packed_note, options.sorts); } - prev_fields = fields; + prev_packed_note = packed_note; let note_hash_for_read_request = compute_note_hash_for_read_request(retrieved_note, storage_slot); 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 e0d0d02b3533..57bc5e5d51e9 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr @@ -47,7 +47,7 @@ pub trait NoteInterface { /// 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 note contents, including the storage slot (for indexing) and some random + /// 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; } diff --git a/noir-projects/aztec-nr/uint-note/src/uint_note.nr b/noir-projects/aztec-nr/uint-note/src/uint_note.nr index ccf3552cce54..a72294de0e14 100644 --- a/noir-projects/aztec-nr/uint-note/src/uint_note.nr +++ b/noir-projects/aztec-nr/uint-note/src/uint_note.nr @@ -19,7 +19,7 @@ pub struct UintNote { // The amount of tokens in the note value: U128, owner: AztecAddress, - // Randomness of the note to hide its contents + // Randomness of the note to protect against note hash preimage attacks randomness: Field, } // docs:end:UintNote diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index 4643b25e9b4d..5b94b8685613 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -15,7 +15,7 @@ pub struct SubscriptionNote { owner: AztecAddress, expiry_block_number: Field, remaining_txs: Field, - // Randomness of the note to hide its contents + // Randomness of the note to protect against note hash preimage attacks randomness: Field, } diff --git a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr index c02d76a041fa..a20ff16b07a7 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr @@ -95,22 +95,22 @@ impl Packable for EcdsaPublicKeyNote { } // Cannot use the automatic unpacking for the aforementioned reasons - fn unpack(packed_content: [Field; ECDSA_PUBLIC_KEY_NOTE_PCKD_LEN]) -> EcdsaPublicKeyNote { + fn unpack(packed_note: [Field; ECDSA_PUBLIC_KEY_NOTE_PCKD_LEN]) -> EcdsaPublicKeyNote { let mut x: [u8; 32] = [0; 32]; let mut y: [u8; 32] = [0; 32]; - let part_x:[u8; 32] = packed_content[0].to_be_bytes(); + let part_x:[u8; 32] = packed_note[0].to_be_bytes(); for i in 0..31 { x[i] = part_x[i + 1]; } - x[31] = packed_content[1].to_be_bytes::<32>()[31]; + x[31] = packed_note[1].to_be_bytes::<32>()[31]; - let part_y:[u8; 32] = packed_content[2].to_be_bytes(); + let part_y:[u8; 32] = packed_note[2].to_be_bytes(); for i in 0..31 { y[i] = part_y[i + 1]; } - y[31] = packed_content[3].to_be_bytes::<32>()[31]; + y[31] = packed_note[3].to_be_bytes::<32>()[31]; - EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(packed_content[4]) } + EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(packed_note[4]) } } } \ No newline at end of file diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr index 6888a42c0c77..8a8c69602f80 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr @@ -17,7 +17,7 @@ pub struct NFTNote { token_id: Field, // The owner of the note owner: AztecAddress, - // Randomness of the note to hide its contents + // Randomness of the note to protect against note hash preimage attacks randomness: Field, } // docs:end:nft_note diff --git a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr index 6f0c53baf0c0..3491a97d2f7c 100644 --- a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr @@ -21,7 +21,7 @@ pub struct TokenNote { amount: U128, // The owner of the note owner: AztecAddress, - // Randomness of the note to hide its contents + // Randomness of the note to protect against note hash preimage attacks randomness: Field, } // docs:end:TokenNote diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index a85f52989ad5..57c135fd2dc7 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -20,7 +20,7 @@ pub struct TokenNote { // The amount of tokens in the note amount: U128, owner: AztecAddress, - // Randomness of the note to hide its contents + // Randomness of the note to protect against note hash preimage attacks randomness: Field, } diff --git a/yarn-project/pxe/src/database/note_dao.ts b/yarn-project/pxe/src/database/note_dao.ts index 242968609db6..d27d851c8a22 100644 --- a/yarn-project/pxe/src/database/note_dao.ts +++ b/yarn-project/pxe/src/database/note_dao.ts @@ -27,7 +27,7 @@ export class NoteDao implements NoteData { // Computed values /** - * The inner hash (non-unique, non-siloed) of the note. Each contract determines how the note content is hashed. Can + * The inner hash (non-unique, non-siloed) of the note. Each contract determines how the note is hashed. Can * be used alongside contractAddress and nonce to compute the uniqueNoteHash and the siloedNoteHash. */ public noteHash: Fr, @@ -50,7 +50,7 @@ export class NoteDao implements NoteData { public l2BlockHash: string, /** The index of the leaf in the global note hash tree the note is stored at */ public index: bigint, - /** The public key with which the note content was encrypted during delivery. */ + /** The public key with which the note log was encrypted during delivery. */ public addressPoint: PublicKey, /** The note type identifier for the contract. From 4f189042b8bd64757d09f94c93066fabbe6b45c5 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 12:28:41 +0000 Subject: [PATCH 07/16] separating generate_note_interface_for_partial_note from generate_note_interface --- .../aztec-nr/aztec/src/macros/notes/mod.nr | 47 +++++++++++++++---- 1 file changed, 38 insertions(+), 9 deletions(-) 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 15158d937030..a8d2e06c7306 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -65,6 +65,40 @@ comptime fn derive_packable_if_not_implemented_and_get_len(s: StructDefinition) /// ... /// } /// } +comptime fn generate_note_interface(s: StructDefinition, note_type_id: Field) -> Quoted { + let name = s.name(); + + quote { + impl aztec::note::note_interface::NoteInterface for $name { + fn get_note_type_id() -> Field { + $note_type_id + } + + 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) + } + } + } +} + +/// Generates default `NoteInterface` implementation for a given partial 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 { +/// ... +/// } +/// } +/// +/// # On differences from `generate_note_interface` +/// 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 +/// constrains-wise. /// /// # On including length in note hash preimage /// For a given commitment C = a*G1 + b*G2 + c*G3 we take an x-coordinate of C.x and use it as the hash. @@ -78,7 +112,7 @@ comptime fn derive_packable_if_not_implemented_and_get_len(s: StructDefinition) /// /// 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( +comptime fn generate_note_interface_for_partial_note( s: StructDefinition, note_type_id: Field, indexed_fixed_fields: [(Quoted, Type, u32)], @@ -86,7 +120,7 @@ comptime fn generate_note_interface( ) -> Quoted { let name = s.name(); - // Second we compute quotes for MSM + // First we compute quotes for MSM // `compute_note_hash()` is computed over all the fields so we need to merge fixed and nullable. let merged_fields = indexed_fixed_fields.append(indexed_nullable_fields); // Now we prefix each of the merged fields with `self.` since they refer to the struct members here. @@ -894,7 +928,7 @@ 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( + let note_interface_impl = generate_note_interface_for_partial_note( s, note_type_id, indexed_fixed_fields, @@ -935,12 +969,7 @@ pub comptime fn note(s: StructDefinition) -> Quoted { 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, - indexed_fixed_fields, - indexed_nullable_fields, - ); + let note_interface_impl = generate_note_interface(s, note_type_id); let (packable_impl, note_packed_len) = derive_packable_if_not_implemented_and_get_len(s); register_note( From 3752142a8adc1ba75cfb29913ae4caabf4800746 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 12:46:35 +0000 Subject: [PATCH 08/16] WIP --- .../ecdsa_public_key_note/src/lib.nr | 27 ++------ .../contracts/test_contract/src/test.nr | 6 +- .../contracts/test_contract/src/test_note.nr | 67 ++++++++++--------- 3 files changed, 48 insertions(+), 52 deletions(-) diff --git a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr index a20ff16b07a7..a0c507b24590 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_public_key_note/src/lib.nr @@ -1,37 +1,20 @@ -use dep::aztec::prelude::{NoteInterface, NullifiableNote, PrivateContext, RetrievedNote}; +use dep::aztec::prelude::{NullifiableNote, PrivateContext, RetrievedNote}; use dep::aztec::{ note::utils::compute_note_hash_for_nullify, keys::getters::{get_nsk_app, get_public_keys}, - protocol_types::{address::AztecAddress, constants::{GENERATOR_INDEX__NOTE_NULLIFIER, GENERATOR_INDEX__NOTE_HASH}, hash::poseidon2_hash_with_separator, utils::arrays::array_concat, traits::Packable}, - macros::notes::note_custom_interface + protocol_types::{address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, hash::poseidon2_hash_with_separator, traits::Packable}, + macros::notes::note }; -global ECDSA_PUBLIC_KEY_NOTE_PCKD_LEN: u32 = 5; - // Stores an ECDSA public key composed of two 32-byte elements // TODO: Do we need to include a nonce, in case we want to read/nullify/recreate with the same pubkey value? -#[note_custom_interface] +#[note] pub struct EcdsaPublicKeyNote { x: [u8; 32], y: [u8; 32], owner: AztecAddress, } -impl NoteInterface for EcdsaPublicKeyNote { - fn get_note_type_id() -> Field { - // randomly chosen note type id --> has to fit within 7 bits - 76 - } - - fn compute_note_hash(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 inputs = array_concat(self.pack(), [storage_slot]); - poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__NOTE_HASH) - } -} - impl NullifiableNote for EcdsaPublicKeyNote { fn compute_nullifier(self, context: &mut PrivateContext, note_hash_for_nullify: Field) -> Field { @@ -68,6 +51,8 @@ impl EcdsaPublicKeyNote { } } +global ECDSA_PUBLIC_KEY_NOTE_PCKD_LEN: u32 = 5; + impl Packable for EcdsaPublicKeyNote { // Cannot use the automatic packing since x and y don't fit. Pack the note as 5 fields where: // [0] = x[0..31] (upper bound excluded) diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test_contract/src/test.nr index 9a299c74d71d..e0429ce353ab 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/test.nr @@ -9,7 +9,11 @@ unconstrained fn test_note_type_id() { // but the important thing is that they are sequential and start from 0. assert_eq(UintNote::get_note_type_id(), 0, "UintNote type id should be 0"); assert_eq(ValueNote::get_note_type_id(), 1, "ValueNote type id should be 1"); - assert_eq(TestNote::get_note_type_id(), 2, "TestNote type id should be 2"); + + // TestNote id should be 76 because we have manually set it to that value in our custom `NoteInterface` + // implementation. Custom `NoteInterface` implementations is expected to be provided when `#[note_custom_interface]` + // macro is used. + assert_eq(TestNote::get_note_type_id(), 76, "TestNote type id should be 76"); } #[test] 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 e988a074ca1b..b5aa0628ae79 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 @@ -1,20 +1,48 @@ use dep::aztec::{ context::PrivateContext, - macros::notes::note, - note::note_interface::NullifiableNote, - protocol_types::{address::AztecAddress, traits::{Deserialize, Packable, Serialize}}, + macros::notes::note_custom_interface, + note::note_interface::{NoteInterface, NullifiableNote}, + protocol_types::{ + address::AztecAddress, + constants::GENERATOR_INDEX__NOTE_HASH, + hash::poseidon2_hash_with_separator, + traits::{Deserialize, Serialize}, + utils::arrays::array_concat, + }, }; -// A note which stores a field and is expected to be passed around using the `addNote` function. -// WARNING: This Note is not private as it does not contain randomness and hence it can be easy to perform -// serialized_note attack on it. This note has been developed purely for testing purposes so that it can easily be -// manually added to PXE. Do not use for real applications. -#[note] -#[derive(Serialize, Deserialize)] +// TODO(benesjan): Manual import of `Packable` trait here is a macro bug. +use dep::aztec::protocol_types::traits::Packable; + +/// A note which stores a field and is expected to be passed around using the `addNote` function. +/// +/// WARNING: This Note is not private as it does not contain randomness and hence it can be easy to perform +/// note hash preimage attack on it. This note has been developed purely for testing purposes so that it can easily be +/// manually added to PXE. Do not use for real applications. +/// +/// Note: We are using `#[note_custom_interface]` here even though we don't really need it custom implementation here +/// just to test that the macro works (it's not used anywhere else so far). +#[note_custom_interface] +#[derive(Eq, Deserialize, Serialize)] pub struct TestNote { value: Field, } +impl NoteInterface for TestNote { + fn get_note_type_id() -> Field { + // randomly chosen note type id --> has to fit within 7 bits + 76 + } + + fn compute_note_hash(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 inputs = array_concat(self.pack(), [storage_slot]); + poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__NOTE_HASH) + } +} + impl NullifiableNote for TestNote { fn compute_nullifier( @@ -42,24 +70,3 @@ impl TestNote { TestNote { value } } } - -// Note: We are not deriving Eq here because generally we don't want to include the header in the comparison. -// This is bad note design and a tech debt. Ideally derive it once the note design is fixed. -impl Eq for TestNote { - fn eq(self, other: Self) -> bool { - self.value == other.value - } -} - -// Note: We are not deriving Packable here because that would pack the whole struct including the note header -// (so the resulting field array size would be 5). -// This is bad note design and a tech debt. Ideally derive it once the note design is fixed. -impl Packable<1> for TestNote { - fn pack(self) -> [Field; 1] { - [self.value] - } - - fn unpack(fields: [Field; 1]) -> Self { - TestNote { value: fields[0] } - } -} From 1ddccdcda88a5b561b8f832a8bd2fe9f7995aea3 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 13:07:10 +0000 Subject: [PATCH 09/16] fix --- .../noir-protocol-circuits/crates/types/src/meta/mod.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr index 75c8de81fdf4..b060ce4c10c4 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr @@ -61,7 +61,7 @@ use super::traits::{Deserialize, Packable, Serialize}; /// # Panics /// - If the deserialization logic encounters a type it does not support. /// - If an incorrect number of fields are consumed when deserializing a string. -pub comptime fn generate_deserialize_from_fields( +pub comptime fn generate_deserialize_from_fields( name: Quoted, typ: Type, field_array_name: Quoted, From 7df3ae6b712f4f5908817a2919c0073d8aad764f Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 13:51:52 +0000 Subject: [PATCH 10/16] migration notes --- docs/docs/migration_notes.md | 44 +++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index 20ef987cee3e..c7f30937904e 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -92,7 +92,7 @@ The new check an indexed tree allows is non-membership of addresses of non proto ``` ### [Aztec.nr] Changes to `NoteInterface` -We removed `NoteHeader` from notes and we've introduced `RetrievedNote` struct. +We removed `NoteHeader` from notes, we've introduced a `RetrievedNote` struct and instead of the `pack_content` and `unpack_content` functions we make notes implement the standard `Packable` trait. This led us to do the following changes to `NoteInterface`: ```diff @@ -102,11 +102,12 @@ pub trait NullifiableNote { + unconstrained fn fn compute_nullifier_without_context(self, storage_slot: Field, contract_address: AztecAddress, note_nonce: Field) -> Field; } -pub trait NoteInterface { +-pub trait NoteInterface { ++pub trait NoteInterface { +- fn pack_content(self) -> [Field; N]; +- fn unpack_content(fields: [Field; N]) -> Self; - fn get_header(self) -> NoteHeader; - - fn set_header(&mut self, header: NoteHeader) -> (); - - fn compute_note_hash(self) -> Field; + fn compute_note_hash(self, storage_slot: Field) -> Field; } @@ -121,13 +122,15 @@ These are the changes that needed to be done to our `EcdsaPublicKeyNote`: -use dep::aztec::prelude::{NoteHeader}; +use dep::aztec::prelude::{RetrievedNote}; -impl NoteInterface for EcdsaPublicKeyNote { -... - fn unpack_content(packed_content: [Field; ECDSA_PUBLIC_KEY_NOTE_LEN]) -> EcdsaPublicKeyNote { - ... -- EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(packed_content[4]), header: NoteHeader::empty() } -+ EcdsaPublicKeyNote { x, y, owner: AztecAddress::from_field(packed_content[4]) } - } +- impl NoteInterface for EcdsaPublicKeyNote { ++ impl NoteInterface for EcdsaPublicKeyNote { +- fn pack_content(self) -> [Field; ECDSA_PUBLIC_KEY_NOTE_LEN] { +- ... +- } + +- fn unpack_content(packed_content: [Field; ECDSA_PUBLIC_KEY_NOTE_LEN]) -> EcdsaPublicKeyNote { +- ... +- } - fn get_header(self) -> NoteHeader { - self.header @@ -145,6 +148,25 @@ impl NoteInterface for EcdsaPublicKeyNote { } } +If you need to keep the custom implementation of the packing functionality, manually implement the `Packable` trait: + +```diff ++ use dep::aztec::protocol_types::traits::Packable; + ++impl Packable for YourNote { ++ fn pack(self) -> [Field; N] { ++ ... ++ } ++ ++ fn unpack(fields: [Field; N]) -> Self { ++ ... ++ } ++} +``` + +If you don't provide a custom implementation of the `Packable` trait, a default one will be generated. + +```diff impl NullifiableNote for EcdsaPublicKeyNote { ... - unconstrained fn compute_nullifier_without_context(self, storage_slot: Field) -> Field { From f83d289242fb4c3109aff3139d4852164b5e3299 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 13:52:10 +0000 Subject: [PATCH 11/16] deriving Eq for notes + linking issue --- .../aztec-nr/address-note/src/address_note.nr | 19 ++++++------------ .../aztec-nr/uint-note/src/uint_note.nr | 9 +++++---- .../aztec-nr/value-note/src/value_note.nr | 14 +++++-------- .../src/subscription_note.nr | 7 ++++--- .../src/types/card_note.nr | 20 +++++++------------ .../nft_contract/src/types/nft_note.nr | 14 +++++-------- .../src/public_key_note.nr | 5 ++++- .../spam_contract/src/types/token_note.nr | 14 +++++-------- .../contracts/test_contract/src/test_note.nr | 4 ++-- .../src/types/token_note.nr | 14 +++++-------- .../src/types/transparent_note.nr | 9 +-------- .../crates/types/src/meta/mod.nr | 2 +- 12 files changed, 50 insertions(+), 81 deletions(-) diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index 27107f48bfb0..f48474e34ada 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -8,18 +8,19 @@ use dep::aztec::{ }, oracle::random::random, protocol_types::{ - address::AztecAddress, - constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, - traits::{Packable, Serialize}, + address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, + hash::poseidon2_hash_with_separator, traits::Serialize, }, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + // docs:start:address_note_def // docs:start:address_note_struct // Stores an address #[note] -#[derive(Serialize)] +#[derive(Eq, Serialize)] pub struct AddressNote { address: AztecAddress, owner: AztecAddress, @@ -71,11 +72,3 @@ impl AddressNote { } // docs:end:address_note_def } - -impl Eq for AddressNote { - fn eq(self, other: Self) -> bool { - (self.address == other.address) - & (self.owner == other.owner) - & (self.randomness == other.randomness) - } -} diff --git a/noir-projects/aztec-nr/uint-note/src/uint_note.nr b/noir-projects/aztec-nr/uint-note/src/uint_note.nr index a72294de0e14..154f4d152be0 100644 --- a/noir-projects/aztec-nr/uint-note/src/uint_note.nr +++ b/noir-projects/aztec-nr/uint-note/src/uint_note.nr @@ -5,13 +5,14 @@ use dep::aztec::{ oracle::random::random, prelude::{NullifiableNote, PrivateContext, RetrievedNote}, protocol_types::{ - address::AztecAddress, - constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, - traits::{Packable, Serialize}, + address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, + hash::poseidon2_hash_with_separator, traits::Serialize, }, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + // docs:start:UintNote #[partial_note(quote {value})] #[derive(Eq, Serialize)] diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index 19978005a545..95956b816e92 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -9,14 +9,18 @@ use dep::aztec::{ oracle::random::random, protocol_types::{ address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, traits::Packable, + hash::poseidon2_hash_with_separator, }, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + pub(crate) global VALUE_NOTE_LEN: u32 = 3; // 3 plus a header. // docs:start:value-note-def #[note] +#[derive(Eq)] pub struct ValueNote { value: Field, owner: AztecAddress, @@ -72,11 +76,3 @@ impl ValueNote { ValueNote { value, owner, randomness } } } - -impl Eq for ValueNote { - fn eq(self, other: Self) -> bool { - (self.value == other.value) - & (self.owner == other.owner) - & (self.randomness == other.randomness) - } -} diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index 5b94b8685613..58fec434636b 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -5,11 +5,12 @@ use dep::aztec::{ note::{retrieved_note::RetrievedNote, utils::compute_note_hash_for_nullify}, oracle::random::random, prelude::{NullifiableNote, PrivateContext}, - protocol_types::{ - address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, traits::Packable, - }, + protocol_types::{address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER}, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + #[note] pub struct SubscriptionNote { owner: AztecAddress, diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index d46db59d0769..4e0531ad874c 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -2,19 +2,21 @@ use dep::aztec::{ keys::getters::{get_nsk_app, get_public_keys}, macros::notes::note, note::utils::compute_note_hash_for_nullify, + prelude::{NullifiableNote, PrivateContext, RetrievedNote}, protocol_types::{ - address::AztecAddress, - constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, - traits::{Packable, Serialize}, + address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, + hash::poseidon2_hash_with_separator, traits::Serialize, }, }; -use dep::aztec::prelude::{NullifiableNote, PrivateContext, RetrievedNote}; + +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; // docs:start:state_vars-CardNote global CARD_NOTE_LEN: u32 = 3; // 3 plus a header. #[note] +#[derive(Eq)] pub struct CardNote { points: u8, randomness: Field, @@ -72,11 +74,3 @@ impl Serialize<3> for CardNote { } } // docs:end:serialize - -impl Eq for CardNote { - fn eq(self, other: Self) -> bool { - (self.points == other.points) - & (self.owner == other.owner) - & (self.randomness == other.randomness) - } -} diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr index 8a8c69602f80..f7a46d9495cf 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr @@ -6,12 +6,16 @@ use dep::aztec::{ prelude::{NullifiableNote, PrivateContext, RetrievedNote}, protocol_types::{ address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, traits::Packable, + hash::poseidon2_hash_with_separator, }, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + // docs:start:nft_note #[partial_note(quote { token_id})] +#[derive(Eq)] pub struct NFTNote { // ID of the token token_id: Field, @@ -67,11 +71,3 @@ impl NFTNote { NFTNote { token_id, owner, randomness } } } - -impl Eq for NFTNote { - fn eq(self, other: Self) -> bool { - (self.token_id == other.token_id) - & (self.owner == other.owner) - & (self.randomness == other.randomness) - } -} diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index 68a468bcb399..42f9c7736891 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -5,10 +5,13 @@ use aztec::{ prelude::{NullifiableNote, PrivateContext, RetrievedNote}, protocol_types::{ address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, traits::Packable, + hash::poseidon2_hash_with_separator, }, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + // Stores a public key composed of two fields // TODO: Do we need to include a nonce, in case we want to read/nullify/recreate with the same pubkey value? #[note] diff --git a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr index 3491a97d2f7c..aab944d0d192 100644 --- a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr @@ -6,16 +6,20 @@ use dep::aztec::{ prelude::{NullifiableNote, PrivateContext, RetrievedNote}, protocol_types::{ address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, traits::Packable, + hash::poseidon2_hash_with_separator, }, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + trait OwnedNote { fn new(amount: U128, owner: AztecAddress) -> Self; fn get_amount(self) -> U128; } // docs:start:TokenNote #[note] +#[derive(Eq)] pub struct TokenNote { // The amount of tokens in the note amount: U128, @@ -61,14 +65,6 @@ impl NullifiableNote for TokenNote { } } -impl Eq for TokenNote { - fn eq(self, other: Self) -> bool { - (self.amount == other.amount) - & (self.owner == other.owner) - & (self.randomness == other.randomness) - } -} - impl OwnedNote for TokenNote { fn new(amount: U128, owner: AztecAddress) -> Self { // Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, 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 b5aa0628ae79..4bece56dd32d 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 @@ -11,8 +11,8 @@ use dep::aztec::{ }, }; -// TODO(benesjan): Manual import of `Packable` trait here is a macro bug. -use dep::aztec::protocol_types::traits::Packable; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; /// A note which stores a field and is expected to be passed around using the `addNote` function. /// diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 57c135fd2dc7..484b410b424c 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -6,16 +6,20 @@ use dep::aztec::{ prelude::{NullifiableNote, PrivateContext, RetrievedNote}, protocol_types::{ address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - hash::poseidon2_hash_with_separator, traits::Packable, + hash::poseidon2_hash_with_separator, }, }; +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +use aztec::protocol_types::traits::Packable; + trait OwnedNote { fn new(amount: U128, owner: AztecAddress) -> Self; fn get_amount(self) -> U128; } #[note] +#[derive(Eq)] pub struct TokenNote { // The amount of tokens in the note amount: U128, @@ -59,14 +63,6 @@ impl NullifiableNote for TokenNote { } } -impl Eq for TokenNote { - fn eq(self, other: Self) -> bool { - (self.amount == other.amount) - & (self.owner == other.owner) - & (self.randomness == other.randomness) - } -} - impl OwnedNote for TokenNote { fn new(amount: U128, owner: AztecAddress) -> Self { // Safety: We use the randomness to preserve the privacy of the note recipient by preventing brute-forcing, 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 6fd549e2c846..2497ce428a30 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,4 +1,3 @@ -// docs:start:token_types_all use dep::aztec::{ macros::notes::note, note::utils::compute_note_hash_for_nullify, @@ -16,6 +15,7 @@ use dep::std::mem::zeroed; // Owner of the tokens provides a "secret_hash" as an argument to the public "shield" function and then the tokens // can be redeemed in private by presenting the preimage of the "secret_hash" (the secret). #[note] +#[derive(Eq)] pub struct TransparentNote { amount: Field, secret_hash: Field, @@ -62,10 +62,3 @@ impl TransparentNote { TransparentNote { amount, secret_hash } } } - -impl Eq for TransparentNote { - fn eq(self, other: Self) -> bool { - (self.amount == other.amount) & (self.secret_hash == other.secret_hash) - } -} -// docs:end:token_types_all diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr index b060ce4c10c4..c64ca823eebc 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/meta/mod.nr @@ -453,7 +453,7 @@ pub(crate) comptime fn derive_deserialize(s: StructDefinition) -> Quoted { } } -/// Generates the `Packable` implementation for a given struct and returns the packed length. +/// Generates `Packable` implementation for a given struct and returns the packed length. /// /// Note: We are having this function separate from `derive_packable` because we use this in the note macros to get /// the packed length of a note as well as the `Packable` implementation. We need the length to be able to register From 8f407b06345a37d59c0faec8f4c6d902d96d07b0 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 14:04:40 +0000 Subject: [PATCH 12/16] cleanup --- .../smart_contracts/functions/attributes.md | 21 ++++--------------- docs/docs/migration_notes.md | 2 +- .../aztec-nr/address-note/src/address_note.nr | 2 +- .../aztec-nr/aztec/src/macros/notes/mod.nr | 20 +++++++----------- .../aztec-nr/uint-note/src/uint_note.nr | 2 +- .../aztec-nr/value-note/src/value_note.nr | 2 +- .../src/subscription_note.nr | 2 +- .../src/types/card_note.nr | 2 +- .../nft_contract/src/types/nft_note.nr | 2 +- .../src/public_key_note.nr | 2 +- .../spam_contract/src/types/token_note.nr | 2 +- .../contracts/test_contract/src/test_note.nr | 2 +- .../src/types/token_note.nr | 2 +- 13 files changed, 23 insertions(+), 40 deletions(-) diff --git a/docs/docs/aztec/smart_contracts/functions/attributes.md b/docs/docs/aztec/smart_contracts/functions/attributes.md index cf12a349dee7..62d4a6334442 100644 --- a/docs/docs/aztec/smart_contracts/functions/attributes.md +++ b/docs/docs/aztec/smart_contracts/functions/attributes.md @@ -220,25 +220,13 @@ struct CustomNote { ```rust impl NoteInterface for CustomNote { fn get_note_type_id() -> Field { - 0 + // Assigned by macros by incrementing a counter + 2 } fn compute_note_hash(self, storage_slot: Field) -> Field { - let point = std::embedded_curve_ops::multi_scalar_mul( - [ - Point { x: 0x..., y: 0x... }, - Point { x: 0x..., y: 0x... }, - Point { x: 0x..., y: 0x... }, - Point { x: 0x..., y: 0x... } - ], - [ - std::hash::from_field_unsafe(self.data), - std::hash::from_field_unsafe(self.owner.to_field()), - std::hash::from_field_unsafe(storage_slot), - std::hash::from_field_unsafe(3) // Length of the rest of the preimage - ] - ); - point.x + let inputs = array_concat(self.pack(), [storage_slot]); + poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__NOTE_HASH) } } @@ -284,7 +272,6 @@ impl Packable<2> for CustomNote { [self.data, self.owner.to_field()] } - // Cannot use the automatic unpacking for the aforementioned reasons fn unpack(packed_content: [Field; 2]) -> CustomNote { CustomNote { data: packed_content[0], owner: AztecAddress { inner: packed_content[1] } } } diff --git a/docs/docs/migration_notes.md b/docs/docs/migration_notes.md index c7f30937904e..d054fff98109 100644 --- a/docs/docs/migration_notes.md +++ b/docs/docs/migration_notes.md @@ -103,7 +103,7 @@ pub trait NullifiableNote { } -pub trait NoteInterface { -+pub trait NoteInterface { ++pub trait NoteInterface { - fn pack_content(self) -> [Field; N]; - fn unpack_content(fields: [Field; N]) -> Self; - fn get_header(self) -> NoteHeader; diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index f48474e34ada..9cf95850d662 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -13,7 +13,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; // docs:start:address_note_def 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 a8d2e06c7306..7e1ced8df8cb 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -28,28 +28,24 @@ comptime fn get_next_note_type_id() -> Field { note_type_id } -/// Returns the packed length of a given note struct `s` wrapped in an Option if it implements `Packable`. Returns -/// `Option::none()` if not. -comptime fn get_packable_length(s: StructDefinition) -> Option { +/// Generates a quote that implements `Packable` for a given note struct `s`. +/// If the note struct already implements `Packable`, we return an empty quote. +comptime fn derive_packable_if_not_implemented_and_get_len(s: StructDefinition) -> (Quoted, u32) { + // We try to get the packed length of the note struct. If it does not implement `Packable`, we get Option::none() let packed_len_typ = std::meta::typ::fresh_type_variable(); - // We don't care about the result of the implements check. We just want the get the packed length. let _ = s.as_type().implements( quote { crate::protocol_types::traits::Packable<$packed_len_typ> }.as_trait_constraint(), ); - packed_len_typ.as_constant() -} - -/// Generates a quote that implements `Packable` for a given note struct `s`. -/// If the note struct already implements `Packable`, we return an empty quote. -comptime fn derive_packable_if_not_implemented_and_get_len(s: StructDefinition) -> (Quoted, u32) { - let maybe_packed_length = get_packable_length(s); + let maybe_packed_length = packed_len_typ.as_constant(); if maybe_packed_length.is_some() { + // We got some packed length meaning that the note struct implements `Packable`. For this reason we return + // an empty quote for the implementation and the packed length. (quote {}, maybe_packed_length.unwrap()) } else { // We didn't manage to get the packed length which means the note struct doesn't implement `Packable` - // so we derive it and then get the packed length again. + // so we derive it and return it along with the packed length. derive_packable_and_get_packed_len(s) } } diff --git a/noir-projects/aztec-nr/uint-note/src/uint_note.nr b/noir-projects/aztec-nr/uint-note/src/uint_note.nr index 154f4d152be0..0a8ca726aeda 100644 --- a/noir-projects/aztec-nr/uint-note/src/uint_note.nr +++ b/noir-projects/aztec-nr/uint-note/src/uint_note.nr @@ -10,7 +10,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; // docs:start:UintNote diff --git a/noir-projects/aztec-nr/value-note/src/value_note.nr b/noir-projects/aztec-nr/value-note/src/value_note.nr index 95956b816e92..7fc36ad89643 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -13,7 +13,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; pub(crate) global VALUE_NOTE_LEN: u32 = 3; // 3 plus a header. diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index 58fec434636b..03dbc46e892e 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -8,7 +8,7 @@ use dep::aztec::{ protocol_types::{address::AztecAddress, constants::GENERATOR_INDEX__NOTE_NULLIFIER}, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; #[note] diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index 4e0531ad874c..f582ca02473f 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -9,7 +9,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; // docs:start:state_vars-CardNote diff --git a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr index f7a46d9495cf..86913add9100 100644 --- a/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr +++ b/noir-projects/noir-contracts/contracts/nft_contract/src/types/nft_note.nr @@ -10,7 +10,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; // docs:start:nft_note diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index 42f9c7736891..5f3cfbfe1dea 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -9,7 +9,7 @@ use aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; // Stores a public key composed of two fields diff --git a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr index aab944d0d192..6bf71efb3224 100644 --- a/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/spam_contract/src/types/token_note.nr @@ -10,7 +10,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; trait OwnedNote { 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 4bece56dd32d..150910f9e26a 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 @@ -11,7 +11,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; /// A note which stores a field and is expected to be passed around using the `addNote` function. diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 484b410b424c..b6a6e913a995 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -10,7 +10,7 @@ use dep::aztec::{ }, }; -// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a macro bug. +// TODO(#12008): Remove the need for the manual import of `Packable` trait here. This is a bug in macros. use aztec::protocol_types::traits::Packable; trait OwnedNote { From f9590d05903279745ed4dd09e7953797a01f0cc8 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 14:21:56 +0000 Subject: [PATCH 13/16] last touches --- noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr | 2 +- .../contracts/test_contract/src/test_note.nr | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) 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 7e1ced8df8cb..5eefb239c6bf 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -28,7 +28,7 @@ comptime fn get_next_note_type_id() -> Field { note_type_id } -/// Generates a quote that implements `Packable` for a given note struct `s`. +/// Generates a quote that implements `Packable` for a given struct `s`. /// If the note struct already implements `Packable`, we return an empty quote. comptime fn derive_packable_if_not_implemented_and_get_len(s: StructDefinition) -> (Quoted, u32) { // We try to get the packed length of the note struct. If it does not implement `Packable`, we get Option::none() 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 150910f9e26a..d80be161cb1b 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 @@ -16,11 +16,11 @@ use aztec::protocol_types::traits::Packable; /// A note which stores a field and is expected to be passed around using the `addNote` function. /// -/// WARNING: This Note is not private as it does not contain randomness and hence it can be easy to perform -/// note hash preimage attack on it. This note has been developed purely for testing purposes so that it can easily be -/// manually added to PXE. Do not use for real applications. +/// WARNING: This Note is not private as it does not contain randomness, making it vulnerable to +/// note hash preimage attacks. This note was developed purely for testing purposes so it could be +/// easily added to PXE manually. Do not use for real applications. /// -/// Note: We are using `#[note_custom_interface]` here even though we don't really need it custom implementation here +/// Note: We are using `#[note_custom_interface]` here even though we don't need a custom implementation, /// just to test that the macro works (it's not used anywhere else so far). #[note_custom_interface] #[derive(Eq, Deserialize, Serialize)] @@ -35,9 +35,6 @@ impl NoteInterface for TestNote { } fn compute_note_hash(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 inputs = array_concat(self.pack(), [storage_slot]); poseidon2_hash_with_separator(inputs, GENERATOR_INDEX__NOTE_HASH) } From 01e192f8e84a4e3c196e22c6d88a6114ebf24181 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 16:02:05 +0000 Subject: [PATCH 14/16] fix --- noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr | 1 + .../noir-contracts/contracts/test_contract/src/test.nr | 6 +----- .../noir-contracts/contracts/test_contract/src/test_note.nr | 5 +++-- 3 files changed, 5 insertions(+), 7 deletions(-) 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 5eefb239c6bf..f02a6d2746c0 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -992,6 +992,7 @@ pub comptime fn note(s: StructDefinition) -> Quoted { /// For more details on the generated code, see the individual functions. pub comptime fn note_custom_interface(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, &[]); diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/test.nr b/noir-projects/noir-contracts/contracts/test_contract/src/test.nr index e0429ce353ab..9a299c74d71d 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/test.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/test.nr @@ -9,11 +9,7 @@ unconstrained fn test_note_type_id() { // but the important thing is that they are sequential and start from 0. assert_eq(UintNote::get_note_type_id(), 0, "UintNote type id should be 0"); assert_eq(ValueNote::get_note_type_id(), 1, "ValueNote type id should be 1"); - - // TestNote id should be 76 because we have manually set it to that value in our custom `NoteInterface` - // implementation. Custom `NoteInterface` implementations is expected to be provided when `#[note_custom_interface]` - // macro is used. - assert_eq(TestNote::get_note_type_id(), 76, "TestNote type id should be 76"); + assert_eq(TestNote::get_note_type_id(), 2, "TestNote type id should be 2"); } #[test] 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 d80be161cb1b..ce76fcaede25 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 @@ -29,9 +29,10 @@ pub struct TestNote { } impl NoteInterface for TestNote { + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/12012): This is broken fn get_note_type_id() -> Field { - // randomly chosen note type id --> has to fit within 7 bits - 76 + // id has to fit within 7 bits + 2 } fn compute_note_hash(self, storage_slot: Field) -> Field { From a1afb7a6c96f8e167e5c7e6cc20b9a817ebbfdc5 Mon Sep 17 00:00:00 2001 From: benesjan Date: Fri, 14 Feb 2025 16:39:52 +0000 Subject: [PATCH 15/16] test fix --- yarn-project/end-to-end/src/e2e_2_pxes.test.ts | 3 +++ yarn-project/pxe/src/database/note_dao.ts | 2 +- yarn-project/pxe/src/simulator_oracle/index.ts | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts index 29b3c0b8c4f4..c07647df5021 100644 --- a/yarn-project/end-to-end/src/e2e_2_pxes.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_pxes.test.ts @@ -249,6 +249,9 @@ describe('e2e_2_pxes', () => { note = notes[0]; } + // TODO(#12013): We need to do this hack because NoteDao no longer populates noteTypeId + note.noteTypeId = TestContract.notes.ValueNote.id; + // 3. Nullify the note { const receipt = await testContract.methods.call_destroy_note(noteStorageSlot).send().wait({ debug: true }); diff --git a/yarn-project/pxe/src/database/note_dao.ts b/yarn-project/pxe/src/database/note_dao.ts index d27d851c8a22..c6e219882b9d 100644 --- a/yarn-project/pxe/src/database/note_dao.ts +++ b/yarn-project/pxe/src/database/note_dao.ts @@ -54,7 +54,7 @@ export class NoteDao implements NoteData { public addressPoint: PublicKey, /** The note type identifier for the contract. - * TODO: remove + * TODO(#12013): remove */ public noteTypeId: NoteSelector, ) {} diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 9f12e100d621..087994eb6b99 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -775,7 +775,7 @@ export class SimulatorOracle implements DBOracle { receipt.blockHash!.toString(), uniqueNoteHashTreeIndex, await recipient.toAddressPoint(), - NoteSelector.empty(), // todo: remove + NoteSelector.empty(), // TODO(#12013): remove ); } From 0def9c18ed28595b7be475edda5480e39c91e5e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Tue, 18 Feb 2025 17:17:59 +0100 Subject: [PATCH 16/16] Update noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolás Venturo --- noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f02a6d2746c0..cfb3821aa423 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -94,7 +94,7 @@ comptime fn generate_note_interface(s: StructDefinition, note_type_id: Field) -> /// 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 -/// constrains-wise. +/// constraints-wise. /// /// # On including length in note hash preimage /// For a given commitment C = a*G1 + b*G2 + c*G3 we take an x-coordinate of C.x and use it as the hash.