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 e2a243fdf10..0b823862832 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -8,6 +8,8 @@ use dep::aztec::{ }; global ADDRESS_NOTE_LEN: Field = 3; +// ADDRESS_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global ADDRESS_NOTE_BYTES_LEN: Field = 3 * 32 + 64; // docs:start:address_note_def // Stores an address @@ -19,7 +21,7 @@ struct AddressNote { randomness: Field, } -impl NoteInterface for AddressNote { +impl NoteInterface for AddressNote { fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_consumption(self); diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 26199d2f93e..0d4eda18c82 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -317,14 +317,14 @@ impl PrivateContext { self.encrypted_logs_hashes.push(side_effect); } - pub fn encrypt_and_emit_note( + pub fn encrypt_and_emit_note( &mut self, contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, ivpk_m: GrumpkinPoint, note: Note - ) where Note: NoteInterface, [Field; N]: LensForEncryptedLog { + ) where Note: NoteInterface, [Field; N]: LensForEncryptedLog { let note_hash: Field = compute_note_hash_for_insertion(note); let note_exists_index = find_index( self.new_note_hashes.storage, diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr index 04f6eb59691..a96a1666e5e 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr @@ -2,7 +2,7 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_private_key::GrumpkinP use crate::keys::point_to_symmetric_key::point_to_symmetric_key; -use dep::std::aes128::aes128_encrypt_slice; +use dep::std::aes128::aes128_encrypt; struct EncryptedLogHeader { address: AztecAddress, @@ -24,8 +24,8 @@ impl EncryptedLogHeader { iv[i] = full_key[i + 16]; } - let input: [u8] = self.address.to_field().to_be_bytes(32); - aes128_encrypt_slice(input, iv, sym_key).as_array() + let input: [u8; 32] = self.address.to_field().to_be_bytes(32).as_array(); + aes128_encrypt(input, iv, sym_key).as_array() } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr index ee99daf8943..a85543e1ab7 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr @@ -1,51 +1,20 @@ use crate::note::{note_interface::NoteInterface}; use dep::protocol_types::{grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint}; -use dep::std::aes128::aes128_encrypt_slice; +use dep::std::aes128::aes128_encrypt; use crate::keys::point_to_symmetric_key::point_to_symmetric_key; -struct EncryptedLogIncomingBody { - storage_slot: Field, - note_type_id: Field, - note: Note, +struct EncryptedLogIncomingBody { + plaintext: [u8; M] } -impl EncryptedLogIncomingBody { - pub fn new( - storage_slot: Field, - note_type_id: Field, - note: Note - ) -> Self where Note: NoteInterface { - Self { storage_slot, note_type_id, note } +impl EncryptedLogIncomingBody { + pub fn from_note(note: T, storage_slot: Field) -> Self where T: NoteInterface { + let mut plaintext = note.to_be_bytes(storage_slot); + EncryptedLogIncomingBody { plaintext } } - pub fn compute_ciphertext( - self, - eph_sk: GrumpkinPrivateKey, - ivpk_app: GrumpkinPoint - ) -> [u8] where Note: NoteInterface { - let serialized_note: [Field; N] = self.note.serialize_content(); - - let mut buffer_slice: [u8] = &[]; - - let storage_slot_bytes = self.storage_slot.to_be_bytes(32); - let note_type_id_bytes = self.note_type_id.to_be_bytes(32); - - for i in 0..32 { - buffer_slice = buffer_slice.push_back(storage_slot_bytes[i]); - } - - for i in 0..32 { - buffer_slice = buffer_slice.push_back(note_type_id_bytes[i]); - } - - for i in 0..serialized_note.len() { - let bytes = serialized_note[i].to_be_bytes(32); - for j in 0..32 { - buffer_slice = buffer_slice.push_back(bytes[j]); - } - } - + pub fn compute_ciphertext(self, eph_sk: GrumpkinPrivateKey, ivpk_app: GrumpkinPoint) -> [u8] { let full_key = point_to_symmetric_key(eph_sk, ivpk_app); let mut sym_key = [0; 16]; let mut iv = [0; 16]; @@ -54,7 +23,7 @@ impl EncryptedLogIncomingBody { sym_key[i] = full_key[i]; iv[i] = full_key[i + 16]; } - aes128_encrypt_slice(buffer_slice, iv, sym_key) + aes128_encrypt(self.plaintext, iv, sym_key) } } @@ -78,11 +47,12 @@ mod test { } global ADDRESS_NOTE_LEN: Field = 3; + global ADDRESS_NOTE_BYTES_LEN = 32 * 3 + 64; - impl NoteInterface for AddressNote { + impl NoteInterface for AddressNote { fn compute_note_content_hash(self) -> Field {1} - fn get_note_type_id() -> Field {2} + fn get_note_type_id() -> Field {1} fn get_header(self) -> NoteHeader { self.header} @@ -99,6 +69,28 @@ mod test { fn deserialize_content(fields: [Field; ADDRESS_NOTE_LEN]) -> Self { AddressNote { address: AztecAddress::from_field(fields[0]), owner: AztecAddress::from_field(fields[1]), randomness: fields[2], header: NoteHeader::empty() } } + + fn to_be_bytes(self, storage_slot: Field) -> [u8; ADDRESS_NOTE_BYTES_LEN] { + let serialized_note = self.serialize_content(); + + let mut buffer: [u8; ADDRESS_NOTE_BYTES_LEN] = [0; ADDRESS_NOTE_BYTES_LEN]; + + let storage_slot_bytes = storage_slot.to_be_bytes(32); + let note_type_id_bytes = AddressNote::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]; + } + + 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 AddressNote { @@ -115,9 +107,7 @@ mod test { 3 ); - let note_type_id = 1; let storage_slot = 2; - let body = EncryptedLogIncomingBody::new(storage_slot, note_type_id, note); let eph_sk = GrumpkinPrivateKey::new( 0x0000000000000000000000000000000023b3127c127b1f29a7adff5cccf8fb06, @@ -128,15 +118,18 @@ mod test { 0x1e96887b117afca01c00468264f4f80b5bb16d94c1808a448595f115556e5c8e ); + let body = EncryptedLogIncomingBody::from_note(note, storage_slot); + let ciphertext = body.compute_ciphertext(eph_sk, ivpk_app); let expected_body_ciphertext = [ 131, 119, 105, 129, 244, 32, 151, 205, 12, 99, 93, 62, 10, 180, 72, 21, 47, 232, 95, 17, 240, 230, 80, 129, 174, 158, 23, 76, 114, 185, 43, 18, 254, 148, 147, 230, 66, 216, 167, 62, 180, 213, 238, 33, 108, 29, 84, 139, 99, 206, 212, 253, 92, 116, 137, 31, 0, 104, 45, 91, 250, 109, 141, 114, 189, 53, 35, 60, 108, 156, 170, 206, 150, 114, 150, 187, 198, 13, 62, 153, 133, 13, 169, 167, 242, 221, 40, 168, 186, 203, 104, 82, 47, 238, 142, 179, 90, 37, 9, 70, 245, 176, 122, 247, 42, 87, 75, 7, 20, 89, 166, 123, 14, 26, 230, 156, 49, 94, 0, 94, 72, 58, 171, 239, 115, 174, 155, 7, 151, 17, 60, 206, 193, 134, 70, 87, 215, 88, 21, 194, 63, 26, 106, 105, 124, 213, 252, 152, 192, 71, 115, 13, 181, 5, 169, 15, 170, 196, 174, 228, 170, 192, 91, 76, 110, 220, 89, 47, 248, 144, 189, 251, 167, 149, 248, 226 ]; + assert_eq(expected_body_ciphertext.len(), ciphertext.len()); + for i in 0..expected_body_ciphertext.len() { assert_eq(ciphertext[i], expected_body_ciphertext[i]); } - assert_eq(expected_body_ciphertext.len(), ciphertext.len()); } } 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 0d47e4d84f4..08549a10476 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note_inclusion.nr @@ -7,7 +7,7 @@ use crate::{ oracle::get_membership_witness::get_note_hash_membership_witness }; -pub fn _note_inclusion(note: Note, header: Header) where Note: NoteInterface { +pub fn _note_inclusion(note: Note, header: Header) where Note: NoteInterface { // 1) Compute note_hash let note_hash = compute_note_hash_for_consumption(note); @@ -20,15 +20,18 @@ pub fn _note_inclusion(note: Note, header: Header) where Note: NoteInte ); } -pub fn prove_note_inclusion(note: Note, context: PrivateContext) where Note: NoteInterface { +pub fn prove_note_inclusion( + note: Note, + context: PrivateContext +) where Note: NoteInterface { _note_inclusion(note, context.historical_header); } -pub fn prove_note_inclusion_at( +pub fn prove_note_inclusion_at( note: Note, block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { let header = context.get_header_at(block_number); _note_inclusion(note, header); 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 c929f15eca9..08bc9d529db 100644 --- a/noir-projects/aztec-nr/aztec/src/history/note_validity.nr +++ b/noir-projects/aztec-nr/aztec/src/history/note_validity.nr @@ -7,17 +7,17 @@ use crate::{ note::{utils::compute_siloed_nullifier, note_interface::NoteInterface} }; -pub fn prove_note_validity(note: Note, context: &mut PrivateContext) where Note: NoteInterface { +pub fn prove_note_validity(note: Note, context: &mut PrivateContext) where Note: NoteInterface { prove_note_inclusion(note, *context); prove_note_not_nullified(note, context); } // A helper function that proves that a note is valid at the given block number -pub fn prove_note_validity_at( +pub fn prove_note_validity_at( note: Note, block_number: u32, context: &mut PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { // We are calling the internal functions here because we want to avoid calling get_header_at twice let header = context.get_header_at(block_number); _note_inclusion(note, header); 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 09d27840c88..a0347e0a4d6 100644 --- a/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/nullifier_inclusion.nr @@ -39,20 +39,16 @@ pub fn prove_nullifier_inclusion_at( _nullifier_inclusion(nullifier, header); } -pub fn prove_note_is_nullified( +pub fn prove_note_is_nullified( note: Note, context: &mut PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note, context); _nullifier_inclusion(nullifier, context.historical_header); } -pub fn prove_note_is_nullified_at( - note: Note, - block_number: u32, - context: &mut PrivateContext -) where Note: NoteInterface { +pub fn prove_note_is_nullified_at(note: Note, block_number: u32, context: &mut PrivateContext) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note, context); let header = context.get_header_at(block_number); 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 1d17e2d96d8..634d7f13acb 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 @@ -43,20 +43,17 @@ pub fn prove_nullifier_not_included_at(nullifier: Field, block_number: u32, cont _nullifier_non_inclusion(nullifier, header); } -pub fn prove_note_not_nullified( - note: Note, - context: &mut PrivateContext -) where Note: NoteInterface { +pub fn prove_note_not_nullified(note: Note, context: &mut PrivateContext) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note, context); _nullifier_non_inclusion(nullifier, context.historical_header); } -pub fn prove_note_not_nullified_at( +pub fn prove_note_not_nullified_at( note: Note, block_number: u32, // The block at which we'll prove that the note was not nullified context: &mut PrivateContext -) where Note: NoteInterface { +) where Note: NoteInterface { let nullifier = compute_siloed_nullifier(note, context); let header = context.get_header_at(block_number); diff --git a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr index af461ff44cb..17ec0efbab0 100644 --- a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr +++ b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr @@ -6,13 +6,13 @@ use crate::note::{ }; use crate::oracle::notes::{notify_created_note, notify_nullified_note}; -pub fn create_note( +pub fn create_note( context: &mut PrivateContext, storage_slot: Field, note: &mut Note, broadcast: bool, ivpk_m: GrumpkinPoint -) where Note: NoteInterface { +) where Note: NoteInterface { let contract_address = (*context).this_address(); let header = NoteHeader { contract_address, storage_slot, nonce: 0, is_transient: true }; @@ -42,11 +42,11 @@ pub fn create_note( } } -pub fn create_note_hash_from_public( +pub fn create_note_hash_from_public( context: &mut PublicContext, storage_slot: Field, note: &mut Note -) where Note: NoteInterface { +) where Note: NoteInterface { let contract_address = (*context).this_address(); let header = NoteHeader { contract_address, storage_slot, nonce: 0, is_transient: true }; @@ -57,7 +57,10 @@ pub fn create_note_hash_from_public( context.push_new_note_hash(inner_note_hash); } -pub fn destroy_note(context: &mut PrivateContext, note: Note) where Note: NoteInterface { +pub fn destroy_note( + context: &mut PrivateContext, + note: Note +) where Note: NoteInterface { let mut nullifier = 0; let mut consumed_note_hash: Field = 0; nullifier = note.compute_nullifier(context); diff --git a/noir-projects/aztec-nr/aztec/src/note/note_getter.nr b/noir-projects/aztec-nr/aztec/src/note/note_getter.nr index 6602653dd47..ecf3ec522e5 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_getter.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_getter.nr @@ -29,11 +29,11 @@ fn extract_property_value_from_selector( value_field } -fn check_note_header( +fn check_note_header( context: PrivateContext, storage_slot: Field, note: Note -) where Note: NoteInterface { +) where Note: NoteInterface { let header = note.get_header(); let contract_address = context.this_address(); assert(header.contract_address.eq(contract_address)); @@ -84,10 +84,10 @@ fn check_notes_order( } } -pub fn get_note( +pub fn get_note( context: &mut PrivateContext, storage_slot: Field -) -> Note where Note: NoteInterface { +) -> Note where Note: NoteInterface { let note = get_note_internal(storage_slot); check_note_header(*context, storage_slot, note); @@ -98,22 +98,22 @@ pub fn get_note( note } -pub fn get_notes( +pub fn get_notes( context: &mut PrivateContext, storage_slot: Field, - options: NoteGetterOptions -) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { + options: NoteGetterOptions +) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { let opt_notes = get_notes_internal(storage_slot, options); _get_notes_constrain_get_notes_internal(context, storage_slot, opt_notes, options) } -pub fn _get_notes_constrain_get_notes_internal( +pub fn _get_notes_constrain_get_notes_internal( context: &mut PrivateContext, storage_slot: Field, opt_notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], - options: NoteGetterOptions -) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { + options: NoteGetterOptions +) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { let mut returned_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let mut num_notes = 0; @@ -150,7 +150,7 @@ pub fn _get_notes_constrain_get_notes_internal( returned_notes } -unconstrained fn get_note_internal(storage_slot: Field) -> Note where Note: NoteInterface { +unconstrained fn get_note_internal(storage_slot: Field) -> Note where Note: NoteInterface { let placeholder_note = [Option::none()]; let placeholder_fields = [0; GET_NOTE_ORACLE_RETURN_LENGTH]; let placeholder_note_length = [0; N]; @@ -175,10 +175,10 @@ unconstrained fn get_note_internal(storage_slot: Field) -> Note where N )[0].unwrap() // Notice: we don't allow dummies to be returned from get_note (singular). } -unconstrained fn get_notes_internal( +unconstrained fn get_notes_internal( storage_slot: Field, - options: NoteGetterOptions -) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { + options: NoteGetterOptions +) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { 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); let placeholder_opt_notes = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let placeholder_fields = [0; GET_NOTES_ORACLE_RETURN_LENGTH]; @@ -208,10 +208,10 @@ unconstrained fn get_notes_internal( filter(opt_notes, filter_args) } -unconstrained pub fn view_notes( +unconstrained pub fn view_notes( storage_slot: Field, - options: NoteViewerOptions -) -> [Option; MAX_NOTES_PER_PAGE] where Note: NoteInterface { + options: NoteViewerOptions +) -> [Option; MAX_NOTES_PER_PAGE] where Note: NoteInterface { 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); let placeholder_opt_notes = [Option::none(); MAX_NOTES_PER_PAGE]; let placeholder_fields = [0; VIEW_NOTE_ORACLE_RETURN_LENGTH]; 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 0389ee76a18..39c6668cf24 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 @@ -78,7 +78,7 @@ fn return_all_notes( } // docs:start:NoteGetterOptions -struct NoteGetterOptions { +struct NoteGetterOptions { selects: BoundedVec, N>, sorts: BoundedVec, N>, limit: u32, @@ -93,9 +93,9 @@ struct NoteGetterOptions { // The database-level configurations are applied first: // `selects` to specify fields, `sorts` to establish sorting criteria, `offset` to skip items, and `limit` to cap the result size. // And finally, a custom filter to refine the outcome further. -impl NoteGetterOptions { +impl NoteGetterOptions { // This function initializes a NoteGetterOptions that simply returns the maximum number of notes allowed in a call. - pub fn new() -> NoteGetterOptions where Note: NoteInterface { + pub fn new() -> NoteGetterOptions where Note: NoteInterface { NoteGetterOptions { selects: BoundedVec::new(), sorts: BoundedVec::new(), @@ -112,7 +112,7 @@ impl NoteGetterOptions { pub fn with_filter( filter: fn([Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], FILTER_ARGS) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], filter_args: FILTER_ARGS - ) -> Self where Note: NoteInterface { + ) -> Self where Note: NoteInterface { NoteGetterOptions { selects: BoundedVec::new(), sorts: BoundedVec::new(), 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 54b6783769a..ab2dfbd69dd 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr @@ -3,7 +3,7 @@ use crate::note::note_header::NoteHeader; use dep::protocol_types::grumpkin_point::GrumpkinPoint; // docs:start:note_interface -trait NoteInterface { +trait NoteInterface { fn compute_nullifier(self, context: &mut PrivateContext) -> Field; fn compute_nullifier_without_context(self) -> Field; @@ -27,6 +27,9 @@ trait NoteInterface { // Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation fn get_note_type_id() -> Field; + + // Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation + fn to_be_bytes(self, storage_slot: Field) -> [u8; M]; } // docs:end:note_interface 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 c2bd2002cca..5678757bb4a 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 @@ -5,7 +5,7 @@ use crate::note::note_interface::NoteInterface; use crate::note::constants::MAX_NOTES_PER_PAGE; // docs:start:NoteViewerOptions -struct NoteViewerOptions { +struct NoteViewerOptions { selects: BoundedVec, N>, sorts: BoundedVec, N>, limit: u32, @@ -14,8 +14,8 @@ struct NoteViewerOptions { } // docs:end:NoteViewerOptions -impl NoteViewerOptions { - pub fn new() -> NoteViewerOptions where Note: NoteInterface { +impl NoteViewerOptions { + pub fn new() -> NoteViewerOptions where Note: NoteInterface { NoteViewerOptions { selects: BoundedVec::new(), sorts: 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 59298bd3230..4a91aa3271e 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -19,7 +19,7 @@ fn compute_unique_hash(nonce: Field, inner_note_hash: Field) -> Field { pedersen_hash(inputs, GENERATOR_INDEX__UNIQUE_NOTE_HASH) } -fn compute_inner_note_hash(note: Note) -> Field where Note: NoteInterface { +fn compute_inner_note_hash(note: Note) -> Field where Note: NoteInterface { let header = note.get_header(); let note_hash = note.compute_note_content_hash(); @@ -29,7 +29,7 @@ fn compute_inner_note_hash(note: Note) -> Field where Note: NoteInterfa ) } -fn compute_unique_note_hash(note_with_header: Note) -> Field where Note: NoteInterface { +fn compute_unique_note_hash(note_with_header: Note) -> Field where Note: NoteInterface { let header = note_with_header.get_header(); let inner_note_hash = compute_inner_note_hash(note_with_header); @@ -37,7 +37,7 @@ fn compute_unique_note_hash(note_with_header: Note) -> Field where Note compute_unique_hash(header.nonce, inner_note_hash) } -fn compute_siloed_note_hash(note_with_header: Note) -> Field where Note: NoteInterface { +fn compute_siloed_note_hash(note_with_header: Note) -> Field where Note: NoteInterface { let header = note_with_header.get_header(); let unique_note_hash = if (header.nonce == 0) { @@ -52,10 +52,10 @@ fn compute_siloed_note_hash(note_with_header: Note) -> Field where Note compute_siloed_hash(header.contract_address, unique_note_hash) } -pub fn compute_siloed_nullifier( +pub fn compute_siloed_nullifier( note_with_header: Note, context: &mut PrivateContext -) -> Field where Note: NoteInterface { +) -> Field where Note: NoteInterface { let header = note_with_header.get_header(); let inner_nullifier = note_with_header.compute_nullifier(context); @@ -63,11 +63,11 @@ pub fn compute_siloed_nullifier( pedersen_hash(input, GENERATOR_INDEX__OUTER_NULLIFIER) } -pub fn compute_note_hash_for_insertion(note: Note) -> Field where Note: NoteInterface { +pub fn compute_note_hash_for_insertion(note: Note) -> Field where Note: NoteInterface { compute_inner_note_hash(note) } -pub fn compute_note_hash_for_read_request(note: Note) -> Field where Note: NoteInterface { +pub fn compute_note_hash_for_read_request(note: Note) -> Field where Note: NoteInterface { let header = note.get_header(); if (header.nonce != 0) { @@ -77,7 +77,7 @@ pub fn compute_note_hash_for_read_request(note: Note) -> Field where No } } -pub fn compute_note_hash_for_consumption(note: Note) -> Field where Note: NoteInterface { +pub fn compute_note_hash_for_consumption(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, and is transient. @@ -104,12 +104,12 @@ pub fn compute_note_hash_for_consumption(note: Note) -> Field where Not } } -pub fn compute_note_hash_and_nullifier( +pub fn compute_note_hash_and_nullifier( // docs:start:compute_note_hash_and_nullifier_args deserialize_content: fn([Field; N]) -> T, note_header: NoteHeader, serialized_note: [Field; S] // docs:end:compute_note_hash_and_nullifier_args -) -> [Field; 4] where T: NoteInterface { +) -> [Field; 4] where T: NoteInterface { let mut note = deserialize_content(arr_copy_slice(serialized_note, [0; N], 0)); // TODO: change this to note.set_header(header) once https://github.com/noir-lang/noir/issues/4095 is fixed T::set_header((&mut note), note_header); diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index da3b3af393a..d9e11e4c563 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -100,7 +100,7 @@ unconstrained fn get_notes_oracle_wrapper( ) } -unconstrained pub fn get_notes( +unconstrained pub fn get_notes( storage_slot: Field, num_selects: u8, select_by_indexes: [u8; M], @@ -118,7 +118,7 @@ unconstrained pub fn get_notes( mut placeholder_opt_notes: [Option; 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 -) -> [Option; S] where Note: NoteInterface { +) -> [Option; S] where Note: NoteInterface { let fields = get_notes_oracle_wrapper( storage_slot, num_selects, 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 cbbb8d8a3b5..7a63f6583bb 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 @@ -44,12 +44,12 @@ impl PrivateImmutable { impl PrivateImmutable { // docs:start:initialize - pub fn initialize( + pub fn initialize( self, note: &mut Note, broadcast: bool, ivpk_m: GrumpkinPoint - ) where Note: NoteInterface { + ) where Note: NoteInterface { // Nullify the storage slot. let nullifier = self.compute_initialization_nullifier(); self.context.push_new_nullifier(nullifier, 0); @@ -59,7 +59,7 @@ impl PrivateImmutable { // docs:end:initialize // docs:start:get_note - pub fn get_note(self) -> Note where Note: NoteInterface { + pub fn get_note(self) -> Note where Note: NoteInterface { let storage_slot = self.storage_slot; get_note(self.context, storage_slot) } @@ -76,7 +76,7 @@ impl PrivateImmutable { // view_note does not actually use the context, but it calls oracles that are only available in private // docs:start:view_note - unconstrained pub fn view_note(self) -> Note where Note: NoteInterface { + unconstrained pub fn view_note(self) -> Note where Note: NoteInterface { let mut options = NoteViewerOptions::new(); view_notes(self.storage_slot, options.set_limit(1))[0].unwrap() } 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 95d8a0badb0..e566bc5a28f 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 @@ -50,12 +50,12 @@ impl PrivateMutable { impl PrivateMutable { // docs:start:initialize - pub fn initialize( + pub fn initialize( self, note: &mut Note, broadcast: bool, ivpk_m: GrumpkinPoint - ) where Note: NoteInterface { + ) where Note: NoteInterface { // Nullify the storage slot. let nullifier = self.compute_initialization_nullifier(); self.context.push_new_nullifier(nullifier, 0); @@ -65,12 +65,12 @@ impl PrivateMutable { // docs:end:initialize // docs:start:replace - pub fn replace( + pub fn replace( self, new_note: &mut Note, broadcast: bool, ivpk_m: GrumpkinPoint - ) where Note: NoteInterface { + ) where Note: NoteInterface { let prev_note: Note = get_note(self.context, self.storage_slot); // Nullify previous note. @@ -82,11 +82,11 @@ impl PrivateMutable { // docs:end:replace // docs:start:get_note - pub fn get_note( + pub fn get_note( self, broadcast: bool, ivpk_m: GrumpkinPoint - ) -> Note where Note: NoteInterface { + ) -> Note where Note: NoteInterface { let mut note = get_note(self.context, self.storage_slot); // Nullify current note to make sure it's reading the latest note. @@ -108,7 +108,7 @@ impl PrivateMutable { } // docs:start:view_note - unconstrained pub fn view_note(self) -> Note where Note: NoteInterface { + unconstrained pub fn view_note(self) -> Note where Note: NoteInterface { let mut options = NoteViewerOptions::new(); view_notes(self.storage_slot, options.set_limit(1))[0].unwrap() } 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 a976420cfd5..2f6215c4b47 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 @@ -31,7 +31,7 @@ impl PrivateSet { impl PrivateSet { // docs:start:insert_from_public - pub fn insert_from_public(self, note: &mut Note) where Note: NoteInterface { + pub fn insert_from_public(self, note: &mut Note) where Note: NoteInterface { create_note_hash_from_public(self.context, self.storage_slot, note); } // docs:end:insert_from_public @@ -39,12 +39,12 @@ impl PrivateSet { impl PrivateSet { // docs:start:insert - pub fn insert( + pub fn insert( self, note: &mut Note, broadcast: bool, ivpk_m: GrumpkinPoint - ) where Note: NoteInterface { + ) where Note: NoteInterface { create_note(self.context, self.storage_slot, note, broadcast, ivpk_m); } // docs:end:insert @@ -64,7 +64,7 @@ impl PrivateSet { } // docs:start:remove - pub fn remove(self, note: Note) where Note: NoteInterface { + pub fn remove(self, note: Note) where Note: NoteInterface { let note_hash = compute_note_hash_for_read_request(note); let has_been_read = self.context.note_hash_read_requests.any(|r: ReadRequest| r.value == note_hash); assert(has_been_read, "Can only remove a note that has been read from the set."); @@ -74,10 +74,10 @@ impl PrivateSet { // docs:end:remove // docs:start:get_notes - pub fn get_notes( + pub fn get_notes( self, - options: NoteGetterOptions - ) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { + options: NoteGetterOptions + ) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where Note: NoteInterface { let storage_slot = self.storage_slot; let opt_notes = get_notes(self.context, storage_slot, options); opt_notes @@ -87,10 +87,10 @@ impl PrivateSet { impl PrivateSet { // docs:start:view_notes - unconstrained pub fn view_notes( + unconstrained pub fn view_notes( self, - options: NoteViewerOptions - ) -> [Option; MAX_NOTES_PER_PAGE] where Note: NoteInterface { + options: NoteViewerOptions + ) -> [Option; MAX_NOTES_PER_PAGE] where Note: NoteInterface { view_notes(self.storage_slot, options) } // docs:end:view_notes diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr index fa9b21ca11e..0f8cce2323c 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/storage.nr @@ -10,7 +10,7 @@ trait Storage where T: Serialize + Deserialize { // Every entry in the storage struct will be exported in the compilation artifact as a // Storable entity, containing the storage slot and the type of the variable struct Storable { - slot: Field, - typ: str - } + slot: Field, + typ: str +} diff --git a/noir-projects/aztec-nr/tests/src/mock/test_note.nr b/noir-projects/aztec-nr/tests/src/mock/test_note.nr index 3aadf5fdd94..3d77911fb99 100644 --- a/noir-projects/aztec-nr/tests/src/mock/test_note.nr +++ b/noir-projects/aztec-nr/tests/src/mock/test_note.nr @@ -5,13 +5,15 @@ use dep::aztec::{ }; global TEST_NOTE_LENGTH = 1; +// TEST_NOTE_LENGTH * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global TEST_NOTE_BYTES_LENGTH: Field = 1 * 32 + 64; struct TestNote { header: NoteHeader, value: Field, } -impl NoteInterface for TestNote { +impl NoteInterface for TestNote { fn serialize_content(self) -> [Field; TEST_NOTE_LENGTH] { [self.value] } @@ -52,6 +54,28 @@ impl NoteInterface for TestNote { false, "TestNote does not support broadcast." ); } + + fn to_be_bytes(self, storage_slot: Field) -> [u8; TEST_NOTE_BYTES_LENGTH] { + let serialized_note = self.serialize_content(); + + let mut buffer: [u8; TEST_NOTE_BYTES_LENGTH] = [0; TEST_NOTE_BYTES_LENGTH]; + + let storage_slot_bytes = storage_slot.to_be_bytes(32); + let note_type_id_bytes = TestNote::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]; + } + + 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 TestNote { diff --git a/noir-projects/aztec-nr/value-note/src/utils.nr b/noir-projects/aztec-nr/value-note/src/utils.nr index 659ecb38b8f..c3a39e7877d 100644 --- a/noir-projects/aztec-nr/value-note/src/utils.nr +++ b/noir-projects/aztec-nr/value-note/src/utils.nr @@ -2,11 +2,11 @@ use dep::aztec::prelude::{AztecAddress, PrivateContext, PrivateSet, NoteGetterOp use dep::aztec::note::note_getter_options::SortOrder; use dep::aztec::protocol_types::grumpkin_point::GrumpkinPoint; use dep::aztec::keys::getters::{get_npk_m_hash, get_ivpk_m}; -use crate::{filter::filter_notes_min_sum, value_note::{ValueNote, VALUE_NOTE_LEN}}; +use crate::{filter::filter_notes_min_sum, value_note::{ValueNote, VALUE_NOTE_LEN, VALUE_NOTE_BYTES_LEN}}; // Sort the note values (0th field) in descending order. // Pick the fewest notes whose sum is equal to or greater than `amount`. -pub fn create_note_getter_options_for_decreasing_balance(amount: Field) -> NoteGetterOptions { +pub fn create_note_getter_options_for_decreasing_balance(amount: Field) -> NoteGetterOptions { NoteGetterOptions::with_filter(filter_notes_min_sum, amount).sort(ValueNote::properties().value, SortOrder.DESC) } 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 bdc4fcc33b6..eaff41e238c 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -9,6 +9,8 @@ use dep::aztec::{ }; global VALUE_NOTE_LEN: Field = 3; // 3 plus a header. +// VALUE_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global VALUE_NOTE_BYTES_LEN: Field = 3 * 32 + 64; // docs:start:value-note-def #[aztec(note)] @@ -20,7 +22,7 @@ struct ValueNote { } // docs:end:value-note-def -impl NoteInterface for ValueNote { +impl NoteInterface for ValueNote { // docs:start:nullifier fn compute_nullifier(self, context: &mut PrivateContext) -> Field { 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 e1c40e40f45..5b8a03bcec2 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 @@ -6,6 +6,8 @@ use dep::aztec::{ }; global SUBSCRIPTION_NOTE_LEN: Field = 3; +// ADDRESS_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global SUBSCRIPTION_NOTE_BYTES_LEN: Field = 3 * 32 + 64; // 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? @@ -17,7 +19,7 @@ struct SubscriptionNote { remaining_txs: Field, } -impl NoteInterface for SubscriptionNote { +impl NoteInterface for SubscriptionNote { fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/options.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/options.nr index bffab5a4dc6..86cb169fe55 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/options.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/options.nr @@ -1,4 +1,4 @@ -use crate::types::card_note::{CardNote, CARD_NOTE_LEN}; +use crate::types::card_note::{CardNote, CARD_NOTE_LEN, CARD_NOTE_BYTES_LEN}; use dep::aztec::prelude::{AztecAddress, NoteGetterOptions}; use dep::aztec::protocol_types::constants::MAX_NOTE_HASH_READ_REQUESTS_PER_CALL; @@ -10,7 +10,7 @@ use dep::aztec::note::note_getter_options::{Sort, SortOrder}; pub fn create_account_card_getter_options( account_npk_m_hash: Field, offset: u32 -) -> NoteGetterOptions { +) -> NoteGetterOptions { // TODO (#6312): This will break with key rotation. Fix this. Will not be able to find any notes after rotating key. let mut options = NoteGetterOptions::new(); options.select( @@ -26,7 +26,7 @@ pub fn create_exact_card_getter_options( points: u8, secret: Field, account_npk_m_hash: Field -) -> NoteGetterOptions { +) -> NoteGetterOptions { // TODO (#6312): This will break with key rotation. Fix this. Will not be able to find any notes after rotating key. let mut options = NoteGetterOptions::new(); options.select(CardNote::properties().points, points as Field, Option::none()).select(CardNote::properties().randomness, secret, Option::none()).select( @@ -55,7 +55,10 @@ pub fn filter_min_points( // docs:end:state_vars-OptionFilter // docs:start:state_vars-NoteGetterOptionsFilter -pub fn create_account_cards_with_min_points_getter_options(account_npk_m_hash: Field, min_points: u8) -> NoteGetterOptions { +pub fn create_account_cards_with_min_points_getter_options( + account_npk_m_hash: Field, + min_points: u8 +) -> NoteGetterOptions { // TODO (#6312): This will break with key rotation. Fix this. Will not be able to find any notes after rotating key. NoteGetterOptions::with_filter(filter_min_points, min_points).select( CardNote::properties().npk_m_hash, @@ -66,7 +69,7 @@ pub fn create_account_cards_with_min_points_getter_options(account_npk_m_hash: F // docs:end:state_vars-NoteGetterOptionsFilter // docs:start:state_vars-NoteGetterOptionsPickOne -pub fn create_largest_account_card_getter_options(account_npk_m_hash: Field) -> NoteGetterOptions { +pub fn create_largest_account_card_getter_options(account_npk_m_hash: Field) -> NoteGetterOptions { // TODO (#6312): This will break with key rotation. Fix this. Will not be able to find any notes after rotating key. let mut options = NoteGetterOptions::new(); options.select( 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 169c20ae7ad..41e5f294bd9 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 @@ -11,6 +11,8 @@ use dep::aztec::{ // Shows how to create a custom note global CARD_NOTE_LEN: Field = 3; +// CARD_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global CARD_NOTE_BYTES_LEN: Field = 3 * 32 + 64; // docs:start:state_vars-CardNote #[aztec(note)] @@ -28,7 +30,7 @@ impl CardNote { } } -impl NoteInterface for CardNote { +impl NoteInterface for CardNote { fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index 30121822ee7..5c37262e058 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -7,6 +7,8 @@ use dep::aztec::{ }; global ECDSA_PUBLIC_KEY_NOTE_LEN: Field = 5; +// ECDSA_PUBLIC_KEY_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global ECDSA_PUBLIC_KEY_NOTE_BYTES_LEN: Field = 5 * 32 + 64; // 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? @@ -18,7 +20,7 @@ struct EcdsaPublicKeyNote { npk_m_hash: Field, } -impl NoteInterface for EcdsaPublicKeyNote { +impl NoteInterface for EcdsaPublicKeyNote { // Cannot use the automatic serialization since x and y don't fit. Serialize the note as 5 fields where: // [0] = x[0..31] (upper bound excluded) // [1] = x[31] 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 a24b9a1fe8a..e548b3d8ab8 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,6 +5,8 @@ use dep::aztec::{ }; global PUBLIC_KEY_NOTE_LEN: Field = 3; +// PUBLIC_KEY_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global PUBLIC_KEY_NOTE_BYTES_LEN: Field = 3 * 32 + 64; // 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? @@ -16,7 +18,7 @@ struct PublicKeyNote { npk_m_hash: Field, } -impl NoteInterface for PublicKeyNote { +impl NoteInterface for PublicKeyNote { fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_consumption(self); let secret = context.request_nsk_app(self.npk_m_hash); diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 58656cc2646..c795f68d26c 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -380,7 +380,7 @@ contract Test { value: Field ) -> [u8; 112] { let note = TestNote::new(value); - EncryptedLogIncomingBody::new(storage_slot, TestNote::get_note_type_id(), note).compute_ciphertext(secret, point).as_array() + EncryptedLogIncomingBody::from_note(note, storage_slot).compute_ciphertext(secret, point).as_array() } #[aztec(private)] 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 228f406c63e..6f7fe56fba1 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 @@ -4,6 +4,8 @@ use dep::aztec::{ }; global TEST_NOTE_LEN: Field = 1; +// TEST_NOTE_LENGTH * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global TEST_NOTE_BYTES_LENGTH: Field = 1 * 32 + 64; // 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 @@ -14,7 +16,7 @@ struct TestNote { value: Field, } -impl NoteInterface for TestNote { +impl NoteInterface for TestNote { fn compute_nullifier(self, _context: &mut PrivateContext) -> Field { // This note is expected to be shared between users and for this reason can't be nullified using a secret. 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 dd9c3acf344..9bc7a802ae3 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 @@ -27,18 +27,18 @@ impl BalancesMap { } impl BalancesMap { - unconstrained pub fn balance_of( + unconstrained pub fn balance_of( self: Self, owner: AztecAddress - ) -> U128 where T: NoteInterface + OwnedNote { + ) -> U128 where T: NoteInterface + OwnedNote { self.balance_of_with_offset(owner, 0) } - unconstrained pub fn balance_of_with_offset( + unconstrained pub fn balance_of_with_offset( self: Self, owner: AztecAddress, offset: u32 - ) -> U128 where T: NoteInterface + OwnedNote { + ) -> U128 where T: NoteInterface + OwnedNote { let mut balance = U128::from_integer(0); // docs:start:view_notes let mut options = NoteViewerOptions::new(); @@ -59,11 +59,11 @@ impl BalancesMap { } impl BalancesMap { - pub fn add( + pub fn add( self: Self, owner: AztecAddress, addend: U128 - ) where T: NoteInterface + OwnedNote { + ) where T: NoteInterface + OwnedNote { let context = self.map.context; let owner_ivpk_m = get_ivpk_m(context, owner); @@ -76,11 +76,11 @@ impl BalancesMap { // docs:end:insert } - pub fn sub( + pub fn sub( self: Self, owner: AztecAddress, subtrahend: U128 - ) where T: NoteInterface + OwnedNote { + ) where T: NoteInterface + OwnedNote { // docs:start:get_notes let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend); let maybe_notes = self.map.at(owner).get_notes(options); @@ -112,10 +112,10 @@ impl BalancesMap { } } -pub fn filter_notes_min_sum( +pub fn filter_notes_min_sum( notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], min_sum: U128 -) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where T: NoteInterface + OwnedNote { +) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where T: NoteInterface + OwnedNote { let mut selected = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let mut sum = U128::from_integer(0); for i in 0..notes.len() { 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 ec62873e67b..7d87907a91b 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 @@ -12,6 +12,8 @@ trait OwnedNote { } global TOKEN_NOTE_LEN: Field = 3; // 3 plus a header. +// TOKEN_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global TOKEN_NOTE_BYTES_LEN: Field = 3 * 32 + 64; #[aztec(note)] struct TokenNote { @@ -23,7 +25,7 @@ struct TokenNote { randomness: Field, } -impl NoteInterface for TokenNote { +impl NoteInterface for TokenNote { // docs:start:nullifier fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_consumption(self); 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 061ca460642..f34ce047341 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 @@ -6,6 +6,8 @@ use dep::aztec::{ }; global TRANSPARENT_NOTE_LEN: Field = 2; +// TRANSPARENT_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global TRANSPARENT_NOTE_BYTES_LEN: Field = 2 * 32 + 64; // Transparent note represents a note that is created in the clear (public execution), but can only be spent by those // that know the preimage of the "secret_hash" (the secret). This is typically used when shielding a token balance. @@ -22,7 +24,7 @@ struct TransparentNoteProperties { secret_hash: PropertySelector, } -impl NoteInterface for TransparentNote { +impl NoteInterface for TransparentNote { // Custom serialization to avoid disclosing the secret field fn serialize_content(self) -> [Field; TRANSPARENT_NOTE_LEN] { diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr index 5f45d7279dc..e2aaaadbf9e 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr @@ -27,18 +27,18 @@ impl BalancesMap { } impl BalancesMap { - unconstrained pub fn balance_of( + unconstrained pub fn balance_of( self: Self, owner: AztecAddress - ) -> U128 where T: NoteInterface + OwnedNote { + ) -> U128 where T: NoteInterface + OwnedNote { self.balance_of_with_offset(owner, 0) } - unconstrained pub fn balance_of_with_offset( + unconstrained pub fn balance_of_with_offset( self: Self, owner: AztecAddress, offset: u32 - ) -> U128 where T: NoteInterface + OwnedNote { + ) -> U128 where T: NoteInterface + OwnedNote { let mut balance = U128::from_integer(0); // docs:start:view_notes let mut options = NoteViewerOptions::new(); @@ -59,11 +59,11 @@ impl BalancesMap { } impl BalancesMap { - pub fn add( + pub fn add( self: Self, owner: AztecAddress, addend: U128 - ) where T: NoteInterface + OwnedNote { + ) where T: NoteInterface + OwnedNote { let context = self.map.context; let owner_ivpk_m = get_ivpk_m(context, owner); @@ -76,11 +76,11 @@ impl BalancesMap { // docs:end:insert } - pub fn sub( + pub fn sub( self: Self, owner: AztecAddress, subtrahend: U128 - ) where T: NoteInterface + OwnedNote { + ) where T: NoteInterface + OwnedNote { // docs:start:get_notes let options = NoteGetterOptions::with_filter(filter_notes_min_sum, subtrahend); let maybe_notes = self.map.at(owner).get_notes(options); @@ -112,10 +112,10 @@ impl BalancesMap { } } -pub fn filter_notes_min_sum( +pub fn filter_notes_min_sum( notes: [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], min_sum: U128 -) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where T: NoteInterface + OwnedNote { +) -> [Option; MAX_NOTE_HASH_READ_REQUESTS_PER_CALL] where T: NoteInterface + OwnedNote { let mut selected = [Option::none(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]; let mut sum = U128::from_integer(0); for i in 0..notes.len() { diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr index ec62873e67b..7d87907a91b 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -12,6 +12,8 @@ trait OwnedNote { } global TOKEN_NOTE_LEN: Field = 3; // 3 plus a header. +// TOKEN_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global TOKEN_NOTE_BYTES_LEN: Field = 3 * 32 + 64; #[aztec(note)] struct TokenNote { @@ -23,7 +25,7 @@ struct TokenNote { randomness: Field, } -impl NoteInterface for TokenNote { +impl NoteInterface for TokenNote { // docs:start:nullifier fn compute_nullifier(self, context: &mut PrivateContext) -> Field { let note_hash_for_nullify = compute_note_hash_for_consumption(self); diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr index 061ca460642..f34ce047341 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/transparent_note.nr @@ -6,6 +6,8 @@ use dep::aztec::{ }; global TRANSPARENT_NOTE_LEN: Field = 2; +// TRANSPARENT_NOTE_LEN * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) +global TRANSPARENT_NOTE_BYTES_LEN: Field = 2 * 32 + 64; // Transparent note represents a note that is created in the clear (public execution), but can only be spent by those // that know the preimage of the "secret_hash" (the secret). This is typically used when shielding a token balance. @@ -22,7 +24,7 @@ struct TransparentNoteProperties { secret_hash: PropertySelector, } -impl NoteInterface for TransparentNote { +impl NoteInterface for TransparentNote { // Custom serialization to avoid disclosing the secret field fn serialize_content(self) -> [Field; TRANSPARENT_NOTE_LEN] { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr index 4d272c90cba..e4e2b1825ec 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/traits.nr @@ -105,4 +105,4 @@ impl Serialize for str { trait Deserialize { fn deserialize(fields: [Field; N]) -> Self; } -// docs:end:deserialize +// docs:end:deserialize \ No newline at end of file diff --git a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs index f183c69b27a..fdce8b81db2 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -76,19 +76,27 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt // Identify the note type (struct name), its fields and its serialized length (generic param of NoteInterface trait impl) let note_type = note_struct.name.0.contents.to_string(); let mut note_fields = vec![]; - let note_serialized_len = match &trait_impl.trait_generics[0].typ { - UnresolvedTypeData::Named(path, _, _) => Ok(path.last_segment().0.contents.to_string()), - UnresolvedTypeData::Expression(UnresolvedTypeExpression::Constant(val, _)) => { - Ok(val.to_string()) - } - _ => Err(AztecMacroError::CouldNotImplementNoteInterface { - span: trait_impl.object_type.span, - secondary_message: Some(format!( - "Cannot find note serialization length for: {}", - note_type - )), - }), - }?; + let note_interface_generics = trait_impl + .trait_generics + .iter() + .map(|gen| match gen.typ.clone() { + UnresolvedTypeData::Named(path, _, _) => { + Ok(path.last_segment().0.contents.to_string()) + } + UnresolvedTypeData::Expression(UnresolvedTypeExpression::Constant(val, _)) => { + Ok(val.to_string()) + } + _ => Err(AztecMacroError::CouldNotImplementNoteInterface { + span: trait_impl.object_type.span, + secondary_message: Some(format!( + "NoteInterface must be generic over NOTE_LEN and NOTE_BYTES_LEN: {}", + note_type + )), + }), + }) + .collect::, _>>()?; + let [note_serialized_len, note_bytes_len]: [_; 2] = + note_interface_generics.try_into().unwrap(); let note_type_id = note_type_id(¬e_type); // Automatically inject the header field if it's not present @@ -186,12 +194,73 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt generate_compute_note_content_hash(¬e_type, note_interface_impl_span)?; trait_impl.items.push(TraitImplItem::Function(get_header_fn)); } + + if !check_trait_method_implemented(trait_impl, "to_be_bytes") { + let get_header_fn = generate_note_to_be_bytes( + ¬e_type, + note_bytes_len.as_str(), + note_serialized_len.as_str(), + note_interface_impl_span, + )?; + trait_impl.items.push(TraitImplItem::Function(get_header_fn)); + } } module.types.extend(structs_to_inject); Ok(()) } +fn generate_note_to_be_bytes( + note_type: &String, + byte_length: &str, + serialized_length: &str, + impl_span: Option, +) -> Result { + let function_source = format!( + " + fn to_be_bytes(self: {1}, storage_slot: Field) -> [u8; {0}] {{ + assert({0} == {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; {0}] = [0; {0}]; + + let storage_slot_bytes = storage_slot.to_be_bytes(32); + let note_type_id_bytes = {1}::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]; + }} + + 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 + }} + ", + byte_length, note_type, serialized_length + ) + .to_string(); + + let (function_ast, errors) = parse_program(&function_source); + if !errors.is_empty() { + dbg!(errors); + return Err(AztecMacroError::CouldNotImplementNoteInterface { + secondary_message: Some("Failed to parse Noir macro code (fn to_be_bytes). This is either a bug in the compiler or the Noir macro code".to_string()), + span: impl_span + }); + } + + let mut function_ast = function_ast.into_sorted(); + let mut noir_fn = function_ast.functions.remove(0); + noir_fn.def.span = impl_span.unwrap(); + noir_fn.def.visibility = ItemVisibility::Public; + Ok(noir_fn) +} + fn generate_note_get_header( note_type: &String, note_header_field_name: &String, @@ -209,6 +278,7 @@ fn generate_note_get_header( let (function_ast, errors) = parse_program(&function_source); if !errors.is_empty() { + dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { secondary_message: Some("Failed to parse Noir macro code (fn get_header). This is either a bug in the compiler or the Noir macro code".to_string()), span: impl_span diff --git a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs index 99b02acd606..3e92217ee9c 100644 --- a/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs +++ b/noir/noir-repo/aztec_macros/src/utils/hir_utils.rs @@ -324,7 +324,7 @@ pub fn get_serialized_length( .iter() .find_map(|&trait_id| { let r#trait = interner.get_trait(trait_id); - if r#trait.name.0.contents == trait_name && r#trait.generics.len() == 1 { + if r#trait.name.0.contents == trait_name { interner.lookup_all_trait_implementations(typ, trait_id).into_iter().next() } else { None diff --git a/noir/noir-repo/noir_stdlib/src/aes128.nr b/noir/noir-repo/noir_stdlib/src/aes128.nr index cd61021a953..e6e2a5e4997 100644 --- a/noir/noir-repo/noir_stdlib/src/aes128.nr +++ b/noir/noir-repo/noir_stdlib/src/aes128.nr @@ -2,6 +2,3 @@ // docs:start:aes128 pub fn aes128_encrypt(input: [u8; N], iv: [u8; 16], key: [u8; 16]) -> [u8] {} // docs:end:aes128 - -#[foreign(aes128_encrypt)] -pub fn aes128_encrypt_slice(input: [u8], iv: [u8; 16], key: [u8; 16]) -> [u8] {} diff --git a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts index 44d37fc5962..1ba216d9854 100644 --- a/yarn-project/aztec.js/src/contract/contract_function_interaction.ts +++ b/yarn-project/aztec.js/src/contract/contract_function_interaction.ts @@ -80,9 +80,6 @@ export class ContractFunctionInteraction extends BaseContractInteraction { } const txRequest = await this.create(); - // const from = - // this.functionDao.functionType == FunctionType.PRIVATE ? options.from ?? this.wallet.getAddress() : undefined; - const simulatedTx = await this.wallet.simulateTx(txRequest, true, options?.from); // As account entrypoints are private, for private functions we retrieve the return values from the first nested call