From d631fd28a4d9181c2e5787bd04556b86f76a8825 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Mon, 13 May 2024 11:54:39 +0000 Subject: [PATCH] feat: Update the encrypted note log format --- .../aztec-nr/address-note/src/address_note.nr | 2 +- .../aztec/src/context/private_context.nr | 39 ++-- .../aztec/src/encrypted_logs/outgoing_body.nr | 2 +- noir-projects/aztec-nr/aztec/src/hash.nr | 56 ----- .../aztec-nr/aztec/src/oracle/logs.nr | 33 ++- .../aztec-nr/aztec/src/oracle/logs_traits.nr | 52 ++--- .../aztec-nr/value-note/src/value_note.nr | 2 +- .../src/subscription_note.nr | 2 +- .../src/types/card_note.nr | 2 +- .../src/ecdsa_public_key_note.nr | 2 +- .../src/public_key_note.nr | 2 +- .../src/types/token_note.nr | 2 +- .../token_contract/src/types/token_note.nr | 2 +- .../src/logs/encrypted_log_payload.test.ts | 42 ---- .../src/logs/encrypted_log_payload.ts | 209 ------------------ yarn-project/circuit-types/src/logs/index.ts | 6 +- .../l1_note_payload/encrypt_buffer.test.ts | 3 +- .../logs/l1_note_payload/encrypt_buffer.ts | 24 +- .../encrypted_log_header.test.ts | 0 .../encrypted_log_header.ts | 2 +- .../encrypted_log_incoming_body.test.ts | 2 +- .../encrypted_log_incoming_body.ts | 3 +- .../encrypted_log_outgoing_body.test.ts | 0 .../encrypted_log_outgoing_body.ts | 0 .../logs/l1_note_payload/encryption_utils.ts | 24 ++ .../l1_note_payload/l1_note_payload.test.ts | 49 ++-- .../logs/l1_note_payload/l1_note_payload.ts | 167 ++++++++++++-- .../logs/l1_note_payload/tagged_note.test.ts | 54 +++-- .../src/logs/l1_note_payload/tagged_note.ts | 77 ++++--- .../end-to-end/src/e2e_block_building.test.ts | 2 +- .../src/orchestrator/orchestrator.ts | 1 + .../src/note_processor/note_processor.test.ts | 12 +- .../pxe/src/note_processor/note_processor.ts | 3 +- .../simulator/src/acvm/oracle/oracle.ts | 30 +-- .../simulator/src/acvm/oracle/typed_oracle.ts | 11 +- .../src/client/client_execution_context.ts | 43 ++-- 36 files changed, 430 insertions(+), 532 deletions(-) delete mode 100644 yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts delete mode 100644 yarn-project/circuit-types/src/logs/encrypted_log_payload.ts rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_header.test.ts (100%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_header.ts (96%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_incoming_body.test.ts (97%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_incoming_body.ts (97%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_outgoing_body.test.ts (100%) rename yarn-project/circuit-types/src/logs/{ => l1_note_payload}/encrypted_log_outgoing_body.ts (100%) create mode 100644 yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts diff --git a/noir-projects/aztec-nr/address-note/src/address_note.nr b/noir-projects/aztec-nr/address-note/src/address_note.nr index 002c56de9348..7d1f23326b25 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -43,7 +43,7 @@ impl NoteInterface for AddressNote { fn broadcast(self, context: &mut PrivateContext, slot: Field) { let ivpk_m = get_ivpk_m(context, self.owner); // docs:start:encrypted - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/aztec-nr/aztec/src/context/private_context.nr b/noir-projects/aztec-nr/aztec/src/context/private_context.nr index 9768e71fcf0b..5bc62c68e5bc 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -1,10 +1,11 @@ use crate::{ context::{inputs::PrivateContextInputs, interface::ContextInterface}, messaging::process_l1_to_l2_message, - hash::{hash_args_array, ArgsHasher, compute_encrypted_log_hash, compute_unencrypted_log_hash}, + hash::{hash_args_array, ArgsHasher, compute_unencrypted_log_hash}, oracle::{ arguments, returns, call_private_function::call_private_function_internal, header::get_header_at, - logs::emit_encrypted_log, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, + logs::{emit_encrypted_log, compute_encrypted_log}, + logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, nullifier_key::{get_nullifier_keys, get_nullifier_keys_with_npk_m_hash, NullifierKeys}, enqueue_public_function_call::{ enqueue_public_function_call_internal, set_public_teardown_function_call_internal, @@ -13,6 +14,7 @@ use crate::{ } }; use dep::protocol_types::{ + hash::sha256_to_field, abis::{ function_selector::FunctionSelector, max_block_number::MaxBlockNumber, nullifier_key_validation_request::NullifierKeyValidationRequest, @@ -307,29 +309,34 @@ impl PrivateContext { self.unencrypted_log_preimages_length += 44 + N*32; } - pub fn emit_encrypted_log( + pub fn encrypt_and_emit_note( &mut self, contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, ivpk_m: GrumpkinPoint, preimage: [Field; N] - ) where [Field; N]: LensForEncryptedLog { - // TODO(1139): perform encryption in the circuit - // The oracle call should come last, but we require the encrypted value for now + ) where [Field; N]: LensForEncryptedLog { let counter = self.next_counter(); - let encrypted_log: [Field; M] = emit_encrypted_log( - contract_address, - storage_slot, - note_type_id, - ivpk_m, - preimage, - counter - ); - let log_hash = compute_encrypted_log_hash(encrypted_log); + // TODO(1139): perform encryption in the circuit + let encrypted_log: [u8; M] = compute_encrypted_log(contract_address, storage_slot, note_type_id, ivpk_m, preimage); + emit_encrypted_log(encrypted_log, counter); + + let log_hash = sha256_to_field(encrypted_log); + let side_effect = SideEffect { value: log_hash, counter }; self.encrypted_logs_hashes.push(side_effect); - let encrypted_log_byte_len = 112 + 32 * (N + 3); + + // Current unoptimized size of the encrypted log + // incoming_tag (32 bytes) + // outgoing_tag (32 bytes) + // eph_pk (64 bytes) + // incoming_header (48 bytes) + // outgoing_header (48 bytes) + // outgoing_body (176 bytes) + // incoming_body_fixed (64 bytes) + // incoming_body_variable (N * 32 bytes + 16 bytes padding) + let encrypted_log_byte_len = 32 + 32 + 64 + 48 + 48 + 176 + 64 + N * 32 + 16; // + processed log len (4) self.encrypted_log_preimages_length += encrypted_log_byte_len + 4; } 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 5cd8c736b7db..162c4630bd5d 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 @@ -75,7 +75,7 @@ mod test { use crate::{ note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption}, - oracle::{unsafe_rand::unsafe_rand, nullifier_key::get_app_nullifier_secret_key, get_public_key::get_public_key}, + oracle::{unsafe_rand::unsafe_rand, nullifier_key::get_app_nullifier_secret_key}, context::PrivateContext, hash::poseidon2_hash }; diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index 989ecfb0f314..6ffce090f4bf 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -12,24 +12,6 @@ pub fn compute_secret_hash(secret: Field) -> Field { pedersen_hash([secret], GENERATOR_INDEX__SECRET_HASH) } -pub fn compute_encrypted_log_hash(encrypted_log: [Field; M]) -> Field where [Field; N]: LensForEncryptedLog { - let mut bytes = [0; L]; - // Note that bytes.append(encrypted_log[i].to_be_bytes(31)) results in bound error - for i in 0..M - 1 { - let to_add = encrypted_log[i].to_be_bytes(31); - for j in 0..31 { - bytes[i*31 + j] = to_add[j]; - } - } - // can't assign as L - not in scope error for: L-31*(M-1) - let num_bytes = bytes.len() as u32 - 31 * (M - 1); - let to_add_final = encrypted_log[M - 1].to_be_bytes(num_bytes); - for j in 0..num_bytes { - bytes[(M-1)*31 + j] = to_add_final[j]; - } - sha256_to_field(bytes) -} - pub fn compute_unencrypted_log_hash( contract_address: AztecAddress, event_selector: Field, @@ -167,44 +149,6 @@ fn compute_var_args_hash() { assert(hash == 0x05a1023fef839ac88731f49ae983e172c1b600a3c8f3393ad0ac25d819ac0f0f); } -#[test] -fn compute_enc_log_hash_304() { - let input = [ - 0x0000000000000000000000000000000000000000000000000000000000000000, - 0x0021a0d4aa9989656b592187cf6da1965df53ab2ff2277421e663465cf20d3e9, - 0x00c3969cc350f3474f8187a33ac1317181961f5f94043b07ce888d85a5d20cb5, - 0x0058198041ed1547b056955b5141a5a8a1551b0c8d094255ec9daaf3604d9348, - 0x00247ad96df2e4d984cf795ed7316234743a681f824a45c46253de8bfde48850, - 0x007fc251f4ce44f4e9aba3dbf6567228be28fac85660156f2825ddb0b0577457, - 0x009315851323c6bc2aaa42e23fe5f3be97208f2d8167eafdfc5742d94f2f4dd4, - 0x00b938289e563b0fe01982cd9b8d9e33e3069046768ad01c0fb05e429e7b7909, - 0x00fbcc257a3211f705b471eee763b0f43876a2b2178fab6d2b09bd2b7e086584, - 0x000000000000008c3289b5793b7448f4d45ecde039d004b6f037cad10b5c2336 - ]; - let hash = compute_encrypted_log_hash(input); - assert(hash == 0x001e3c013994947fe28957a876bf1b2c3a69ac69cc92909efd4f2ae9b972f893); -} - -#[test] -fn compute_enc_log_hash_368() { - let input = [ - 0x0000000000000000000000000000000000000000000000000000000000000000, - 0x002190697d2a50e229a7a077e0951073f7d51e46679f10466153c308b63b1ea9, - 0x00543e346facc6799b94514c9d461bcc836c04b083b13c2e4544a39130473c1e, - 0x000df76d59526f8f953bcc7d9f77cdaefd36435931f0d7348f794bc275b42ded, - 0x00a6d390ee1723af7f7ac1ae4fc81a266b2370fe07040a36d06dbe242e02413e, - 0x00acbce15b6af1fbe94bd0f7b70f11768265dff77bfe63398f2a053efdfdf26d, - 0x00b8b131b9f42c689beb095ba4f4a836d4d15c9068d0422e9add6ca82b786329, - 0x00661a6a654b38f0f97d404ef5553e0efea9ed670561ae86685b31bbb2824fac, - 0x00113a6b58edfaec0065b365f66ba8d8aa68254b8690035e8d671a17a843f0a1, - 0x0023f2d2eae8c4449bac8f268a3e62a3faace1fe1401f0efdc8b0ccfbc8fb271, - 0x00cf6603f8c61993dd2f662c719671c61727a2f4e925fb988b23d31feccd77d9, - 0x0000000000a402a84b7294671799c38dd805f6a827a3a12633fdf91a57debe1f - ]; - let hash = compute_encrypted_log_hash(input); - assert(hash == 0x00a0d651ac0cbc01b72430fa6a05d91738595af6e0229347b4c9968223387aeb); -} - #[test] fn compute_unenc_log_hash_array() { let contract_address = AztecAddress::from_field(0x233a3e0df23b2b15b324194cb4a151f26c0b7333250781d34cc269d85dc334c6); diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index a1d933915eed..874aa083c562 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -2,31 +2,30 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; // TODO(1139): Should take encrypted data. // Currently returns encrypted data to be hashed -// = 112 + 32 * (N + 3) bytes = N + 7 fields +// = 480 + 32 * N bytes #[oracle(emitEncryptedLog)] -fn emit_encrypted_log_oracle( +fn emit_encrypted_log_oracle(_encrypted_note: [u8; M], _counter: u32) {} + +unconstrained pub fn emit_encrypted_log(encrypted_note: [u8; M], counter: u32) { + emit_encrypted_log_oracle(encrypted_note, counter) +} + +// = 480 + 32 * N bytes +#[oracle(computeEncryptedLog)] +fn compute_encrypted_log_oracle( _contract_address: AztecAddress, _storage_slot: Field, _note_type_id: Field, _encryption_pub_key: GrumpkinPoint, - _preimage: [Field; N], - _counter: u32 -) -> [Field; M] {} + _preimage: [Field; N] +) -> [u8; M] {} -unconstrained pub fn emit_encrypted_log( +unconstrained pub fn compute_encrypted_log( contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, ivpk_m: GrumpkinPoint, - preimage: [Field; N], - counter: u32 -) -> [Field; M] { - emit_encrypted_log_oracle( - contract_address, - storage_slot, - note_type_id, - ivpk_m, - preimage, - counter - ) + preimage: [Field; N] +) -> [u8; M] { + compute_encrypted_log_oracle(contract_address, storage_slot, note_type_id, ivpk_m, preimage) } diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr index 267979da364c..c6632f5a4d3d 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr @@ -1,8 +1,7 @@ use dep::protocol_types::address::AztecAddress; -// TODO: this is awful but since we can't have a fn that maps [Field; N] -> [Field; N+7] -// (where N is encrypted log preimage size and N+7 is encryption output size) -// and can't return slices from oracles, this at least compiles and runs +// TODO: this is awful but since we can't have a fn that maps [Field; N] -> [u8; 480 + N * 32] +// (where N is the note pre-image size and 480 + N * 32 is the encryption output size) // The fns for LensForEncryptedLog are never used, it's just to tell the compiler what the lens are // The to_bytes fn for ToBytesForUnencryptedLog is used to allow us to hash some generic T @@ -10,37 +9,38 @@ use dep::protocol_types::address::AztecAddress; // I could have omitted N from the trait, but wanted to keep it strictly for field arrs // TODO(1139): Once we enc inside the circuit, we will no longer need the oracle to return // anything, so we can remove this trait -trait LensForEncryptedLog { +trait LensForEncryptedLog { // N = note preimage input in fields - // M = encryption output len in fields (= N + 7 = N + 3 fields for addr, slot, type + 3.5 fields for AES data) - // L = encryption output len in bytes (= 32*M - 16) - fn output_fields(self: [Field; N]) -> [Field; M]; - fn output_bytes(self: [Field; N]) -> [u8; L]; + // M = encryption output len in bytes (= 480 + N * 32) + fn output_fields(self: [Field; N]) -> [Field; N]; + fn output_bytes(self: [Field; N]) -> [u8; M]; } -impl LensForEncryptedLog<1, 8, 240> for [Field; 1] { - fn output_fields(self) -> [Field; 8] {[self[0]; 8]} - fn output_bytes(self) -> [u8; 240] {[self[0] as u8; 240]} +impl LensForEncryptedLog<1, 512> for [Field; 1] { + fn output_fields(self) -> [Field; 1] {[self[0]; 1]} + fn output_bytes(self) -> [u8; 512] {[self[0] as u8; 512]} } -impl LensForEncryptedLog<2, 9, 272> for [Field; 2] { - fn output_fields(self) -> [Field; 9] {[self[0]; 9]} - fn output_bytes(self) -> [u8; 272] {[self[0] as u8; 272]} +impl LensForEncryptedLog<2, 544> for [Field; 2] { + fn output_fields(self) -> [Field; 2] {[self[0]; 2]} + fn output_bytes(self) -> [u8; 544] {[self[0] as u8; 544]} } -impl LensForEncryptedLog<3, 10, 304> for [Field; 3] { - fn output_fields(self) -> [Field; 10] {[self[0]; 10]} - fn output_bytes(self) -> [u8; 304] {[self[0] as u8; 304]} +impl LensForEncryptedLog<3, 576> for [Field; 3] { + fn output_fields(self) -> [Field; 3] {[self[0]; 3]} + fn output_bytes(self) -> [u8; 576] {[self[0] as u8; 576]} } -impl LensForEncryptedLog<4, 11, 336> for [Field; 4] { - fn output_fields(self) -> [Field; 11] {[self[0]; 11]} - fn output_bytes(self) -> [u8; 336] {[self[0] as u8; 336]} +impl LensForEncryptedLog<4, 608> for [Field; 4] { + fn output_fields(self) -> [Field; 4] {[self[0]; 4]} + fn output_bytes(self) -> [u8; 608] {[self[0] as u8; 608]} + } -impl LensForEncryptedLog<5, 12, 368> for [Field; 5] { - fn output_fields(self) -> [Field; 12] {[self[0]; 12]} - fn output_bytes(self) -> [u8; 368] {[self[0] as u8; 368]} +impl LensForEncryptedLog<5, 640> for [Field; 5] { + fn output_fields(self) -> [Field; 5] {[self[0]; 5]} + fn output_bytes(self) -> [u8; 640] {[self[0] as u8; 640]} } -impl LensForEncryptedLog<6, 13, 400> for [Field; 6] { - fn output_fields(self) -> [Field; 13] {[self[0]; 13]} - fn output_bytes(self) -> [u8; 400] {[self[0] as u8; 400]} +impl LensForEncryptedLog<6, 672> for [Field; 6] { + fn output_fields(self) -> [Field; 6] {[self[0]; 6]} + fn output_bytes(self) -> [u8; 672] {[self[0] as u8; 672]} + } // This trait defines the length of the inputs in bytes to 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 ac790864aa85..bcf121a87703 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -45,7 +45,7 @@ impl NoteInterface for ValueNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let ivpk_m = get_ivpk_m(context, self.owner); - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr index 665393b166fe..795d099b0a05 100644 --- a/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr +++ b/noir-projects/noir-contracts/contracts/app_subscription_contract/src/subscription_note.nr @@ -40,7 +40,7 @@ impl NoteInterface for SubscriptionNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let ivpk_m = get_ivpk_m(context, self.owner); - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr index aa2fea463d4e..e39b83c4aa28 100644 --- a/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr +++ b/noir-projects/noir-contracts/contracts/docs_example_contract/src/types/card_note.nr @@ -48,7 +48,7 @@ impl NoteInterface for CardNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let ivpk_m = get_ivpk_m(context, self.owner); - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr index eaadbbc60ace..fa34c813a0df 100644 --- a/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/ecdsa_account_contract/src/ecdsa_public_key_note.nr @@ -86,7 +86,7 @@ impl NoteInterface for EcdsaPublicKeyNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let ivpk_m = get_ivpk_m(context, self.owner); - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr index 74812ec7465b..0d509bfabef4 100644 --- a/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr +++ b/noir-projects/noir-contracts/contracts/schnorr_account_contract/src/public_key_note.nr @@ -40,7 +40,7 @@ impl NoteInterface for PublicKeyNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let ivpk_m = get_ivpk_m(context, self.owner); - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr index 8492b92a1fca..70d0e846fe70 100644 --- a/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_blacklist_contract/src/types/token_note.nr @@ -53,7 +53,7 @@ impl NoteInterface for TokenNote { // We only bother inserting the note if non-empty to save funds on gas. if !(self.amount == U128::from_integer(0)) { let ivpk_m = get_ivpk_m(context, self.owner); - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr index fa13976f9d58..f1bb2e99da57 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/token_note.nr @@ -54,7 +54,7 @@ impl NoteInterface for TokenNote { // TODO: (#5901) This will be changed a lot, as it should use the updated encrypted log format if !(self.amount == U128::from_integer(0)) { let ivpk_m = get_ivpk_m_with_npk_m_hash(self.npk_m_hash); - context.emit_encrypted_log( + context.encrypt_and_emit_note( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts b/yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts deleted file mode 100644 index bb62424faf04..000000000000 --- a/yarn-project/circuit-types/src/logs/encrypted_log_payload.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { AztecAddress, GrumpkinScalar } from '@aztec/circuits.js'; -import { Grumpkin } from '@aztec/circuits.js/barretenberg'; - -import { EncryptedLogPayload } from './encrypted_log_payload.js'; -import { L1NotePayload } from './l1_note_payload/l1_note_payload.js'; - -describe('encrypt and decrypt a full log', () => { - let grumpkin: Grumpkin; - - let ovsk: GrumpkinScalar; - let ivsk: GrumpkinScalar; - - let payload: EncryptedLogPayload; - let encrypted: Buffer; - - beforeAll(() => { - grumpkin = new Grumpkin(); - - ovsk = GrumpkinScalar.random(); - ivsk = GrumpkinScalar.random(); - - const ephSk = GrumpkinScalar.random(); - - const recipientAddress = AztecAddress.random(); - const ivpk = grumpkin.mul(Grumpkin.generator, ivsk); - - payload = EncryptedLogPayload.fromL1NotePayload(L1NotePayload.random()); - encrypted = payload.encrypt(ephSk, recipientAddress, ivpk, ovsk); - }); - - it('decrypt a log as incoming', () => { - const recreated = EncryptedLogPayload.decryptAsIncoming(encrypted, ivsk); - - expect(recreated.toBuffer()).toEqual(payload.toBuffer()); - }); - - it('decrypt a log as outgoing', () => { - const recreated = EncryptedLogPayload.decryptAsOutgoing(encrypted, ovsk); - - expect(recreated.toBuffer()).toEqual(payload.toBuffer()); - }); -}); diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_payload.ts b/yarn-project/circuit-types/src/logs/encrypted_log_payload.ts deleted file mode 100644 index a239bad2355d..000000000000 --- a/yarn-project/circuit-types/src/logs/encrypted_log_payload.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { - AztecAddress, - Fr, - type GrumpkinPrivateKey, - Point, - type PublicKey, - computeIvpkApp, - computeIvskApp, - computeOvskApp, - derivePublicKey, -} from '@aztec/circuits.js'; -import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; - -import { EncryptedLogHeader } from './encrypted_log_header.js'; -import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; -import { EncryptedLogOutgoingBody } from './encrypted_log_outgoing_body.js'; -import { type L1NotePayload } from './l1_note_payload/l1_note_payload.js'; -import { Note } from './l1_note_payload/note.js'; - -// A placeholder tag until we have a proper tag system in place. -const PLACEHOLDER_TAG = new Fr(33); - -// Both the incoming and the outgoing header are 48 bytes. -// 32 bytes for the address, and 16 bytes padding to follow PKCS#7 -const HEADER_SIZE = 48; - -// The outgoing body is constant size of 176 bytes. -// 160 bytes for the secret key, address, and public key, and 16 bytes padding to follow PKCS#7 -const OUTGOING_BODY_SIZE = 176; - -export class EncryptedLogPayload { - constructor( - /** - * A note as emitted from Noir contract. Can be used along with private key to compute nullifier. - */ - public note: Note, - /** - * Address of the contract this tx is interacting with. - */ - public contractAddress: AztecAddress, - /** - * Storage slot of the underlying note. - */ - public storageSlot: Fr, - /** - * Type identifier for the underlying note, required to determine how to compute its hash and nullifier. - */ - public noteTypeId: Fr, - ) {} - - toBuffer() { - return serializeToBuffer([this.note, this.contractAddress, this.storageSlot, this.noteTypeId]); - } - - static fromBuffer(buffer: Buffer | BufferReader): EncryptedLogPayload { - const reader = BufferReader.asReader(buffer); - return new EncryptedLogPayload( - reader.readObject(Note), - reader.readObject(AztecAddress), - Fr.fromBuffer(reader), - Fr.fromBuffer(reader), - ); - } - - static fromL1NotePayload(l1NotePayload: L1NotePayload) { - return new EncryptedLogPayload( - l1NotePayload.note, - l1NotePayload.contractAddress, - l1NotePayload.storageSlot, - l1NotePayload.noteTypeId, - ); - } - - /** - * Encrypts a note payload for a given recipient and sender. - * Creates an incoming log the the recipient using the recipient's ivsk, and - * an outgoing log for the sender using the sender's ovsk. - * - * @param ephSk - An ephemeral secret key used for the encryption - * @param recipient - The recipient address, retrievable by the sender for his logs - * @param ivpk - The incoming viewing public key of the recipient - * @param ovsk - The outgoing viewing secret key of the sender - * @returns A buffer containing the encrypted log payload - */ - public encrypt(ephSk: GrumpkinPrivateKey, recipient: AztecAddress, ivpk: PublicKey, ovsk: GrumpkinPrivateKey) { - const ephPk = derivePublicKey(ephSk); - const ovpk = derivePublicKey(ovsk); - - const header = new EncryptedLogHeader(this.contractAddress); - - const incomingHeaderCiphertext = header.computeCiphertext(ephSk, ivpk); - const outgoingHeaderCiphertext = header.computeCiphertext(ephSk, ovpk); - - const ivpkApp = computeIvpkApp(ivpk, this.contractAddress); - - const incomingBodyCiphertext = new EncryptedLogIncomingBody( - this.storageSlot, - this.noteTypeId, - this.note, - ).computeCiphertext(ephSk, ivpkApp); - - const ovskApp = computeOvskApp(ovsk, this.contractAddress); - - const outgoingBodyCiphertext = new EncryptedLogOutgoingBody(ephSk, recipient, ivpkApp).computeCiphertext( - ovskApp, - ephPk, - ); - - return Buffer.concat([ - PLACEHOLDER_TAG.toBuffer(), - PLACEHOLDER_TAG.toBuffer(), - ephPk.toBuffer(), - incomingHeaderCiphertext, - outgoingHeaderCiphertext, - outgoingBodyCiphertext, - incomingBodyCiphertext, - ]); - } - - /** - * Decrypts a ciphertext as an incoming log. - * - * This is executable by the recipient of the note, and uses the ivsk to decrypt the payload. - * The outgoing parts of the log are ignored entirely. - * - * Produces the same output as `decryptAsOutgoing`. - * - * @param ciphertext - The ciphertext for the log - * @param ivsk - The incoming viewing secret key, used to decrypt the logs - * @returns The decrypted log payload - */ - public static decryptAsIncoming(ciphertext: Buffer | bigint[], ivsk: GrumpkinPrivateKey) { - const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); - const reader = BufferReader.asReader(input); - - // We don't use the tags as part of the decryption here, we just gotta read to skip them. - reader.readObject(Fr); // incoming tag - reader.readObject(Fr); // outgoing tag - - const ephPk = reader.readObject(Point); - - const incomingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ivsk, ephPk); - - // Skipping the outgoing header and body - reader.readBytes(HEADER_SIZE); - reader.readBytes(OUTGOING_BODY_SIZE); - - // The incoming can be of variable size, so we read until the end - const incomingBodySlice = reader.readToEnd(); - - const ivskApp = computeIvskApp(ivsk, incomingHeader.address); - const incomingBody = EncryptedLogIncomingBody.fromCiphertext(incomingBodySlice, ivskApp, ephPk); - - return new EncryptedLogPayload( - incomingBody.note, - incomingHeader.address, - incomingBody.storageSlot, - incomingBody.noteTypeId, - ); - } - - /** - * Decrypts a ciphertext as an outgoing log. - * - * This is executable by the sender of the note, and uses the ovsk to decrypt the payload. - * The outgoing parts are decrypted to retrieve information that allows the sender to - * decrypt the incoming log, and learn about the note contents. - * - * Produces the same output as `decryptAsIncoming`. - * - * @param ciphertext - The ciphertext for the log - * @param ovsk - The outgoing viewing secret key, used to decrypt the logs - * @returns The decrypted log payload - */ - public static decryptAsOutgoing(ciphertext: Buffer | bigint[], ovsk: GrumpkinPrivateKey) { - const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); - const reader = BufferReader.asReader(input); - - // We don't use the tags as part of the decryption here, we just gotta read to skip them. - reader.readObject(Fr); // incoming tag - reader.readObject(Fr); // outgoing tag - - const ephPk = reader.readObject(Point); - - // Skip the incoming header - reader.readBytes(HEADER_SIZE); - - const outgoingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ovsk, ephPk); - - const ovskApp = computeOvskApp(ovsk, outgoingHeader.address); - const outgoingBody = EncryptedLogOutgoingBody.fromCiphertext(reader.readBytes(OUTGOING_BODY_SIZE), ovskApp, ephPk); - - // The incoming can be of variable size, so we read until the end - const incomingBodySlice = reader.readToEnd(); - - const incomingBody = EncryptedLogIncomingBody.fromCiphertext( - incomingBodySlice, - outgoingBody.ephSk, - outgoingBody.recipientIvpkApp, - ); - - return new EncryptedLogPayload( - incomingBody.note, - outgoingHeader.address, - incomingBody.storageSlot, - incomingBody.noteTypeId, - ); - } -} diff --git a/yarn-project/circuit-types/src/logs/index.ts b/yarn-project/circuit-types/src/logs/index.ts index 0e4b8200391b..6b4a4199c289 100644 --- a/yarn-project/circuit-types/src/logs/index.ts +++ b/yarn-project/circuit-types/src/logs/index.ts @@ -10,6 +10,6 @@ export * from './l1_note_payload/index.js'; export * from './tx_l2_logs.js'; export * from './unencrypted_l2_log.js'; export * from './extended_unencrypted_l2_log.js'; -export * from './encrypted_log_header.js'; -export * from './encrypted_log_incoming_body.js'; -export * from './encrypted_log_outgoing_body.js'; +export * from './l1_note_payload/encrypted_log_header.js'; +export * from './l1_note_payload/encrypted_log_incoming_body.js'; +export * from './l1_note_payload/encrypted_log_outgoing_body.js'; diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts index d8a0c4b9998f..3db91fe87a0e 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.test.ts @@ -3,7 +3,8 @@ import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { randomBytes } from '@aztec/foundation/crypto'; import { updateInlineTestData } from '@aztec/foundation/testing'; -import { decryptBuffer, deriveAESSecret, encryptBuffer } from './encrypt_buffer.js'; +import { decryptBuffer, encryptBuffer } from './encrypt_buffer.js'; +import { deriveAESSecret } from './encryption_utils.js'; describe('encrypt buffer', () => { let grumpkin: Grumpkin; diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts index 28262ff3ba62..dd8e954a7afe 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypt_buffer.ts @@ -1,30 +1,10 @@ -import { GeneratorIndex, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; +import { type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { sha256 } from '@aztec/foundation/crypto'; import { Point } from '@aztec/foundation/fields'; -import { numToUInt8 } from '@aztec/foundation/serialize'; import { createCipheriv, createDecipheriv } from 'browserify-cipher'; -/** - * Derive an AES secret key using Elliptic Curve Diffie-Hellman (ECDH) and SHA-256. - * The function takes in an ECDH public key, a private key, and a Grumpkin instance to compute - * the shared secret. The shared secret is then hashed using SHA-256 to produce the final - * AES secret key. - * - * @param secretKey - The secret key used to derive shared secret. - * @param publicKey - The public key used to derive shared secret. - * @returns A derived AES secret key. - * TODO(#5726): This function is called point_to_symmetric_key in Noir. I don't like that name much since point is not - * the only input of the function. Unify naming once we have a better name. - */ -export function deriveAESSecret(secretKey: GrumpkinPrivateKey, publicKey: PublicKey): Buffer { - const curve = new Grumpkin(); - const sharedSecret = curve.mul(publicKey, secretKey); - const secretBuffer = Buffer.concat([sharedSecret.toBuffer(), numToUInt8(GeneratorIndex.SYMMETRIC_KEY)]); - const hash = sha256(secretBuffer); - return hash; -} +import { deriveAESSecret } from './encryption_utils.js'; /** * Encrypt a given data buffer using the owner's public key and an ephemeral private key. diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_header.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.test.ts similarity index 100% rename from yarn-project/circuit-types/src/logs/encrypted_log_header.test.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.test.ts diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_header.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.ts similarity index 96% rename from yarn-project/circuit-types/src/logs/encrypted_log_header.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.ts index baef6cc264be..ac19b40cc3c8 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_log_header.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_header.ts @@ -1,7 +1,7 @@ import { AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; import { Aes128 } from '@aztec/circuits.js/barretenberg'; -import { deriveAESSecret } from './l1_note_payload/encrypt_buffer.js'; +import { deriveAESSecret } from './encryption_utils.js'; /** * An encrypted log header, containing the address of the log along with utility diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.test.ts similarity index 97% rename from yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.test.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.test.ts index bb1ab2c2588b..934621b508ed 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.test.ts @@ -3,7 +3,7 @@ import { Grumpkin } from '@aztec/circuits.js/barretenberg'; import { updateInlineTestData } from '@aztec/foundation/testing'; import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; -import { Note } from './l1_note_payload/note.js'; +import { Note } from './note.js'; describe('encrypt log incoming body', () => { let grumpkin: Grumpkin; diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.ts similarity index 97% rename from yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.ts index 45e1f5382bf0..3001d6963de2 100644 --- a/yarn-project/circuit-types/src/logs/encrypted_log_incoming_body.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_incoming_body.ts @@ -2,7 +2,8 @@ import { Fr, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js' import { Aes128 } from '@aztec/circuits.js/barretenberg'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { Note, deriveAESSecret } from './l1_note_payload/index.js'; +import { deriveAESSecret } from './encryption_utils.js'; +import { Note } from './note.js'; export class EncryptedLogIncomingBody { constructor(public storageSlot: Fr, public noteTypeId: Fr, public note: Note) {} diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.test.ts similarity index 100% rename from yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.test.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.test.ts diff --git a/yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.ts similarity index 100% rename from yarn-project/circuit-types/src/logs/encrypted_log_outgoing_body.ts rename to yarn-project/circuit-types/src/logs/l1_note_payload/encrypted_log_outgoing_body.ts diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts new file mode 100644 index 000000000000..2673af92c61d --- /dev/null +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/encryption_utils.ts @@ -0,0 +1,24 @@ +import { GeneratorIndex, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; +import { Grumpkin } from '@aztec/circuits.js/barretenberg'; +import { sha256 } from '@aztec/foundation/crypto'; +import { numToUInt8 } from '@aztec/foundation/serialize'; + +/** + * Derive an AES secret key using Elliptic Curve Diffie-Hellman (ECDH) and SHA-256. + * The function takes in an ECDH public key, a private key, and a Grumpkin instance to compute + * the shared secret. The shared secret is then hashed using SHA-256 to produce the final + * AES secret key. + * + * @param secretKey - The secret key used to derive shared secret. + * @param publicKey - The public key used to derive shared secret. + * @returns A derived AES secret key. + * TODO(#5726): This function is called point_to_symmetric_key in Noir. I don't like that name much since point is not + * the only input of the function. Unify naming once we have a better name. + */ +export function deriveAESSecret(secretKey: GrumpkinPrivateKey, publicKey: PublicKey): Buffer { + const curve = new Grumpkin(); + const sharedSecret = curve.mul(publicKey, secretKey); + const secretBuffer = Buffer.concat([sharedSecret.toBuffer(), numToUInt8(GeneratorIndex.SYMMETRIC_KEY)]); + const hash = sha256(secretBuffer); + return hash; +} diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts index 288e46db36d2..321a8e82985f 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.test.ts @@ -1,5 +1,6 @@ +import { AztecAddress } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { GrumpkinScalar, Point } from '@aztec/foundation/fields'; +import { GrumpkinScalar } from '@aztec/foundation/fields'; import { L1NotePayload } from './l1_note_payload.js'; @@ -16,22 +17,36 @@ describe('L1 Note Payload', () => { expect(L1NotePayload.fromBuffer(buf)).toEqual(payload); }); - it('convert to and from encrypted buffer', () => { - const payload = L1NotePayload.random(); - const ownerPrivKey = GrumpkinScalar.random(); - const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey); - const encrypted = payload.toEncryptedBuffer(ownerPubKey); - const decrypted = L1NotePayload.fromEncryptedBuffer(encrypted, ownerPrivKey); - expect(decrypted).not.toBeUndefined(); - expect(decrypted).toEqual(payload); - }); + describe('encrypt and decrypt a full log', () => { + let ovsk: GrumpkinScalar; + let ivsk: GrumpkinScalar; - it('return undefined if unable to decrypt the encrypted buffer', () => { - const payload = L1NotePayload.random(); - const ownerPubKey = Point.random(); - const encrypted = payload.toEncryptedBuffer(ownerPubKey); - const randomPrivKey = GrumpkinScalar.random(); - const decrypted = L1NotePayload.fromEncryptedBuffer(encrypted, randomPrivKey); - expect(decrypted).toBeUndefined(); + let payload: L1NotePayload; + let encrypted: Buffer; + + beforeAll(() => { + ovsk = GrumpkinScalar.random(); + ivsk = GrumpkinScalar.random(); + + const ephSk = GrumpkinScalar.random(); + + const recipientAddress = AztecAddress.random(); + const ivpk = grumpkin.mul(Grumpkin.generator, ivsk); + + payload = L1NotePayload.random(); + encrypted = payload.encrypt(ephSk, recipientAddress, ivpk, ovsk); + }); + + it('decrypt a log as incoming', () => { + const recreated = L1NotePayload.decryptAsIncoming(encrypted, ivsk); + + expect(recreated.toBuffer()).toEqual(payload.toBuffer()); + }); + + it('decrypt a log as outgoing', () => { + const recreated = L1NotePayload.decryptAsOutgoing(encrypted, ovsk); + + expect(recreated.toBuffer()).toEqual(payload.toBuffer()); + }); }); }); diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts index 463512782a70..2c7f11c59da3 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/l1_note_payload.ts @@ -1,10 +1,27 @@ -import { AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; -import { Fr, GrumpkinScalar } from '@aztec/foundation/fields'; +import { + AztecAddress, + type GrumpkinPrivateKey, + type PublicKey, + computeIvpkApp, + computeIvskApp, + computeOvskApp, + derivePublicKey, +} from '@aztec/circuits.js'; +import { Fr, Point } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; -import { decryptBuffer, encryptBuffer } from './encrypt_buffer.js'; +import { EncryptedLogHeader } from './encrypted_log_header.js'; +import { EncryptedLogIncomingBody } from './encrypted_log_incoming_body.js'; +import { EncryptedLogOutgoingBody } from './encrypted_log_outgoing_body.js'; import { Note } from './note.js'; +// Both the incoming and the outgoing header are 48 bytes. +// 32 bytes for the address, and 16 bytes padding to follow PKCS#7 +const HEADER_SIZE = 48; + +// The outgoing body is constant size of 176 bytes. +// 160 bytes for the secret key, address, and public key, and 16 bytes padding to follow PKCS#7 +const OUTGOING_BODY_SIZE = 176; /** * A class which wraps note data which is pushed on L1. * @remarks This data is required to compute a nullifier/to spend a note. Along with that this class contains @@ -21,7 +38,7 @@ export class L1NotePayload { */ public contractAddress: AztecAddress, /** - * Storage slot of the contract this tx is interacting with. + * Storage slot of the underlying note. */ public storageSlot: Fr, /** @@ -54,34 +71,136 @@ export class L1NotePayload { } /** - * Encrypt the L1NotePayload object using the owner's public key and the ephemeral private key. - * @param incomingViewingPubKey - Public key of the owner of the L1NotePayload object. - * @returns The encrypted L1NotePayload object. + * Create a random L1NotePayload object (useful for testing purposes). + * @returns A random L1NotePayload object. */ - public toEncryptedBuffer(incomingViewingPubKey: PublicKey): Buffer { - const ephSecretKey: GrumpkinPrivateKey = GrumpkinScalar.random(); - return encryptBuffer(this.toBuffer(), ephSecretKey, incomingViewingPubKey); + static random() { + return new L1NotePayload(Note.random(), AztecAddress.random(), Fr.random(), Fr.random()); } /** - * Decrypts the L1NotePayload object using the owner's incoming viewing secret key. - * @param data - Encrypted L1NotePayload object. - * @param incomingViewingSecretKey - Incoming viewing secret key of the owner of the L1NotePayload object. - * @returns Instance of L1NotePayload if the decryption was successful, undefined otherwise. + * Encrypts a note payload for a given recipient and sender. + * Creates an incoming log the the recipient using the recipient's ivsk, and + * an outgoing log for the sender using the sender's ovsk. + * + * @param ephSk - An ephemeral secret key used for the encryption + * @param recipient - The recipient address, retrievable by the sender for his logs + * @param ivpk - The incoming viewing public key of the recipient + * @param ovsk - The outgoing viewing secret key of the sender + * @returns A buffer containing the encrypted log payload */ - static fromEncryptedBuffer(data: Buffer, incomingViewingSecretKey: GrumpkinPrivateKey): L1NotePayload | undefined { - const buf = decryptBuffer(data, incomingViewingSecretKey); - if (!buf) { - return; - } - return L1NotePayload.fromBuffer(buf); + public encrypt(ephSk: GrumpkinPrivateKey, recipient: AztecAddress, ivpk: PublicKey, ovsk: GrumpkinPrivateKey) { + const ephPk = derivePublicKey(ephSk); + const ovpk = derivePublicKey(ovsk); + + const header = new EncryptedLogHeader(this.contractAddress); + + const incomingHeaderCiphertext = header.computeCiphertext(ephSk, ivpk); + const outgoingHeaderCiphertext = header.computeCiphertext(ephSk, ovpk); + + const ivpkApp = computeIvpkApp(ivpk, this.contractAddress); + + const incomingBodyCiphertext = new EncryptedLogIncomingBody( + this.storageSlot, + this.noteTypeId, + this.note, + ).computeCiphertext(ephSk, ivpkApp); + + const ovskApp = computeOvskApp(ovsk, this.contractAddress); + + const outgoingBodyCiphertext = new EncryptedLogOutgoingBody(ephSk, recipient, ivpkApp).computeCiphertext( + ovskApp, + ephPk, + ); + + return Buffer.concat([ + ephPk.toBuffer(), + incomingHeaderCiphertext, + outgoingHeaderCiphertext, + outgoingBodyCiphertext, + incomingBodyCiphertext, + ]); } /** - * Create a random L1NotePayload object (useful for testing purposes). - * @returns A random L1NotePayload object. + * Decrypts a ciphertext as an incoming log. + * + * This is executable by the recipient of the note, and uses the ivsk to decrypt the payload. + * The outgoing parts of the log are ignored entirely. + * + * Produces the same output as `decryptAsOutgoing`. + * + * @param ciphertext - The ciphertext for the log + * @param ivsk - The incoming viewing secret key, used to decrypt the logs + * @returns The decrypted log payload */ - static random() { - return new L1NotePayload(Note.random(), AztecAddress.random(), Fr.random(), Fr.random()); + public static decryptAsIncoming(ciphertext: Buffer | bigint[], ivsk: GrumpkinPrivateKey) { + const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + + const ephPk = reader.readObject(Point); + + const incomingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ivsk, ephPk); + + // Skipping the outgoing header and body + reader.readBytes(HEADER_SIZE); + reader.readBytes(OUTGOING_BODY_SIZE); + + // The incoming can be of variable size, so we read until the end + const incomingBodySlice = reader.readToEnd(); + + const ivskApp = computeIvskApp(ivsk, incomingHeader.address); + const incomingBody = EncryptedLogIncomingBody.fromCiphertext(incomingBodySlice, ivskApp, ephPk); + + return new L1NotePayload( + incomingBody.note, + incomingHeader.address, + incomingBody.storageSlot, + incomingBody.noteTypeId, + ); + } + + /** + * Decrypts a ciphertext as an outgoing log. + * + * This is executable by the sender of the note, and uses the ovsk to decrypt the payload. + * The outgoing parts are decrypted to retrieve information that allows the sender to + * decrypt the incoming log, and learn about the note contents. + * + * Produces the same output as `decryptAsIncoming`. + * + * @param ciphertext - The ciphertext for the log + * @param ovsk - The outgoing viewing secret key, used to decrypt the logs + * @returns The decrypted log payload + */ + public static decryptAsOutgoing(ciphertext: Buffer | bigint[], ovsk: GrumpkinPrivateKey) { + const input = Buffer.isBuffer(ciphertext) ? ciphertext : Buffer.from(ciphertext.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + + const ephPk = reader.readObject(Point); + + // Skip the incoming header + reader.readBytes(HEADER_SIZE); + + const outgoingHeader = EncryptedLogHeader.fromCiphertext(reader.readBytes(HEADER_SIZE), ovsk, ephPk); + + const ovskApp = computeOvskApp(ovsk, outgoingHeader.address); + const outgoingBody = EncryptedLogOutgoingBody.fromCiphertext(reader.readBytes(OUTGOING_BODY_SIZE), ovskApp, ephPk); + + // The incoming can be of variable size, so we read until the end + const incomingBodySlice = reader.readToEnd(); + + const incomingBody = EncryptedLogIncomingBody.fromCiphertext( + incomingBodySlice, + outgoingBody.ephSk, + outgoingBody.recipientIvpkApp, + ); + + return new L1NotePayload( + incomingBody.note, + outgoingHeader.address, + incomingBody.storageSlot, + incomingBody.noteTypeId, + ); } } diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts index bbd171f37020..392291c794b9 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.test.ts @@ -1,5 +1,6 @@ +import { AztecAddress } from '@aztec/circuits.js'; import { Grumpkin } from '@aztec/circuits.js/barretenberg'; -import { GrumpkinScalar, Point } from '@aztec/foundation/fields'; +import { GrumpkinScalar } from '@aztec/foundation/fields'; import { L1NotePayload } from './l1_note_payload.js'; import { TaggedNote } from './tagged_note.js'; @@ -18,24 +19,39 @@ describe('L1 Note Payload', () => { expect(TaggedNote.fromBuffer(buf).notePayload).toEqual(taggedNote.notePayload); }); - it('convert to and from encrypted buffer', () => { - const payload = L1NotePayload.random(); - const taggedNote = new TaggedNote(payload); - const ownerPrivKey = GrumpkinScalar.random(); - const ownerPubKey = grumpkin.mul(Grumpkin.generator, ownerPrivKey); - const encrypted = taggedNote.toEncryptedBuffer(ownerPubKey); - const decrypted = TaggedNote.fromEncryptedBuffer(encrypted, ownerPrivKey); - expect(decrypted).not.toBeUndefined(); - expect(decrypted?.notePayload).toEqual(payload); - }); + describe('encrypt and decrypt a full log', () => { + let ovsk: GrumpkinScalar; + let ivsk: GrumpkinScalar; - it('return undefined if unable to decrypt the encrypted buffer', () => { - const payload = L1NotePayload.random(); - const taggedNote = new TaggedNote(payload); - const ownerPubKey = Point.random(); - const encrypted = taggedNote.toEncryptedBuffer(ownerPubKey); - const randomPrivKey = GrumpkinScalar.random(); - const decrypted = TaggedNote.fromEncryptedBuffer(encrypted, randomPrivKey); - expect(decrypted).toBeUndefined(); + let taggedNote: TaggedNote; + let encrypted: Buffer; + + beforeAll(() => { + ovsk = GrumpkinScalar.random(); + ivsk = GrumpkinScalar.random(); + + const ephSk = GrumpkinScalar.random(); + + const recipientAddress = AztecAddress.random(); + const ivpk = grumpkin.mul(Grumpkin.generator, ivsk); + + const payload = L1NotePayload.random(); + + taggedNote = new TaggedNote(payload); + + encrypted = taggedNote.encrypt(ephSk, recipientAddress, ivpk, ovsk); + }); + + it('decrypt a log as incoming', () => { + const recreated = TaggedNote.decryptAsIncoming(encrypted, ivsk); + + expect(recreated?.toBuffer()).toEqual(taggedNote.toBuffer()); + }); + + it('decrypt a log as outgoing', () => { + const recreated = TaggedNote.decryptAsOutgoing(encrypted, ovsk); + + expect(recreated?.toBuffer()).toEqual(taggedNote.toBuffer()); + }); }); }); diff --git a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts index 4e698e382eb2..aa1e45a1aa1c 100644 --- a/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts +++ b/yarn-project/circuit-types/src/logs/l1_note_payload/tagged_note.ts @@ -1,4 +1,4 @@ -import { type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; +import { type AztecAddress, type GrumpkinPrivateKey, type PublicKey } from '@aztec/circuits.js'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -11,7 +11,11 @@ const PLACEHOLDER_TAG = new Fr(33); * Encrypted note payload with a tag used for retrieval by clients. */ export class TaggedNote { - constructor(public notePayload: L1NotePayload, public tag = PLACEHOLDER_TAG) {} + constructor( + public notePayload: L1NotePayload, + public incomingTag = PLACEHOLDER_TAG, + public outgoingTag = PLACEHOLDER_TAG, + ) {} /** * Deserializes the TaggedNote object from a Buffer. @@ -20,9 +24,10 @@ export class TaggedNote { */ static fromBuffer(buffer: Buffer | BufferReader): TaggedNote { const reader = BufferReader.asReader(buffer); - const tag = Fr.fromBuffer(reader); + const incomingTag = Fr.fromBuffer(reader); + const outgoingTag = Fr.fromBuffer(reader); const payload = L1NotePayload.fromBuffer(reader); - return new TaggedNote(payload, tag); + return new TaggedNote(payload, incomingTag, outgoingTag); } /** @@ -30,39 +35,53 @@ export class TaggedNote { * @returns Buffer representation of the TaggedNote object (unencrypted). */ public toBuffer(): Buffer { - return serializeToBuffer(this.tag, this.notePayload); + return serializeToBuffer(this.incomingTag, this.outgoingTag, this.notePayload); } - /** - * Encrypt the L1NotePayload object using the owner's public key and the ephemeral private key, then attach the tag. - * @param ownerPubKey - Public key of the owner of the TaggedNote object. - * @returns The encrypted TaggedNote object. - */ - public toEncryptedBuffer(ownerPubKey: PublicKey): Buffer { - const encryptedL1NotePayload = this.notePayload.toEncryptedBuffer(ownerPubKey); - return serializeToBuffer(this.tag, encryptedL1NotePayload); + static random(): TaggedNote { + return new TaggedNote(L1NotePayload.random()); } - /** - * Decrypts the L1NotePayload object using the owner's private key. - * @param data - Encrypted TaggedNote object. - * @param ownerPrivKey - Private key of the owner of the TaggedNote object. - * @returns Instance of TaggedNote if the decryption was successful, undefined otherwise. - */ - static fromEncryptedBuffer(data: Buffer, ownerPrivKey: GrumpkinPrivateKey): TaggedNote | undefined { - const reader = BufferReader.asReader(data); - const tag = Fr.fromBuffer(reader); - - const encryptedL1NotePayload = reader.readToEnd(); + public encrypt( + ephSk: GrumpkinPrivateKey, + recipient: AztecAddress, + ivpk: PublicKey, + ovsk: GrumpkinPrivateKey, + ): Buffer { + return serializeToBuffer( + this.incomingTag, + this.outgoingTag, + this.notePayload.encrypt(ephSk, recipient, ivpk, ovsk), + ); + } - const payload = L1NotePayload.fromEncryptedBuffer(encryptedL1NotePayload, ownerPrivKey); - if (!payload) { + static decryptAsIncoming(data: Buffer | bigint[], ivsk: GrumpkinPrivateKey) { + // Right now heavily abusing that we will likely fail if bad decryption + // as some field will likely end up not being in the field etc. + try { + const input = Buffer.isBuffer(data) ? data : Buffer.from(data.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + const incomingTag = Fr.fromBuffer(reader); + const outgoingTag = Fr.fromBuffer(reader); + const payload = L1NotePayload.decryptAsIncoming(reader.readToEnd(), ivsk); + return new TaggedNote(payload, incomingTag, outgoingTag); + } catch (e) { return; } - return new TaggedNote(payload, tag); } - static random(): TaggedNote { - return new TaggedNote(L1NotePayload.random()); + static decryptAsOutgoing(data: Buffer | bigint[], ovsk: GrumpkinPrivateKey) { + // Right now heavily abusing that we will likely fail if bad decryption + // as some field will likely end up not being in the field etc. + try { + const input = Buffer.isBuffer(data) ? data : Buffer.from(data.map((x: bigint) => Number(x))); + const reader = BufferReader.asReader(input); + const incomingTag = Fr.fromBuffer(reader); + const outgoingTag = Fr.fromBuffer(reader); + const payload = L1NotePayload.decryptAsOutgoing(reader.readToEnd(), ovsk); + return new TaggedNote(payload, incomingTag, outgoingTag); + } catch (e) { + return; + } } } 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 0688b129d9df..5146e3105d6b 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 @@ -275,7 +275,7 @@ describe('e2e_block_building', () => { expect(rct.status).toEqual('mined'); const decryptedLogs = tx.encryptedLogs .unrollLogs() - .map(l => TaggedNote.fromEncryptedBuffer(l.data, keys.masterIncomingViewingSecretKey)); + .map(l => TaggedNote.decryptAsIncoming(l.data, keys.masterIncomingViewingSecretKey)); const notevalues = decryptedLogs.map(l => l?.notePayload.note.items[0]); expect(notevalues[0]).toEqual(new Fr(10)); expect(notevalues[1]).toEqual(new Fr(11)); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index 302f35acd5c3..93c1c9f0112a 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -413,6 +413,7 @@ export class ProvingOrchestrator { .toBuffer() .equals(tx.processedTx.encryptedLogs.hash()) ) { + // @todo This rejection messages is never seen. Never making it out to the logs provingState.reject( `Encrypted logs hash mismatch: ${ tx.baseRollupInputs.kernelData.publicInputs.end.encryptedLogsHash 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 8c2ba5d8b014..e9c656c09d35 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.test.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.test.ts @@ -10,6 +10,7 @@ import { TaggedNote, } from '@aztec/circuit-types'; import { + AztecAddress, Fr, type GrumpkinPrivateKey, INITIAL_L2_BLOCK_NUM, @@ -18,7 +19,7 @@ import { deriveKeys, } from '@aztec/circuits.js'; import { pedersenHash } from '@aztec/foundation/crypto'; -import { Point } from '@aztec/foundation/fields'; +import { GrumpkinScalar, Point } from '@aztec/foundation/fields'; import { openTmpStore } from '@aztec/kv-store/utils'; import { type AcirSimulator } from '@aztec/simulator'; @@ -62,7 +63,7 @@ describe('Note Processor', () => { const logs: EncryptedFunctionL2Logs[] = []; for (let noteIndex = 0; noteIndex < MAX_NEW_NOTE_HASHES_PER_TX; ++noteIndex) { const isOwner = ownedDataIndices.includes(noteIndex); - const publicKey = isOwner ? ownerMasterIncomingViewingPublicKey : Point.random(); + const ivsk = isOwner ? ownerMasterIncomingViewingPublicKey : Point.random(); const note = (isOwner && ownedNotes[usedOwnedNote]) || TaggedNote.random(); usedOwnedNote += note === ownedNotes[usedOwnedNote] ? 1 : 0; newNotes.push(note); @@ -70,7 +71,12 @@ describe('Note Processor', () => { ownedL1NotePayloads.push(note.notePayload); } // const encryptedNote = - const log = note.toEncryptedBuffer(publicKey); + //const log = note.toEncryptedBuffer(publicKey); + + const ephSk = GrumpkinScalar.random(); + const ovsk = GrumpkinScalar.random(); + const recipient = AztecAddress.random(); + const log = note.encrypt(ephSk, recipient, ivsk, ovsk); // 1 tx containing 1 function invocation containing 1 log logs.push(new EncryptedFunctionL2Logs([new EncryptedL2Log(log)])); } diff --git a/yarn-project/pxe/src/note_processor/note_processor.ts b/yarn-project/pxe/src/note_processor/note_processor.ts index 3eaa6b3006d5..f8b4c13b571b 100644 --- a/yarn-project/pxe/src/note_processor/note_processor.ts +++ b/yarn-project/pxe/src/note_processor/note_processor.ts @@ -130,7 +130,8 @@ export class NoteProcessor { for (const functionLogs of txFunctionLogs) { for (const log of functionLogs.logs) { this.stats.seen++; - const taggedNote = TaggedNote.fromEncryptedBuffer(log.data, secretKey); + // @todo Issue(#6410) We should also try decrypting as outgoing if this fails. + const taggedNote = TaggedNote.decryptAsIncoming(log.data, secretKey); if (taggedNote?.notePayload) { const { notePayload: payload } = taggedNote; // We have successfully decrypted the data. diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index 400203a085b2..fc99f4583f45 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -306,33 +306,33 @@ export class Oracle { return newValues.map(toACVMField); } - emitEncryptedLog( + emitEncryptedLog(encryptedNote: ACVMField[], [counter]: ACVMField[]): void { + // Convert each field to a number and then to a buffer (1 byte is stored in 1 field) + const processedInput = Buffer.from(encryptedNote.map(fromACVMField).map(f => f.toNumber())); + this.typedOracle.emitEncryptedLog(processedInput, +counter); + } + + computeEncryptedLog( [contractAddress]: ACVMField[], [storageSlot]: ACVMField[], [noteTypeId]: ACVMField[], [publicKeyX]: ACVMField[], [publicKeyY]: ACVMField[], - log: ACVMField[], - [counter]: ACVMField[], + preimage: ACVMField[], ): ACVMField[] { const publicKey = new Point(fromACVMField(publicKeyX), fromACVMField(publicKeyY)); - const encLog = this.typedOracle.emitEncryptedLog( + const encLog = this.typedOracle.computeEncryptedLog( AztecAddress.fromString(contractAddress), Fr.fromString(storageSlot), Fr.fromString(noteTypeId), publicKey, - log.map(fromACVMField), - +counter, + preimage.map(fromACVMField), ); - // TODO(1139): We should encrypt in the circuit, but instead we inject here - // encryption output is 112 + 32 * (N + 3) bytes, for log len N - // so split into N + 7 fields (gross but avoids 300+ ACVMFields) - const encLogFields = []; - for (let i = 0; i < Math.ceil(encLog.length / 31); i++) { - encLogFields.push(toACVMField(encLog.subarray(31 * i, Math.min(31 * (i + 1), encLog.length)))); - } - - return encLogFields; + const bytes: ACVMField[] = []; + encLog.forEach(v => { + bytes.push(toACVMField(v)); + }); + return bytes; } emitUnencryptedLog( diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 7da764e01ffb..db86cc641187 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -183,15 +183,18 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('storageWrite'); } - emitEncryptedLog( + emitEncryptedLog(_encryptedNote: Buffer, _counter: number): void { + throw new OracleMethodNotAvailableError('emitEncryptedLog'); + } + + computeEncryptedLog( _contractAddress: AztecAddress, _storageSlot: Fr, _noteTypeId: Fr, _publicKey: PublicKey, - _log: Fr[], - _counter: number, + _preimage: Fr[], ): Buffer { - throw new OracleMethodNotAvailableError('emitEncryptedLog'); + throw new OracleMethodNotAvailableError('computeEncryptedLog'); } emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 6b381dd723c1..c8d0c1561c11 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -22,8 +22,8 @@ import { import { Aes128 } from '@aztec/circuits.js/barretenberg'; import { computePublicDataTreeLeafSlot, computeUniqueNoteHash, siloNoteHash } from '@aztec/circuits.js/hash'; import { type FunctionAbi, type FunctionArtifact, countArgumentsSize } from '@aztec/foundation/abi'; -import { type AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr, type Point } from '@aztec/foundation/fields'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { Fr, GrumpkinScalar, type Point } from '@aztec/foundation/fields'; import { applyStringFormatting, createDebugLogger } from '@aztec/foundation/log'; import { type NoteData, toACVMWitness } from '../acvm/index.js'; @@ -337,29 +337,42 @@ export class ClientExecutionContext extends ViewDataOracle { } /** - * Encrypt a note and emit it as a log. + * Emit encrypted note data + * @param encryptedNote - The encrypted note data. + * @param counter - The effects counter. + */ + public override emitEncryptedLog(encryptedNote: Buffer, counter: number) { + const encryptedLog = new EncryptedL2Log(encryptedNote); + this.encryptedLogs.push(new CountedLog(encryptedLog, counter)); + this.logsCache.addEncryptedLog(encryptedLog); + } + + /** + * Encrypt a note * @param contractAddress - The contract address of the note. * @param storageSlot - The storage slot the note is at. * @param noteTypeId - The type ID of the note. - * @param publicKey - The public key of the account that can decrypt the log. - * @param log - The log contents. + * @param ivpk - The master incoming viewing public key. + * @param preimage - The note preimage. */ - public override emitEncryptedLog( + public override computeEncryptedLog( contractAddress: AztecAddress, storageSlot: Fr, noteTypeId: Fr, - publicKey: Point, - log: Fr[], - counter: number, + ivpk: Point, + preimage: Fr[], ) { - const note = new Note(log); + const note = new Note(preimage); const l1NotePayload = new L1NotePayload(note, contractAddress, storageSlot, noteTypeId); const taggedNote = new TaggedNote(l1NotePayload); - const encryptedNote = taggedNote.toEncryptedBuffer(publicKey); - const encryptedLog = new EncryptedL2Log(encryptedNote); - this.encryptedLogs.push(new CountedLog(encryptedLog, counter)); - this.logsCache.addEncryptedLog(encryptedLog); - return encryptedNote; + + const ephSk = GrumpkinScalar.random(); + + // @todo Issue(#6410) Right now we are completely ignoring the outgoing log. Just drawing random data. + const ovsk = GrumpkinScalar.random(); + const recipient = AztecAddress.random(); + + return taggedNote.encrypt(ephSk, recipient, ivpk, ovsk); } /**