diff --git a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md index 8b01fa45062..02e5b0a5984 100644 --- a/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md +++ b/docs/docs/aztec/concepts/smart_contracts/functions/inner_workings.md @@ -192,7 +192,7 @@ When a struct is annotated with `#[aztec(note)]`, the Aztec macro applies a seri - `serialize_content` and `deserialize_content` - `get_header` and `set_header` - `get_note_type_id` - - `compute_note_content_hash` + - `compute_note_hiding_point` - `to_be_bytes` - A `properties` method in the note's implementation @@ -219,7 +219,7 @@ struct CustomNote { } ``` -### After expansaion +### After expansion ```rust impl CustomNote { @@ -248,10 +248,10 @@ impl CustomNote { self.header = header; } - fn compute_note_content_hash(self: CustomNote) -> Field { - aztec::hash::pedersen_hash( + fn compute_note_hiding_point(self: CustomNote) -> Point { + aztec::hash::pedersen_commitment( self.serialize_content(), - aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH + aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HIDING_POINT ) } diff --git a/docs/docs/guides/smart_contracts/writing_contracts/storage/storage_slots.md b/docs/docs/guides/smart_contracts/writing_contracts/storage/storage_slots.md index 6c40b6a681b..35f384bdd25 100644 --- a/docs/docs/guides/smart_contracts/writing_contracts/storage/storage_slots.md +++ b/docs/docs/guides/smart_contracts/writing_contracts/storage/storage_slots.md @@ -8,7 +8,7 @@ For the case of the example, we will look at what is inserted into the note hash #include_code increase_private_balance noir-projects/noir-contracts/contracts/token_contract/src/main.nr rust -This function is creating a new note and inserting it into the balance set of the recipient `to`. Recall that to ensure privacy, only the note commitment is really inserted into the note hashes tree. To share the contents of the note with `to` the contract can emit an encrypted log (which this one does), or it can require an out-of-band data transfer sharing the information. Below, we will walk through the steps of how the note commitment is computed and inserted into the tree. For this, we don't care about the encrypted log, so we are going to ignore that part of the function call for now. +This function is creating a new note and inserting it into the balance set of the recipient `to`. Recall that to ensure privacy, only the note hash is really inserted into the note hashes tree. To share the contents of the note with `to` the contract can emit an encrypted log (which this one does), or it can require an out-of-band data transfer sharing the information. Below, we will walk through the steps of how the note hash is computed and inserted into the tree. For this, we don't care about the encrypted log, so we are going to ignore that part of the function call for now. Outlining it in more detail below as a sequence diagram, we can see how the calls make their way down the stack. In the end a siloed note hash is computed in the kernel. @@ -30,25 +30,27 @@ sequenceDiagram BalanceSet->>Set: insert(note) Set->>LifeCycle: create_note(derived_slot, note) LifeCycle->>LifeCycle: note.header = NoteHeader { contract_address,
storage_slot: derived_slot, nonce: 0, note_hash_counter } - LifeCycle->>Utils: compute_inner_note_hash(note) - Utils->>TokenNote: note.compute_note_content_hash() - TokenNote->>Utils: note_hash = H(amount, to, randomness) - Utils->>NoteHash: compute_inner_hash(derived_slot, note_hash) - NoteHash->>LifeCycle: inner_note_hash = H(derived_slot, note_hash) - LifeCycle->>Context: push_note_hash(inner_note_hash) + LifeCycle->>Utils: compute_slotted_note_hash(note) + Utils->>TokenNote: note.compute_note_hiding_point() + TokenNote->>Utils: note_hiding_point = MSM([G_amt, G_to, G_rand], [amount, to, randomness]) + Utils->>NoteHash: compute_slotted_note_hash(derived_slot, note_hiding_point) + NoteHash->>LifeCycle: slotted_note_hash = CURVE_ADD(derived_slot_point, note_hiding_point).x + LifeCycle->>Context: push_note_hash(slotted_note_hash) end - Context->>Kernel: siloed_note_hash = H(contract_address, inner_note_hash) + Context->>Kernel: siloed_note_hash = H(contract_address, slotted_note_hash) ``` -Notice the `siloed_note_hash` at the very end. It's a commitment that will be inserted into the note hashes tree. To clarify what this really is, we "unroll" the values to their simplest components. This gives us a better idea around what is actually inserted into the tree. +Notice the `siloed_note_hash` at the very end. It's a hash that will be inserted into the note hashes tree. To clarify what this really is, we "unroll" the values to their simplest components. This gives us a better idea around what is actually inserted into the tree. ```rust -siloed_note_hash = H(contract_address, inner_note_hash) -siloed_note_hash = H(contract_address, H(derived_slot, note_hash)) -siloed_note_hash = H(contract_address, H(H(map_slot, to), note_hash)) -siloed_note_hash = H(contract_address, H(H(map_slot, to), H(amount, to, randomness))) +siloed_note_hash = H(contract_address, slotted_note_hash) +siloed_note_hash = H(contract_address, CURVE_ADD(derived_slot_point, note_hiding_point).x) +siloed_note_hash = H(contract_address, CURVE_ADD(MSM([G_slot], [derived_slot]), note_hiding_point).x) +siloed_note_hash = H(contract_address, CURVE_ADD(MSM([G_slot], [derived_slot]), MSM([G_amt, G_to, G_rand], [amount, to, randomness])).x) ``` +CURVE_ADD is a point addition and MSM is a multi scalar multiplication on a grumpkin curve and G_* values are generators. + And `to` is the actor who receives the note, `amount` of the note and `randomness` is the randomness used to make the note hiding. Without the `randomness` the note could just as well be plaintext (computational cost of a preimage attack would be trivial in such a case). :::info 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 386e2338a6f..66fa4d22c43 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr @@ -29,7 +29,7 @@ impl EncryptedLogHeader { } #[test] -fn test_encrypted_log_header() { +fn test_encrypted_log_header_matches_noir() { let address = AztecAddress::from_field(0xdeadbeef); let header = EncryptedLogHeader::new(address); let secret = Scalar { @@ -44,9 +44,11 @@ fn test_encrypted_log_header() { let ciphertext = header.compute_ciphertext(secret, point); - let expected_header_ciphertext = [ - 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 55, 203, 226, 19, 114, 103, 58, 237, 108, 231, 35, 198, 54, 61, 190, 255, 241, 225, 151, 180, 6, 163, 124, 27, 151, 78, 237, 65, 120, 106, 255, 236 + // The following value was generated by `encrypted_log_header.test.ts`. + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let expected_header_ciphertext_from_typescript = [ + 226, 240, 253, 6, 28, 52, 19, 131, 33, 132, 178, 212, 245, 62, 14, 190, 194, 44, 7, 131, 160, 83, 64, 181, 98, 38, 153, 214, 62, 171, 253, 161, 111, 191, 28, 247, 216, 26, 222, 171, 176, 218, 48, 209, 73, 89, 200, 209 ]; - assert_eq(ciphertext, expected_header_ciphertext); + assert_eq(ciphertext, expected_header_ciphertext_from_typescript); } 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 6b25525a7ae..ab96b9d36bc 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 @@ -37,15 +37,14 @@ impl EncryptedLogIncomingBody { } mod test { - use crate::encrypted_logs::incoming_body::EncryptedLogIncomingBody; use dep::protocol_types::{ - address::AztecAddress, traits::Empty, constants::GENERATOR_INDEX__NOTE_NULLIFIER, - scalar::Scalar, point::Point, traits::Serialize, abis::event_selector::EventSelector + address::AztecAddress, scalar::Scalar, point::Point, traits::Serialize, + abis::event_selector::EventSelector }; use crate::{ note::{note_header::NoteHeader, note_interface::NoteInterface}, - event::event_interface::EventInterface, oracle::unsafe_rand::unsafe_rand, + encrypted_logs::incoming_body::EncryptedLogIncomingBody, event::event_interface::EventInterface, context::PrivateContext }; @@ -60,7 +59,9 @@ mod test { global ADDRESS_NOTE_BYTES_LEN = 32 * 3 + 64; impl NoteInterface for AddressNote { - fn compute_note_content_hash(_self: Self) -> Field {1} + fn compute_note_hiding_point(self) -> Point { + crate::generators::Ga1 + } fn get_note_type_id() -> Field { 1 @@ -112,7 +113,8 @@ mod test { } #[test] - fn test_encrypted_note_log_incoming_body() { + fn test_encrypted_note_log_incoming_body_matches_typescript() { + // All the values in this test were copied over from `encrypted_note_log_incoming_body.test.ts` let note = AddressNote::new( AztecAddress::from_field(0x1), AztecAddress::from_field(0x2), @@ -131,18 +133,25 @@ mod test { is_infinite: false }; + /// 1. `EncryptedLogIncomingBody::from_note` calls `note.to_be_bytes(storage_slot)` function which serializes + /// the note to bytes - note that in the case of `AddressNote` the `to_be_bytes` function was automatically + /// implemented by Aztec macros. let body = EncryptedLogIncomingBody::from_note(note, storage_slot); + /// 2. `body.compute_ciphertext(...)` function then derives symmetric key from `eph_sk` and `ivpk` and encrypts + // the note plaintext using AES-128. let ciphertext = body.compute_ciphertext(eph_sk, ivpk); - let expected_note_body_ciphertext = [ - 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 195, 85, 255, 81, 66, 72, 192, 192, 96, 10, 54, 139, 136, 153, 252, 114, 248, 128, 253, 66, 249, 16, 71, 45, 2, 213, 250, 193, 241, 75, 90, 70, 39, 26, 104, 139, 20, 45, 1, 1, 166, 72, 133, 55, 247, 142, 150, 215, 217, 224, 84, 23, 245, 71, 207, 166, 136, 34, 221, 76, 90, 166, 44, 217, 246, 98, 157, 34, 198, 164, 99, 117, 15, 185, 145, 231, 189, 140, 201, 241, 135, 94, 71, 131, 156, 86, 144, 131, 248, 242, 83, 101, 18, 189, 1, 94, 25, 238, 76, 106, 85, 205, 4, 70, 21, 9, 64, 63, 27, 164, 73, 181, 75, 199, 86, 255, 105, 239, 216, 34, 217, 184, 154, 76, 67, 1, 210, 251, 23, 185, 114, 146, 195, 28, 76, 219, 150, 175, 37, 76, 144, 227, 99, 243, 123, 161, 66, 171, 148, 181, 162, 2, 196, 53, 207, 154, 114, 166, 155, 166 + // The following value was generated by `encrypted_note_log_incoming_body.test.ts`. + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let note_body_ciphertext_from_typescript = [ + 226, 240, 253, 6, 28, 52, 19, 131, 33, 132, 178, 212, 245, 62, 14, 190, 147, 228, 160, 190, 146, 61, 95, 203, 124, 153, 68, 168, 17, 150, 92, 0, 99, 214, 85, 64, 191, 78, 157, 131, 149, 96, 236, 253, 96, 172, 157, 30, 27, 176, 228, 74, 242, 190, 138, 48, 33, 93, 46, 37, 223, 130, 25, 245, 188, 163, 159, 223, 187, 24, 139, 206, 131, 154, 159, 130, 37, 17, 158, 114, 242, 141, 124, 193, 232, 54, 146, 96, 145, 100, 125, 234, 57, 43, 95, 115, 183, 39, 121, 232, 134, 229, 148, 25, 46, 77, 87, 127, 95, 7, 77, 188, 37, 234, 245, 142, 232, 87, 252, 28, 67, 67, 90, 214, 254, 89, 47, 68, 66, 187, 227, 8, 59, 162, 25, 141, 97, 141, 217, 197, 115, 15, 212, 202, 157, 41, 150, 62, 219, 57, 224, 92, 185, 212, 142, 94, 146, 41, 178, 145, 68, 169, 23, 185, 206, 138, 70, 47, 176, 210, 165, 236, 23, 206, 229, 108 ]; - assert_eq(expected_note_body_ciphertext.len(), ciphertext.len()); + assert_eq(note_body_ciphertext_from_typescript.len(), ciphertext.len()); - for i in 0..expected_note_body_ciphertext.len() { - assert_eq(ciphertext[i], expected_note_body_ciphertext[i]); + for i in 0..note_body_ciphertext_from_typescript.len() { + assert_eq(ciphertext[i], note_body_ciphertext_from_typescript[i]); } } @@ -237,14 +246,16 @@ mod test { let ciphertext = body.compute_ciphertext(eph_sk, ivpk); - let expected_event_body_ciphertext = [ - 166, 212, 106, 246, 139, 59, 228, 9, 133, 152, 127, 172, 141, 166, 237, 199, 195, 85, 255, 81, 66, 72, 192, 192, 96, 10, 54, 139, 136, 153, 252, 114, 248, 128, 253, 66, 249, 16, 71, 45, 2, 213, 250, 193, 241, 75, 90, 70, 19, 153, 62, 117, 71, 55, 48, 114, 160, 232, 97, 118, 93, 53, 145, 92, 0, 225, 51, 81, 156, 69, 72, 224, 10, 89, 32, 121, 167, 197, 84, 245, 188, 235, 143, 202, 179, 197, 164, 121, 11, 105, 116, 239, 46, 222, 50, 138, 112, 237, 97, 8, 176, 199, 1, 151, 89, 218, 60, 45, 91, 85, 16, 38, 195, 127, 157, 182, 0, 10, 232, 184, 148, 76, 244, 63, 40, 222, 219, 139, 236, 169, 213, 17, 32, 210, 50, 6, 5, 83, 80, 1, 111, 246, 197, 83, 166, 71, 31, 246, 234, 75, 12, 151, 227, 247, 143, 229, 95, 219, 159, 75, 174, 232, 64, 7, 102, 76, 207, 45, 143, 208, 101, 113, 175, 37, 83, 166 + // The following value was generated by `encrypted_event_log_incoming_body.test.ts` + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let event_body_ciphertext_from_typescript = [ + 226, 240, 253, 6, 28, 52, 19, 131, 33, 132, 178, 212, 245, 62, 14, 190, 147, 228, 160, 190, 146, 61, 95, 203, 124, 153, 68, 168, 17, 150, 92, 0, 99, 214, 85, 64, 191, 78, 157, 131, 149, 96, 236, 253, 96, 172, 157, 30, 185, 29, 14, 152, 216, 130, 219, 151, 80, 185, 43, 223, 167, 8, 89, 189, 88, 188, 101, 137, 255, 136, 84, 252, 79, 18, 52, 3, 110, 54, 54, 206, 244, 209, 246, 226, 207, 247, 143, 253, 211, 75, 160, 224, 172, 41, 45, 7, 208, 137, 90, 56, 59, 4, 234, 48, 53, 23, 130, 230, 49, 249, 142, 243, 170, 72, 183, 242, 49, 124, 46, 52, 198, 75, 55, 102, 56, 89, 254, 67, 59, 157, 249, 120, 184, 67, 154, 16, 148, 227, 93, 37, 120, 199, 93, 166, 80, 127, 173, 52, 80, 135, 87, 1, 168, 164, 51, 48, 126, 120, 47, 102, 211, 227, 234, 170, 208, 99, 111, 198, 170, 226, 156, 244, 241, 174, 206, 30 ]; - assert_eq(expected_event_body_ciphertext.len(), ciphertext.len()); + assert_eq(event_body_ciphertext_from_typescript.len(), ciphertext.len()); - for i in 0..expected_event_body_ciphertext.len() { - assert_eq(ciphertext[i], expected_event_body_ciphertext[i]); + for i in 0..event_body_ciphertext_from_typescript.len() { + assert_eq(ciphertext[i], event_body_ciphertext_from_typescript[i]); } } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr index 59b6268a816..06de95353a4 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr @@ -17,6 +17,8 @@ impl EncryptedLogOutgoingBody { Self { eph_sk, recipient, recipient_ivpk } } + /// Encrypts ephemeral secret key and recipient's ivpk --> with this information the recipient of outgoing will + /// be able to derive the key with which the incoming log can be decrypted. pub fn compute_ciphertext(self, ovsk_app: Scalar, eph_pk: Point) -> [u8; 144] { // Again, we could compute `eph_pk` here, but we keep the interface more similar // and also make it easier to optimise it later as we just pass it along @@ -68,7 +70,7 @@ mod test { use crate::context::PrivateContext; #[test] - fn test_encrypted_log_outgoing_body() { + fn test_encrypted_log_outgoing_body_matches_typescript() { let eph_sk = Scalar { lo: 0x00000000000000000000000000000000d0d302ee245dfaf2807e604eec4715fe, hi: 0x000000000000000000000000000000000f096b423017226a18461115fa8d34bb @@ -91,13 +93,15 @@ mod test { let ciphertext = body.compute_ciphertext(sender_ovsk_app, eph_pk); - let expected_outgoing_body_ciphertext = [ - 127, 84, 96, 176, 101, 107, 236, 57, 68, 8, 53, 202, 138, 74, 186, 54, 74, 193, 245, 7, 109, 59, 218, 33, 1, 31, 205, 225, 241, 209, 64, 222, 94, 245, 4, 150, 47, 241, 187, 64, 152, 20, 102, 158, 200, 217, 213, 82, 1, 240, 170, 185, 51, 80, 27, 109, 63, 231, 235, 120, 174, 44, 133, 248, 10, 97, 60, 40, 222, 190, 147, 76, 187, 48, 91, 206, 48, 106, 56, 118, 38, 127, 82, 4, 182, 188, 44, 224, 31, 129, 47, 107, 134, 252, 20, 25, 249, 193, 215, 137, 195, 43, 98, 42, 54, 96, 254, 89, 134, 31, 103, 142, 16, 43, 92, 211, 145, 113, 217, 253, 161, 240, 121, 205, 146, 200, 168, 160, 221, 32, 229, 116, 26, 216, 86, 189, 78, 120, 10, 224, 85, 52, 40, 244 + // The following value was generated by `encrypted_log_outgoing_body.test.ts` + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let outgoing_body_ciphertext_from_typescript = [ + 126, 10, 214, 39, 130, 143, 96, 143, 79, 143, 22, 36, 55, 41, 234, 255, 226, 26, 138, 236, 91, 188, 204, 216, 172, 133, 134, 69, 161, 237, 134, 5, 75, 192, 10, 6, 229, 54, 194, 56, 103, 243, 57, 248, 147, 237, 4, 3, 39, 28, 226, 30, 237, 228, 212, 115, 246, 244, 105, 39, 129, 119, 126, 207, 176, 14, 75, 134, 241, 23, 2, 187, 239, 86, 47, 56, 239, 20, 92, 176, 70, 12, 219, 226, 150, 70, 192, 43, 125, 53, 230, 153, 135, 228, 210, 197, 227, 106, 242, 138, 119, 83, 182, 150, 233, 111, 9, 104, 128, 222, 85, 136, 205, 244, 77, 230, 210, 217, 223, 106, 220, 4, 115, 33, 157, 212, 217, 133, 87, 179, 67, 158, 81, 85, 226, 105, 22, 8, 154, 130, 193, 214, 144, 212 ]; - for i in 0..expected_outgoing_body_ciphertext.len() { - assert_eq(ciphertext[i], expected_outgoing_body_ciphertext[i]); + for i in 0..outgoing_body_ciphertext_from_typescript.len() { + assert_eq(ciphertext[i], outgoing_body_ciphertext_from_typescript[i]); } - assert_eq(expected_outgoing_body_ciphertext.len(), ciphertext.len()); + assert_eq(outgoing_body_ciphertext_from_typescript.len(), ciphertext.len()); } } diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr index 4c7601db80c..5b3583b7fbd 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -1,19 +1,16 @@ -use dep::protocol_types::{ - address::AztecAddress, scalar::Scalar, point::Point, - constants::{GENERATOR_INDEX__IVSK_M, GENERATOR_INDEX__OVSK_M}, hash::poseidon2_hash +use dep::protocol_types::{address::AztecAddress, scalar::Scalar, point::Point}; +use std::{ + embedded_curve_ops::fixed_base_scalar_mul as derive_public_key, + hash::from_field_unsafe as fr_to_fq_unsafe, field::bn254::decompose }; -use std::embedded_curve_ops::fixed_base_scalar_mul as derive_public_key; -use std::field::bytes32_to_field; -use crate::oracle::unsafe_rand::unsafe_rand; -use crate::utils::point::point_to_bytes; - -use crate::event::event_interface::EventInterface; -use crate::note::note_interface::NoteInterface; - -use crate::encrypted_logs::{ +use crate::{ + event::event_interface::EventInterface, oracle::unsafe_rand::unsafe_rand, + utils::point::point_to_bytes, note::note_interface::NoteInterface, + encrypted_logs::{ header::EncryptedLogHeader, incoming_body::EncryptedLogIncomingBody, outgoing_body::EncryptedLogOutgoingBody +} }; pub fn compute_encrypted_event_log( @@ -25,9 +22,7 @@ pub fn compute_encrypted_event_log recipient: AztecAddress, event: Event ) -> [u8; OB] where Event: EventInterface { - // @todo Need to draw randomness from the full domain of Fq not only Fr - let eph_sk: Scalar = fr_to_fq(unsafe_rand()); - let eph_pk = derive_public_key(eph_sk); + let (eph_sk, eph_pk) = generate_ephemeral_key_pair(); let header = EncryptedLogHeader::new(contract_address); @@ -78,9 +73,7 @@ pub fn compute_encrypted_note_log( recipient: AztecAddress, note: Note ) -> [u8; M] where Note: NoteInterface { - // @todo Need to draw randomness from the full domain of Fq not only Fr - let eph_sk: Scalar = fr_to_fq(unsafe_rand()); - let eph_pk = derive_public_key(eph_sk); + let (eph_sk, eph_pk) = generate_ephemeral_key_pair(); let header = EncryptedLogHeader::new(contract_address); @@ -122,23 +115,22 @@ pub fn compute_encrypted_note_log( encrypted_bytes } -/// Converts a base field elememt to scalar field element. +/// Converts a base field element to scalar field element. /// This is fine because modulus of the base field is smaller than the modulus of the scalar field. fn fr_to_fq(r: Field) -> Scalar { - let r_bytes = r.to_be_bytes(32); + let (lo, hi) = decompose(r); - let mut high_bytes = [0; 32]; - let mut low_bytes = [0; 32]; - - for i in 0..16 { - high_bytes[16 + i] = r_bytes[i]; - low_bytes[16 + i] = r_bytes[i + 16]; - } + Scalar { lo, hi } +} - let lo = bytes32_to_field(low_bytes); - let hi = bytes32_to_field(high_bytes); +fn generate_ephemeral_key_pair() -> (Scalar, Point) { + // @todo Need to draw randomness from the full domain of Fq not only Fr + // We use the unsafe version of `fr_to_fq` because multi_scalar_mul (called by derive_public_key) will constrain + // the scalars. + let eph_sk = fr_to_fq_unsafe(unsafe_rand()); + let eph_pk = derive_public_key(eph_sk); - Scalar { lo, hi } + (eph_sk, eph_pk) } mod test { @@ -147,7 +139,8 @@ mod test { use std::test::OracleMock; #[test] - fn test_compute_encrypted_note_log() { + fn test_encrypted_note_log_matches_typescript() { + // All the values in this test were copied over from `tagged_log.test.ts` let contract_address = AztecAddress::from_field(0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04); let storage_slot = 0x0fe46be583b71f4ab5b70c2657ff1d05cccf1d292a9369628d1a194f944e6599; let ovsk_app = 0x1b99ba138fa7ef8a2f122a98dd80c8ee70d447218dd780f45e165ac17ca38a5e; @@ -180,12 +173,14 @@ mod test { note ); - let expected_encrypted_note_log = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 159, 53, 114, 117, 237, 57, 131, 19, 111, 150, 50, 83, 173, 155, 234, 225, 71, 187, 141, 79, 245, 43, 111, 83, 219, 149, 124, 68, 12, 244, 253, 216, 0, 62, 108, 232, 118, 80, 87, 140, 215, 185, 111, 48, 128, 236, 110, 92, 46, 205, 7, 226, 131, 66, 205, 0, 103, 83, 217, 90, 60, 138, 6, 172, 246, 129, 92, 172, 69, 73, 77, 65, 147, 18, 231, 20, 35, 217, 180, 253, 72, 242, 32, 57, 45, 11, 2, 235, 24, 96, 244, 224, 33, 61, 151, 225, 136, 173, 178, 40, 2, 125, 229, 20, 220, 82, 28, 191, 147, 133, 137, 1, 45, 243, 229, 140, 115, 165, 150, 154, 96, 22, 120, 223, 237, 213, 182, 252, 192, 8, 132, 43, 21, 56, 243, 116, 144, 182, 75, 16, 30, 222, 222, 60, 205, 147, 214, 53, 41, 62, 53, 16, 147, 117, 72, 169, 220, 125, 208, 210, 45, 65, 233, 40, 87, 88, 140, 237, 200, 161, 9, 86, 82, 128, 191, 51, 4, 195, 243, 100, 102, 240, 54, 129, 176, 116, 139, 73, 27, 98, 222, 1, 243, 199, 72, 238, 213, 66, 91, 159, 183, 143, 36, 103, 94, 5, 62, 50, 13, 217, 161, 79, 30, 231, 41, 228, 109, 139, 243, 119, 166, 54, 37, 250, 193, 6, 67, 29, 148, 185, 153, 58, 64, 210, 164, 219, 165, 80, 35, 75, 109, 177, 14, 168, 136, 105, 21, 235, 62, 159, 71, 61, 245, 193, 234, 169, 100, 165, 8, 222, 157, 239, 41, 221, 223, 67, 80, 61, 252, 54, 27, 100, 1, 104, 2, 121, 62, 41, 23, 132, 15, 124, 120, 21, 198, 113, 151, 172, 42, 161, 64, 240, 166, 205, 80, 169, 58, 191, 111, 130, 55, 58, 141, 26, 97, 118, 114, 216, 69, 207, 212, 227, 250, 199, 21, 72, 144, 85, 43, 76, 213, 28, 132, 134, 16, 221, 105, 112, 82, 238, 114, 61, 36, 144, 179, 178, 68, 198, 162, 212, 85, 100, 116, 186, 131, 232, 33, 229, 101, 251, 5, 251 + // The following value was generated by `tagged_log.test.ts` + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let encrypted_note_log_from_typescript = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 141, 70, 12, 14, 67, 77, 132, 110, 193, 234, 40, 110, 64, 144, 235, 86, 55, 111, 242, 123, 221, 193, 170, 202, 225, 216, 86, 84, 159, 112, 31, 167, 126, 79, 51, 186, 47, 71, 253, 172, 99, 112, 241, 59, 197, 241, 107, 186, 232, 87, 187, 230, 171, 62, 228, 234, 42, 51, 145, 146, 238, 242, 42, 71, 206, 13, 244, 66, 111, 195, 20, 203, 98, 148, 204, 242, 145, 183, 156, 29, 141, 54, 44, 220, 194, 35, 229, 16, 32, 204, 211, 49, 142, 112, 82, 202, 116, 241, 254, 146, 42, 217, 20, 189, 70, 228, 182, 171, 205, 104, 27, 99, 171, 28, 91, 244, 21, 30, 130, 240, 5, 72, 174, 124, 97, 197, 157, 248, 204, 203, 140, 171, 181, 152, 130, 169, 179, 41, 52, 173, 45, 43, 198, 1, 152, 72, 158, 249, 11, 41, 9, 160, 48, 78, 123, 132, 203, 140, 215, 13, 22, 201, 88, 255, 139, 154, 76, 20, 63, 134, 125, 108, 239, 208, 63, 59, 33, 117, 139, 225, 184, 0, 64, 153, 21, 131, 204, 111, 41, 84, 23, 144, 222, 245, 200, 12, 234, 11, 48, 10, 221, 20, 252, 38, 122, 40, 249, 66, 248, 197, 198, 209, 79, 20, 59, 66, 197, 215, 16, 18, 145, 228, 239, 124, 81, 67, 103, 49, 196, 58, 228, 195, 64, 199, 243, 184, 112, 173, 29, 196, 215, 77, 217, 85, 82, 149, 113, 76, 201, 93, 95, 148, 37, 95, 222, 233, 210, 150, 1, 182, 28, 132, 59, 148, 156, 129, 36, 230, 55, 199, 149, 36, 205, 103, 212, 60, 151, 141, 10, 151, 222, 151, 180, 43, 91, 148, 201, 110, 165, 10, 238, 32, 134, 235, 99, 216, 200, 182, 31, 22, 156, 18, 209, 222, 172, 239, 193, 212, 86, 99, 62, 70, 182, 45, 175, 241, 91, 202, 179, 225, 99, 1, 150, 232, 2, 252, 20, 83, 49, 132, 162, 93, 116, 212, 87, 71, 211, 58, 159, 163, 40, 253, 31, 3, 192, 48, 14, 201, 80, 24, 135, 154, 207, 58, 140, 128, 29, 101, 207, 189, 182, 191, 71, 210, 64, 172, 131, 83, 46, 232, 19, 216, 183, 108, 234, 17, 104, 60, 113, 231, 145, 195, 157, 24 ]; - for i in 0..expected_encrypted_note_log.len() { - assert_eq(log[i], expected_encrypted_note_log[i]); + for i in 0..encrypted_note_log_from_typescript.len() { + assert_eq(log[i], encrypted_note_log_from_typescript[i]); } - assert_eq(expected_encrypted_note_log.len(), log.len()); + assert_eq(encrypted_note_log_from_typescript.len(), log.len()); } } diff --git a/noir-projects/aztec-nr/aztec/src/generators.nr b/noir-projects/aztec-nr/aztec/src/generators.nr index 02eae3307fb..7af58db4602 100644 --- a/noir-projects/aztec-nr/aztec/src/generators.nr +++ b/noir-projects/aztec-nr/aztec/src/generators.nr @@ -4,17 +4,28 @@ use dep::protocol_types::point::Point; global Ga1 = Point { x: 0x30426e64aee30e998c13c8ceecda3a77807dbead52bc2f3bf0eae851b4b710c1, y: 0x113156a068f603023240c96b4da5474667db3b8711c521c748212a15bc034ea6, is_infinite: false }; global Ga2 = Point { x: 0x2825c79cc6a5cbbeef7d6a8f1b6a12b312aa338440aefeb4396148c89147c049, y: 0x129bfd1da54b7062d6b544e7e36b90736350f6fba01228c41c72099509f5701e, is_infinite: false }; global Ga3 = Point { x: 0x0edb1e293c3ce91bfc04e3ceaa50d2c541fa9d091c72eb403efb1cfa2cb3357f, y: 0x1341d675fa030ece3113ad53ca34fd13b19b6e9762046734f414824c4d6ade35, is_infinite: false }; +// If you change this update `G_SLOT` in `yarn-project/simulator/src/client/test_utils.ts` as well +global G_slot = Point { x: 0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15, y: 0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791, is_infinite: false }; + +// TODO(#7551): nuke this func - is only temporarily used in note_interface.rs before we get some AVM compatible +// hash func hashing to a point +pub fn field_to_point(slot_preimage: Field) -> Point { + // We use the unsafe version because the multi_scalar_mul will constrain the scalars. + let slot_preimage_scalar = dep::std::hash::from_field_unsafe(slot_preimage); + dep::std::embedded_curve_ops::multi_scalar_mul([G_slot], [slot_preimage_scalar]) +} mod test { - use crate::generators::{Ga1, Ga2, Ga3}; + use crate::generators::{Ga1, Ga2, Ga3, G_slot}; use dep::protocol_types::point::Point; use std::hash::derive_generators; #[test] -fn test_generators() { - let generators: [Point; 3] = derive_generators("aztec_nr_generators".as_bytes(), 0); + fn test_generators() { + let generators: [Point; 4] = derive_generators("aztec_nr_generators".as_bytes(), 0); assert_eq(generators[0], Ga1); assert_eq(generators[1], Ga2); assert_eq(generators[2], Ga3); + assert_eq(generators[3], G_slot); } } diff --git a/noir-projects/aztec-nr/aztec/src/history/public_storage.nr b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr index 6f1922122e7..c07c3d15804 100644 --- a/noir-projects/aztec-nr/aztec/src/history/public_storage.nr +++ b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr @@ -13,7 +13,7 @@ trait PublicStorageHistoricalRead { impl PublicStorageHistoricalRead for Header { fn public_storage_historical_read(self, storage_slot: Field, contract_address: AztecAddress) -> Field { // 1) Compute the leaf slot by siloing the storage slot with the contract address - let public_value_leaf_slot = pedersen_hash( + let public_data_tree_index = pedersen_hash( [contract_address.to_field(), storage_slot], GENERATOR_INDEX__PUBLIC_LEAF_INDEX ); @@ -21,7 +21,7 @@ impl PublicStorageHistoricalRead for Header { // 2) Get the membership witness of the slot let witness = get_public_data_witness( self.global_variables.block_number as u32, - public_value_leaf_slot + public_data_tree_index ); // 3) Extract the value from the witness leaf and check that the storage slot is correct @@ -30,15 +30,15 @@ impl PublicStorageHistoricalRead for Header { // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` // 1. The value is the same as the one in the witness // 2. The value was never initialized and is zero - let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); - let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); + let is_less_than_slot = full_field_less_than(preimage.slot, public_data_tree_index); + let is_next_greater_than = full_field_less_than(public_data_tree_index, preimage.next_slot); let is_max = ((preimage.next_index == 0) & (preimage.next_slot == 0)); let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); let value = if is_in_range { 0 } else { - assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot doesn't match witness"); + assert_eq(preimage.slot, public_data_tree_index, "Public data tree index doesn't match witness"); preimage.value }; diff --git a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr index f84dd3fe861..fc27829d578 100644 --- a/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr +++ b/noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr @@ -6,7 +6,6 @@ use std::{hash::sha256, embedded_curve_ops::multi_scalar_mul}; // point is not the only input of the function. Unify naming with TS once we have a better name. pub fn point_to_symmetric_key(secret: Scalar, point: Point) -> [u8; 32] { let shared_secret: Point = multi_scalar_mul([point], [secret]); - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/6061): make the func return Point struct directly let shared_secret = point_to_bytes(shared_secret); let mut shared_secret_bytes_with_separator = [0 as u8; 33]; shared_secret_bytes_with_separator = arr_copy_slice(shared_secret, shared_secret_bytes_with_separator, 0); @@ -15,7 +14,7 @@ pub fn point_to_symmetric_key(secret: Scalar, point: Point) -> [u8; 32] { } #[test] -fn check_point_to_symmetric_key() { +fn test_point_to_symmetric_key_matches_noir() { // Value taken from "derive shared secret" test in encrypt_buffer.test.ts let secret = Scalar { lo: 0x00000000000000000000000000000000649e7ca01d9de27b21624098b897babd, @@ -28,9 +27,11 @@ fn check_point_to_symmetric_key() { }; let key = point_to_symmetric_key(secret, point); - // The following value gets updated when running encrypt_buffer.test.ts with AZTEC_GENERATE_TEST_DATA=1 - let expected_key = [ - 217, 245, 196, 116, 55, 39, 202, 184, 117, 231, 19, 56, 102, 254, 94, 14, 172, 169, 123, 96, 61, 247, 209, 140, 4, 132, 119, 222, 79, 1, 154, 136 + + // The following value was generated by `encrypt_buffer.test.ts`. + // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. + let key_from_typescript = [ + 251, 232, 177, 34, 2, 174, 35, 92, 165, 118, 168, 3, 153, 140, 46, 210, 203, 154, 184, 158, 236, 33, 95, 77, 93, 120, 72, 88, 190, 209, 64, 159 ]; - assert_eq(key, expected_key); + assert_eq(key, key_from_typescript); } diff --git a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr index 2209157faf9..649d9fe1c54 100644 --- a/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr +++ b/noir-projects/aztec-nr/aztec/src/note/lifecycle.nr @@ -1,7 +1,7 @@ use crate::context::{PrivateContext, PublicContext}; use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::{compute_inner_note_hash, compute_note_hash_for_consumption}, note_emission::NoteEmission + utils::{compute_slotted_note_hash, compute_note_hash_for_consumption}, note_emission::NoteEmission }; use crate::oracle::notes::{notify_created_note, notify_nullified_note}; @@ -15,7 +15,7 @@ pub fn create_note( let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter }; note.set_header(header); - let inner_note_hash = compute_inner_note_hash(*note); + let slotted_note_hash = compute_slotted_note_hash(*note); let serialized_note = Note::serialize_content(*note); assert( @@ -23,13 +23,13 @@ pub fn create_note( storage_slot, Note::get_note_type_id(), serialized_note, - inner_note_hash, + slotted_note_hash, note_hash_counter ) == 0 ); - context.push_note_hash(inner_note_hash); + context.push_note_hash(slotted_note_hash); NoteEmission::new(*note) } @@ -43,9 +43,9 @@ pub fn create_note_hash_from_public( // Public note hashes are transient, but have no side effect counters, so we just need note_hash_counter != 0 let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter: 1 }; note.set_header(header); - let inner_note_hash = compute_inner_note_hash(*note); + let slotted_note_hash = compute_slotted_note_hash(*note); - context.push_note_hash(inner_note_hash); + context.push_note_hash(slotted_note_hash); } pub fn destroy_note( diff --git a/noir-projects/aztec-nr/aztec/src/note/note_header.nr b/noir-projects/aztec-nr/aztec/src/note/note_header.nr index 1cec738dd5c..3db3cc5b62c 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_header.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_header.nr @@ -1,5 +1,6 @@ -use dep::protocol_types::address::AztecAddress; -use dep::protocol_types::traits::{Empty, Eq, Serialize}; +use dep::protocol_types::{address::AztecAddress, traits::{Empty, Eq, Serialize}}; + +global NOTE_HEADER_LENGTH: Field = 4; struct NoteHeader { contract_address: AztecAddress, @@ -31,8 +32,14 @@ impl NoteHeader { } } -impl Serialize<4> for NoteHeader { - fn serialize(self) -> [Field; 4] { +impl Serialize for NoteHeader { + /// The following method is used by implementations of the Serialize trait for notes --> the implementation + // of the Serialize trait for notes needs to be implemented when the note is passed as an argument to a contract + // function --> in that situation the serialize method is called by aztec-nr when computing an arguments hash. + fn serialize(self) -> [Field; NOTE_HEADER_LENGTH] { + // Note: If you change this function don't forget to update implementations of Serialize trait for notes. + // (Serialize trait needs to be implemented for a note when it's passed as an argument to a contract function + // --> then it's used when computing args hash.) [self.contract_address.to_field(), self.nonce, self.storage_slot, self.note_hash_counter as Field] } } 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 317059d90e9..53afa05820c 100644 --- a/noir-projects/aztec-nr/aztec/src/note/note_interface.nr +++ b/noir-projects/aztec-nr/aztec/src/note/note_interface.nr @@ -1,5 +1,6 @@ use crate::context::PrivateContext; use crate::note::note_header::NoteHeader; +use dep::protocol_types::point::Point; // docs:start:note_interface trait NoteInterface { @@ -14,7 +15,7 @@ trait NoteInterface { fn deserialize_content(fields: [Field; N]) -> Self; // Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation - fn compute_note_content_hash(self) -> Field; + fn compute_note_hiding_point(self) -> Point; // Autogenerated by the #[aztec(note)] macro unless it is overridden by a custom implementation fn get_header(self) -> NoteHeader; diff --git a/noir-projects/aztec-nr/aztec/src/note/utils.nr b/noir-projects/aztec-nr/aztec/src/note/utils.nr index dfe87cc4346..5e8ff069687 100644 --- a/noir-projects/aztec-nr/aztec/src/note/utils.nr +++ b/noir-projects/aztec-nr/aztec/src/note/utils.nr @@ -1,26 +1,32 @@ -use crate::{context::PrivateContext, note::{note_header::NoteHeader, note_interface::NoteInterface}}; +use crate::{ + context::PrivateContext, generators::G_slot, + note::{note_header::NoteHeader, note_interface::NoteInterface} +}; use dep::protocol_types::{ - constants::GENERATOR_INDEX__INNER_NOTE_HASH, hash::{ - pedersen_hash, compute_unique_note_hash, compute_siloed_note_hash as compute_siloed_note_hash, + compute_unique_note_hash, compute_siloed_note_hash as compute_siloed_note_hash, compute_siloed_nullifier as compute_siloed_nullifier_from_preimage }, - utils::arr_copy_slice + point::Point, utils::arr_copy_slice }; +use dep::std::{embedded_curve_ops::multi_scalar_mul, hash::from_field_unsafe}; + +pub fn compute_slotted_note_hash_raw(storage_slot: Field, note_hiding_point: Point) -> Field { + // We use the unsafe version because the multi_scalar_mul will constrain the scalars. + let storage_slot_scalar = from_field_unsafe(storage_slot); + let storage_slot_point = multi_scalar_mul([G_slot], [storage_slot_scalar]); + let slotted_note_hiding_point = storage_slot_point + note_hiding_point; + let slotted_note_hash = slotted_note_hiding_point.x; -pub fn compute_inner_note_hash_from_preimage(storage_slot: Field, note_content_hash: Field) -> Field { - pedersen_hash( - [storage_slot, note_content_hash], - GENERATOR_INDEX__INNER_NOTE_HASH - ) + slotted_note_hash } -pub fn compute_inner_note_hash(note: Note) -> Field where Note: NoteInterface { +pub fn compute_slotted_note_hash(note: Note) -> Field where Note: NoteInterface { let header = note.get_header(); - let note_hash = note.compute_note_content_hash(); + let note_hash = note.compute_note_hiding_point(); - compute_inner_note_hash_from_preimage(header.storage_slot, note_hash) + compute_slotted_note_hash_raw(header.storage_slot, note_hash) } pub fn compute_siloed_nullifier( @@ -33,24 +39,21 @@ pub fn compute_siloed_nullifier( compute_siloed_nullifier_from_preimage(header.contract_address, inner_nullifier) } -fn compute_note_hash_for_read_request_from_innter_and_nonce( - inner_note_hash: Field, - nonce: Field -) -> Field { +fn compute_note_hash_for_read_request_from_slotted_and_nonce(slotted_note_hash: Field, nonce: Field) -> Field { // TODO(#1386): This if-else can be nuked once we have nonces injected from public if (nonce == 0) { // If nonce is zero, that means we are reading a public note. - inner_note_hash + slotted_note_hash } else { - compute_unique_note_hash(nonce, inner_note_hash) + compute_unique_note_hash(nonce, slotted_note_hash) } } pub fn compute_note_hash_for_read_request(note: Note) -> Field where Note: NoteInterface { - let inner_note_hash = compute_inner_note_hash(note); + let slotted_note_hash = compute_slotted_note_hash(note); let nonce = note.get_header().nonce; - compute_note_hash_for_read_request_from_innter_and_nonce(inner_note_hash, nonce) + compute_note_hash_for_read_request_from_slotted_and_nonce(slotted_note_hash, nonce) } pub fn compute_note_hash_for_consumption(note: Note) -> Field where Note: NoteInterface { @@ -60,17 +63,17 @@ pub fn compute_note_hash_for_consumption(note: Not // 2. The note was inserted in a previous transaction, and was inserted in public // 3. The note was inserted in a previous transaction, and was inserted in private - let inner_note_hash = compute_inner_note_hash(note); + let slotted_note_hash = compute_slotted_note_hash(note); if (header.note_hash_counter != 0) { - // If a note is transient, we just read the inner_note_hash (kernel will silo by contract address). - inner_note_hash + // If a note is transient, we just read the slotted_note_hash (kernel will silo by contract address). + slotted_note_hash } else { // If a note is not transient, that means we are reading a settled note (from tree) created in a // previous TX. So we need the siloed_note_hash which has already been hashed with // nonce and then contract address. This hash will match the existing leaf in the note hash // tree, so the kernel can just perform a membership check directly on this hash/leaf. - let unique_note_hash = compute_note_hash_for_read_request_from_innter_and_nonce(inner_note_hash, header.nonce); + let unique_note_hash = compute_note_hash_for_read_request_from_slotted_and_nonce(slotted_note_hash, header.nonce); compute_siloed_note_hash(header.contract_address, unique_note_hash) // IMPORTANT NOTE ON REDUNDANT SILOING BY CONTRACT ADDRESS: The note hash computed above is // "siloed" by contract address. When a note hash is computed solely for the purpose of @@ -92,8 +95,8 @@ pub fn compute_note_hash_and_optionally_a_nullifier [Field; PUBLIC_DATA_WITNESS] {} -unconstrained pub fn get_public_data_witness(block_number: u32, leaf_slot: Field) -> PublicDataWitness { - let fields = get_public_data_witness_oracle(block_number, leaf_slot); +unconstrained pub fn get_public_data_witness(block_number: u32, public_data_tree_index: Field) -> PublicDataWitness { + let fields = get_public_data_witness_oracle(block_number, public_data_tree_index); PublicDataWitness { index: fields[0], leaf_preimage: PublicDataTreeLeafPreimage { slot: fields[1], value: fields[2], next_index: fields[3] as u32, next_slot: fields[4] }, diff --git a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr index 9b7755fd135..5fd6908000a 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/notes.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/notes.nr @@ -7,7 +7,7 @@ unconstrained fn notify_created_note_oracle( _storage_slot: Field, _note_type_id: Field, _serialized_note: [Field; N], - _inner_note_hash: Field, + _slotted_note_hash: Field, _counter: u32 ) -> Field {} @@ -15,27 +15,27 @@ unconstrained pub fn notify_created_note( storage_slot: Field, note_type_id: Field, serialized_note: [Field; N], - inner_note_hash: Field, + slotted_note_hash: Field, counter: u32 ) -> Field { notify_created_note_oracle( storage_slot, note_type_id, serialized_note, - inner_note_hash, + slotted_note_hash, counter ) } #[oracle(notifyNullifiedNote)] -unconstrained fn notify_nullified_note_oracle(_nullifier: Field, _inner_note_hash: Field, _counter: u32) -> Field {} +unconstrained fn notify_nullified_note_oracle(_nullifier: Field, _slotted_note_hash: Field, _counter: u32) -> Field {} unconstrained pub fn notify_nullified_note( nullifier: Field, - inner_note_hash: Field, + slotted_note_hash: Field, counter: u32 ) -> Field { - notify_nullified_note_oracle(nullifier, inner_note_hash, counter) + notify_nullified_note_oracle(nullifier, slotted_note_hash, counter) } #[oracle(getNotes)] diff --git a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr index e8a03ea7e12..6ce7feabce5 100644 --- a/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr +++ b/noir-projects/aztec-nr/aztec/src/test/helpers/cheatcodes.nr @@ -95,8 +95,8 @@ unconstrained pub fn add_nullifiers(contractAddress: AztecAddress, nullifiers: [ oracle_add_nullifiers(contractAddress, nullifiers) } -unconstrained pub fn add_note_hashes(contractAddress: AztecAddress, inner_note_hashes: [Field]) { - oracle_add_note_hashes(contractAddress, inner_note_hashes) +unconstrained pub fn add_note_hashes(contractAddress: AztecAddress, slotted_note_hashes: [Field]) { + oracle_add_note_hashes(contractAddress, slotted_note_hashes) } unconstrained pub fn get_function_selector() -> FunctionSelector { @@ -180,7 +180,7 @@ unconstrained fn oracle_assert_private_call_fails( unconstrained fn oracle_add_nullifiers(contractAddress: AztecAddress, nullifiers: [Field]) {} #[oracle(addNoteHashes)] -unconstrained fn oracle_add_note_hashes(contractAddress: AztecAddress, inner_note_hashes: [Field]) {} +unconstrained fn oracle_add_note_hashes(contractAddress: AztecAddress, slotted_note_hashes: [Field]) {} #[oracle(getFunctionSelector)] unconstrained fn oracle_get_function_selector() -> FunctionSelector {} 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 f5c3436c9fe..923373f2d23 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 @@ -14,7 +14,7 @@ use crate::hash::{hash_args, hash_args_array}; use crate::note::{ note_header::NoteHeader, note_interface::NoteInterface, - utils::{compute_inner_note_hash, compute_note_hash_for_consumption} + utils::{compute_slotted_note_hash, compute_note_hash_for_consumption} }; use crate::oracle::{execution::{get_block_number, get_contract_address}, notes::notify_created_note}; @@ -219,14 +219,14 @@ impl TestEnvironment { let header = NoteHeader { contract_address, storage_slot, nonce: 0, note_hash_counter }; note.set_header(header); - let inner_note_hash = compute_inner_note_hash(*note); + let slotted_note_hash = compute_slotted_note_hash(*note); let serialized_note = Note::serialize_content(*note); assert( notify_created_note( storage_slot, Note::get_note_type_id(), serialized_note, - inner_note_hash, + slotted_note_hash, note_hash_counter ) == 0 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 f5dd8c400c6..85cc21f9106 100644 --- a/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr +++ b/noir-projects/aztec-nr/aztec/src/test/mocks/mock_note.nr @@ -1,6 +1,6 @@ use crate::{context::PrivateContext, note::{note_header::NoteHeader, note_interface::NoteInterface}}; -use dep::protocol_types::{address::AztecAddress, traits::Eq}; +use dep::protocol_types::{address::AztecAddress, point::Point, traits::Eq}; global MOCK_NOTE_LENGTH = 1; // MOCK_NOTE_LENGTH * 32 + 32(storage_slot as bytes) + 32(note_type_id as bytes) @@ -23,8 +23,9 @@ impl NoteInterface for MockNote { } } - fn compute_note_content_hash(_self: Self) -> Field { - 0 + fn compute_note_hiding_point(_self: Self) -> Point { + // TODO(#7636): Properly implement these functions. + crate::generators::Ga1 } fn get_header(self) -> NoteHeader { diff --git a/noir-projects/aztec-nr/aztec/src/utils/point.nr b/noir-projects/aztec-nr/aztec/src/utils/point.nr index c2d0d2635d5..20c6af687f8 100644 --- a/noir-projects/aztec-nr/aztec/src/utils/point.nr +++ b/noir-projects/aztec-nr/aztec/src/utils/point.nr @@ -14,7 +14,9 @@ pub fn point_to_bytes(pk: Point) -> [u8; 32] { let mut result = pk.x.to_be_bytes(32); // We store only a "sign" of the y coordinate because the rest can be derived from the x coordinate. To get - // the sign we check if the y coordinate is greater than the curve's order minus 1 divided by 2. + // the sign we check if the y coordinate is less or equal than the curve's order minus 1 divided by 2. + // Ideally we'd do `y <= MOD_DIV_2`, but there's no `lte` function, so instead we do `!(y > MOD_DIV_2)`, which is + // equivalent, and then rewrite that as `!(MOD_DIV_2 < y)`, since we also have no `gt` function. if !BN254_FR_MODULUS_DIV_2.lt(pk.y) { // y is <= (modulus - 1) / 2 so we set the sign bit to 1 // Here we leverage that field fits into 254 bits (log2(Fr.MODULUS) < 254) and given that we serialize Fr to 32 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 1b135577ec7..5b97967aae1 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -58,6 +58,10 @@ impl ValueNote { } impl Serialize<7> for ValueNote { + /// The following method needed to be implemented because the note is passed as an argument to a contract function + /// --> the serialize method is called by aztec-nr when computing an arguments hash. + /// Note that when the note is about to be encrypted and emitted as a log the to_be_bytes function auto-implemented + /// by aztec macros is called instead. fn serialize(self) -> [Field; 7] { let header = self.header.serialize(); diff --git a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr index 8c11b8f6986..c6fdf0b9da0 100644 --- a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr @@ -91,7 +91,7 @@ contract PendingNoteHashes { // Nested/inner function to create and insert a note // TESTING: inserts a static randomness value to test notes with - // the same inner note hash are dealt with correctly + // the same slotted note hash are dealt with correctly #[aztec(private)] fn insert_note_static_randomness( amount: Field, diff --git a/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr b/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr index 3692df2c74d..6c0890726d6 100644 --- a/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/private_token_contract/src/main.nr @@ -6,7 +6,7 @@ mod test; contract PrivateToken { use dep::compressed_string::FieldCompressedString; use dep::aztec::{ - note::utils::compute_inner_note_hash_from_preimage, hash::compute_secret_hash, + note::utils::compute_slotted_note_hash_raw, hash::compute_secret_hash, prelude::{NoteGetterOptions, Map, PublicMutable, SharedImmutable, PrivateSet, AztecAddress}, protocol_types::{abis::function_selector::FunctionSelector, point::Point, hash::pedersen_hash}, oracle::unsafe_rand::unsafe_rand, @@ -200,21 +200,21 @@ contract PrivateToken { #[aztec(public)] #[aztec(internal)] fn complete_refund(fee_payer_point: Point, user_point: Point) { - // 1. We get the final note content hashes by calling the `complete_refund` on the note. + // 1. We get the final note hiding points by calling a `complete_refund` function on the note. // We use 1:1 exchange rate between fee juice and token. So using `tx_fee` is enough let tx_fee = context.transaction_fee(); - let (fee_payer_note_content_hash, user_note_content_hash) = TokenNote::complete_refund(fee_payer_point, user_point, tx_fee); + let (fee_payer_note_hiding_point, user_note_hiding_point) = TokenNote::complete_refund(fee_payer_point, user_point, tx_fee); - // 2. Now we "manually" compute the inner note hashes. - let fee_payer_inner_note_hash = compute_inner_note_hash_from_preimage( + // 2. Now we "manually" compute the slotted note hashes. + let fee_payer_slotted_note_hash = compute_slotted_note_hash_raw( PrivateToken::storage().balances.slot, - fee_payer_note_content_hash + fee_payer_note_hiding_point ); - let user_inner_note_hash = compute_inner_note_hash_from_preimage(PrivateToken::storage().balances.slot, user_note_content_hash); + let user_slotted_note_hash = compute_slotted_note_hash_raw(PrivateToken::storage().balances.slot, user_note_hiding_point); // 3. At last we emit the note hashes. - context.push_note_hash(fee_payer_inner_note_hash); - context.push_note_hash(user_inner_note_hash); + context.push_note_hash(fee_payer_slotted_note_hash); + context.push_note_hash(user_slotted_note_hash); // --> Once the tx is settled user and fee recipient can add the notes to their pixies. } diff --git a/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr index 1825f0921d9..f97e2706a79 100644 --- a/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/private_token_contract/src/test/utils.nr @@ -1,7 +1,6 @@ use dep::aztec::{ hash::compute_secret_hash, prelude::AztecAddress, test::helpers::{cheatcodes, test_environment::TestEnvironment}, - protocol_types::storage::map::derive_storage_slot_in_map, note::{note_getter::{MAX_NOTES_PER_PAGE, view_notes}, note_viewer_options::NoteViewerOptions}, oracle::{execution::get_contract_address, unsafe_rand::unsafe_rand, storage::storage_read}, context::PrivateContext diff --git a/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr index d2f0ddf2d38..951caecdce9 100644 --- a/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/private_token_contract/src/types/token_note.nr @@ -1,9 +1,9 @@ use dep::aztec::{ + generators::{Ga1 as G_amt, Ga2 as G_npk, Ga3 as G_rnd}, prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}, protocol_types::{constants::GENERATOR_INDEX__NOTE_NULLIFIER, point::Point, scalar::Scalar, hash::poseidon2_hash}, note::utils::compute_note_hash_for_consumption, oracle::unsafe_rand::unsafe_rand, - keys::getters::get_nsk_app, note::note_getter_options::PropertySelector, - generators::{Ga1 as G_amt, Ga2 as G_npk, Ga3 as G_rnd} + keys::getters::get_nsk_app, note::note_getter_options::PropertySelector }; use dep::std::field::bn254::decompose; use dep::std::embedded_curve_ops::multi_scalar_mul; @@ -28,7 +28,7 @@ trait PrivatelyRefundable { incomplete_fee_payer_point: Point, incomplete_user_point: Point, transaction_fee: Field - ) -> (Field, Field); + ) -> (Point, Point); } global TOKEN_NOTE_LEN: Field = 3; // 3 plus a header. @@ -71,10 +71,10 @@ impl NoteInterface for TokenNote { - fn compute_note_content_hash(self) -> Field { + fn compute_note_hiding_point(self) -> Point { let (npk_lo, npk_hi) = decompose(self.npk_m_hash); let (random_lo, random_hi) = decompose(self.randomness); - // We compute the note content hash as an x-coordinate of `G_amt * amount + G_npk * npk_m_hash + G_rnd * randomness` instead + // We compute the note hiding point as `G_amt * amount + G_npk * npk_m_hash + G_rnd * randomness` instead // of using pedersen or poseidon2 because it allows us to privately add and subtract from amount in public // by leveraging homomorphism. multi_scalar_mul( @@ -91,7 +91,7 @@ impl NoteInterface for TokenNote { lo: random_lo, hi: random_hi, }] - ).x + ) } } @@ -135,8 +135,7 @@ impl OwnedNote for TokenNote { * * So you can think of these (x, y) points as "partial notes": they encode part of the internals of the notes. * - * This is because the compute_note_content_hash function above defines the content hash to be - * the x-coordinate of a point defined as: + * This is because the compute_note_hiding_point function above defines the hiding point as: * * G_amt * amount + G_npk * npk_m_hash + G_rnd * randomness * @@ -184,7 +183,7 @@ impl OwnedNote for TokenNote { * = (G_amt * funded_amount + G_npk * user_npk + G_rnd + user_randomness) - G_amt * transaction_fee = * = G_amt * (funded_amount - transaction_fee) + G_npk * user_npk + G_rnd + user_randomness * - * The x-coordinate of points above identically matches the note_content_hash of (and therefore *is*) notes like: + * The point above matches the note_hiding_point of (and therefore *is*) notes like: * { * amount: (funded_amount - transaction_fee), * npk_m_hash: user_npk, @@ -209,6 +208,7 @@ impl PrivatelyRefundable for TokenNote { fn generate_refund_points(fee_payer_npk_m_hash: Field, user_npk_m_hash: Field, funded_amount: Field, user_randomness: Field, fee_payer_randomness: Field) -> (Point, Point) { // 1. To be able to multiply generators with randomness and npk_m_hash using barretneberg's (BB) blackbox // function we first need to convert the fields to high and low limbs. + // TODO(#7606): replace decompose with from_field_unsafe let (fee_payer_randomness_lo, fee_payer_randomness_hi) = decompose(fee_payer_randomness); let (fee_payer_npk_m_hash_lo, fee_payer_npk_m_hash_hi) = decompose(fee_payer_npk_m_hash); @@ -253,7 +253,7 @@ impl PrivatelyRefundable for TokenNote { (incomplete_fee_payer_point, incomplete_user_point) } - fn complete_refund(incomplete_fee_payer_point: Point, incomplete_user_point: Point, transaction_fee: Field) -> (Field, Field) { + fn complete_refund(incomplete_fee_payer_point: Point, incomplete_user_point: Point, transaction_fee: Field) -> (Point, Point) { // 1. We convert the transaction fee to high and low limbs to be able to use BB API. let (transaction_fee_lo, transaction_fee_hi) = decompose(transaction_fee); @@ -273,7 +273,7 @@ impl PrivatelyRefundable for TokenNote { assert_eq(user_point.is_infinite, false); - // Finally we return the x-coordinates of the points which are the note content hashes. - (fee_payer_point.x, user_point.x) + // Finally we return the points which represent the note hiding points. + (fee_payer_point, user_point) } } diff --git a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr index 7513a0e3a02..d778c72d504 100644 --- a/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr +++ b/noir-projects/noir-protocol-circuits/crates/reset-kernel-lib/src/note_hash_read_request_reset.nr @@ -55,21 +55,21 @@ mod tests { global contract_address = AztecAddress::from_field(123); // Create 4 note hashes. 10 and 11 are settled. 12 and 13 are pending. - global inner_note_hashes = [10, 11, 12, 13]; - global note_hashes = inner_note_hashes.map(|n| compute_siloed_note_hash(contract_address, n)); + global slotted_note_hashes = [10, 11, 12, 13]; + global note_hashes = slotted_note_hashes.map(|n| compute_siloed_note_hash(contract_address, n)); // Create 5 read requests. 0 and 3 are reading settled note hashes. 1, 2 and 4 are reading pending note hashes. global read_requests = [ - ReadRequest { value: inner_note_hashes[1], counter: 11 }.scope(contract_address), // settled - ReadRequest { value: inner_note_hashes[3], counter: 13 }.scope(contract_address), // pending - ReadRequest { value: inner_note_hashes[2], counter: 39 }.scope(contract_address), // pending - ReadRequest { value: inner_note_hashes[0], counter: 46 }.scope(contract_address), // settled - ReadRequest { value: inner_note_hashes[3], counter: 78 }.scope(contract_address), // pending + ReadRequest { value: slotted_note_hashes[1], counter: 11 }.scope(contract_address), // settled + ReadRequest { value: slotted_note_hashes[3], counter: 13 }.scope(contract_address), // pending + ReadRequest { value: slotted_note_hashes[2], counter: 39 }.scope(contract_address), // pending + ReadRequest { value: slotted_note_hashes[0], counter: 46 }.scope(contract_address), // settled + ReadRequest { value: slotted_note_hashes[3], counter: 78 }.scope(contract_address), // pending ]; global pending_values = [ - NoteHash { value: inner_note_hashes[2], counter: 2, }.scope(contract_address), - NoteHash { value: inner_note_hashes[3], counter: 8, }.scope(contract_address), + NoteHash { value: slotted_note_hashes[2], counter: 2, }.scope(contract_address), + NoteHash { value: slotted_note_hashes[3], counter: 8, }.scope(contract_address), ]; global pending_read_hints = [ PendingReadHint { read_request_index: 1, pending_value_index: 1 }, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr index 131b6746b7e..b788d2e8cb6 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -339,9 +339,8 @@ global GENERATOR_INDEX__OVSK_M = 50; global GENERATOR_INDEX__TSK_M = 51; global GENERATOR_INDEX__PUBLIC_KEYS_HASH = 52; global GENERATOR_INDEX__NOTE_NULLIFIER = 53; -global GENERATOR_INDEX__INNER_NOTE_HASH = 54; -global GENERATOR_INDEX__NOTE_CONTENT_HASH = 55; -global GENERATOR_INDEX__SYMMETRIC_KEY: u8 = 56; +global GENERATOR_INDEX__NOTE_HIDING_POINT: u32 = 54; +global GENERATOR_INDEX__SYMMETRIC_KEY: u8 = 55; global SENDER_SELECTOR: u32 = 0; // "address" actually does not exist in PublicCircuitPublicInputs, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index 5e98e6f5776..44162202a69 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -45,8 +45,8 @@ fn compute_note_hash_nonce(tx_hash: Field, note_index_in_tx: u32) -> Field { ) } -pub fn compute_unique_note_hash(nonce: Field, inner_note_hash: Field) -> Field { - let inputs = [nonce, inner_note_hash]; +pub fn compute_unique_note_hash(nonce: Field, slotted_note_hash: Field) -> Field { + let inputs = [nonce, slotted_note_hash]; pedersen_hash(inputs, GENERATOR_INDEX__UNIQUE_NOTE_HASH) } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr b/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr index 797cc666bac..13a82bc58b5 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr @@ -3,3 +3,20 @@ use crate::{hash::pedersen_hash, traits::ToField}; pub fn derive_storage_slot_in_map(storage_slot: Field, key: K) -> Field where K: ToField { pedersen_hash([storage_slot, key.to_field()], 0) } + +mod test { + use crate::{address::AztecAddress, storage::map::derive_storage_slot_in_map}; + + #[test] + fn test_derive_storage_slot_in_map_matches_typescript() { + let map_slot = 0x132258fb6962c4387ba659d9556521102d227549a386d39f0b22d1890d59c2b5; + let key = AztecAddress::from_field(0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f); + + let slot = derive_storage_slot_in_map(map_slot, key); + + // The following value was generated by `map_slot.test.ts` + let slot_from_typescript = 0x2499880e2b1b831785c17286f99a0d5122fee784ce7b1c04e380c4a991da819a; + + assert_eq(slot, slot_from_typescript); + } +} 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 3233e12ab73..251ec46e101 100644 --- a/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs +++ b/noir/noir-repo/aztec_macros/src/transforms/note_interface.rs @@ -196,10 +196,10 @@ pub fn generate_note_interface_impl(module: &mut SortedModule) -> Result<(), Azt trait_impl.items.push(TraitImplItem::Function(get_note_type_id_fn)); } - if !check_trait_method_implemented(trait_impl, "compute_note_content_hash") { - let compute_note_content_hash_fn = - generate_compute_note_content_hash(¬e_type, note_interface_impl_span)?; - trait_impl.items.push(TraitImplItem::Function(compute_note_content_hash_fn)); + if !check_trait_method_implemented(trait_impl, "compute_note_hiding_point") { + let compute_note_hiding_point_fn = + generate_compute_note_hiding_point(¬e_type, note_interface_impl_span)?; + trait_impl.items.push(TraitImplItem::Function(compute_note_hiding_point_fn)); } if !check_trait_method_implemented(trait_impl, "to_be_bytes") { @@ -494,19 +494,23 @@ fn generate_note_properties_fn( Ok(noir_fn) } -// Automatically generate the method to compute the note's content hash as: -// fn compute_note_content_hash(self: NoteType) -> Field { -// aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) +// Automatically generate the method to compute the note's hiding point as: +// fn compute_note_hiding_point(self: NoteType) -> Point { +// aztec::hash::pedersen_commitment(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HIDING_POINT) // } // -fn generate_compute_note_content_hash( +fn generate_compute_note_hiding_point( note_type: &String, impl_span: Option, ) -> Result { + // TODO(#7551): Replace the pedersen hash with something that returns a point directly to avoid + // the superfluous call to field_to_point(...). I was trying to use pedersen_commitment for that + // but that is currently not supported by the AVM (but might be soon). let function_source = format!( " - fn compute_note_content_hash(self: {}) -> Field {{ - aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_CONTENT_HASH) + fn compute_note_hiding_point(self: {}) -> aztec::protocol_types::point::Point {{ + let h = aztec::hash::pedersen_hash(self.serialize_content(), aztec::protocol_types::constants::GENERATOR_INDEX__NOTE_HIDING_POINT); + aztec::generators::field_to_point(h) }} ", note_type @@ -515,7 +519,7 @@ fn generate_compute_note_content_hash( if !errors.is_empty() { dbg!(errors); return Err(AztecMacroError::CouldNotImplementNoteInterface { - secondary_message: Some("Failed to parse Noir macro code (fn compute_note_content_hash). This is either a bug in the compiler or the Noir macro code".to_string()), + secondary_message: Some("Failed to parse Noir macro code (fn compute_note_hiding_point). This is either a bug in the compiler or the Noir macro code".to_string()), span: impl_span }); } diff --git a/noir/noir-repo/noir_stdlib/src/hash/mod.nr b/noir/noir-repo/noir_stdlib/src/hash/mod.nr index 320b89353d9..cea33a136cb 100644 --- a/noir/noir-repo/noir_stdlib/src/hash/mod.nr +++ b/noir/noir-repo/noir_stdlib/src/hash/mod.nr @@ -49,7 +49,7 @@ fn pedersen_commitment_with_separator(input: [Field; N], separator: fn pedersen_commitment_with_separator_noir(input: [Field; N], separator: u32) -> EmbeddedCurvePoint { let mut points = [EmbeddedCurveScalar { lo: 0, hi: 0 }; N]; for i in 0..N { - // we use the unsafe version because the multi_scalar_mul will constraint the scalars. + // we use the unsafe version because the multi_scalar_mul will constrain the scalars. points[i] = from_field_unsafe(input[i]); } let generators = derive_generators("DEFAULT_DOMAIN_SEPARATOR".as_bytes(), separator); diff --git a/yarn-project/aztec.js/src/utils/cheat_codes.ts b/yarn-project/aztec.js/src/utils/cheat_codes.ts index 95cceaf0e39..d05045a8dd2 100644 --- a/yarn-project/aztec.js/src/utils/cheat_codes.ts +++ b/yarn-project/aztec.js/src/utils/cheat_codes.ts @@ -1,7 +1,8 @@ import { type Note, type PXE } from '@aztec/circuit-types'; import { type AztecAddress, type EthAddress, Fr } from '@aztec/circuits.js'; +import { deriveStorageSlotInMap } from '@aztec/circuits.js/hash'; import { toBigIntBE, toHex } from '@aztec/foundation/bigint-buffer'; -import { keccak256, pedersenHash } from '@aztec/foundation/crypto'; +import { keccak256 } from '@aztec/foundation/crypto'; import { createDebugLogger } from '@aztec/foundation/log'; import fs from 'fs'; @@ -239,14 +240,12 @@ export class AztecCheatCodes { /** * Computes the slot value for a given map and key. - * @param baseSlot - The base slot of the map (specified in Aztec.nr contract) + * @param mapSlot - The slot of the map (specified in Aztec.nr contract) * @param key - The key to lookup in the map * @returns The storage slot of the value in the map */ - public computeSlotInMap(baseSlot: Fr | bigint, key: Fr | bigint | AztecAddress): Fr { - // Based on `at` function in - // aztec3-packages/aztec-nr/aztec/src/state_vars/map.nr - return pedersenHash([new Fr(baseSlot), new Fr(key)]); + public computeSlotInMap(mapSlot: Fr | bigint, key: Fr | bigint | AztecAddress): Fr { + return deriveStorageSlotInMap(mapSlot, new Fr(key)); } /** diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.test.ts index 6f38c689e99..ab318001fa2 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypt_buffer.test.ts @@ -32,7 +32,7 @@ describe('encrypt buffer', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/keys/point_to_symmetric_key.nr', - 'expected_key', + 'key_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_header.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_header.test.ts index bf93841caa5..93df3fee728 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_header.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_header.test.ts @@ -46,7 +46,7 @@ describe('encrypt log header', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/header.nr', - 'expected_header_ciphertext', + 'expected_header_ciphertext_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts index 6e431995d26..fe3e6b752b0 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_event_log_incoming_body.test.ts @@ -55,7 +55,7 @@ describe('encrypt log incoming body', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr', - 'expected_event_body_ciphertext', + 'event_body_ciphertext_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts index 18613e08e6e..47a33fada1d 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_incoming_body/encrypted_note_log_incoming_body.test.ts @@ -34,7 +34,7 @@ describe('encrypt log incoming body', () => { }); it('encrypt a note log incoming body, generate input for noir test', () => { - // The following 2 are arbitrary fixed values - fixed in order to test a match with Noir + // All the values in this test were arbitrarily set and copied over to `incoming_body.nr` const ephSecretKey = new GrumpkinScalar(0x23b3127c127b1f29a7adff5cccf8fb06649e7ca01d9de27b21624098b897babdn); const viewingSecretKey = new GrumpkinScalar(0x1fdd0dd8c99b21af8e00d2d130bdc263b36dadcbea84ac5ec9293a0660deca01n); @@ -56,7 +56,7 @@ describe('encrypt log incoming body', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/incoming_body.nr', - 'expected_note_body_ciphertext', + 'note_body_ciphertext_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts index e4de515d00b..35b89c559e0 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_outgoing_body.test.ts @@ -56,7 +56,7 @@ describe('encrypt log outgoing body', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/outgoing_body.nr', - 'expected_outgoing_body_ciphertext', + 'outgoing_body_ciphertext_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts index af8397ff49d..01000c26e56 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/tagged_log.test.ts @@ -57,6 +57,7 @@ describe('L1 Note Payload', () => { }); it('encrypted tagged log matches Noir', () => { + // All the values in this test were arbitrarily set and copied over to `payload.nr` const contract = AztecAddress.fromString('0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04'); const storageSlot = new Fr(0x0fe46be583b71f4ab5b70c2657ff1d05cccf1d292a9369628d1a194f944e6599n); const noteValue = new Fr(0x301640ceea758391b2e161c92c0513f129020f4125256afdae2646ce31099f5cn); @@ -82,7 +83,7 @@ describe('L1 Note Payload', () => { const encrypted = taggedLog.encrypt(ephSk, recipientAddress, ivpk, ovKeys).toString('hex'); expect(encrypted).toMatchInlineSnapshot( - `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa79f357275ed3983136f963253ad9beae147bb8d4ff52b6f53db957c440cf4fdd8003e6ce87650578cd7b96f3080ec6e5c2ecd07e28342cd006753d95a3c8a06acf6815cac45494d419312e71423d9b4fd48f220392d0b02eb1860f4e0213d97e188adb228027de514dc521cbf938589012df3e58c73a5969a601678dfedd5b6fcc008842b1538f37490b64b101edede3ccd93d635293e3510937548a9dc7dd0d22d41e92857588cedc8a109565280bf3304c3f36466f03681b0748b491b62de01f3c748eed5425b9fb78f24675e053e320dd9a14f1ee729e46d8bf377a63625fac106431d94b9993a40d2a4dba550234b6db10ea8886915eb3e9f473df5c1eaa964a508de9def29dddf43503dfc361b64016802793e2917840f7c7815c67197ac2aa140f0a6cd50a93abf6f82373a8d1a617672d845cfd4e3fac7154890552b4cd51c848610dd697052ee723d2490b3b244c6a2d4556474ba83e821e565fb05fb"`, + `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa77e4f33ba2f47fdac6370f13bc5f16bbae857bbe6ab3ee4ea2a339192eef22a47ce0df4426fc314cb6294ccf291b79c1d8d362cdcc223e51020ccd3318e7052ca74f1fe922ad914bd46e4b6abcd681b63ab1c5bf4151e82f00548ae7c61c59df8cccb8cabb59882a9b32934ad2d2bc60198489ef90b2909a0304e7b84cb8cd70d16c958ff8b9a4c143f867d6cefd03f3b21758be1b80040991583cc6f29541790def5c80cea0b300add14fc267a28f942f8c5c6d14f143b42c5d7101291e4ef7c51436731c43ae4c340c7f3b870ad1dc4d74dd9555295714cc95d5f94255fdee9d29601b61c843b949c8124e637c79524cd67d43c978d0a97de97b42b5b94c96ea50aee2086eb63d8c8b61f169c12d1deacefc1d456633e46b62daff15bcab3e1630196e802fc14533184a25d74d45747d33a9fa328fd1f03c0300ec95018879acf3a8c801d65cfbdb6bf47d240ac83532ee813d8b76cea11683c71e791c39d18"`, ); const byteArrayString = `[${encrypted.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; @@ -90,7 +91,7 @@ describe('L1 Note Payload', () => { // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr', - 'expected_encrypted_note_log', + 'encrypted_note_log_from_typescript', byteArrayString, ); }); diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 2ecf599ca56..62caab71d9e 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -263,7 +263,6 @@ export enum GeneratorIndex { TSK_M = 51, PUBLIC_KEYS_HASH = 52, NOTE_NULLIFIER = 53, - INNER_NOTE_HASH = 54, - NOTE_CONTENT_HASH = 55, - SYMMETRIC_KEY = 56, + NOTE_HIDING_POINT = 54, + SYMMETRIC_KEY = 55, } diff --git a/yarn-project/circuits.js/src/hash/hash.test.ts b/yarn-project/circuits.js/src/hash/hash.test.ts index 99216b0c0b1..0b688151822 100644 --- a/yarn-project/circuits.js/src/hash/hash.test.ts +++ b/yarn-project/circuits.js/src/hash/hash.test.ts @@ -26,8 +26,8 @@ describe('hash', () => { it('computes unique note hash', () => { const nonce = new Fr(123n); - const innerNoteHash = new Fr(456); - const res = computeUniqueNoteHash(nonce, innerNoteHash); + const slottedNoteHash = new Fr(456); + const res = computeUniqueNoteHash(nonce, slottedNoteHash); expect(res).toMatchSnapshot(); }); diff --git a/yarn-project/circuits.js/src/hash/hash.ts b/yarn-project/circuits.js/src/hash/hash.ts index 3d78b779071..12954c4213f 100644 --- a/yarn-project/circuits.js/src/hash/hash.ts +++ b/yarn-project/circuits.js/src/hash/hash.ts @@ -44,41 +44,22 @@ export function computeNoteHashNonce(nullifierZero: Fr, noteHashIndex: number): * Computes a siloed note hash, given the contract address and the note hash itself. * A siloed note hash effectively namespaces a note hash to a specific contract. * @param contract - The contract address - * @param innerNoteHash - The note hash to silo. + * @param uniqueNoteHash - The unique note hash to silo. * @returns A siloed note hash. */ export function siloNoteHash(contract: AztecAddress, uniqueNoteHash: Fr): Fr { return pedersenHash([contract, uniqueNoteHash], GeneratorIndex.SILOED_NOTE_HASH); } -/** - * Computes a note content hash. - * @param noteContent - The note content (e.g. note.items). - * @returns A note content hash. - */ -export function computeNoteContentHash(noteContent: Fr[]): Fr { - return pedersenHash(noteContent, GeneratorIndex.NOTE_CONTENT_HASH); -} - -/** - * Computes an inner note hash, given a storage slot and a note hash. - * @param storageSlot - The storage slot. - * @param noteHash - The note hash. - * @returns An inner note hash. - */ -export function computeInnerNoteHash(storageSlot: Fr, noteHash: Fr): Fr { - return pedersenHash([storageSlot, noteHash], GeneratorIndex.INNER_NOTE_HASH); -} - /** * Computes a unique note hash. * @dev Includes a nonce which contains data that guarantees the resulting note hash will be unique. - * @param nonce - The contract address. - * @param innerNoteHash - An inner note hash. + * @param nonce - A nonce (typically derived from tx hash and note hash index in the tx). + * @param slottedNoteHash - A slotted note hash. * @returns A unique note hash. */ -export function computeUniqueNoteHash(nonce: Fr, innerNoteHash: Fr): Fr { - return pedersenHash([nonce, innerNoteHash], GeneratorIndex.UNIQUE_NOTE_HASH); +export function computeUniqueNoteHash(nonce: Fr, slottedNoteHash: Fr): Fr { + return pedersenHash([nonce, slottedNoteHash], GeneratorIndex.UNIQUE_NOTE_HASH); } /** diff --git a/yarn-project/circuits.js/src/hash/index.ts b/yarn-project/circuits.js/src/hash/index.ts index c3e9a375b11..b2ddb1d68d3 100644 --- a/yarn-project/circuits.js/src/hash/index.ts +++ b/yarn-project/circuits.js/src/hash/index.ts @@ -1 +1,2 @@ export * from './hash.js'; +export * from './map_slot.js'; diff --git a/yarn-project/circuits.js/src/hash/map_slot.test.ts b/yarn-project/circuits.js/src/hash/map_slot.test.ts new file mode 100644 index 00000000000..e43047d4b2d --- /dev/null +++ b/yarn-project/circuits.js/src/hash/map_slot.test.ts @@ -0,0 +1,25 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr } from '@aztec/foundation/fields'; +import { updateInlineTestData } from '@aztec/foundation/testing'; + +import { deriveStorageSlotInMap } from './index.js'; + +describe('Map slot', () => { + it('derived map slot matches Noir', () => { + const mapSlot = new Fr(0x132258fb6962c4387ba659d9556521102d227549a386d39f0b22d1890d59c2b5n); + const key = AztecAddress.fromString('0x302dbc2f9b50a73283d5fb2f35bc01eae8935615817a0b4219a057b2ba8a5a3f'); + + const slot = deriveStorageSlotInMap(mapSlot, key); + + expect(slot.toString()).toMatchInlineSnapshot( + `"0x2499880e2b1b831785c17286f99a0d5122fee784ce7b1c04e380c4a991da819a"`, + ); + + // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + updateInlineTestData( + 'noir-projects/noir-protocol-circuits/crates/types/src/storage/map.nr', + 'slot_from_typescript', + slot.toString(), + ); + }); +}); diff --git a/yarn-project/circuits.js/src/hash/map_slot.ts b/yarn-project/circuits.js/src/hash/map_slot.ts new file mode 100644 index 00000000000..d14b85b0ea9 --- /dev/null +++ b/yarn-project/circuits.js/src/hash/map_slot.ts @@ -0,0 +1,18 @@ +import { pedersenHash } from '@aztec/foundation/crypto'; +import { type Fr } from '@aztec/foundation/fields'; + +/** + * Computes the resulting storage slot for an entry in a map. + * @param mapSlot - The slot of the map within state. + * @param key - The key of the map. + * @returns The slot in the contract storage where the value is stored. + */ +export function deriveStorageSlotInMap( + mapSlot: Fr | bigint, + key: { + /** Convert key to a field. */ + toField: () => Fr; + }, +): Fr { + return pedersenHash([mapSlot, key.toField()]); +} diff --git a/yarn-project/circuits.js/src/hints/build_note_hash_read_request_hints.test.ts b/yarn-project/circuits.js/src/hints/build_note_hash_read_request_hints.test.ts index e812f869a4d..59638719276 100644 --- a/yarn-project/circuits.js/src/hints/build_note_hash_read_request_hints.test.ts +++ b/yarn-project/circuits.js/src/hints/build_note_hash_read_request_hints.test.ts @@ -39,7 +39,7 @@ describe('buildNoteHashReadRequestHints', () => { let numSettledReads = 0; let futureNoteHashes: ScopedNoteHash[]; - const innerNoteHash = (index: number) => index + 9999; + const slottedNoteHash = (index: number) => index + 9999; const makeReadRequest = (value: number, counter = 2) => new ReadRequest(new Fr(value), counter).scope(contractAddress); @@ -49,7 +49,7 @@ describe('buildNoteHashReadRequestHints', () => { const readPendingNoteHash = (noteHashIndex: number) => { const readRequestIndex = numReadRequests; const hintIndex = numPendingReads; - noteHashReadRequests[readRequestIndex] = makeReadRequest(innerNoteHash(noteHashIndex)); + noteHashReadRequests[readRequestIndex] = makeReadRequest(slottedNoteHash(noteHashIndex)); expectedHints.readRequestStatuses[readRequestIndex] = ReadRequestStatus.pending(hintIndex); expectedHints.pendingReadHints[hintIndex] = new PendingReadHint(readRequestIndex, noteHashIndex); numReadRequests++; @@ -89,7 +89,7 @@ describe('buildNoteHashReadRequestHints', () => { beforeEach(() => { noteHashReadRequests = makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_TX, ScopedReadRequest.empty); - noteHashes = makeTuple(MAX_NOTE_HASHES_PER_TX, i => makeNoteHash(innerNoteHash(i))); + noteHashes = makeTuple(MAX_NOTE_HASHES_PER_TX, i => makeNoteHash(slottedNoteHash(i))); noteHashLeafIndexMap = new Map(); expectedHints = NoteHashReadRequestHintsBuilder.empty( MAX_NOTE_HASH_READ_REQUESTS_PER_TX, @@ -100,7 +100,7 @@ describe('buildNoteHashReadRequestHints', () => { numSettledReads = 0; futureNoteHashes = new Array(MAX_NOTE_HASHES_PER_TX) .fill(null) - .map((_, i) => makeNoteHash(innerNoteHash(i + MAX_NOTE_HASHES_PER_TX))); + .map((_, i) => makeNoteHash(slottedNoteHash(i + MAX_NOTE_HASHES_PER_TX))); }); it('builds empty hints', async () => { diff --git a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts index 1d465504b8b..6bbd6215923 100644 --- a/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_aztec_js_browser.test.ts @@ -24,13 +24,17 @@ const pageLogger = createDebugLogger('aztec:e2e_aztec_browser.js:web:page'); * 1) Build the whole repository, * 2) go to `yarn-project/end-to-end` and build the web packed package with `yarn build:web`, * 3) start anvil: `anvil`, - * 4) if you intend to use a remotely running environment then export the URL of your PXE e.g. `export PXE_URL='http://localhost:8080'` + * 4) if you intend to use a remotely running environment then export the URL of your PXE e.g. + * `export PXE_URL='http://localhost:8080'` * 5) go to `yarn-project/end-to-end` and run the test: `yarn test aztec_js_browser` - * 6) If you get dependency error run `apt install libssn3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libxdamage1 libxkbcommon0 libpango-1.0-0 libcairo2`. + * 6) If you get dependency error run `apt install libnss3 libatk1.0-0t64 libatk-bridge2.0-0t64 libcups2t64 libxdamage1 libxkbcommon0 libpango-1.0-0 libcairo2 libasound2t64`. * * NOTE 1: If you see the logs spammed with unexpected logs there is probably a chrome process with a webpage - * unexpectedly running in the background. Kill it with `killall chrome` + * unexpectedly running in the background. Kill it with `killall chrome` * NOTE 2: Don't forget to run `yarn build:web` once you make changes! + * NOTE 3: The test serializes token contract artifact to and from buffer. If you introduce a new type in the artifact + * you have to register it on `TypeRegistry` class, implement fromJSON method just like TypeRegistry requires + * and add a case in `contractArtifactFromBuffer(...)` function. */ const setupApp = async () => { diff --git a/yarn-project/end-to-end/src/e2e_block_building.test.ts b/yarn-project/end-to-end/src/e2e_block_building.test.ts index 206e19e00b4..b1773d71856 100644 --- a/yarn-project/end-to-end/src/e2e_block_building.test.ts +++ b/yarn-project/end-to-end/src/e2e_block_building.test.ts @@ -236,7 +236,7 @@ describe('e2e_block_building', () => { }); describe('logs in nested calls are ordered as expected', () => { - // This test was originally writted for e2e_nested, but it was refactored + // This test was originally written for e2e_nested, but it was refactored // to not use TestContract. let testContract: TestContract; diff --git a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts index ba938b63ef2..3c369d8bd59 100644 --- a/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_pending_note_hashes_contract.test.ts @@ -188,7 +188,7 @@ describe('e2e_pending_note_hashes_contract', () => { await expectNoteLogsSquashedExcept(1); }); - it('Squash! Aztec.nr function can "create" 2 notes with the same inner note hash and "nullify" 1 in the same TX', async () => { + it('Squash! Aztec.nr function can "create" 2 notes with the same slotted note hash and "nullify" 1 in the same TX', async () => { // Kernel will squash one noteHash and its nullifier, where two notes with the same inner hash exist. // The other note will become persistent! // Realistic way to describe this test is "Mint notes A and B, then burn note A in the same transaction" diff --git a/yarn-project/foundation/src/fields/point.ts b/yarn-project/foundation/src/fields/point.ts index eb443cbf6c0..253173c6a3c 100644 --- a/yarn-project/foundation/src/fields/point.ts +++ b/yarn-project/foundation/src/fields/point.ts @@ -267,19 +267,6 @@ export class Point { } } -/** - * Does this object look like a point? - * @param obj - Object to test if it is a point. - * @returns Whether it looks like a point. - */ -export function isPoint(obj: object): obj is Point { - if (!obj) { - return false; - } - const point = obj as Point; - return point.kind === 'point' && point.x !== undefined && point.y !== undefined; -} - export class NotOnCurveError extends Error { constructor(x: Fr) { super('The given x-coordinate is not on the Grumpkin curve: ' + x.toString()); diff --git a/yarn-project/pxe/src/database/incoming_note_dao.test.ts b/yarn-project/pxe/src/database/incoming_note_dao.test.ts index f20e957fc1b..00c70ea296e 100644 --- a/yarn-project/pxe/src/database/incoming_note_dao.test.ts +++ b/yarn-project/pxe/src/database/incoming_note_dao.test.ts @@ -11,7 +11,7 @@ export const randomIncomingNoteDao = ({ storageSlot = Fr.random(), noteTypeId = NoteSelector.random(), nonce = Fr.random(), - innerNoteHash = Fr.random(), + slottedNoteHash = Fr.random(), siloedNullifier = Fr.random(), index = Fr.random().toBigInt(), ivpkM = Point.random(), @@ -23,7 +23,7 @@ export const randomIncomingNoteDao = ({ noteTypeId, txHash, nonce, - innerNoteHash, + slottedNoteHash, siloedNullifier, index, ivpkM, diff --git a/yarn-project/pxe/src/database/incoming_note_dao.ts b/yarn-project/pxe/src/database/incoming_note_dao.ts index 0a128a74259..fb51b025c8f 100644 --- a/yarn-project/pxe/src/database/incoming_note_dao.ts +++ b/yarn-project/pxe/src/database/incoming_note_dao.ts @@ -23,10 +23,10 @@ export class IncomingNoteDao implements NoteData { /** The nonce of the note. */ public nonce: Fr, /** - * Inner note hash of the note. This is customizable by the app circuit. + * Slotted note hash of the note. This is customizable by the app circuit. * We can use this value to compute siloedNoteHash and uniqueSiloedNoteHash. */ - public innerNoteHash: Fr, + public slottedNoteHash: Fr, /** * The nullifier of the note (siloed by contract address). * Note: Might be set as 0 if the note was added to PXE as nullified. @@ -46,7 +46,7 @@ export class IncomingNoteDao implements NoteData { this.noteTypeId, this.txHash.buffer, this.nonce, - this.innerNoteHash, + this.slottedNoteHash, this.siloedNullifier, this.index, this.ivpkM, @@ -61,7 +61,7 @@ export class IncomingNoteDao implements NoteData { const noteTypeId = reader.readObject(NoteSelector); const txHash = reader.readObject(TxHash); const nonce = Fr.fromBuffer(reader); - const innerNoteHash = Fr.fromBuffer(reader); + const slottedNoteHash = Fr.fromBuffer(reader); const siloedNullifier = Fr.fromBuffer(reader); const index = toBigIntBE(reader.readBytes(32)); const publicKey = Point.fromBuffer(reader); @@ -73,7 +73,7 @@ export class IncomingNoteDao implements NoteData { noteTypeId, txHash, nonce, - innerNoteHash, + slottedNoteHash, siloedNullifier, index, publicKey, diff --git a/yarn-project/pxe/src/database/outgoing_note_dao.test.ts b/yarn-project/pxe/src/database/outgoing_note_dao.test.ts index 9e7241760ff..cb404be360b 100644 --- a/yarn-project/pxe/src/database/outgoing_note_dao.test.ts +++ b/yarn-project/pxe/src/database/outgoing_note_dao.test.ts @@ -11,7 +11,7 @@ export const randomOutgoingNoteDao = ({ storageSlot = Fr.random(), noteTypeId = NoteSelector.random(), nonce = Fr.random(), - innerNoteHash = Fr.random(), + slottedNoteHash = Fr.random(), index = Fr.random().toBigInt(), ovpkM = Point.random(), }: Partial = {}) => { @@ -22,7 +22,7 @@ export const randomOutgoingNoteDao = ({ noteTypeId, txHash, nonce, - innerNoteHash, + slottedNoteHash, index, ovpkM, ); diff --git a/yarn-project/pxe/src/database/outgoing_note_dao.ts b/yarn-project/pxe/src/database/outgoing_note_dao.ts index 03075f9f7df..1c038e5dc48 100644 --- a/yarn-project/pxe/src/database/outgoing_note_dao.ts +++ b/yarn-project/pxe/src/database/outgoing_note_dao.ts @@ -22,10 +22,10 @@ export class OutgoingNoteDao { /** The nonce of the note. */ public nonce: Fr, /** - * Inner note hash of the note. This is customizable by the app circuit. + * Slotted note hash of the note. This is customizable by the app circuit. * We can use this value to compute siloedNoteHash and uniqueSiloedNoteHash. */ - public innerNoteHash: Fr, + public slottedNoteHash: Fr, /** The location of the relevant note in the note hash tree. */ public index: bigint, /** The public key with which the note was encrypted. */ @@ -40,7 +40,7 @@ export class OutgoingNoteDao { this.noteTypeId, this.txHash.buffer, this.nonce, - this.innerNoteHash, + this.slottedNoteHash, this.index, this.ovpkM, ]); @@ -54,7 +54,7 @@ export class OutgoingNoteDao { const noteTypeId = reader.readObject(NoteSelector); const txHash = new TxHash(reader.readBytes(TxHash.SIZE)); const nonce = Fr.fromBuffer(reader); - const innerNoteHash = Fr.fromBuffer(reader); + const slottedNoteHash = Fr.fromBuffer(reader); const index = toBigIntBE(reader.readBytes(32)); const publicKey = Point.fromBuffer(reader); @@ -65,7 +65,7 @@ export class OutgoingNoteDao { noteTypeId, txHash, nonce, - innerNoteHash, + slottedNoteHash, index, publicKey, ); diff --git a/yarn-project/pxe/src/note_processor/note_processor.test.ts b/yarn-project/pxe/src/note_processor/note_processor.test.ts index 8fb954ea986..3757a1c4171 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -166,7 +166,7 @@ describe('Note Processor', () => { simulator.computeNoteHashAndOptionallyANullifier.mockImplementation((...args) => Promise.resolve({ - innerNoteHash: Fr.random(), + slottedNoteHash: Fr.random(), uniqueNoteHash: Fr.random(), siloedNoteHash: pedersenHash(args[5].items), // args[5] is note innerNullifier: Fr.random(), diff --git a/yarn-project/pxe/src/note_processor/produce_note_dao.ts b/yarn-project/pxe/src/note_processor/produce_note_dao.ts index 46d64c7125f..b8325f76415 100644 --- a/yarn-project/pxe/src/note_processor/produce_note_dao.ts +++ b/yarn-project/pxe/src/note_processor/produce_note_dao.ts @@ -51,7 +51,7 @@ export async function produceNoteDaos( try { if (ivpkM) { - const { noteHashIndex, nonce, innerNoteHash, siloedNullifier } = await findNoteIndexAndNullifier( + const { noteHashIndex, nonce, slottedNoteHash, siloedNullifier } = await findNoteIndexAndNullifier( simulator, noteHashes, txHash, @@ -69,7 +69,7 @@ export async function produceNoteDaos( payload.noteTypeId, txHash, nonce, - innerNoteHash, + slottedNoteHash, siloedNullifier, index, ivpkM, @@ -108,12 +108,12 @@ export async function produceNoteDaos( payload.noteTypeId, txHash, incomingNote.nonce, - incomingNote.innerNoteHash, + incomingNote.slottedNoteHash, incomingNote.index, ovpkM, ); } else { - const { noteHashIndex, nonce, innerNoteHash } = await findNoteIndexAndNullifier( + const { noteHashIndex, nonce, slottedNoteHash } = await findNoteIndexAndNullifier( simulator, noteHashes, txHash, @@ -130,7 +130,7 @@ export async function produceNoteDaos( payload.noteTypeId, txHash, nonce, - innerNoteHash, + slottedNoteHash, index, ovpkM, ); @@ -189,7 +189,7 @@ async function findNoteIndexAndNullifier( ) { let noteHashIndex = 0; let nonce: Fr | undefined; - let innerNoteHash: Fr | undefined; + let slottedNoteHash: Fr | undefined; let siloedNoteHash: Fr | undefined; let innerNullifier: Fr | undefined; const firstNullifier = Fr.fromBuffer(txHash.toBuffer()); @@ -205,7 +205,7 @@ async function findNoteIndexAndNullifier( } const expectedNonce = computeNoteHashNonce(firstNullifier, noteHashIndex); - ({ innerNoteHash, siloedNoteHash, innerNullifier } = await simulator.computeNoteHashAndOptionallyANullifier( + ({ slottedNoteHash, siloedNoteHash, innerNullifier } = await simulator.computeNoteHashAndOptionallyANullifier( contractAddress, expectedNonce, storageSlot, @@ -229,7 +229,7 @@ async function findNoteIndexAndNullifier( return { noteHashIndex, nonce, - innerNoteHash: innerNoteHash!, + slottedNoteHash: slottedNoteHash!, siloedNullifier: siloNullifier(contractAddress, innerNullifier!), }; } diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index ba1541f68ab..45b95686f59 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -350,7 +350,7 @@ export class PXEService implements PXE { } for (const nonce of nonces) { - const { innerNoteHash, siloedNoteHash, innerNullifier } = + const { slottedNoteHash, siloedNoteHash, innerNullifier } = await this.simulator.computeNoteHashAndOptionallyANullifier( note.contractAddress, nonce, @@ -379,7 +379,7 @@ export class PXEService implements PXE { note.noteTypeId, note.txHash, nonce, - innerNoteHash, + slottedNoteHash, siloedNullifier, index, owner.publicKeys.masterIncomingViewingPublicKey, @@ -400,7 +400,7 @@ export class PXEService implements PXE { } for (const nonce of nonces) { - const { innerNoteHash, siloedNoteHash, innerNullifier } = + const { slottedNoteHash, siloedNoteHash, innerNullifier } = await this.simulator.computeNoteHashAndOptionallyANullifier( note.contractAddress, nonce, @@ -427,7 +427,7 @@ export class PXEService implements PXE { note.noteTypeId, note.txHash, nonce, - innerNoteHash, + slottedNoteHash, Fr.ZERO, // We are not able to derive index, owner.publicKeys.masterIncomingViewingPublicKey, diff --git a/yarn-project/pxe/src/simulator_oracle/index.ts b/yarn-project/pxe/src/simulator_oracle/index.ts index 6d1dcee0588..ce517998b2e 100644 --- a/yarn-project/pxe/src/simulator_oracle/index.ts +++ b/yarn-project/pxe/src/simulator_oracle/index.ts @@ -83,12 +83,12 @@ export class SimulatorOracle implements DBOracle { storageSlot, status, }); - return noteDaos.map(({ contractAddress, storageSlot, nonce, note, innerNoteHash, siloedNullifier, index }) => ({ + return noteDaos.map(({ contractAddress, storageSlot, nonce, note, slottedNoteHash, siloedNullifier, index }) => ({ contractAddress, storageSlot, nonce, note, - innerNoteHash, + slottedNoteHash, siloedNullifier, // PXE can use this index to get full MembershipWitness index, diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 2ebd3db2152..4714b23832f 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -247,14 +247,14 @@ export class Oracle { [storageSlot]: ACVMField[], [noteTypeId]: ACVMField[], note: ACVMField[], - [innerNoteHash]: ACVMField[], + [slottedNoteHash]: ACVMField[], [counter]: ACVMField[], ): ACVMField { this.typedOracle.notifyCreatedNote( fromACVMField(storageSlot), NoteSelector.fromField(fromACVMField(noteTypeId)), note.map(fromACVMField), - fromACVMField(innerNoteHash), + fromACVMField(slottedNoteHash), +counter, ); return toACVMField(0); @@ -262,10 +262,10 @@ export class Oracle { async notifyNullifiedNote( [innerNullifier]: ACVMField[], - [innerNoteHash]: ACVMField[], + [slottedNoteHash]: ACVMField[], [counter]: ACVMField[], ): Promise { - await this.typedOracle.notifyNullifiedNote(fromACVMField(innerNullifier), fromACVMField(innerNoteHash), +counter); + await this.typedOracle.notifyNullifiedNote(fromACVMField(innerNullifier), fromACVMField(slottedNoteHash), +counter); return toACVMField(0); } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 36a84e4963b..28c08526b1d 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -27,8 +27,8 @@ export interface NoteData { storageSlot: Fr; /** The nonce of the note. */ nonce: Fr; - /** The inner note hash of the note. */ - innerNoteHash: Fr; + /** The slotted note hash of the note. */ + slottedNoteHash: Fr; /** The corresponding nullifier of the note. Undefined for pending notes. */ siloedNullifier?: Fr; /** The note's leaf index in the note hash tree. Undefined for pending notes. */ @@ -162,13 +162,13 @@ export abstract class TypedOracle { _storageSlot: Fr, _noteTypeId: NoteSelector, _note: Fr[], - _innerNoteHash: Fr, + _slottedNoteHash: Fr, _counter: number, ): void { throw new OracleMethodNotAvailableError('notifyCreatedNote'); } - notifyNullifiedNote(_innerNullifier: Fr, _innerNoteHash: Fr, _counter: number): Promise { + notifyNullifiedNote(_innerNullifier: Fr, _slottedNoteHash: Fr, _counter: number): Promise { throw new OracleMethodNotAvailableError('notifyNullifiedNote'); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 49363be658f..e79d6ea6c12 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -135,7 +135,7 @@ export class ClientExecutionContext extends ViewDataOracle { /** * Get the data for the newly created notes. - * @param innerNoteHashes - Inner note hashes for the notes. + * @param slottedNoteHashes - Slotted note hashes for the notes. */ public getNewNotes(): NoteAndSlot[] { return this.newNotes; @@ -278,7 +278,7 @@ export class ClientExecutionContext extends ViewDataOracle { if (n.index !== undefined) { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1386) // Should always call computeUniqueNoteHash when publicly created notes include nonces. - const uniqueNoteHash = n.nonce.isZero() ? n.innerNoteHash : computeUniqueNoteHash(n.nonce, n.innerNoteHash); + const uniqueNoteHash = n.nonce.isZero() ? n.slottedNoteHash : computeUniqueNoteHash(n.nonce, n.slottedNoteHash); const siloedNoteHash = siloNoteHash(n.contractAddress, uniqueNoteHash); const noteHashForReadRequest = siloedNoteHash; this.noteHashLeafIndexMap.set(noteHashForReadRequest.toBigInt(), n.index); @@ -295,14 +295,14 @@ export class ClientExecutionContext extends ViewDataOracle { * @param storageSlot - The storage slot. * @param noteTypeId - The type ID of the note. * @param noteItems - The items to be included in a Note. - * @param innerNoteHash - The inner note hash of the new note. + * @param slottedNoteHash - The slotted note hash of the new note. * @returns */ public override notifyCreatedNote( storageSlot: Fr, noteTypeId: NoteSelector, noteItems: Fr[], - innerNoteHash: Fr, + slottedNoteHash: Fr, counter: number, ) { const note = new Note(noteItems); @@ -313,7 +313,7 @@ export class ClientExecutionContext extends ViewDataOracle { nonce: Fr.ZERO, // Nonce cannot be known during private execution. note, siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. - innerNoteHash, + slottedNoteHash, }, counter, ); @@ -328,13 +328,13 @@ export class ClientExecutionContext extends ViewDataOracle { * Adding a siloed nullifier into the current set of all pending nullifiers created * within the current transaction/execution. * @param innerNullifier - The pending nullifier to add in the list (not yet siloed by contract address). - * @param innerNoteHash - The inner note hash of the new note. + * @param slottedNoteHash - The slotted note hash of the new note. */ - public override notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, counter: number) { + public override notifyNullifiedNote(innerNullifier: Fr, slottedNoteHash: Fr, counter: number) { const nullifiedNoteHashCounter = this.noteCache.nullifyNote( this.callContext.storageContractAddress, innerNullifier, - innerNoteHash, + slottedNoteHash, ); if (nullifiedNoteHashCounter !== undefined) { this.nullifiedNoteHashCounters.set(nullifiedNoteHashCounter, counter); diff --git a/yarn-project/simulator/src/client/db_oracle.ts b/yarn-project/simulator/src/client/db_oracle.ts index 57d0d185ac6..5aa044dc5c9 100644 --- a/yarn-project/simulator/src/client/db_oracle.ts +++ b/yarn-project/simulator/src/client/db_oracle.ts @@ -78,8 +78,8 @@ export interface DBOracle extends CommitmentsDB { * The query result is paginated using 'limit' and 'offset' values. * Returns an object containing an array of note data. * - * @param contractAddress - The AztecAddress instance representing the contract address. - * @param storageSlot - The Fr instance representing the storage slot of the notes. + * @param contractAddress - The contract address of the notes. + * @param storageSlot - The storage slot of the notes. * @param status - The status of notes to fetch. * @returns A Promise that resolves to an array of note data. */ diff --git a/yarn-project/simulator/src/client/execution_note_cache.ts b/yarn-project/simulator/src/client/execution_note_cache.ts index 3615b012c99..2405870f927 100644 --- a/yarn-project/simulator/src/client/execution_note_cache.ts +++ b/yarn-project/simulator/src/client/execution_note_cache.ts @@ -42,20 +42,20 @@ export class ExecutionNoteCache { * @param contractAddress - Contract address of the note. * @param storageSlot - Storage slot of the note. * @param innerNullifier - Inner nullifier of the note. - * @param innerNoteHash - Inner note hash of the note. If this value equals 0, it means the + * @param slottedNoteHash - Slotted note hash of the note. If this value equals 0, it means the * note being nullified is from a previous transaction (and thus not a new note). */ - public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, innerNoteHash: Fr) { + public nullifyNote(contractAddress: AztecAddress, innerNullifier: Fr, slottedNoteHash: Fr) { const siloedNullifier = siloNullifier(contractAddress, innerNullifier); const nullifiers = this.getNullifiers(contractAddress); nullifiers.add(siloedNullifier.value); this.nullifiers.set(contractAddress.toBigInt(), nullifiers); let nullifiedNoteHashCounter: number | undefined = undefined; - // Find and remove the matching new note and log(s) if the emitted innerNoteHash is not empty. - if (!innerNoteHash.equals(Fr.ZERO)) { + // Find and remove the matching new note and log(s) if the emitted slottedNoteHash is not empty. + if (!slottedNoteHash.equals(Fr.ZERO)) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; - const noteIndexToRemove = notes.findIndex(n => n.note.innerNoteHash.equals(innerNoteHash)); + const noteIndexToRemove = notes.findIndex(n => n.note.slottedNoteHash.equals(slottedNoteHash)); if (noteIndexToRemove === -1) { throw new Error('Attempt to remove a pending note that does not exist.'); } @@ -81,11 +81,11 @@ export class ExecutionNoteCache { * Check if a note exists in the newNotes array. * @param contractAddress - Contract address of the note. * @param storageSlot - Storage slot of the note. - * @param innerNoteHash - Inner note hash of the note. + * @param slottedNoteHash - Slotted note hash of the note. **/ - public checkNoteExists(contractAddress: AztecAddress, innerNoteHash: Fr) { + public checkNoteExists(contractAddress: AztecAddress, slottedNoteHash: Fr) { const notes = this.newNotes.get(contractAddress.toBigInt()) ?? []; - return notes.some(n => n.note.innerNoteHash.equals(innerNoteHash)); + return notes.some(n => n.note.slottedNoteHash.equals(slottedNoteHash)); } /** diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index d2384e803c9..0fa47d1f4ae 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -32,7 +32,12 @@ import { getContractInstanceFromDeployParams, getNonEmptyItems, } from '@aztec/circuits.js'; -import { computeNoteHashNonce, computeSecretHash, computeVarArgsHash } from '@aztec/circuits.js/hash'; +import { + computeNoteHashNonce, + computeSecretHash, + computeVarArgsHash, + deriveStorageSlotInMap, +} from '@aztec/circuits.js/hash'; import { makeHeader } from '@aztec/circuits.js/testing'; import { type FunctionArtifact, @@ -66,10 +71,10 @@ import { toFunctionSelector } from 'viem'; import { MessageLoadOracleInputs } from '../acvm/index.js'; import { buildL1ToL2Message } from '../test/utils.js'; -import { computeSlotForMapping } from '../utils.js'; import { type DBOracle } from './db_oracle.js'; import { CountedPublicExecutionRequest, type ExecutionResult, collectSortedEncryptedLogs } from './execution_result.js'; import { AcirSimulator } from './simulator.js'; +import { computeNoteHidingPoint, computeSlottedNoteHash } from './test_utils.js'; jest.setTimeout(60_000); @@ -316,14 +321,14 @@ describe('Private Execution test suite', () => { const noteHashIndex = randomInt(1); // mock index in TX's final noteHashes array const nonce = computeNoteHashNonce(mockFirstNullifier, noteHashIndex); const note = new Note([new Fr(amount), ownerNpkMHash, Fr.random()]); - const innerNoteHash = pedersenHash(note.items); + const slottedNoteHash = computeSlottedNoteHash(storageSlot, computeNoteHidingPoint(note.items)); return { contractAddress, storageSlot, noteTypeId, nonce, note, - innerNoteHash, + slottedNoteHash, siloedNullifier: new Fr(0), index: currentNoteIndex++, }; @@ -349,13 +354,13 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(newNote.storageSlot).toEqual(deriveStorageSlotInMap(new Fr(1n), owner)); expect(newNote.noteTypeId).toEqual(valueNoteTypeId); // ValueNote const noteHashes = getNonEmptyItems(result.callStackItem.publicInputs.noteHashes); expect(noteHashes).toHaveLength(1); expect(noteHashes[0].value).toEqual( - await acirSimulator.computeInnerNoteHash( + await acirSimulator.computeSlottedNoteHash( contractAddress, newNote.storageSlot, newNote.noteTypeId, @@ -379,13 +384,13 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const newNote = result.newNotes[0]; - expect(newNote.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(newNote.storageSlot).toEqual(deriveStorageSlotInMap(new Fr(1n), owner)); expect(newNote.noteTypeId).toEqual(valueNoteTypeId); // ValueNote const noteHashes = getNonEmptyItems(result.callStackItem.publicInputs.noteHashes); expect(noteHashes).toHaveLength(1); expect(noteHashes[0].value).toEqual( - await acirSimulator.computeInnerNoteHash( + await acirSimulator.computeSlottedNoteHash( contractAddress, newNote.storageSlot, newNote.noteTypeId, @@ -406,8 +411,8 @@ describe('Private Execution test suite', () => { const amountToTransfer = 100n; const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); - const storageSlot = computeSlotForMapping(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); - const recipientStorageSlot = computeSlotForMapping( + const storageSlot = deriveStorageSlotInMap(StatefulTestContractArtifact.storageLayout['notes'].slot, owner); + const recipientStorageSlot = deriveStorageSlotInMap( StatefulTestContractArtifact.storageLayout['notes'].slot, recipient, ); @@ -446,17 +451,17 @@ describe('Private Execution test suite', () => { const noteHashes = getNonEmptyItems(result.callStackItem.publicInputs.noteHashes); expect(noteHashes).toHaveLength(2); const [changeNoteHash, recipientNoteHash] = noteHashes; - const [recipientInnerNoteHash, changeInnerNoteHash] = [ - await acirSimulator.computeInnerNoteHash( + const [recipientSlottedNoteHash, changeSlottedNoteHash] = [ + await acirSimulator.computeSlottedNoteHash( contractAddress, recipientStorageSlot, valueNoteTypeId, recipientNote.note, ), - await acirSimulator.computeInnerNoteHash(contractAddress, storageSlot, valueNoteTypeId, changeNote.note), + await acirSimulator.computeSlottedNoteHash(contractAddress, storageSlot, valueNoteTypeId, changeNote.note), ]; - expect(recipientNoteHash.value).toEqual(recipientInnerNoteHash); - expect(changeNoteHash.value).toEqual(changeInnerNoteHash); + expect(recipientNoteHash.value).toEqual(recipientSlottedNoteHash); + expect(changeNoteHash.value).toEqual(changeSlottedNoteHash); expect(recipientNote.note.items[0]).toEqual(new Fr(amountToTransfer)); expect(changeNote.note.items[0]).toEqual(new Fr(40n)); @@ -483,7 +488,7 @@ describe('Private Execution test suite', () => { const balance = 160n; const artifact = getFunctionArtifact(StatefulTestContractArtifact, 'destroy_and_create_no_init_check'); - const storageSlot = computeSlotForMapping(new Fr(1n), owner); + const storageSlot = deriveStorageSlotInMap(new Fr(1n), owner); const notes = [ buildNote( @@ -835,7 +840,7 @@ describe('Private Execution test suite', () => { storageSlot, nonce: Fr.ZERO, note, - innerNoteHash: Fr.ZERO, + slottedNoteHash: Fr.ZERO, siloedNullifier: Fr.random(), index: 1n, }, @@ -954,7 +959,7 @@ describe('Private Execution test suite', () => { expect(result.newNotes).toHaveLength(1); const noteAndSlot = result.newNotes[0]; - expect(noteAndSlot.storageSlot).toEqual(computeSlotForMapping(new Fr(1n), owner)); + expect(noteAndSlot.storageSlot).toEqual(deriveStorageSlotInMap(new Fr(1n), owner)); expect(noteAndSlot.note.items[0]).toEqual(new Fr(amountToTransfer)); @@ -962,18 +967,18 @@ describe('Private Execution test suite', () => { expect(noteHashes).toHaveLength(1); const noteHash = noteHashes[0].value; - const storageSlot = computeSlotForMapping( + const storageSlot = deriveStorageSlotInMap( PendingNoteHashesContractArtifact.storageLayout['balances'].slot, owner, ); - const innerNoteHash = await acirSimulator.computeInnerNoteHash( + const slottedNoteHash = await acirSimulator.computeSlottedNoteHash( contractAddress, storageSlot, valueNoteTypeId, noteAndSlot.note, ); - expect(noteHash).toEqual(innerNoteHash); + expect(noteHash).toEqual(slottedNoteHash); const newEncryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.noteEncryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(1); @@ -983,15 +988,15 @@ describe('Private Execution test suite', () => { expect(encryptedLog.noteHashCounter).toEqual(result.noteEncryptedLogs[0].noteHashCounter); expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.noteEncryptedLogs[0].log.hash())); - // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) + // read request should match slottedNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) const readRequest = getNonEmptyItems(result.callStackItem.publicInputs.noteHashReadRequests)[0]; - expect(readRequest.value).toEqual(innerNoteHash); + expect(readRequest.value).toEqual(slottedNoteHash); expect(result.returnValues).toEqual([new Fr(amountToTransfer)]); const nullifier = result.callStackItem.publicInputs.nullifiers[0]; const expectedNullifier = poseidon2Hash([ - innerNoteHash, + slottedNoteHash, computeAppNullifierSecretKey(ownerNskM, contractAddress), GeneratorIndex.NOTE_NULLIFIER, ]); @@ -1035,7 +1040,7 @@ describe('Private Execution test suite', () => { const execInsert = result.nestedExecutions[0]; const execGetThenNullify = result.nestedExecutions[1]; - const storageSlot = computeSlotForMapping( + const storageSlot = deriveStorageSlotInMap( PendingNoteHashesContractArtifact.storageLayout['balances'].slot, owner, ); @@ -1051,13 +1056,13 @@ describe('Private Execution test suite', () => { expect(noteHashes).toHaveLength(1); const noteHash = noteHashes[0].value; - const innerNoteHash = await acirSimulator.computeInnerNoteHash( + const slottedNoteHash = await acirSimulator.computeSlottedNoteHash( contractAddress, noteAndSlot.storageSlot, noteAndSlot.noteTypeId, noteAndSlot.note, ); - expect(noteHash).toEqual(innerNoteHash); + expect(noteHash).toEqual(slottedNoteHash); const newEncryptedLogs = getNonEmptyItems(execInsert.callStackItem.publicInputs.noteEncryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(1); @@ -1067,15 +1072,15 @@ describe('Private Execution test suite', () => { expect(encryptedLog.noteHashCounter).toEqual(execInsert.noteEncryptedLogs[0].noteHashCounter); expect(encryptedLog.value).toEqual(Fr.fromBuffer(execInsert.noteEncryptedLogs[0].log.hash())); - // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) + // read request should match slottedNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) const readRequest = execGetThenNullify.callStackItem.publicInputs.noteHashReadRequests[0]; - expect(readRequest.value).toEqual(innerNoteHash); + expect(readRequest.value).toEqual(slottedNoteHash); expect(execGetThenNullify.returnValues).toEqual([new Fr(amountToTransfer)]); const nullifier = execGetThenNullify.callStackItem.publicInputs.nullifiers[0]; const expectedNullifier = poseidon2Hash([ - innerNoteHash, + slottedNoteHash, computeAppNullifierSecretKey(ownerNskM, contractAddress), GeneratorIndex.NOTE_NULLIFIER, ]); diff --git a/yarn-project/simulator/src/client/simulator.test.ts b/yarn-project/simulator/src/client/simulator.test.ts index a761bb4df8f..8b15e719630 100644 --- a/yarn-project/simulator/src/client/simulator.test.ts +++ b/yarn-project/simulator/src/client/simulator.test.ts @@ -1,11 +1,6 @@ import { type AztecNode, CompleteAddress, Note } from '@aztec/circuit-types'; import { GeneratorIndex, KeyValidationRequest, computeAppNullifierSecretKey, deriveKeys } from '@aztec/circuits.js'; -import { - computeInnerNoteHash, - computeNoteContentHash, - computeUniqueNoteHash, - siloNoteHash, -} from '@aztec/circuits.js/hash'; +import { computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash'; import { type FunctionArtifact, getFunctionArtifact } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { poseidon2Hash } from '@aztec/foundation/crypto'; @@ -16,6 +11,7 @@ import { type MockProxy, mock } from 'jest-mock-extended'; import { type DBOracle } from './db_oracle.js'; import { AcirSimulator } from './simulator.js'; +import { computeNoteHidingPoint, computeSlottedNoteHash } from './test_utils.js'; describe('Simulator', () => { let oracle: MockProxy; @@ -62,9 +58,9 @@ describe('Simulator', () => { oracle.getFunctionArtifactByName.mockResolvedValue(artifact); const note = createNote(); - const tokenNoteHash = computeNoteContentHash(note.items); - const innerNoteHash = computeInnerNoteHash(storageSlot, tokenNoteHash); - const uniqueNoteHash = computeUniqueNoteHash(nonce, innerNoteHash); + const noteHidingPoint = computeNoteHidingPoint(note.items); + const slottedNoteHash = computeSlottedNoteHash(storageSlot, noteHidingPoint); + const uniqueNoteHash = computeUniqueNoteHash(nonce, slottedNoteHash); const siloedNoteHash = siloNoteHash(contractAddress, uniqueNoteHash); const innerNullifier = poseidon2Hash([siloedNoteHash, appNullifierSecretKey, GeneratorIndex.NOTE_NULLIFIER]); @@ -78,7 +74,7 @@ describe('Simulator', () => { ); expect(result).toEqual({ - innerNoteHash, + slottedNoteHash, uniqueNoteHash, siloedNoteHash, innerNullifier, diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index 0f31da45780..e23bb42ea6d 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -188,14 +188,14 @@ export class AcirSimulator { returnTypes: artifact.returnTypes, }; - const [innerNoteHash, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained( + const [slottedNoteHash, uniqueNoteHash, siloedNoteHash, innerNullifier] = (await this.runUnconstrained( execRequest, artifact, contractAddress, )) as bigint[]; return { - innerNoteHash: new Fr(innerNoteHash), + slottedNoteHash: new Fr(slottedNoteHash), uniqueNoteHash: new Fr(uniqueNoteHash), siloedNoteHash: new Fr(siloedNoteHash), innerNullifier: new Fr(innerNullifier), @@ -203,20 +203,20 @@ export class AcirSimulator { } /** - * Computes the inner note hash of a note, which contains storage slot and the custom note hash. + * Computes the slotted note hash of a note, which contains storage slot and the custom note hash. * @param contractAddress - The address of the contract. * @param storageSlot - The storage slot. * @param noteTypeId - The note type identifier. * @param note - The note. * @returns The note hash. */ - public async computeInnerNoteHash( + public async computeSlottedNoteHash( contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: NoteSelector, note: Note, ) { - const { innerNoteHash } = await this.computeNoteHashAndOptionallyANullifier( + const { slottedNoteHash } = await this.computeNoteHashAndOptionallyANullifier( contractAddress, Fr.ZERO, storageSlot, @@ -224,6 +224,6 @@ export class AcirSimulator { false, note, ); - return innerNoteHash; + return slottedNoteHash; } } diff --git a/yarn-project/simulator/src/client/test_utils.ts b/yarn-project/simulator/src/client/test_utils.ts new file mode 100644 index 00000000000..0470397fafd --- /dev/null +++ b/yarn-project/simulator/src/client/test_utils.ts @@ -0,0 +1,35 @@ +import { Fq, Fr, GeneratorIndex, Point } from '@aztec/circuits.js'; +import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { pedersenHash } from '@aztec/foundation/crypto'; + +// Copied over from `noir-projects/aztec-nr/aztec/src/generators.nr` +const G_SLOT = new Point( + new Fr(0x041223147b680850dc82e8a55a952d4df20256fe0593d949a9541ca00f0abf15n), + new Fr(0x0a8c72e60d0e60f5d804549d48f3044d06140b98ed717a9b532af630c1530791n), + false, +); + +/** + * Computes a note hiding point as is done by the default implementation injected by macros. + * @param noteContent - The note content (e.g. note.items). + * @returns A note hiding point. + */ +export function computeNoteHidingPoint(noteContent: Fr[]): Point { + // TODO(#7551): how this is computed will need to be updated + const h = pedersenHash(noteContent, GeneratorIndex.NOTE_HIDING_POINT); + const grumpkin = new Grumpkin(); + return grumpkin.mul(G_SLOT, new Fq(h.toBigInt())); +} + +/** + * Computes an slotted note hash, given a storage slot and a note hiding point. + * @param storageSlot - The storage slot. + * @param noteHidingPoint - The note hiding point. + * @returns A slotted note hash. + */ +export function computeSlottedNoteHash(storageSlot: Fr, noteHidingPoint: Point): Fr { + const grumpkin = new Grumpkin(); + const storageSlotPoint = grumpkin.mul(G_SLOT, new Fq(storageSlot.toBigInt())); + const slottedNoteHidingPoint = grumpkin.add(storageSlotPoint, noteHidingPoint); + return slottedNoteHidingPoint.x; +} diff --git a/yarn-project/simulator/src/client/unconstrained_execution.test.ts b/yarn-project/simulator/src/client/unconstrained_execution.test.ts index fbca7486e34..e446f01d8b5 100644 --- a/yarn-project/simulator/src/client/unconstrained_execution.test.ts +++ b/yarn-project/simulator/src/client/unconstrained_execution.test.ts @@ -61,7 +61,7 @@ describe('Unconstrained Execution test suite', () => { nonce: Fr.random(), isSome: new Fr(1), note, - innerNoteHash: Fr.random(), + slottedNoteHash: Fr.random(), siloedNullifier: Fr.random(), index: BigInt(index), })), diff --git a/yarn-project/simulator/src/index.ts b/yarn-project/simulator/src/index.ts index 444584e171a..fc5a4653413 100644 --- a/yarn-project/simulator/src/index.ts +++ b/yarn-project/simulator/src/index.ts @@ -6,4 +6,3 @@ export * from './public/index.js'; export * from './providers/index.js'; export * from './mocks/index.js'; export * from './stats/index.js'; -export * from './utils.js'; diff --git a/yarn-project/simulator/src/public/fee_payment.ts b/yarn-project/simulator/src/public/fee_payment.ts index 721f9055d4e..48082451122 100644 --- a/yarn-project/simulator/src/public/fee_payment.ts +++ b/yarn-project/simulator/src/public/fee_payment.ts @@ -1,16 +1,14 @@ import { GAS_TOKEN_ADDRESS } from '@aztec/circuits.js'; -import { computePublicDataTreeLeafSlot } from '@aztec/circuits.js/hash'; +import { computePublicDataTreeLeafSlot, deriveStorageSlotInMap } from '@aztec/circuits.js/hash'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { GasTokenArtifact } from '@aztec/protocol-contracts/gas-token'; -import { computeSlotForMapping } from '../utils.js'; - /** * Computes the storage slot within the gas token contract for the balance of the fee payer. */ export function computeFeePayerBalanceStorageSlot(feePayer: AztecAddress) { - return computeSlotForMapping(GasTokenArtifact.storageLayout.balances.slot, feePayer); + return deriveStorageSlotInMap(GasTokenArtifact.storageLayout.balances.slot, feePayer); } /** diff --git a/yarn-project/simulator/src/utils.ts b/yarn-project/simulator/src/utils.ts deleted file mode 100644 index 380222fa578..00000000000 --- a/yarn-project/simulator/src/utils.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { pedersenHash } from '@aztec/foundation/crypto'; -import { type Fr } from '@aztec/foundation/fields'; - -/** - * Computes the resulting storage slot for an entry in a mapping. - * @param mappingSlot - The slot of the mapping within state. - * @param key - The key of the mapping. - * @returns The slot in the contract storage where the value is stored. - */ -export function computeSlotForMapping( - mappingSlot: Fr, - key: { - /** Serialize to a field. */ - toField: () => Fr; - }, -) { - return pedersenHash([mappingSlot, key.toField()]); -} diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 474ba90cbe9..949e181179a 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -213,9 +213,9 @@ export class TXE implements TypedOracle { return Promise.resolve(); } - async avmOpcodeEmitNoteHash(innerNoteHash: Fr) { + async avmOpcodeEmitNoteHash(slottedNoteHash: Fr) { const db = this.trees.asLatest(); - const noteHash = siloNoteHash(this.contractAddress, innerNoteHash); + const noteHash = siloNoteHash(this.contractAddress, slottedNoteHash); await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, [noteHash]); return Promise.resolve(); } @@ -240,9 +240,9 @@ export class TXE implements TypedOracle { await db.batchInsert(MerkleTreeId.NULLIFIER_TREE, siloedNullifiers, NULLIFIER_SUBTREE_HEIGHT); } - async addNoteHashes(contractAddress: AztecAddress, innerNoteHashes: Fr[]) { + async addNoteHashes(contractAddress: AztecAddress, slottedNoteHashes: Fr[]) { const db = this.trees.asLatest(); - const siloedNoteHashes = innerNoteHashes.map(innerNoteHash => siloNoteHash(contractAddress, innerNoteHash)); + const siloedNoteHashes = slottedNoteHashes.map(slottedNoteHash => siloNoteHash(contractAddress, slottedNoteHash)); await db.appendLeaves(MerkleTreeId.NOTE_HASH_TREE, siloedNoteHashes); } @@ -402,7 +402,7 @@ export class TXE implements TypedOracle { return Promise.resolve(notes); } - notifyCreatedNote(storageSlot: Fr, noteTypeId: NoteSelector, noteItems: Fr[], innerNoteHash: Fr, counter: number) { + notifyCreatedNote(storageSlot: Fr, noteTypeId: NoteSelector, noteItems: Fr[], slottedNoteHash: Fr, counter: number) { const note = new Note(noteItems); this.noteCache.addNewNote( { @@ -411,15 +411,15 @@ export class TXE implements TypedOracle { nonce: Fr.ZERO, // Nonce cannot be known during private execution. note, siloedNullifier: undefined, // Siloed nullifier cannot be known for newly created note. - innerNoteHash, + slottedNoteHash, }, counter, ); return Promise.resolve(); } - notifyNullifiedNote(innerNullifier: Fr, innerNoteHash: Fr, _counter: number) { - this.noteCache.nullifyNote(this.contractAddress, innerNullifier, innerNoteHash); + notifyNullifiedNote(innerNullifier: Fr, slottedNoteHash: Fr, _counter: number) { + this.noteCache.nullifyNote(this.contractAddress, innerNullifier, slottedNoteHash); return Promise.resolve(); } diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index 08dfa7fca25..a71ec4a388d 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -442,14 +442,14 @@ export class TXEService { storageSlot: ForeignCallSingle, noteTypeId: ForeignCallSingle, note: ForeignCallArray, - innerNoteHash: ForeignCallSingle, + slottedNoteHash: ForeignCallSingle, counter: ForeignCallSingle, ) { this.typedOracle.notifyCreatedNote( fromSingle(storageSlot), NoteSelector.fromField(fromSingle(noteTypeId)), fromArray(note), - fromSingle(innerNoteHash), + fromSingle(slottedNoteHash), fromSingle(counter).toNumber(), ); return toForeignCallResult([toSingle(new Fr(0))]); @@ -457,12 +457,12 @@ export class TXEService { async notifyNullifiedNote( innerNullifier: ForeignCallSingle, - innerNoteHash: ForeignCallSingle, + slottedNoteHash: ForeignCallSingle, counter: ForeignCallSingle, ) { await this.typedOracle.notifyNullifiedNote( fromSingle(innerNullifier), - fromSingle(innerNoteHash), + fromSingle(slottedNoteHash), fromSingle(counter).toNumber(), ); return toForeignCallResult([toSingle(new Fr(0))]); @@ -511,8 +511,8 @@ export class TXEService { return toForeignCallResult([]); } - async avmOpcodeEmitNoteHash(innerNoteHash: ForeignCallSingle) { - await (this.typedOracle as TXE).avmOpcodeEmitNoteHash(fromSingle(innerNoteHash)); + async avmOpcodeEmitNoteHash(slottedNoteHash: ForeignCallSingle) { + await (this.typedOracle as TXE).avmOpcodeEmitNoteHash(fromSingle(slottedNoteHash)); return toForeignCallResult([]); }