From 83621f27e7af9cab5cb583d0792865c1aae2c9f6 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 30 Oct 2024 14:16:31 +0000 Subject: [PATCH 1/4] Extend private log to fixed size. --- .../src/core/libraries/ConstantsGen.sol | 1 + .../encrypted_event_emission.nr | 20 +- .../encrypted_logs/encrypted_note_emission.nr | 10 +- .../aztec/src/encrypted_logs/payload.nr | 190 ++++++++++++++---- .../aztec-nr/aztec/src/macros/notes/mod.nr | 25 +-- .../crates/types/src/constants.nr | 1 + .../l1_payload/encrypted_log_payload.test.ts | 31 ++- .../logs/l1_payload/encrypted_log_payload.ts | 45 ++++- yarn-project/circuits.js/src/constants.gen.ts | 1 + 9 files changed, 243 insertions(+), 81 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 8c42fe769d9d..5bc176a13e54 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -92,6 +92,7 @@ library Constants { uint256 internal constant FUNCTION_SELECTOR_NUM_BYTES = 4; uint256 internal constant INITIALIZATION_SLOT_SEPARATOR = 1000000000; uint256 internal constant INITIAL_L2_BLOCK_NUM = 1; + uint256 internal constant PRIVATE_LOG_SIZE_IN_BYTES = 608; uint256 internal constant BLOB_SIZE_IN_BYTES = 126976; uint256 internal constant ETHEREUM_SLOT_DURATION = 12; uint256 internal constant AZTEC_SLOT_DURATION = 24; diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr index 1ebb4b5e195c..85860c5450da 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_event_emission.nr @@ -2,7 +2,10 @@ use crate::{ context::PrivateContext, encrypted_logs::payload::compute_private_log_payload, event::event_interface::EventInterface, keys::getters::get_ovsk_app, oracle::random::random, }; -use dep::protocol_types::{address::AztecAddress, hash::sha256_to_field, public_keys::OvpkM}; +use dep::protocol_types::{ + address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_BYTES, hash::sha256_to_field, + public_keys::OvpkM, +}; /// Computes private event log payload and a log hash fn compute_payload_and_hash( @@ -12,22 +15,15 @@ fn compute_payload_and_hash( ovsk_app: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> ([u8; 416 + N * 32], Field) +) -> ([u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Event: EventInterface, { let contract_address: AztecAddress = context.this_address(); let plaintext = event.private_to_be_bytes(randomness); - // For event logs we never include public values prefix as there are never any public values - let encrypted_log: [u8; 416 + N * 32] = compute_private_log_payload( - contract_address, - ovsk_app, - ovpk, - recipient, - plaintext, - false, - ); + let encrypted_log = + compute_private_log_payload(contract_address, ovsk_app, ovpk, recipient, plaintext); let log_hash = sha256_to_field(encrypted_log); (encrypted_log, log_hash) } @@ -38,7 +34,7 @@ unconstrained fn compute_payload_and_hash_unconstrained( randomness: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> ([u8; 416 + N * 32], Field) +) -> ([u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Event: EventInterface, { diff --git a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr index ef5aa2696425..a0b81028f4e3 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/encrypted_note_emission.nr @@ -7,6 +7,7 @@ use crate::{ use dep::protocol_types::{ abis::note_hash::NoteHash, address::AztecAddress, + constants::PRIVATE_LOG_SIZE_IN_BYTES, hash::sha256_to_field, public_keys::{OvpkM, PublicKeys}, }; @@ -18,7 +19,7 @@ fn compute_payload_and_hash( ovsk_app: Field, ovpk: OvpkM, recipient: AztecAddress, -) -> (u32, [u8; 417 + N * 32], Field) +) -> (u32, [u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Note: NoteInterface, { @@ -34,9 +35,8 @@ where let plaintext = note.to_be_bytes(storage_slot); - // For note logs we always include public values prefix - let encrypted_log: [u8; 417 + N * 32] = - compute_private_log_payload(contract_address, ovsk_app, ovpk, recipient, plaintext, true); + let encrypted_log = + compute_private_log_payload(contract_address, ovsk_app, ovpk, recipient, plaintext); let log_hash = sha256_to_field(encrypted_log); (note_hash_counter, encrypted_log, log_hash) @@ -47,7 +47,7 @@ unconstrained fn compute_payload_and_hash_unconstrained( note: Note, ovpk: OvpkM, recipient: AztecAddress, -) -> (u32, [u8; 417 + N * 32], Field) +) -> (u32, [u8; PRIVATE_LOG_SIZE_IN_BYTES], Field) where Note: NoteInterface, { 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 fc2b0c34507c..8736432c1af3 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -1,6 +1,10 @@ use dep::protocol_types::{ - address::AztecAddress, constants::GENERATOR_INDEX__SYMMETRIC_KEY, - hash::poseidon2_hash_with_separator, point::Point, public_keys::OvpkM, scalar::Scalar, + address::AztecAddress, + constants::{GENERATOR_INDEX__SYMMETRIC_KEY, PRIVATE_LOG_SIZE_IN_BYTES}, + hash::poseidon2_hash_with_separator, + point::Point, + public_keys::OvpkM, + scalar::Scalar, }; use std::{ aes128::aes128_encrypt, embedded_curve_ops::fixed_base_scalar_mul as derive_public_key, @@ -14,13 +18,92 @@ use crate::{ }; use protocol_types::public_keys::AddressPoint; -fn compute_private_log_payload( +global PRIVATE_LOG_OVERHEAD_IN_BYTES: u32 = 336; + +// 1 byte for storage slot, 1 byte for note type id, allowing 6 bytes for custom note fields. +global MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES: u32 = 8 * 32; + +global MAX_PRIVATE_EVENT_LOG_PLAINTEXT_SIZE_IN_BYTES: u32 = + MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES - 32; // Reserve 1 field for address tag. + +// PRIVATE_LOG_SIZE_IN_BYTES +// - PRIVATE_LOG_OVERHEAD_IN_BYTES, consisting of: +// - 32 bytes for incoming_tag +// - 32 bytes for outgoing_tag +// - 32 bytes for eph_pk +// - 48 bytes for incoming_header +// - 48 bytes for outgoing_header +// - 144 bytes for outgoing_body +// - 16 + MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES for incoming_body, consisting of: +// - 1 byte for plaintext length +// - MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES for the actual plaintext and padded random values +// - 15 bytes for AES padding + +// Note: Update PRIVATE_LOG_SIZE_IN_BYTES in `constants.nr` if any of the above fields change. +// This value ideally should be set by the protocol, allowing users (or `aztec-nr`) to fit data within the defined size limits. +// Currently, we adjust this value as the structure changes, then update `constants.nr` to match. +// Once the structure is finalized with defined overhead and max note field sizes, this value will be fixed and should remain unaffected by further payload composition changes. + +pub fn compute_private_log_payload( + contract_address: AztecAddress, + ovsk_app: Field, + ovpk: OvpkM, + recipient: AztecAddress, + plaintext: [u8; P], +) -> [u8; PRIVATE_LOG_SIZE_IN_BYTES] { + let extended_plaintext: [u8; MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES + 1] = + extend_private_log_plaintext(plaintext); + compute_encrypted_log( + contract_address, + ovsk_app, + ovpk, + recipient, + extended_plaintext, + ) +} + +pub fn compute_event_log_payload( + contract_address: AztecAddress, + ovsk_app: Field, + ovpk: OvpkM, + recipient: AztecAddress, + plaintext: [u8; P], +) -> [u8; PRIVATE_LOG_SIZE_IN_BYTES] { + let extended_plaintext: [u8; MAX_PRIVATE_EVENT_LOG_PLAINTEXT_SIZE_IN_BYTES + 1] = + extend_private_log_plaintext(plaintext); + compute_encrypted_log( + contract_address, + ovsk_app, + ovpk, + recipient, + extended_plaintext, + ) +} + +pub fn compute_partial_public_log_payload( + contract_address: AztecAddress, + ovsk_app: Field, + ovpk: OvpkM, + recipient: AztecAddress, + plaintext: [u8; P], +) -> [u8; M] { + let extended_plaintext: [u8; M - PRIVATE_LOG_OVERHEAD_IN_BYTES] = + extend_private_log_plaintext(plaintext); + compute_encrypted_log( + contract_address, + ovsk_app, + ovpk, + recipient, + extended_plaintext, + ) +} + +fn compute_encrypted_log( contract_address: AztecAddress, ovsk_app: Field, ovpk: OvpkM, recipient: AztecAddress, plaintext: [u8; P], - include_public_values_prefix: bool, ) -> [u8; M] { let (eph_sk, eph_pk) = generate_ephemeral_key_pair(); @@ -34,30 +117,36 @@ fn compute_private_log_payload( let outgoing_body_ciphertext: [u8; 144] = compute_outgoing_body_ciphertext(recipient, fr_to_fq(ovsk_app), eph_sk, eph_pk); - // If we include the prefix for number of public values, we need to add 1 byte to the offset - let mut offset = if include_public_values_prefix { 1 } else { 0 }; + let mut encrypted_bytes = [0; M]; + let mut offset = 0; - let mut encrypted_bytes: [u8; M] = [0; M]; // @todo We ignore the tags for now + // incoming_tag + // outgoing_tag offset += 64; + // eph_pk let eph_pk_bytes = point_to_bytes(eph_pk); for i in 0..32 { encrypted_bytes[offset + i] = eph_pk_bytes[i]; } - offset += 32; + + // incoming_header + // outgoing_header for i in 0..48 { encrypted_bytes[offset + i] = incoming_header_ciphertext[i]; encrypted_bytes[offset + 48 + i] = outgoing_header_ciphertext[i]; } - offset += 48 * 2; + + // outgoing_body for i in 0..144 { encrypted_bytes[offset + i] = outgoing_body_ciphertext[i]; } - offset += 144; + + // incoming_body // Then we fill in the rest as the incoming body ciphertext let size = M - offset; assert_eq(size, incoming_body_ciphertext.len(), "ciphertext length mismatch"); @@ -65,19 +154,35 @@ fn compute_private_log_payload( encrypted_bytes[offset + i] = incoming_body_ciphertext[i]; } - // Current unoptimized size of the encrypted log - // empty_prefix (1 byte) - // incoming_tag (32 bytes) - // outgoing_tag (32 bytes) - // eph_pk (32 bytes) - // incoming_header (48 bytes) - // outgoing_header (48 bytes) - // outgoing_body (144 bytes) - // incoming_body_fixed (64 bytes) - // incoming_body_variable (P + 16 bytes padding) encrypted_bytes } +// Prepend the plaintext length as the first byte, then copy the plaintext itself starting from the second byte. +// Fill the remaining bytes with random values to reach a fixed length of N. +fn extend_private_log_plaintext(plaintext: [u8; P]) -> [u8; N] { + let mut padded = unsafe { get_random_bytes() }; + padded[0] = P as u8; + for i in 0..P { + padded[i + 1] = plaintext[i]; + } + padded +} + +unconstrained fn get_random_bytes() -> [u8; N] { + let mut bytes = [0; N]; + let mut idx = 32; + let mut randomness = [0; 32]; + for i in 0..N { + if idx == 32 { + randomness = random().to_be_bytes(); + idx = 1; // Skip the first byte as it's always 0. + } + bytes[i] = randomness[idx]; + idx += 1; + } + bytes +} + /// 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 { @@ -166,10 +271,11 @@ pub fn compute_outgoing_body_ciphertext( mod test { use crate::encrypted_logs::payload::{ compute_incoming_body_ciphertext, compute_outgoing_body_ciphertext, - compute_private_log_payload, + compute_private_log_payload, MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES, }; use dep::protocol_types::{ - address::AztecAddress, point::Point, public_keys::OvpkM, scalar::Scalar, + address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_BYTES, point::Point, + public_keys::OvpkM, scalar::Scalar, }; use protocol_types::public_keys::AddressPoint; use std::embedded_curve_ops::fixed_base_scalar_mul as derive_public_key; @@ -199,22 +305,20 @@ mod test { 101, 153, 0, 0, 16, 39, ]; + let randomness = 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f; + let _ = OracleMock::mock("getRandomField").returns(randomness).times( + (MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES as u64 + 1 + 30) / 31, + ); + let eph_sk = 0x1358d15019d4639393d62b97e1588c095957ce74a1c32d6ec7d62fe6705d9538; - let _ = OracleMock::mock("getRandomField").returns(eph_sk); + let _ = OracleMock::mock("getRandomField").returns(eph_sk).times(1); let recipient = AztecAddress::from_field( 0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c, ); - let log: [u8; 448] = compute_private_log_payload( - contract_address, - ovsk_app, - ovpk_m, - recipient, - plaintext, - false, - ); - + let log = + compute_private_log_payload(contract_address, ovsk_app, ovpk_m, recipient, plaintext); // The following value was generated by `encrypted_log_payload.test.ts` // --> Run the test with AZTEC_GENERATE_TEST_DATA=1 flag to update test data. let encrypted_log_from_typescript = [ @@ -234,13 +338,21 @@ mod test { 175, 2, 42, 149, 78, 208, 176, 145, 50, 180, 152, 245, 55, 112, 40, 153, 180, 78, 54, 102, 119, 98, 56, 235, 246, 51, 179, 86, 45, 127, 18, 77, 187, 168, 41, 24, 232, 113, 149, 138, 148, 33, 143, 215, 150, 188, 105, 131, 254, 236, 199, 206, 56, 44, 130, 134, - 29, 99, 254, 69, 153, 146, 68, 234, 148, 148, 178, 38, 221, 182, 148, 178, 100, 13, 206, - 0, 91, 71, 58, 207, 26, 227, 190, 21, 143, 85, 138, 209, 202, 34, 142, 159, 121, 61, 9, - 57, 2, 48, 162, 89, 126, 14, 83, 173, 40, 247, 170, 154, 112, 12, 204, 48, 38, 7, 173, - 108, 38, 234, 20, 16, 115, 91, 106, 140, 121, 63, 99, 23, 247, 0, 148, 9, 163, 145, 43, - 21, 238, 47, 40, 204, 241, 124, 246, 201, 75, 114, 3, 1, 229, 197, 130, 109, 227, 158, - 133, 188, 125, 179, 220, 51, 170, 121, 175, 202, 243, 37, 103, 13, 27, 53, 157, 8, 177, - 11, 208, 120, 64, 211, 148, 201, 240, 56, + 29, 99, 254, 69, 153, 146, 68, 234, 148, 148, 178, 38, 221, 182, 103, 252, 139, 7, 246, + 132, 29, 232, 78, 102, 126, 28, 136, 8, 219, 180, 162, 14, 62, 71, 118, 40, 147, 93, 87, + 188, 231, 32, 93, 56, 193, 194, 197, 120, 153, 164, 139, 114, 18, 149, 2, 226, 19, 170, + 250, 249, 128, 56, 236, 93, 14, 101, 115, 20, 173, 73, 192, 53, 229, 7, 23, 59, 11, 176, + 9, 147, 175, 168, 206, 48, 127, 126, 76, 51, 211, 66, 232, 16, 132, 243, 14, 196, 181, + 118, 12, 71, 236, 250, 253, 71, 249, 122, 30, 23, 23, 19, 89, 47, 193, 69, 240, 164, 34, + 128, 110, 13, 133, 198, 7, 165, 14, 31, 239, 210, 146, 78, 67, 86, 32, 159, 244, 214, + 246, 121, 246, 233, 252, 20, 131, 221, 28, 146, 222, 119, 222, 162, 250, 252, 189, 18, + 147, 12, 142, 177, 222, 178, 122, 248, 113, 197, 40, 199, 152, 251, 91, 81, 243, 25, + 156, 241, 141, 60, 12, 99, 103, 169, 97, 32, 112, 37, 244, 255, 126, 46, 114, 226, 113, + 223, 249, 27, 3, 31, 41, 233, 28, 8, 23, 84, 99, 25, 186, 65, 33, 9, 35, 74, 16, 52, + 169, 48, 161, 134, 233, 242, 136, 39, 162, 105, 205, 43, 253, 183, 36, 138, 186, 87, 31, + 7, 248, 125, 227, 193, 172, 155, 98, 33, 61, 186, 158, 241, 192, 23, 28, 186, 100, 222, + 174, 19, 64, 224, 113, 251, 143, 45, 152, 81, 67, 116, 16, 95, 189, 83, 31, 124, 39, + 155, 142, 66, 0, 120, 197, 221, 161, 62, 75, 192, 255, 186, 200, 10, 135, 7, ]; assert_eq(encrypted_log_from_typescript, log); } diff --git a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr index 788bf550d5b2..2ac550045d77 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -7,8 +7,8 @@ use std::{ }; comptime global NOTE_HEADER_TYPE = type_of(NoteHeader::empty()); -// The following is a fixed ciphertext overhead as defined by `compute_private_log_payload` -comptime global NOTE_CIPHERTEXT_OVERHEAD: u32 = 353; +// The following is a fixed ciphertext overhead as defined by `compute_encrypted_log_payload` +comptime global NOTE_CIPHERTEXT_OVERHEAD: u32 = 352; /// A map from note type to (note_struct_definition, serialized_note_length, note_type_id, fields). /// `fields` is an array of tuples where each tuple contains the name of the field/struct member (e.g. `amount` @@ -392,7 +392,7 @@ comptime fn generate_multi_scalar_mul( /// fn encrypt_log(self, context: &mut PrivateContext, recipient_keys: aztec::protocol_types::public_keys::PublicKeys, recipient: aztec::protocol_types::address::AztecAddress) -> [Field; 17] { /// let ovsk_app: Field = context.request_ovsk_app(recipient_keys.ovpk_m.hash()); /// -/// let encrypted_log_bytes: [u8; 513] = aztec::encrypted_logs::payload::compute_private_log_payload( +/// let encrypted_log_bytes: [u8; 513] = aztec::encrypted_logs::payload::compute_partial_public_log_payload( /// context.this_address(), /// ovsk_app, /// recipient_keys.ovpk_m, @@ -466,13 +466,12 @@ comptime fn generate_setup_payload( fn encrypt_log(self, context: &mut PrivateContext, recipient_keys: aztec::protocol_types::public_keys::PublicKeys, recipient: aztec::protocol_types::address::AztecAddress) -> [Field; $encrypted_log_field_length] { let ovsk_app: Field = context.request_ovsk_app(recipient_keys.ovpk_m.hash()); - let encrypted_log_bytes: [u8; $encrypted_log_byte_length] = aztec::encrypted_logs::payload::compute_private_log_payload( + let encrypted_log_bytes: [u8; $encrypted_log_byte_length] = aztec::encrypted_logs::payload::compute_partial_public_log_payload( context.this_address(), ovsk_app, recipient_keys.ovpk_m, recipient, self.log_plaintext, - true ); aztec::utils::bytes::bytes_to_fields(encrypted_log_bytes) @@ -660,7 +659,9 @@ comptime fn generate_finalization_payload( // Each field contains 31 bytes so the length in fields is computed as ceil(setup_log_byte_length / 31) // --> we achieve rouding by adding 30 and then dividing without remainder let setup_log_field_length = (setup_log_byte_length + 30) / 31; - let finalization_log_byte_length = setup_log_byte_length + public_values_length * 32; + let public_values_field_length = public_values_length * 32; + let finalization_log_byte_length = + 1 /* public_values_length */ + setup_log_byte_length + public_values_field_length; ( quote { @@ -720,19 +721,19 @@ comptime fn generate_finalization_payload( // We append the public value to the log and emit it as unencrypted log let mut finalization_log = [0; $finalization_log_byte_length]; + // Populate the first byte with number of public values + finalization_log[0] = $public_values_length; + // Iterate over the partial log and copy it to the final log - for i in 1..setup_log.len() { - finalization_log[i] = setup_log[i]; + for i in 0..setup_log.len() { + finalization_log[i + 1] = setup_log[i]; } - // Now we populate the first byte with number of public values - finalization_log[0] = $public_values_length; - // Iterate over the public values and append them to the log for i in 0..$public_values_length { let public_value_bytes: [u8; 32] = self.public_values[i].to_be_bytes(); for j in 0..public_value_bytes.len() { - finalization_log[$setup_log_byte_length + i * 32 + j] = public_value_bytes[j]; + finalization_log[1 + $setup_log_byte_length + i * 32 + j] = public_value_bytes[j]; } } 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 2854cfd00682..f04f81825b79 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -133,6 +133,7 @@ global FUNCTION_SELECTOR_NUM_BYTES: Field = 4; // to be large enough so that it's ensured that it doesn't collide with storage slots of other variables. global INITIALIZATION_SLOT_SEPARATOR: Field = 1000_000_000; global INITIAL_L2_BLOCK_NUM: Field = 1; +global PRIVATE_LOG_SIZE_IN_BYTES: u32 = 608; // This is currently defined by aztec-nr/aztec/src/encrypted_logs/payload.nr. See the comment there for how this value is calculated. global BLOB_SIZE_IN_BYTES: Field = 31 * 4096; global ETHEREUM_SLOT_DURATION: u32 = 12; // AZTEC_SLOT_DURATION should be a multiple of ETHEREUM_SLOT_DURATION diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts index f53a8bf30713..76053b365838 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts @@ -2,6 +2,7 @@ import { AztecAddress, CompleteAddress, KeyValidationRequest, + PRIVATE_LOG_SIZE_IN_BYTES, computeAddressSecret, computeOvskApp, computePoint, @@ -122,16 +123,32 @@ describe('EncryptedLogPayload', () => { '0x25afb798ea6d0b8c1618e50fdeafa463059415013d3b7c75d46abf5e242be70c138af8799f2fba962549802469e12e3b7ba4c5f9c999c6421e05c73f45ec68481970dd8ce0250b677759dfc040f6edaf77c5827a7bcd425e66bcdec3fa7e59bc18dd22d6a4032eefe3a7a55703f583396596235f7c186e450c92981186ee74042e49e00996565114016a1a478309842ecbaf930fb716c3f498e7e10370631d7507f696b8b233de2c1935e43c793399586f532da5ff7c0356636a75acb862e964156e8a3e42bfca3663936ba98c7fd26386a14657c23b5f5146f1a94b6c4651542685ea16f17c580a7cc7c8ff2688dce9bde8bf1f50475f4c3281e1c33404ee0025f50db0733f719462b22eff03cec746bb9e3829ae3636c84fbccd2754b5a5a92087a5f41ccf94a03a2671cd341ba3264c45147e75d4ea96e3b1a58498550b89', ); - const encrypted = log - .encrypt(ephSk, recipientCompleteAddress.address, computePoint(recipientCompleteAddress.address), ovKeys) - .toString('hex'); - expect(encrypted).toMatchInlineSnapshot( - `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa70577790aeabcc2d81ec8d0c99e7f5d2bf2f1452025dc777a178404f851d93de818923f85187871d99bdf95d695eff0a9e09ba15153fc9b4d224b6e1e71dfbdcaab06c09d5b3c749bfebe1c0407eccd04f51bbb59142680c8a091b97fc6cbcf61f6c2af9b8ebc8f78537ab23fd0c5e818e4d42d459d265adb77c2ef829bf68f87f2c47b478bb57ae7e41a07643f65c353083d557b94e31da4a2a13127498d2eb3f0346da5eed2e9bc245aaf022a954ed0b09132b498f537702899b44e3666776238ebf633b3562d7f124dbba82918e871958a94218fd796bc6983feecc7ce382c82861d63fe45999244ea9494b226ddb694b2640dce005b473acf1ae3be158f558ad1ca228e9f793d09390230a2597e0e53ad28f7aa9a700ccc302607ad6c26ea1410735b6a8c793f6317f7009409a3912b15ee2f28ccf17cf6c94b720301e5c5826de39e85bc7db3dc33aa79afcaf325670d1b359d08b10bd07840d394c9f038"`, + const fixedRand = (len: number) => { + // The random values in the noir test file after the overhead are [1, 2, ..., 31, 0, 1, 2, ..., 31]. + const offset = plaintext.length + 1; + return Buffer.from( + Array(len) + .fill(0) + .map((_, i) => 1 + ((offset + i) % 31)), + ); + }; + + const encrypted = log.encrypt( + ephSk, + recipientCompleteAddress.address, + computePoint(recipientCompleteAddress.address), + ovKeys, + fixedRand, ); + expect(encrypted.length).toBe(PRIVATE_LOG_SIZE_IN_BYTES); - const byteArrayString = `[${encrypted.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; + const encryptedStr = encrypted.toString('hex'); + expect(encryptedStr).toMatchInlineSnapshot( + `"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008d460c0e434d846ec1ea286e4090eb56376ff27bddc1aacae1d856549f701fa70577790aeabcc2d81ec8d0c99e7f5d2bf2f1452025dc777a178404f851d93de818923f85187871d99bdf95d695eff0a9e09ba15153fc9b4d224b6e1e71dfbdcaab06c09d5b3c749bfebe1c0407eccd04f51bbb59142680c8a091b97fc6cbcf61f6c2af9b8ebc8f78537ab23fd0c5e818e4d42d459d265adb77c2ef829bf68f87f2c47b478bb57ae7e41a07643f65c353083d557b94e31da4a2a13127498d2eb3f0346da5eed2e9bc245aaf022a954ed0b09132b498f537702899b44e3666776238ebf633b3562d7f124dbba82918e871958a94218fd796bc6983feecc7ce382c82861d63fe45999244ea9494b226ddb667fc8b07f6841de84e667e1c8808dbb4a20e3e477628935d57bce7205d38c1c2c57899a48b72129502e213aafaf98038ec5d0e657314ad49c035e507173b0bb00993afa8ce307f7e4c33d342e81084f30ec4b5760c47ecfafd47f97a1e171713592fc145f0a422806e0d85c607a50e1fefd2924e4356209ff4d6f679f6e9fc1483dd1c92de77dea2fafcbd12930c8eb1deb27af871c528c798fb5b51f3199cf18d3c0c6367a961207025f4ff7e2e72e271dff91b031f29e91c0817546319ba412109234a1034a930a186e9f28827a269cd2bfdb7248aba571f07f87de3c1ac9b62213dba9ef1c0171cba64deae1340e071fb8f2d98514374105fbd531f7c279b8e420078c5dda13e4bc0ffbac80a8707"`, + ); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data + const byteArrayString = `[${encryptedStr.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16))}]`; updateInlineTestData( 'noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr', 'encrypted_log_from_typescript', @@ -141,7 +158,7 @@ describe('EncryptedLogPayload', () => { const ivskM = new GrumpkinScalar(0x0d6e27b21c89a7632f7766e35cc280d43f75bea3898d7328400a5fefc804d462n); const addressSecret = computeAddressSecret(recipientCompleteAddress.getPreaddress(), ivskM); - const recreated = EncryptedLogPayload.decryptAsIncoming(Buffer.from(encrypted, 'hex'), addressSecret); + const recreated = EncryptedLogPayload.decryptAsIncoming(encrypted, addressSecret); expect(recreated?.toBuffer()).toEqual(log.toBuffer()); }); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts index e9231ce876e5..67c3109d082e 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts @@ -4,12 +4,14 @@ import { GrumpkinScalar, type KeyValidationRequest, NotOnCurveError, + PRIVATE_LOG_SIZE_IN_BYTES, Point, type PublicKey, computeOvskApp, derivePublicKeyFromSecretKey, } from '@aztec/circuits.js'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { randomBytes } from '@aztec/foundation/crypto'; +import { BufferReader, numToUInt8, serializeToBuffer } from '@aztec/foundation/serialize'; import { decrypt, encrypt } from './encryption_util.js'; import { derivePoseidonAESSecret } from './shared_secret_derivation.js'; @@ -22,6 +24,14 @@ const HEADER_SIZE = 48; // 128 bytes for the secret key, address and public key, and 16 bytes padding to follow PKCS#7 const OUTGOING_BODY_SIZE = 144; +const ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD = + 32 /* incoming_tag */ + + 32 /* outgoing_tag */ + + 32 /* eph_pk */ + + HEADER_SIZE /* incoming_header */ + + HEADER_SIZE /* outgoing_header */ + + OUTGOING_BODY_SIZE; /* outgoing_body */ + /** * Encrypted log payload with a tag used for retrieval by clients. */ @@ -50,6 +60,7 @@ export class EncryptedLogPayload { recipient: AztecAddress, ivpk: PublicKey, ovKeys: KeyValidationRequest, + rand: (len: number) => Buffer = randomBytes, ): Buffer { if (ivpk.isZero()) { throw new Error(`Attempting to encrypt an event log with a zero ivpk.`); @@ -66,7 +77,6 @@ export class EncryptedLogPayload { throw new Error(`Invalid outgoing header size: ${outgoingHeaderCiphertext.length}`); } - const incomingBodyCiphertext = encrypt(this.incomingBodyPlaintext, ephSk, ivpk); // The serialization of Fq is [high, low] check `outgoing_body.nr` const outgoingBodyPlaintext = serializeToBuffer(ephSk.hi, ephSk.lo, recipient, ivpk.toCompressedBuffer()); const outgoingBodyCiphertext = encrypt( @@ -80,15 +90,34 @@ export class EncryptedLogPayload { throw new Error(`Invalid outgoing body size: ${outgoingBodyCiphertext.length}`); } - return serializeToBuffer( + const overhead = serializeToBuffer( this.incomingTag, this.outgoingTag, ephPk.toCompressedBuffer(), incomingHeaderCiphertext, outgoingHeaderCiphertext, outgoingBodyCiphertext, - incomingBodyCiphertext, ); + if (overhead.length !== ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD) { + throw new Error( + `Invalid ciphertext overhead size. Expect ${ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD}. Got ${overhead.length}.`, + ); + } + + const numPaddedBytes = + PRIVATE_LOG_SIZE_IN_BYTES - + ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD - + 1 /* 1 byte for this.incomingBodyPlaintext.length */ - + 15 /* aes padding */ - + this.incomingBodyPlaintext.length; + const paddedIncomingBodyPlaintextWithLength = Buffer.concat([ + numToUInt8(this.incomingBodyPlaintext.length), + this.incomingBodyPlaintext, + rand(numPaddedBytes), + ]); + const incomingBodyCiphertext = encrypt(paddedIncomingBodyPlaintextWithLength, ephSk, ivpk); + + return serializeToBuffer(overhead, incomingBodyCiphertext); } /** @@ -122,7 +151,9 @@ export class EncryptedLogPayload { reader.readBytes(OUTGOING_BODY_SIZE); // The incoming can be of variable size, so we read until the end - const incomingBodyPlaintext = decrypt(reader.readToEnd(), ivsk, ephPk); + const decrypted = decrypt(reader.readToEnd(), ivsk, ephPk); + const length = decrypted.readUint8(0); + const incomingBodyPlaintext = decrypted.subarray(1, 1 + length); return new EncryptedLogPayload( incomingTag, @@ -192,7 +223,9 @@ export class EncryptedLogPayload { } // Now we decrypt the incoming body using the ephSk and recipientIvpk - const incomingBody = decrypt(reader.readToEnd(), ephSk, recipientIvpk); + const decryptedIncomingBody = decrypt(reader.readToEnd(), ephSk, recipientIvpk); + const length = decryptedIncomingBody.readUint8(0); + const incomingBody = decryptedIncomingBody.subarray(1, 1 + length); return new EncryptedLogPayload(incomingTag, outgoingTag, contractAddress, incomingBody); } catch (e: any) { diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 479e46bbfd36..c4150e9e851f 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -79,6 +79,7 @@ export const PRIVATE_KERNEL_RESET_INDEX = 20; export const FUNCTION_SELECTOR_NUM_BYTES = 4; export const INITIALIZATION_SLOT_SEPARATOR = 1000000000; export const INITIAL_L2_BLOCK_NUM = 1; +export const PRIVATE_LOG_SIZE_IN_BYTES = 608; export const BLOB_SIZE_IN_BYTES = 126976; export const ETHEREUM_SLOT_DURATION = 12; export const AZTEC_SLOT_DURATION = 24; From 453fcf5be95d7919713140bc6d745ca02dfbd379 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 30 Oct 2024 18:08:24 +0000 Subject: [PATCH 2/4] Fix. --- .../aztec/src/encrypted_logs/payload.nr | 10 ++--- .../aztec-nr/aztec/src/macros/notes/mod.nr | 18 +++++--- .../l1_payload/encrypted_log_payload.test.ts | 2 +- .../logs/l1_payload/encrypted_log_payload.ts | 19 ++++++--- .../src/logs/l1_payload/l1_note_payload.ts | 41 +++++++++---------- .../src/note_processor/note_processor.test.ts | 4 +- .../pxe/src/note_processor/note_processor.ts | 9 +++- 7 files changed, 59 insertions(+), 44 deletions(-) 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 8736432c1af3..e795d8cdba2e 100644 --- a/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr +++ b/noir-projects/aztec-nr/aztec/src/encrypted_logs/payload.nr @@ -18,7 +18,7 @@ use crate::{ }; use protocol_types::public_keys::AddressPoint; -global PRIVATE_LOG_OVERHEAD_IN_BYTES: u32 = 336; +pub comptime global PRIVATE_LOG_OVERHEAD_IN_BYTES: u32 = 336; // 1 byte for storage slot, 1 byte for note type id, allowing 6 bytes for custom note fields. global MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES: u32 = 8 * 32; @@ -87,8 +87,7 @@ pub fn compute_partial_public_log_payload( recipient: AztecAddress, plaintext: [u8; P], ) -> [u8; M] { - let extended_plaintext: [u8; M - PRIVATE_LOG_OVERHEAD_IN_BYTES] = - extend_private_log_plaintext(plaintext); + let extended_plaintext: [u8; P + 1] = extend_private_log_plaintext(plaintext); compute_encrypted_log( contract_address, ovsk_app, @@ -274,8 +273,7 @@ mod test { compute_private_log_payload, MAX_PRIVATE_LOG_PLAINTEXT_SIZE_IN_BYTES, }; use dep::protocol_types::{ - address::AztecAddress, constants::PRIVATE_LOG_SIZE_IN_BYTES, point::Point, - public_keys::OvpkM, scalar::Scalar, + address::AztecAddress, point::Point, public_keys::OvpkM, scalar::Scalar, }; use protocol_types::public_keys::AddressPoint; use std::embedded_curve_ops::fixed_base_scalar_mul as derive_public_key; @@ -283,7 +281,7 @@ mod test { #[test] unconstrained fn test_encrypted_log_matches_typescript() { - // All the values in this test were copied over from `tagged_log.test.ts` + // All the values in this test were copied over from `encrypted_log_payload.test.ts` let contract_address = AztecAddress::from_field( 0x10f48cd9eff7ae5b209c557c70de2e657ee79166868676b787e9417e19260e04, ); diff --git a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr index a35b6aa73531..d02dfdc2cbfe 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -1,4 +1,8 @@ -use crate::{note::{note_getter_options::PropertySelector, note_header::NoteHeader}, prelude::Point}; +use crate::{ + encrypted_logs::payload::PRIVATE_LOG_OVERHEAD_IN_BYTES, + note::{note_getter_options::PropertySelector, note_header::NoteHeader}, + prelude::Point, +}; use protocol_types::meta::{flatten_to_fields, pack_from_fields}; use std::{ collections::umap::UHashMap, @@ -7,8 +11,6 @@ use std::{ }; comptime global NOTE_HEADER_TYPE = type_of(NoteHeader::empty()); -// The following is a fixed ciphertext overhead as defined by `compute_encrypted_log_payload` -comptime global NOTE_CIPHERTEXT_OVERHEAD: u32 = 352; /// A map from note type to (note_struct_definition, serialized_note_length, note_type_id, fields). /// `fields` is an array of tuples where each tuple contains the name of the field/struct member (e.g. `amount` @@ -436,7 +438,10 @@ comptime fn generate_setup_payload( get_setup_log_plaintext_body(s, log_plaintext_length, indexed_nullable_fields); // Then we compute values for `encrypt_log(...)` function - let encrypted_log_byte_length = NOTE_CIPHERTEXT_OVERHEAD + log_plaintext_length; + let encrypted_log_byte_length = PRIVATE_LOG_OVERHEAD_IN_BYTES + + log_plaintext_length /* log_plaintext */ + + 1 /* log_plaintext_length */ + + 15 /* AES padding */; // Each field contains 31 bytes so the length in fields is computed as ceil(encrypted_log_byte_length / 31) // --> we achieve rouding by adding 30 and then dividing without remainder let encrypted_log_field_length = (encrypted_log_byte_length + 30) / 31; @@ -655,7 +660,10 @@ comptime fn generate_finalization_payload( // Then we compute values for `encrypt_log(...)` function let setup_log_plaintext_length = indexed_fixed_fields.len() * 32 + 64; - let setup_log_byte_length = NOTE_CIPHERTEXT_OVERHEAD + setup_log_plaintext_length; + let setup_log_byte_length = PRIVATE_LOG_OVERHEAD_IN_BYTES + + setup_log_plaintext_length + + 1 /* log_plaintext_length */ + + 15 /* AES padding */; // Each field contains 31 bytes so the length in fields is computed as ceil(setup_log_byte_length / 31) // --> we achieve rouding by adding 30 and then dividing without remainder let setup_log_field_length = (setup_log_byte_length + 30) / 31; diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts index 76053b365838..16aa30591382 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.test.ts @@ -63,7 +63,7 @@ describe('EncryptedLogPayload', () => { }); }); - it('outgoing ciphertest matches Noir', () => { + it('outgoing cipher text matches Noir', () => { const ephSk = GrumpkinScalar.fromHighLow( new Fr(0x000000000000000000000000000000000f096b423017226a18461115fa8d34bbn), new Fr(0x00000000000000000000000000000000d0d302ee245dfaf2807e604eec4715fen), diff --git a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts index 67c3109d082e..4d15db05ab7f 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/encrypted_log_payload.ts @@ -24,7 +24,7 @@ const HEADER_SIZE = 48; // 128 bytes for the secret key, address and public key, and 16 bytes padding to follow PKCS#7 const OUTGOING_BODY_SIZE = 144; -const ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD = +const ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD_SIZE = 32 /* incoming_tag */ + 32 /* outgoing_tag */ + 32 /* eph_pk */ + @@ -32,6 +32,8 @@ const ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD = HEADER_SIZE /* outgoing_header */ + OUTGOING_BODY_SIZE; /* outgoing_body */ +const INCOMING_BODY_SIZE = PRIVATE_LOG_SIZE_IN_BYTES - ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD_SIZE; + /** * Encrypted log payload with a tag used for retrieval by clients. */ @@ -85,7 +87,6 @@ export class EncryptedLogPayload { ephPk, derivePoseidonAESSecret, ); - if (outgoingBodyCiphertext.length !== OUTGOING_BODY_SIZE) { throw new Error(`Invalid outgoing body size: ${outgoingBodyCiphertext.length}`); } @@ -98,15 +99,15 @@ export class EncryptedLogPayload { outgoingHeaderCiphertext, outgoingBodyCiphertext, ); - if (overhead.length !== ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD) { + if (overhead.length !== ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD_SIZE) { throw new Error( - `Invalid ciphertext overhead size. Expect ${ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD}. Got ${overhead.length}.`, + `Invalid ciphertext overhead size. Expected ${ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD_SIZE}. Got ${overhead.length}.`, ); } const numPaddedBytes = PRIVATE_LOG_SIZE_IN_BYTES - - ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD - + ENCRYPTED_LOG_CIPHERTEXT_OVERHEAD_SIZE - 1 /* 1 byte for this.incomingBodyPlaintext.length */ - 15 /* aes padding */ - this.incomingBodyPlaintext.length; @@ -116,6 +117,11 @@ export class EncryptedLogPayload { rand(numPaddedBytes), ]); const incomingBodyCiphertext = encrypt(paddedIncomingBodyPlaintextWithLength, ephSk, ivpk); + if (incomingBodyCiphertext.length !== INCOMING_BODY_SIZE) { + throw new Error( + `Invalid incoming body size. Expected ${INCOMING_BODY_SIZE}. Got ${incomingBodyCiphertext.length}`, + ); + } return serializeToBuffer(overhead, incomingBodyCiphertext); } @@ -151,7 +157,8 @@ export class EncryptedLogPayload { reader.readBytes(OUTGOING_BODY_SIZE); // The incoming can be of variable size, so we read until the end - const decrypted = decrypt(reader.readToEnd(), ivsk, ephPk); + const ciphertext = reader.readToEnd(); + const decrypted = decrypt(ciphertext, ivsk, ephPk); const length = decrypted.readUint8(0); const incomingBodyPlaintext = decrypted.subarray(1, 1 + length); diff --git a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts index 122f81a07ece..9f1b4fb7bcfe 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts @@ -60,9 +60,9 @@ export class L1NotePayload { } } - static decryptAsIncoming(log: Buffer, sk: Fq): L1NotePayload | undefined { - const { publicValues, encryptedLog } = parseLog(log); - const decryptedLog = EncryptedLogPayload.decryptAsIncoming(encryptedLog.data, sk); + static decryptAsIncoming(log: Buffer, sk: Fq, isFromPublic = false): L1NotePayload | undefined { + const { publicValues, encryptedLog } = parseLog(log, isFromPublic); + const decryptedLog = EncryptedLogPayload.decryptAsIncoming(encryptedLog, sk); if (!decryptedLog) { return undefined; } @@ -74,9 +74,9 @@ export class L1NotePayload { ); } - static decryptAsOutgoing(log: Buffer, sk: Fq): L1NotePayload | undefined { - const { publicValues, encryptedLog } = parseLog(log); - const decryptedLog = EncryptedLogPayload.decryptAsOutgoing(encryptedLog.data, sk); + static decryptAsOutgoing(log: Buffer, sk: Fq, isFromPublic = false): L1NotePayload | undefined { + const { publicValues, encryptedLog } = parseLog(log, isFromPublic); + const decryptedLog = EncryptedLogPayload.decryptAsOutgoing(encryptedLog, sk); if (!decryptedLog) { return undefined; } @@ -103,10 +103,10 @@ export class L1NotePayload { * @returns A random L1NotePayload object. */ static random(contract = AztecAddress.random()) { - const numPrivateNoteValues = randomInt(10) + 1; + const numPrivateNoteValues = randomInt(2) + 1; const privateNoteValues = Array.from({ length: numPrivateNoteValues }, () => Fr.random()); - const numPublicNoteValues = randomInt(10) + 1; + const numPublicNoteValues = randomInt(2) + 1; const publicNoteValues = Array.from({ length: numPublicNoteValues }, () => Fr.random()); return new L1NotePayload(contract, Fr.random(), NoteSelector.random(), privateNoteValues, publicNoteValues); @@ -150,20 +150,20 @@ export class L1NotePayload { * @param log - Log to be parsed. * @returns An object containing the public values and the encrypted log. */ -function parseLog(log: Buffer) { +function parseLog(log: Buffer, isFromPublic: boolean) { // First we remove padding bytes - const processedLog = removePaddingBytes(log); + const processedLog = isFromPublic ? removePaddingBytes(log) : log; const reader = new BufferReader(processedLog); // Then we extract public values from the log - const numPublicValues = reader.readUInt8(); + const numPublicValues = isFromPublic ? reader.readUInt8() : 0; const publicValuesLength = numPublicValues * Fr.SIZE_IN_BYTES; const encryptedLogLength = reader.remainingBytes() - publicValuesLength; // Now we get the buffer corresponding to the encrypted log - const encryptedLog = new EncryptedL2NoteLog(reader.readBytes(encryptedLogLength)); + const encryptedLog = reader.readBytes(encryptedLogLength); // At last we load the public values const publicValues = reader.readArray(numPublicValues, Fr); @@ -180,16 +180,15 @@ function parseLog(log: Buffer) { function removePaddingBytes(unprocessedLog: Buffer) { // Determine whether first 31 bytes of each 32 bytes block of bytes are 0 const is1FieldPerByte = unprocessedLog.every((byte, index) => index % 32 === 31 || byte === 0); + if (!is1FieldPerByte) { + throw new Error('Unprocessed log is not from public.'); + } - if (is1FieldPerByte) { - // We take every 32nd byte from the log and return the result - const processedLog = Buffer.alloc(unprocessedLog.length / 32); - for (let i = 0; i < processedLog.length; i++) { - processedLog[i] = unprocessedLog[31 + i * 32]; - } - - return processedLog; + // We take every 32nd byte from the log and return the result + const processedLog = Buffer.alloc(unprocessedLog.length / 32); + for (let i = 0; i < processedLog.length; i++) { + processedLog[i] = unprocessedLog[31 + i * 32]; } - return unprocessedLog; + return processedLog; } 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 db2d55caff70..ace72ece134f 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -66,9 +66,7 @@ class MockNoteRequest { encrypt(): EncryptedL2NoteLog { const ephSk = GrumpkinScalar.random(); const recipient = AztecAddress.random(); - const logWithoutNumPublicValues = this.logPayload.encrypt(ephSk, recipient, this.ivpk, this.ovKeys); - // We prefix the log with an empty byte indicating there are 0 public values. - const log = Buffer.concat([Buffer.alloc(1), logWithoutNumPublicValues]); + const log = this.logPayload.encrypt(ephSk, recipient, this.ivpk, this.ovKeys); return new EncryptedL2NoteLog(log); } diff --git a/yarn-project/pxe/src/note_processor/note_processor.ts b/yarn-project/pxe/src/note_processor/note_processor.ts index 2200bbab86cd..2edd0492b1af 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -146,11 +146,16 @@ export class NoteProcessor { // We iterate over both encrypted and unencrypted logs to decrypt the notes since partial notes are passed // via the unencrypted logs stream. for (const txFunctionLogs of [encryptedTxFunctionLogs, unencryptedTxFunctionLogs]) { + const isFromPublic = txFunctionLogs === unencryptedTxFunctionLogs; for (const functionLogs of txFunctionLogs) { for (const unprocessedLog of functionLogs.logs) { this.stats.seen++; - const incomingNotePayload = L1NotePayload.decryptAsIncoming(unprocessedLog.data, addressSecret); - const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(unprocessedLog.data, ovskM); + const incomingNotePayload = L1NotePayload.decryptAsIncoming( + unprocessedLog.data, + addressSecret, + isFromPublic, + ); + const outgoingNotePayload = L1NotePayload.decryptAsOutgoing(unprocessedLog.data, ovskM, isFromPublic); if (incomingNotePayload || outgoingNotePayload) { if (incomingNotePayload && outgoingNotePayload && !incomingNotePayload.equals(outgoingNotePayload)) { From 1edc8c068ca12c8ab07763f9e799030c7f3b7953 Mon Sep 17 00:00:00 2001 From: Leila Wang Date: Wed, 30 Oct 2024 21:29:49 +0000 Subject: [PATCH 3/4] Fix. --- .../circuit-types/src/logs/l1_payload/l1_note_payload.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts index 9f1b4fb7bcfe..b92f9be282f1 100644 --- a/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_payload/l1_note_payload.ts @@ -4,7 +4,6 @@ import { randomInt } from '@aztec/foundation/crypto'; import { type Fq, Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { EncryptedL2NoteLog } from '../encrypted_l2_note_log.js'; import { EncryptedLogPayload } from './encrypted_log_payload.js'; /** @@ -181,7 +180,7 @@ function removePaddingBytes(unprocessedLog: Buffer) { // Determine whether first 31 bytes of each 32 bytes block of bytes are 0 const is1FieldPerByte = unprocessedLog.every((byte, index) => index % 32 === 31 || byte === 0); if (!is1FieldPerByte) { - throw new Error('Unprocessed log is not from public.'); + return unprocessedLog; } // We take every 32nd byte from the log and return the result From fa04c565cfcfa12266a97ceda6629f7b391ecc69 Mon Sep 17 00:00:00 2001 From: sirasistant Date: Thu, 31 Oct 2024 13:29:28 +0000 Subject: [PATCH 4/4] hack around initialization issue --- noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr index d02dfdc2cbfe..2743290b8abc 100644 --- a/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr +++ b/noir-projects/aztec-nr/aztec/src/macros/notes/mod.nr @@ -729,8 +729,6 @@ comptime fn generate_finalization_payload( // We append the public value to the log and emit it as unencrypted log let mut finalization_log = [0; $finalization_log_byte_length]; - // Populate the first byte with number of public values - finalization_log[0] = $public_values_length; // Iterate over the partial log and copy it to the final log for i in 0..setup_log.len() { @@ -745,6 +743,9 @@ comptime fn generate_finalization_payload( } } + // Populate the first byte with number of public values + finalization_log[0] = $public_values_length; + // We emit the finalization log via the unencrypted logs stream self.context.emit_unencrypted_log(finalization_log);