From 5193d025fec8bd8d5cffe931b26020de8191406a Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Mon, 22 Apr 2024 18:16:22 +0000 Subject: [PATCH 01/21] feat: WIP first commit - logs hashed inside contexts --- .../aztec-nr/address-note/src/address_note.nr | 6 +- .../aztec/src/context/private_context.nr | 91 ++++++++++-- .../aztec/src/context/public_context.nr | 18 ++- noir-projects/aztec-nr/aztec/src/hash.nr | 139 +++++++++++++++++- noir-projects/aztec-nr/aztec/src/lib.nr | 1 - noir-projects/aztec-nr/aztec/src/log.nr | 21 --- .../aztec-nr/aztec/src/oracle/logs.nr | 7 +- noir-projects/aztec-nr/aztec/src/prelude.nr | 2 +- .../aztec-nr/value-note/src/value_note.nr | 5 +- .../src/subscription_note.nr | 5 +- .../src/main.nr | 6 +- .../src/types/card_note.nr | 5 +- .../src/ecdsa_public_key_note.nr | 6 +- .../pending_note_hashes_contract/src/main.nr | 3 +- .../src/public_key_note.nr | 5 +- .../src/types/token_note.nr | 5 +- .../token_contract/src/types/balances_map.nr | 2 +- .../token_contract/src/types/token_note.nr | 5 +- .../crates/types/src/hash.nr | 12 +- .../simulator/src/acvm/oracle/oracle.ts | 31 +++- .../simulator/src/acvm/oracle/typed_oracle.ts | 8 +- .../src/client/client_execution_context.ts | 19 ++- .../src/client/private_execution.test.ts | 66 +++++++++ .../simulator/src/public/index.test.ts | 55 +++++-- .../src/public/public_execution_context.ts | 2 - 25 files changed, 420 insertions(+), 105 deletions(-) delete mode 100644 noir-projects/aztec-nr/aztec/src/log.nr 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 440cfdf3b6fb..03f7ee8462bc 100644 --- a/noir-projects/aztec-nr/address-note/src/address_note.nr +++ b/noir-projects/aztec-nr/address-note/src/address_note.nr @@ -1,6 +1,3 @@ -// docs:start:encrypted_import -use dep::aztec::log::emit_encrypted_log; -// docs:end:encrypted_import use dep::aztec::{ protocol_types::{address::AztecAddress, traits::Empty}, note::{note_header::NoteHeader, note_interface::NoteInterface, utils::compute_note_hash_for_consumption}, @@ -45,8 +42,7 @@ impl NoteInterface for AddressNote { fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); // docs:start:encrypted - emit_encrypted_log( - context, + context.emit_encrypted_log( (*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 a5c7f782e38d..e1f615e6a0ab 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -1,9 +1,11 @@ use crate::{ context::{inputs::PrivateContextInputs, interface::ContextInterface}, - messaging::process_l1_to_l2_message, hash::{hash_args_array, ArgsHasher}, + messaging::process_l1_to_l2_message, + hash::{hash_args_array, ArgsHasher, compute_encrypted_log_hash, compute_unencrypted_log_hash, ToBytesSlice}, oracle::{ arguments, returns, call_private_function::call_private_function_internal, enqueue_public_function_call::enqueue_public_function_call_internal, header::get_header_at, + logs::emit_encrypted_log, nullifier_key::{get_nullifier_keys, NullifierKeys} } }; @@ -28,14 +30,11 @@ use dep::protocol_types::{ MAX_ENCRYPTED_LOGS_PER_CALL, MAX_UNENCRYPTED_LOGS_PER_CALL }, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, - grumpkin_private_key::GrumpkinPrivateKey, header::Header, + grumpkin_private_key::GrumpkinPrivateKey, grumpkin_point::GrumpkinPoint, header::Header, messaging::l2_to_l1_message::L2ToL1Message, utils::reader::Reader, traits::{is_empty, Deserialize, Empty} }; -// TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) -// use dep::std::collections::vec::Vec; - // When finished, one can call .finish() to convert back to the abi struct PrivateContext { // docs:start:private-context @@ -264,25 +263,69 @@ impl PrivateContext { } // docs:end:consume_l1_to_l2_message - pub fn push_encrypted_log(&mut self, log_hash: Field) { - let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; - self.encrypted_logs_hashes.push(side_effect); - self.side_effect_counter = self.side_effect_counter + 1; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - } - // TODO: We might want to remove this since emitting unencrypted logs from private functions is violating privacy. // --> might be a better approach to force devs to make a public function call that emits the log if needed then // it would be less easy to accidentally leak information. // If we decide to keep this function around would make sense to wait for traits and then merge it with emit_unencrypted_log. - pub fn emit_unencrypted_log(&mut self, log: T) { + pub fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesSlice { let event_selector = 5; // TODO: compute actual event selector. - let log_hash = emit_unencrypted_log_private_internal(self.this_address(), event_selector, log); - + let contract_address = self.this_address(); + let log_hash = compute_unencrypted_log_hash( + contract_address, + event_selector, + log.to_be_bytes_slice() + ); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + // TODO(Miranda) add length + // call oracle + let _void = emit_unencrypted_log_private_internal(contract_address, event_selector, log); + } + + // This fn exists separately from emit_unencrypted_log because sha hashing the preimage + // is too large to compile (16,200 fields, 518,400 bytes) => the oracle hashes it + // It is ONLY used with contract_class_registerer_contract since we already assert correctness: + // - Contract class -> we will commit to the packed bytecode (currently a TODO) + // - Private function -> we provide a membership proof + // - Unconstrained function -> we provide a membership proof + // Ordinary logs are not protected by the above so this fn shouldn't be called by anything else + pub fn emit_contract_class_unencrypted_log(&mut self, log: [Field; N]) { + let event_selector = 5; // TODO: compute actual event selector. + let contract_address = self.this_address(); + let log_hash = emit_contract_class_unencrypted_log_private_internal( + contract_address, + event_selector, + log + ); + let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; + self.unencrypted_logs_hashes.push(side_effect); + self.side_effect_counter = self.side_effect_counter + 1; + // TODO(Miranda) add length + } + + pub fn emit_encrypted_log( + &mut self, + contract_address: AztecAddress, + storage_slot: Field, + note_type_id: Field, + encryption_pub_key: GrumpkinPoint, + preimage: [Field; N] + ) { + // TODO(1139): perform encryption in the circuit + // The oracle call should come last, but we require the encrypted value for now + let encrypted_log = emit_encrypted_log( + contract_address, + storage_slot, + note_type_id, + encryption_pub_key, + preimage + ); + let log_hash = compute_encrypted_log_hash(encrypted_log); + let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; + self.encrypted_logs_hashes.push(side_effect); + self.side_effect_counter = self.side_effect_counter + 1; + // TODO(Miranda) add length } pub fn call_private_function( @@ -614,3 +657,19 @@ unconstrained pub fn emit_unencrypted_log_private_internal( // https://github.com/AztecProtocol/aztec-packages/issues/885 emit_unencrypted_log_oracle_private(contract_address, event_selector, message) } + +#[oracle(emitContractClassUnencryptedLog)] +fn emit_contract_class_unencrypted_log_private( + contract_address: AztecAddress, + event_selector: Field, + message: [Field; N] +) -> Field {} + + +unconstrained pub fn emit_contract_class_unencrypted_log_private_internal( + contract_address: AztecAddress, + event_selector: Field, + message: [Field; N] +) -> Field { + emit_contract_class_unencrypted_log_private(contract_address, event_selector, message) +} diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index eab245d65abd..b539bf101a17 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -5,7 +5,7 @@ use crate::{ }, messaging::process_l1_to_l2_message, oracle::{arguments, public_call::call_public_function_internal, returns}, - hash::{hash_args, ArgsHasher} + hash::{hash_args, ArgsHasher, compute_unencrypted_log_hash, ToBytesSlice} }; use dep::protocol_types::{ abis::{ @@ -282,16 +282,20 @@ impl PublicContextInterface for PublicContext { self.push_new_nullifier(nullifier, 0) } - fn emit_unencrypted_log(&mut self, log: T) { + fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesSlice { let event_selector = 5; - let log_hash = emit_unencrypted_log_oracle(self.this_address(), event_selector, log); - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - // Once we hash inside circuits, this replaces push_unencrypted_log - // For now we need an oracle to get the hash + let contract_address = self.this_address(); + let log_hash = compute_unencrypted_log_hash( + contract_address, + event_selector, + log.to_be_bytes_slice() + ); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + // TODO(Miranda) add length + // Call oracle to broadcast log + let _void = emit_unencrypted_log_oracle(contract_address, event_selector, log); } fn call_public_function( diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index f6980f78c2cb..cb8201420431 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -2,18 +2,107 @@ use dep::protocol_types::{ address::{AztecAddress, EthAddress}, constants::{ GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET, GENERATOR_INDEX__NULLIFIER, ARGS_HASH_CHUNK_COUNT, - GENERATOR_INDEX__FUNCTION_ARGS, ARGS_HASH_CHUNK_LENGTH + GENERATOR_INDEX__FUNCTION_ARGS, ARGS_HASH_CHUNK_LENGTH, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS }, - traits::Hash, hash::{pedersen_hash, silo_nullifier} + traits::Hash, hash::{pedersen_hash, silo_nullifier, sha256_to_field, slice_sha256_to_field} }; -use dep::protocol_types::hash::sha256_to_field; - pub fn compute_secret_hash(secret: Field) -> Field { // TODO(#1205) This is probably not the right index to use pedersen_hash([secret], GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET) } +pub fn compute_encrypted_log_hash( + encrypted_log: [Field; 10] +) -> Field { + let mut bytes: [u8] = &[]; + // Note that bytes.append(encrypted_log[i].to_be_bytes(31)) results in bound error + for i in 0..9 { + let to_add = encrypted_log[i].to_be_bytes(31); + for j in 0..31 { + bytes = bytes.push_back(to_add[j]); + } + } + // messy hack to ensure we add the 304 bytes exactly + let to_add = encrypted_log[9].to_be_bytes(25); + for j in 0..25 { + bytes = bytes.push_back(to_add[j]); + } + slice_sha256_to_field(bytes) +} + +// TODO(Miranda) move trait def +trait ToBytesSlice { + fn to_be_bytes_slice(self) -> [u8]; +} + +impl ToBytesSlice for Field { + fn to_be_bytes_slice(self) -> [u8] { + self.to_be_bytes(32) + } +} + +impl ToBytesSlice for AztecAddress { + fn to_be_bytes_slice(self) -> [u8] { + self.to_field().to_be_bytes(32) + } +} + +impl ToBytesSlice for [Field; N] { + fn to_be_bytes_slice(self) -> [u8] { + let mut bytes: [u8] = &[]; + for i in 0..N { + // Note that bytes.append() results in bound error + let to_add = self[i].to_be_bytes(32); + for j in 0..32 { + bytes = bytes.push_back(to_add[j]); + } + } + bytes + } +} + +impl ToBytesSlice for str { + fn to_be_bytes_slice(self) -> [u8] { + self.as_bytes() + } +} + +pub fn compute_unencrypted_log_hash( + contract_address: AztecAddress, + event_selector: Field, + message_bytes: [u8] +) -> Field { + let mut hash_bytes: [u8] = message_bytes; + let len = message_bytes.len().to_field(); + let len_bytes = len.to_be_bytes(4); + // Address is converted to 32 bytes in ts + let mut prepend = contract_address.to_be_bytes_slice(); + // Event selector is 4 bytes long, see foundation/../selector.ts + prepend = prepend.append(event_selector.to_be_bytes(4)); + // prefix message with length of data in 4 bytes + // Note that .append(len_bytes) fails with bound error when compiling noir-contracts + prepend = prepend.append([len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]].as_slice()); + // Note that hash_bytes.append() results in bound error + // So does prepend.append(hash_bytes) when compiling noir-contracts + for i in 0..40 { + // prepend always has 40 bytes + hash_bytes = hash_bytes.push_front(prepend[39 - i]); + } + // HACK: when calling via real context, sha256_slice gives the wrong digest + // Appears to fail only for shorter inputs and with a long trace + // TODO(Miranda): remove slices here? + if (len == 32) { + let mut debug = [0; 72]; + for i in 0..72 { + debug[i] = hash_bytes[i]; + } + sha256_to_field(debug) + } else { + slice_sha256_to_field(hash_bytes) + } +} + pub fn compute_message_hash( sender: EthAddress, chain_id: Field, @@ -121,3 +210,45 @@ fn compute_var_args_hash() { let hash = input.hash(); assert(hash == 0x05a1023fef839ac88731f49ae983e172c1b600a3c8f3393ad0ac25d819ac0f0f); } + +#[test] +fn compute_enc_log_hash() { + 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_unenc_log_hash_array() { + let contract_address = AztecAddress::from_field(0x233a3e0df23b2b15b324194cb4a151f26c0b7333250781d34cc269d85dc334c6); + let event_selector = 5; + let log = [ + 0x20660de09f35f876e3e69d227b2a35166ad05f09d82d06366ec9b6f65a51fec2, + 0x1b52bfe3b8689761916f76dc3d38aa8810860db325cd39ca611eed980091f01c, + 0x2e559c4045c378a56ad13b9edb1e8de4e7ad3b3aa35cc7ba9ec77f7a68fa43a4, + 0x25d0f689c4a4178a29d59306f2675824d19be6d25e44fa03b03f49c263053dd2, + 0x2d513a722d6f352dc0961f156afdc5e31495b9f0e35cb069261a8e55e2df67fd + ]; + let hash = compute_unencrypted_log_hash(contract_address, event_selector, log.to_be_bytes_slice()); + assert(hash == 0x00846d6969c8c2f61d39cd2762efcb0abb14f88d59c2675910251ef2bcffe9a7); +} + +#[test] +fn compute_unenc_log_hash_addr() { + let contract_address = AztecAddress::from_field(0x233a3e0df23b2b15b324194cb4a151f26c0b7333250781d34cc269d85dc334c6); + let event_selector = 5; + let log = AztecAddress::from_field(0x26aa302d4715fd8a687453cb26d616b0768027bd54bcae56b09d908ecd9f8303); + let hash = compute_unencrypted_log_hash(contract_address, event_selector, log.to_be_bytes_slice()); + assert(hash == 0x00880a801230ea08c98a802a11b4786cba474513875f0fc69a615e81c5f9f21c); +} diff --git a/noir-projects/aztec-nr/aztec/src/lib.nr b/noir-projects/aztec-nr/aztec/src/lib.nr index 81390fb52eb9..6839d902d08b 100644 --- a/noir-projects/aztec-nr/aztec/src/lib.nr +++ b/noir-projects/aztec-nr/aztec/src/lib.nr @@ -3,7 +3,6 @@ mod deploy; mod hash; mod history; mod initializer; -mod log; mod messaging; mod note; mod oracle; diff --git a/noir-projects/aztec-nr/aztec/src/log.nr b/noir-projects/aztec-nr/aztec/src/log.nr deleted file mode 100644 index c3e3f361f590..000000000000 --- a/noir-projects/aztec-nr/aztec/src/log.nr +++ /dev/null @@ -1,21 +0,0 @@ -use crate::context::{PrivateContext, PublicContext}; -use crate::oracle; -use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; - -pub fn emit_encrypted_log( - context: &mut PrivateContext, - contract_address: AztecAddress, - storage_slot: Field, - note_type_id: Field, - encryption_pub_key: GrumpkinPoint, - log: [Field; N] -) { - let log_hash = oracle::logs::emit_encrypted_log( - contract_address, - storage_slot, - note_type_id, - encryption_pub_key, - log - ); - context.push_encrypted_log(log_hash); -} diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index 849a5726c9f2..c7fe0e34cb58 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -1,6 +1,7 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; -// TODO: Should take encrypted data. +// TODO(1139): Should take encrypted data. +// Currently returns encrypted data to be hashed (304 bytes) #[oracle(emitEncryptedLog)] fn emit_encrypted_log_oracle( _contract_address: AztecAddress, @@ -8,7 +9,7 @@ fn emit_encrypted_log_oracle( _note_type_id: Field, _encryption_pub_key: GrumpkinPoint, _preimage: [Field; N] -) -> Field {} +) -> [Field; 10] {} unconstrained pub fn emit_encrypted_log( contract_address: AztecAddress, @@ -16,7 +17,7 @@ unconstrained pub fn emit_encrypted_log( note_type_id: Field, encryption_pub_key: GrumpkinPoint, preimage: [Field; N] -) -> Field { +) -> [Field; 10] { emit_encrypted_log_oracle( contract_address, storage_slot, diff --git a/noir-projects/aztec-nr/aztec/src/prelude.nr b/noir-projects/aztec-nr/aztec/src/prelude.nr index 08177f65e7de..4a41dca51530 100644 --- a/noir-projects/aztec-nr/aztec/src/prelude.nr +++ b/noir-projects/aztec-nr/aztec/src/prelude.nr @@ -9,7 +9,7 @@ use crate::{ public_immutable::PublicImmutable, public_mutable::PublicMutable, private_set::PrivateSet, shared_immutable::SharedImmutable, storage::Storable }, - log::emit_encrypted_log, context::{PrivateContext, PackedReturns, FunctionReturns}, + context::{PrivateContext, PackedReturns, FunctionReturns}, note::{ note_header::NoteHeader, note_interface::NoteInterface, note_getter_options::NoteGetterOptions, note_viewer_options::NoteViewerOptions, 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 8875209b9de3..088188cba19a 100644 --- a/noir-projects/aztec-nr/value-note/src/value_note.nr +++ b/noir-projects/aztec-nr/value-note/src/value_note.nr @@ -2,7 +2,7 @@ use dep::aztec::{ protocol_types::{address::AztecAddress, traits::{Deserialize, Serialize}}, 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}, - log::emit_encrypted_log, hash::pedersen_hash, context::PrivateContext + hash::pedersen_hash, context::PrivateContext }; global VALUE_NOTE_LEN: Field = 3; // 3 plus a header. @@ -44,8 +44,7 @@ impl NoteInterface for ValueNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); - emit_encrypted_log( - context, + context.emit_encrypted_log( (*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 dc984c37338e..bc92ff6d68d2 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 @@ -1,4 +1,4 @@ -use dep::aztec::prelude::{AztecAddress, PrivateContext, NoteHeader, emit_encrypted_log, NoteInterface}; +use dep::aztec::prelude::{AztecAddress, PrivateContext, NoteHeader, NoteInterface}; use dep::aztec::{ note::utils::compute_note_hash_for_consumption, hash::pedersen_hash, oracle::{nullifier_key::get_app_nullifier_secret_key, get_public_key::get_public_key} @@ -39,8 +39,7 @@ impl NoteInterface for SubscriptionNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); - emit_encrypted_log( - context, + context.emit_encrypted_log( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr index 9857b3e6f3ef..28551655031c 100644 --- a/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr @@ -48,7 +48,7 @@ contract ContractClassRegisterer { public_bytecode_commitment ] ); - context.emit_unencrypted_log(event.serialize()); + context.emit_contract_class_unencrypted_log(event.serialize()); } #[aztec(private)] @@ -83,7 +83,7 @@ contract ContractClassRegisterer { function_data.metadata_hash ] ); - context.emit_unencrypted_log(event.serialize()); + context.emit_contract_class_unencrypted_log(event.serialize()); } #[aztec(private)] @@ -113,6 +113,6 @@ contract ContractClassRegisterer { function_data.metadata_hash ] ); - context.emit_unencrypted_log(event.serialize()); + context.emit_contract_class_unencrypted_log(event.serialize()); } } 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 617b24ef0254..1f1d0afe45b8 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 @@ -1,4 +1,4 @@ -use dep::aztec::prelude::{AztecAddress, NoteInterface, NoteHeader, PrivateContext, emit_encrypted_log}; +use dep::aztec::prelude::{AztecAddress, NoteInterface, NoteHeader, PrivateContext}; use dep::aztec::{ note::{utils::compute_note_hash_for_consumption}, oracle::{nullifier_key::get_app_nullifier_secret_key, get_public_key::get_public_key}, @@ -46,8 +46,7 @@ impl NoteInterface for CardNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); - emit_encrypted_log( - context, + context.emit_encrypted_log( (*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 22144f965af5..27decc7085e3 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 @@ -1,6 +1,5 @@ use dep::aztec::prelude::{ - AztecAddress, FunctionSelector, NoteHeader, NoteInterface, NoteGetterOptions, PrivateContext, - emit_encrypted_log + AztecAddress, FunctionSelector, NoteHeader, NoteInterface, NoteGetterOptions, PrivateContext }; use dep::aztec::{ @@ -89,8 +88,7 @@ impl NoteInterface for EcdsaPublicKeyNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); - emit_encrypted_log( - context, + context.emit_encrypted_log( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr index 09675c3f409a..f49828140bfd 100644 --- a/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/pending_note_hashes_contract/src/main.nr @@ -5,8 +5,7 @@ contract PendingNoteHashes { // Libs use dep::aztec::prelude::{ - AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, PrivateContext, Map, PrivateSet, - emit_encrypted_log + AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, PrivateContext, Map, PrivateSet }; use dep::value_note::{balance_utils, filter::filter_notes_min_sum, value_note::{VALUE_NOTE_LEN, ValueNote}}; use dep::aztec::context::{PublicContext, Context}; 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 39d25636db7a..b1ecda53b505 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 @@ -1,4 +1,4 @@ -use dep::aztec::prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext, emit_encrypted_log}; +use dep::aztec::prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}; use dep::aztec::{ note::utils::compute_note_hash_for_consumption, hash::pedersen_hash, oracle::{nullifier_key::get_app_nullifier_secret_key, get_public_key::get_public_key} @@ -39,8 +39,7 @@ impl NoteInterface for PublicKeyNote { // Broadcasts the note as an encrypted log on L1. fn broadcast(self, context: &mut PrivateContext, slot: Field) { let encryption_pub_key = get_public_key(self.owner); - emit_encrypted_log( - context, + context.emit_encrypted_log( (*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 3bd6b23d854c..7855aca75e6a 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 @@ -1,4 +1,4 @@ -use dep::aztec::prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext, emit_encrypted_log}; +use dep::aztec::prelude::{AztecAddress, NoteHeader, NoteInterface, PrivateContext}; use dep::aztec::{note::utils::compute_note_hash_for_consumption, hash::pedersen_hash}; use dep::aztec::oracle::{unsafe_rand::unsafe_rand, nullifier_key::get_app_nullifier_secret_key, get_public_key::get_public_key}; @@ -50,8 +50,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 encryption_pub_key = get_public_key(self.owner); - emit_encrypted_log( - context, + context.emit_encrypted_log( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr index 0a71bd9891e3..3862e9d0f38f 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/types/balances_map.nr @@ -1,6 +1,6 @@ use dep::aztec::prelude::{ AztecAddress, NoteGetterOptions, NoteViewerOptions, NoteHeader, NoteInterface, PrivateContext, - PrivateSet, Map, emit_encrypted_log + PrivateSet, Map }; use dep::aztec::{ context::{PublicContext, Context}, hash::pedersen_hash, 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 cd76d49659ce..119bc1d9f95f 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 @@ -1,6 +1,6 @@ use dep::aztec::prelude::{ AztecAddress, NoteInterface, NoteGetterOptions, NoteViewerOptions, NoteHeader, PrivateContext, - PrivateSet, Map, emit_encrypted_log + PrivateSet, Map }; use dep::aztec::{note::utils::compute_note_hash_for_consumption, hash::pedersen_hash}; use dep::aztec::oracle::{unsafe_rand::unsafe_rand, nullifier_key::get_app_nullifier_secret_key, get_public_key::get_public_key}; @@ -53,8 +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 encryption_pub_key = get_public_key(self.owner); - emit_encrypted_log( - context, + context.emit_encrypted_log( (*context).this_address(), slot, Self::get_note_type_id(), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index f43ee8f9b5cc..19184ea4c6f1 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -13,7 +13,7 @@ use crate::constants::{ use crate::traits::Hash; use crate::messaging::l2_to_l1_message::L2ToL1Message; use crate::merkle_tree::root::root_from_sibling_path; -use dep::std::hash::{pedersen_hash_with_separator, sha256}; +use dep::std::hash::{pedersen_hash_with_separator, sha256, sha256_slice}; pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { let sha256_hashed = sha256(bytes_to_hash); @@ -22,6 +22,13 @@ pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { hash_in_a_field } +pub fn slice_sha256_to_field(bytes_to_hash: [u8]) -> Field { + let sha256_hashed = sha256_slice(bytes_to_hash); + let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); + + hash_in_a_field +} + pub fn private_functions_root_from_siblings( selector: FunctionSelector, vk_hash: Field, @@ -187,6 +194,9 @@ fn smoke_sha256_to_field() { assert(result == 0x448ebbc9e1a31220a2f3830c18eef61b9bd070e5084b7fa2a359fe729184c7); + let result_slice = slice_sha256_to_field(full_buffer.as_slice()); + assert(result_slice == result); + // to show correctness of the current ver (truncate one byte) vs old ver (mod full bytes): let result_bytes = sha256(full_buffer); let truncated_field = crate::utils::field::field_from_bytes_32_trunc(result_bytes); diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index f1271b791dd0..576e42256089 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -289,16 +289,23 @@ export class Oracle { [publicKeyX]: ACVMField[], [publicKeyY]: ACVMField[], log: ACVMField[], - ): ACVMField { + ): ACVMField[] { const publicKey = new Point(fromACVMField(publicKeyX), fromACVMField(publicKeyY)); - const logHash = this.typedOracle.emitEncryptedLog( + const encLog = this.typedOracle.emitEncryptedLog( AztecAddress.fromString(contractAddress), Fr.fromString(storageSlot), Fr.fromString(noteTypeId), publicKey, log.map(fromACVMField), ); - return toACVMField(logHash); + // TODO(1139): We should encrypt in the circuit, but instead we inject here + // encryption output is 304 bytes, so split into 10 fields (gross but avoids 304 ACVMFields) + const encLogFields = []; + for (let i = 0; i < 10; i++) { + encLogFields.push(toACVMField(encLog.subarray(31 * i, Math.min(31 * (i + 1), encLog.length)))); + } + + return encLogFields; } emitUnencryptedLog([contractAddress]: ACVMField[], [eventSelector]: ACVMField[], message: ACVMField[]): ACVMField { @@ -309,7 +316,23 @@ export class Oracle { logPayload, ); - const logHash = this.typedOracle.emitUnencryptedLog(log); + this.typedOracle.emitUnencryptedLog(log); + return toACVMField(0); + } + + emitContractClassUnencryptedLog( + [contractAddress]: ACVMField[], + [eventSelector]: ACVMField[], + message: ACVMField[], + ): ACVMField { + const logPayload = Buffer.concat(message.map(fromACVMField).map(f => f.toBuffer())); + const log = new UnencryptedL2Log( + AztecAddress.fromString(contractAddress), + EventSelector.fromField(fromACVMField(eventSelector)), + logPayload, + ); + + const logHash = this.typedOracle.emitContractClassUnencryptedLog(log); return toACVMField(logHash); } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 70f57233af17..661b76d803ed 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -193,14 +193,18 @@ export abstract class TypedOracle { _noteTypeId: Fr, _publicKey: PublicKey, _log: Fr[], - ): Fr { + ): Buffer { throw new OracleMethodNotAvailableError('emitEncryptedLog'); } - emitUnencryptedLog(_log: UnencryptedL2Log): Fr { + emitUnencryptedLog(_log: UnencryptedL2Log): void { throw new OracleMethodNotAvailableError('emitUnencryptedLog'); } + emitContractClassUnencryptedLog(_log: UnencryptedL2Log): Fr { + throw new OracleMethodNotAvailableError('emitContractClassUnencryptedLog'); + } + callPrivateFunction( _targetContractAddress: AztecAddress, _functionSelector: FunctionSelector, diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 1363fe615e71..7e8d3e82be2d 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -327,7 +327,7 @@ export class ClientExecutionContext extends ViewDataOracle { const encryptedNote = taggedNote.toEncryptedBuffer(publicKey, this.curve); const encryptedLog = new EncryptedL2Log(encryptedNote); this.encryptedLogs.push(encryptedLog); - return Fr.fromBuffer(encryptedLog.hash()); + return encryptedNote; } /** @@ -338,6 +338,23 @@ export class ClientExecutionContext extends ViewDataOracle { this.unencryptedLogs.push(log); const text = log.toHumanReadable(); this.log.verbose(`Emitted unencrypted log: "${text.length > 100 ? text.slice(0, 100) + '...' : text}"`); + } + + /** + * Emit a contract class unencrypted log. + * This fn exists separately from emitUnencryptedLog because sha hashing the preimage + * is too large to compile (16,200 fields, 518,400 bytes) => the oracle hashes it. + * See private_context.nr + * @param log - The unencrypted log to be emitted. + */ + public override emitContractClassUnencryptedLog(log: UnencryptedL2Log) { + this.unencryptedLogs.push(log); + const text = log.toHumanReadable(); + this.log.verbose( + `Emitted unencrypted log from ContractClassRegisterer: "${ + text.length > 100 ? text.slice(0, 100) + '...' : text + }"`, + ); return Fr.fromBuffer(log.hash()); } diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 5cbf65ec7673..9117919c174c 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -214,8 +214,17 @@ describe('Private Execution test suite', () => { it('emits a field as an unencrypted log', async () => { const artifact = getFunctionArtifact(TestContractArtifact, 'emit_msg_sender'); const result = await runSimulator({ artifact, msgSender: owner }); + + const newUnencryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.unencryptedLogsHashes), + ); + expect(newUnencryptedLogs).toHaveLength(1); + const [functionLogs] = collectUnencryptedLogs(result); expect(functionLogs.logs).toHaveLength(1); + + const [unencryptedLog] = newUnencryptedLogs; + expect(unencryptedLog).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); // Test that the log payload (ie ignoring address, selector, and header) matches what we emitted expect(functionLogs.logs[0].data.subarray(-32).toString('hex')).toEqual(owner.toBuffer().toString('hex')); }); @@ -224,8 +233,16 @@ describe('Private Execution test suite', () => { const artifact = getFunctionArtifact(TestContractArtifact, 'emit_array_as_unencrypted_log'); const args = [times(5, () => Fr.random())]; const result = await runSimulator({ artifact, msgSender: owner, args }); + + const newUnencryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.unencryptedLogsHashes), + ); + expect(newUnencryptedLogs).toHaveLength(1); const [functionLogs] = collectUnencryptedLogs(result); expect(functionLogs.logs).toHaveLength(1); + + const [unencryptedLog] = newUnencryptedLogs; + expect(unencryptedLog).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); // Test that the log payload (ie ignoring address, selector, and header) matches what we emitted const expected = Buffer.concat(args[0].map(arg => arg.toBuffer())).toString('hex'); expect(functionLogs.logs[0].data.subarray(-32 * 5).toString('hex')).toEqual(expected); @@ -311,6 +328,14 @@ describe('Private Execution test suite', () => { newNote.note, ), ); + + const newEncryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), + ); + expect(newEncryptedLogs).toHaveLength(1); + + const [encryptedLog] = newEncryptedLogs; + expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); }); it('should run the create_note function', async () => { @@ -337,6 +362,14 @@ describe('Private Execution test suite', () => { newNote.note, ), ); + + const newEncryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), + ); + expect(newEncryptedLogs).toHaveLength(1); + + const [encryptedLog] = newEncryptedLogs; + expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); }); it('should run the destroy_and_create function', async () => { @@ -387,6 +420,15 @@ describe('Private Execution test suite', () => { expect(recipientNote.note.items[0]).toEqual(new Fr(amountToTransfer)); expect(changeNote.note.items[0]).toEqual(new Fr(40n)); + const newEncryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), + ); + expect(newEncryptedLogs).toHaveLength(2); + + const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; + expect(encryptedChangeLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedRecipientLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); + const readRequests = sideEffectArrayToValueArray( nonEmptySideEffects(result.callStackItem.publicInputs.noteHashReadRequests), ); @@ -423,6 +465,14 @@ describe('Private Execution test suite', () => { const [changeNote, recipientNote] = result.newNotes; expect(recipientNote.note.items[0]).toEqual(new Fr(amountToTransfer)); expect(changeNote.note.items[0]).toEqual(new Fr(balance - amountToTransfer)); + + const newEncryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), + ); + expect(newEncryptedLogs).toHaveLength(2); + const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; + expect(encryptedChangeLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedRecipientLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); }); }); @@ -886,6 +936,14 @@ describe('Private Execution test suite', () => { ); expect(noteHash).toEqual(innerNoteHash); + const newEncryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), + ); + expect(newEncryptedLogs).toHaveLength(1); + + const [encryptedLog] = newEncryptedLogs; + expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) const readRequest = sideEffectArrayToValueArray(result.callStackItem.publicInputs.noteHashReadRequests)[0]; expect(readRequest).toEqual(innerNoteHash); @@ -956,6 +1014,14 @@ describe('Private Execution test suite', () => { ); expect(noteHash).toEqual(innerNoteHash); + const newEncryptedLogs = sideEffectArrayToValueArray( + nonEmptySideEffects(execInsert.callStackItem.publicInputs.encryptedLogsHashes), + ); + expect(newEncryptedLogs).toHaveLength(1); + + const [encryptedLog] = newEncryptedLogs; + expect(encryptedLog).toEqual(Fr.fromBuffer(execInsert.encryptedLogs.logs[0].hash())); + // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) const readRequest = execGetThenNullify.callStackItem.publicInputs.noteHashReadRequests[0]; expect(readRequest.value).toEqual(innerNoteHash); diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index 5a295aef6cba..d15122153850 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -249,25 +249,32 @@ describe('ACIR public execution simulator', () => { }); describe('Parent/Child contracts', () => { - it('calls the public entry point in the parent', async () => { - const parentContractAddress = AztecAddress.random(); - const parentEntryPointFn = ParentContractArtifact.functions.find(f => f.name === 'pub_entry_point')!; - const parentEntryPointFnSelector = FunctionSelector.fromNameAndParameters( + let parentContractAddress: AztecAddress; + let childContractAddress: AztecAddress; + let parentEntryPointFn: FunctionArtifact; + let parentEntryPointFnSelector: FunctionSelector; + let functionData: FunctionData; + let callContext: CallContext; + + beforeEach(() => { + parentContractAddress = AztecAddress.random(); + parentEntryPointFn = ParentContractArtifact.functions.find(f => f.name === 'pub_entry_point')!; + parentEntryPointFnSelector = FunctionSelector.fromNameAndParameters( parentEntryPointFn.name, parentEntryPointFn.parameters, ); + functionData = new FunctionData(parentEntryPointFnSelector, false); + childContractAddress = AztecAddress.random(); + callContext = makeCallContext(parentContractAddress); + }, 10000); - const childContractAddress = AztecAddress.random(); + it('calls the public entry point in the parent to get value', async () => { const childValueFn = ChildContractArtifact.functions.find(f => f.name === 'pub_get_value')!; const childValueFnSelector = FunctionSelector.fromNameAndParameters(childValueFn.name, childValueFn.parameters); const initialValue = 3n; - - const functionData = new FunctionData(parentEntryPointFnSelector, false); const args = encodeArguments(parentEntryPointFn, [childContractAddress, childValueFnSelector, initialValue]); - const callContext = makeCallContext(parentContractAddress); - // eslint-disable-next-line require-await publicContracts.getBytecode.mockImplementation(async (addr: AztecAddress, selector: FunctionSelector) => { if (addr.equals(parentContractAddress) && selector.equals(parentEntryPointFnSelector)) { @@ -301,6 +308,36 @@ describe('ACIR public execution simulator', () => { ), ); }, 20_000); + + it('calls the public entry point in the parent to set value', async () => { + const childValueFn = ChildContractArtifact.functions.find(f => f.name === 'pub_set_value')!; + const childValueFnSelector = FunctionSelector.fromNameAndParameters(childValueFn.name, childValueFn.parameters); + + const newValue = 5n; + + const args = encodeArguments(parentEntryPointFn, [childContractAddress, childValueFnSelector, newValue]); + + // eslint-disable-next-line require-await + publicContracts.getBytecode.mockImplementation(async (addr: AztecAddress, selector: FunctionSelector) => { + if (addr.equals(parentContractAddress) && selector.equals(parentEntryPointFnSelector)) { + return parentEntryPointFn.bytecode; + } else if (addr.equals(childContractAddress) && selector.equals(childValueFnSelector)) { + return childValueFn.bytecode; + } else { + return undefined; + } + }); + + const execution: PublicExecution = { contractAddress: parentContractAddress, functionData, args, callContext }; + + const result = await executor.simulate(execution, globalVariables); + const childExecutionResult = result.nestedExecutions[0]; + expect(Fr.fromBuffer(childExecutionResult.unencryptedLogs.logs[0].data)).toEqual(new Fr(newValue)); + expect(Fr.fromBuffer(childExecutionResult.unencryptedLogs.logs[0].hash())).toEqual( + childExecutionResult.unencryptedLogsHashes[0].value, + ); + expect(result.returnValues[0]).toEqual(new Fr(newValue)); + }, 20_000); }); describe('Public -> Private / Cross Chain messaging', () => { diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index 96a5627ca215..f338cd828c7c 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -133,10 +133,8 @@ export class PublicExecutionContext extends TypedOracle { * @param log - The unencrypted log to be emitted. */ public override emitUnencryptedLog(log: UnencryptedL2Log) { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/885) this.unencryptedLogs.push(log); this.log.verbose(`Emitted unencrypted log: "${log.toHumanReadable()}"`); - return Fr.fromBuffer(log.hash()); } /** From b945e632219d582d06fe8f0728d8b77ceeab27fa Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Mon, 22 Apr 2024 18:29:22 +0000 Subject: [PATCH 02/21] chore: oh boy deployer addr here we go --- l1-contracts/src/core/libraries/ConstantsGen.sol | 2 +- .../noir-protocol-circuits/crates/types/src/constants.nr | 2 +- yarn-project/circuits.js/src/constants.gen.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 18770c1be97e..27a50262e366 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -89,7 +89,7 @@ library Constants { uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = - 0x0cde95a10e1160d0ff69ac8212cb5902fa5add38d8596595631fcf3a667798e0; + 0x1f47133752dfcd9604f2d89c631797a84ed207c1c51d08533226dafcc8bd8548; uint256 internal constant DEFAULT_GAS_LIMIT = 1_000_000_000; uint256 internal constant DEFAULT_TEARDOWN_GAS_LIMIT = 100_000_000; uint256 internal constant DEFAULT_MAX_FEE_PER_GAS = 10; 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 7faf3c22c0e8..7004102819ef 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -126,7 +126,7 @@ global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af8166354 // CONTRACT INSTANCE CONSTANTS // sha224sum 'struct ContractInstanceDeployed' global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; -global DEPLOYER_CONTRACT_ADDRESS = 0x0cde95a10e1160d0ff69ac8212cb5902fa5add38d8596595631fcf3a667798e0; +global DEPLOYER_CONTRACT_ADDRESS = 0x1f47133752dfcd9604f2d89c631797a84ed207c1c51d08533226dafcc8bd8548; // GAS DEFAULTS global DEFAULT_GAS_LIMIT: u32 = 1_000_000_000; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 4a8b78294c12..c3b1341b83b2 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -74,7 +74,7 @@ export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99n; export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631n; -export const DEPLOYER_CONTRACT_ADDRESS = 0x0cde95a10e1160d0ff69ac8212cb5902fa5add38d8596595631fcf3a667798e0n; +export const DEPLOYER_CONTRACT_ADDRESS = 0x1f47133752dfcd9604f2d89c631797a84ed207c1c51d08533226dafcc8bd8548n; export const DEFAULT_GAS_LIMIT = 1_000_000_000; export const DEFAULT_TEARDOWN_GAS_LIMIT = 100_000_000; export const DEFAULT_MAX_FEE_PER_GAS = 10; From f7346e03980a7a8569d055050cb84f888632e79b Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Tue, 23 Apr 2024 09:27:22 +0000 Subject: [PATCH 03/21] feat: acc logs len in circuit, add docs and tests --- .../writing_contracts/events/emit_event.md | 8 +--- docs/docs/misc/migration_notes.md | 14 ++++++- docs/docs/protocol-specs/logs/index.md | 2 +- .../src/core/libraries/ConstantsGen.sol | 2 +- .../aztec/src/context/private_context.nr | 42 ++++++++++--------- .../aztec/src/context/public_context.nr | 20 ++++----- .../crates/types/src/constants.nr | 2 +- yarn-project/circuits.js/src/constants.gen.ts | 2 +- .../src/client/private_execution.test.ts | 24 +++++++++++ .../simulator/src/client/private_execution.ts | 4 -- yarn-project/simulator/src/mocks/fixtures.ts | 1 + .../src/public/abstract_phase_manager.ts | 4 +- .../simulator/src/public/execution.ts | 4 ++ yarn-project/simulator/src/public/executor.ts | 3 ++ .../simulator/src/public/index.test.ts | 3 ++ .../src/public/transitional_adaptors.ts | 1 + 16 files changed, 87 insertions(+), 49 deletions(-) diff --git a/docs/docs/developers/contracts/writing_contracts/events/emit_event.md b/docs/docs/developers/contracts/writing_contracts/events/emit_event.md index 86794f3afed8..6fa00edc6cfe 100644 --- a/docs/docs/developers/contracts/writing_contracts/events/emit_event.md +++ b/docs/docs/developers/contracts/writing_contracts/events/emit_event.md @@ -63,15 +63,9 @@ In the future we will allow emitting arbitrary information. (If you currently emit arbitrary information, PXE will fail to decrypt, process and store this data, so it will not be queryable). ::: -### Import library - -To emit encrypted logs first import the `emit_encrypted_log` utility function which wraps an [oracle](../oracles/main.md): - -#include_code encrypted_import /noir-projects/aztec-nr/address-note/src/address_note.nr rust - ### Call emit_encrypted_log -After importing, you can call the function: +To emit encrypted logs you don't need to import any library. You call the context method `emit_encrypted_log`: #include_code encrypted /noir-projects/aztec-nr/address-note/src/address_note.nr rust diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index f906c8ad3be9..9e9d058b700d 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -8,6 +8,18 @@ Aztec is in full-speed development. Literally every version breaks compatibility ## 0.36.0 +### [Aztec.nr] Emmiting encrypted logs + +The `emit_encrypted_log` function is now a context method. + +```diff +- use dep::aztec::log::emit_encrypted_log; +- use dep::aztec::logs::emit_encrypted_log; + +- emit_encrypted_log(context, log1); ++ context.emit_encrypted_log(log1); +``` + ### [Aztec.nr] Oracles Oracle `get_nullifier_secret_key` was renamed to `get_app_nullifier_secret_key` and `request_nullifier_secret_key` function on PrivateContext was renamed as `request_app_nullifier_secret_key`. @@ -117,7 +129,7 @@ Note that gas limits are not yet enforced. For now, it is suggested you use `dep Note that this is not required when enqueuing a public function from a private one, since top-level enqueued public functions will always consume all gas available for the transaction, as it is not possible to handle any out-of-gas errors. -### [Aztec.nr] Emmiting unencrypted logs +### [Aztec.nr] Emitting unencrypted logs The `emit_unencrypted_logs` function is now a context method. diff --git a/docs/docs/protocol-specs/logs/index.md b/docs/docs/protocol-specs/logs/index.md index ab8050858342..5722307c0048 100644 --- a/docs/docs/protocol-specs/logs/index.md +++ b/docs/docs/protocol-specs/logs/index.md @@ -29,7 +29,7 @@ Logs on Aztec are similar to logs on Ethereum, enabling smart contracts to conve ### Hash Function -The protocol uses **SHA256** as the hash function for logs, and then reduces the 256-bit result to 253 bits for representation as a field element. +The protocol uses **SHA256** as the hash function for logs, and then reduces the 256-bit result to 248 bits for representation as a field element. diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 27a50262e366..18770c1be97e 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -89,7 +89,7 @@ library Constants { uint256 internal constant DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; uint256 internal constant DEPLOYER_CONTRACT_ADDRESS = - 0x1f47133752dfcd9604f2d89c631797a84ed207c1c51d08533226dafcc8bd8548; + 0x0cde95a10e1160d0ff69ac8212cb5902fa5add38d8596595631fcf3a667798e0; uint256 internal constant DEFAULT_GAS_LIMIT = 1_000_000_000; uint256 internal constant DEFAULT_TEARDOWN_GAS_LIMIT = 100_000_000; uint256 internal constant DEFAULT_MAX_FEE_PER_GAS = 10; 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 e1f615e6a0ab..ea5841927ff7 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -43,8 +43,8 @@ struct PrivateContext { min_revertible_side_effect_counter: u32, - args_hash : Field, - return_hash : Field, + args_hash: Field, + return_hash: Field, max_block_number: MaxBlockNumber, @@ -63,9 +63,10 @@ struct PrivateContext { // Header of a block whose state is used during private execution (not the block the transaction is included in). historical_header: Header, - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) encrypted_logs_hashes: BoundedVec, unencrypted_logs_hashes: BoundedVec, + encrypted_log_preimages_length: Field, + unencrypted_log_preimages_length: Field, // encrypted_logs_preimages: Vec, // unencrypted_logs_preimages: Vec, @@ -137,9 +138,10 @@ impl PrivateContext { private_call_stack_hashes: BoundedVec::new(), public_call_stack_hashes: BoundedVec::new(), new_l2_to_l1_msgs: BoundedVec::new(), - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) encrypted_logs_hashes: BoundedVec::new(), unencrypted_logs_hashes: BoundedVec::new(), + encrypted_log_preimages_length: 0, + unencrypted_log_preimages_length: 0, // encrypted_logs_preimages: Vec::new(), // unencrypted_logs_preimages: Vec::new(), nullifier_key: Option::none() @@ -164,11 +166,7 @@ impl PrivateContext { } pub fn finish(self) -> PrivateCircuitPublicInputs { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - let encrypted_log_preimages_length = 0; - let unencrypted_log_preimages_length = 0; - - let priv_circuit_pub_inputs = PrivateCircuitPublicInputs { + PrivateCircuitPublicInputs { call_context: self.inputs.call_context, args_hash: self.args_hash, returns_hash: self.return_hash, @@ -186,13 +184,11 @@ impl PrivateContext { end_side_effect_counter: self.side_effect_counter, encrypted_logs_hashes: self.encrypted_logs_hashes.storage, unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage, - encrypted_log_preimages_length, - unencrypted_log_preimages_length, + encrypted_log_preimages_length: self.encrypted_log_preimages_length + 4, + unencrypted_log_preimages_length: self.unencrypted_log_preimages_length + 4, historical_header: self.historical_header, tx_context: self.inputs.tx_context - }; - - priv_circuit_pub_inputs + } } pub fn capture_min_revertible_side_effect_counter(&mut self) { @@ -270,15 +266,17 @@ impl PrivateContext { pub fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesSlice { let event_selector = 5; // TODO: compute actual event selector. let contract_address = self.this_address(); + let log_slice = log.to_be_bytes_slice(); let log_hash = compute_unencrypted_log_hash( contract_address, event_selector, - log.to_be_bytes_slice() + log_slice, ); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; - // TODO(Miranda) add length + // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4) + self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + log_slice.len().to_field(); // call oracle let _void = emit_unencrypted_log_private_internal(contract_address, event_selector, log); } @@ -301,7 +299,8 @@ impl PrivateContext { let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; - // TODO(Miranda) add length + // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4) + self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + N*32; } pub fn emit_encrypted_log( @@ -325,7 +324,8 @@ impl PrivateContext { let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.encrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; - // TODO(Miranda) add length + // 308 = encrypted log (304) + processed log len (4) + self.encrypted_log_preimages_length = self.encrypted_log_preimages_length + 308; } pub fn call_private_function( @@ -594,8 +594,8 @@ impl Empty for PrivateContext { inputs: PrivateContextInputs::empty(), side_effect_counter: 0 as u32, min_revertible_side_effect_counter: 0 as u32, - args_hash : 0, - return_hash : 0, + args_hash: 0, + return_hash: 0, max_block_number: MaxBlockNumber::empty(), note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), @@ -608,6 +608,8 @@ impl Empty for PrivateContext { historical_header: Header::empty(), encrypted_logs_hashes: BoundedVec::new(), unencrypted_logs_hashes: BoundedVec::new(), + encrypted_log_preimages_length: 0, + unencrypted_log_preimages_length: 0, nullifier_key: Option::none(), } } diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index b539bf101a17..673b7c4eae83 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -46,7 +46,7 @@ struct PublicContext { new_l2_to_l1_msgs: BoundedVec, unencrypted_logs_hashes: BoundedVec, - unencrypted_logs_preimages_length: Field, + unencrypted_log_preimages_length: Field, // Header of a block whose state is used during public execution. Set by sequencer to be a header of a block // previous to the one in which the tx is included. @@ -70,9 +70,9 @@ impl PublicContext { new_nullifiers: BoundedVec::new(), new_l2_to_l1_msgs: BoundedVec::new(), unencrypted_logs_hashes: BoundedVec::new(), - unencrypted_logs_preimages_length: 0, + unencrypted_log_preimages_length: 0, historical_header: inputs.historical_header, - prover_address: AztecAddress::zero() // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) + prover_address: AztecAddress::zero() // encrypted_logs_preimages: Vec::new(), // unencrypted_logs_preimages: Vec::new(), } @@ -146,8 +146,6 @@ impl PublicContext { } pub fn finish(self) -> PublicCircuitPublicInputs { - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) - let unencrypted_log_preimages_length = 0; // Compute the public call stack hashes let pub_circuit_pub_inputs = PublicCircuitPublicInputs { @@ -165,7 +163,7 @@ impl PublicContext { start_side_effect_counter: self.inputs.start_side_effect_counter, end_side_effect_counter: self.side_effect_counter, unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage, - unencrypted_log_preimages_length, + unencrypted_log_preimages_length: self.unencrypted_log_preimages_length + 4, historical_header: self.inputs.historical_header, prover_address: self.prover_address, revert_code: 0, @@ -283,17 +281,19 @@ impl PublicContextInterface for PublicContext { } fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesSlice { - let event_selector = 5; + let event_selector = 5; // TODO: compute actual event selector. let contract_address = self.this_address(); + let log_slice = log.to_be_bytes_slice(); let log_hash = compute_unencrypted_log_hash( contract_address, event_selector, - log.to_be_bytes_slice() + log_slice ); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; - // TODO(Miranda) add length + // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4) + self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + log_slice.len().to_field(); // Call oracle to broadcast log let _void = emit_unencrypted_log_oracle(contract_address, event_selector, log); } @@ -350,7 +350,7 @@ impl Empty for PublicContext { new_nullifiers: BoundedVec::new(), new_l2_to_l1_msgs: BoundedVec::new(), unencrypted_logs_hashes: BoundedVec::new(), - unencrypted_logs_preimages_length: 0, + unencrypted_log_preimages_length: 0, historical_header: Header::empty(), prover_address: AztecAddress::zero(), } 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 7004102819ef..7faf3c22c0e8 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -126,7 +126,7 @@ global REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af8166354 // CONTRACT INSTANCE CONSTANTS // sha224sum 'struct ContractInstanceDeployed' global DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631; -global DEPLOYER_CONTRACT_ADDRESS = 0x1f47133752dfcd9604f2d89c631797a84ed207c1c51d08533226dafcc8bd8548; +global DEPLOYER_CONTRACT_ADDRESS = 0x0cde95a10e1160d0ff69ac8212cb5902fa5add38d8596595631fcf3a667798e0; // GAS DEFAULTS global DEFAULT_GAS_LIMIT: u32 = 1_000_000_000; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index c3b1341b83b2..4a8b78294c12 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -74,7 +74,7 @@ export const REGISTERER_UNCONSTRAINED_FUNCTION_BROADCASTED_MAGIC_VALUE = 0xe7af816635466f128568edb04c9fa024f6c87fb9010fdbffa68b3d99n; export const DEPLOYER_CONTRACT_INSTANCE_DEPLOYED_MAGIC_VALUE = 0x85864497636cf755ae7bde03f267ce01a520981c21c3682aaf82a631n; -export const DEPLOYER_CONTRACT_ADDRESS = 0x1f47133752dfcd9604f2d89c631797a84ed207c1c51d08533226dafcc8bd8548n; +export const DEPLOYER_CONTRACT_ADDRESS = 0x0cde95a10e1160d0ff69ac8212cb5902fa5add38d8596595631fcf3a667798e0n; export const DEFAULT_GAS_LIMIT = 1_000_000_000; export const DEFAULT_TEARDOWN_GAS_LIMIT = 100_000_000; export const DEFAULT_MAX_FEE_PER_GAS = 10; diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 9117919c174c..28c876806d8a 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -225,6 +225,9 @@ describe('Private Execution test suite', () => { const [unencryptedLog] = newUnencryptedLogs; expect(unencryptedLog).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); + expect(result.callStackItem.publicInputs.unencryptedLogPreimagesLength).toEqual( + new Fr(functionLogs.getSerializedLength()), + ); // Test that the log payload (ie ignoring address, selector, and header) matches what we emitted expect(functionLogs.logs[0].data.subarray(-32).toString('hex')).toEqual(owner.toBuffer().toString('hex')); }); @@ -243,6 +246,9 @@ describe('Private Execution test suite', () => { const [unencryptedLog] = newUnencryptedLogs; expect(unencryptedLog).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); + expect(result.callStackItem.publicInputs.unencryptedLogPreimagesLength).toEqual( + new Fr(functionLogs.getSerializedLength()), + ); // Test that the log payload (ie ignoring address, selector, and header) matches what we emitted const expected = Buffer.concat(args[0].map(arg => arg.toBuffer())).toString('hex'); expect(functionLogs.logs[0].data.subarray(-32 * 5).toString('hex')).toEqual(expected); @@ -336,6 +342,9 @@ describe('Private Execution test suite', () => { const [encryptedLog] = newEncryptedLogs; expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( + new Fr(result.encryptedLogs.getSerializedLength()), + ); }); it('should run the create_note function', async () => { @@ -370,6 +379,9 @@ describe('Private Execution test suite', () => { const [encryptedLog] = newEncryptedLogs; expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( + new Fr(result.encryptedLogs.getSerializedLength()), + ); }); it('should run the destroy_and_create function', async () => { @@ -428,6 +440,9 @@ describe('Private Execution test suite', () => { const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; expect(encryptedChangeLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); expect(encryptedRecipientLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); + expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( + new Fr(result.encryptedLogs.getSerializedLength()), + ); const readRequests = sideEffectArrayToValueArray( nonEmptySideEffects(result.callStackItem.publicInputs.noteHashReadRequests), @@ -473,6 +488,9 @@ describe('Private Execution test suite', () => { const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; expect(encryptedChangeLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); expect(encryptedRecipientLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); + expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( + new Fr(result.encryptedLogs.getSerializedLength()), + ); }); }); @@ -943,6 +961,9 @@ describe('Private Execution test suite', () => { const [encryptedLog] = newEncryptedLogs; expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( + new Fr(result.encryptedLogs.getSerializedLength()), + ); // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) const readRequest = sideEffectArrayToValueArray(result.callStackItem.publicInputs.noteHashReadRequests)[0]; @@ -1021,6 +1042,9 @@ describe('Private Execution test suite', () => { const [encryptedLog] = newEncryptedLogs; expect(encryptedLog).toEqual(Fr.fromBuffer(execInsert.encryptedLogs.logs[0].hash())); + expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( + new Fr(result.encryptedLogs.getSerializedLength()), + ); // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) const readRequest = execGetThenNullify.callStackItem.publicInputs.noteHashReadRequests[0]; diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index 64cf9baae7d8..eb12a2213e27 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -1,7 +1,6 @@ import { type FunctionData, PrivateCallStackItem, PrivateCircuitPublicInputs } from '@aztec/circuits.js'; import { type FunctionArtifactWithDebugMetadata } from '@aztec/foundation/abi'; import { type AztecAddress } from '@aztec/foundation/aztec-address'; -import { Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { witnessMapToFields } from '../acvm/deserialize.js'; @@ -45,9 +44,6 @@ export async function executePrivateFunction( const encryptedLogs = context.getEncryptedLogs(); const unencryptedLogs = context.getUnencryptedLogs(); - // TODO(https://github.com/AztecProtocol/aztec-packages/issues/1165) --> set this in Noir - publicInputs.encryptedLogPreimagesLength = new Fr(encryptedLogs.getSerializedLength()); - publicInputs.unencryptedLogPreimagesLength = new Fr(unencryptedLogs.getSerializedLength()); const callStackItem = new PrivateCallStackItem(contractAddress, functionData, publicInputs); diff --git a/yarn-project/simulator/src/mocks/fixtures.ts b/yarn-project/simulator/src/mocks/fixtures.ts index a68a2181eb81..20923aa98490 100644 --- a/yarn-project/simulator/src/mocks/fixtures.ts +++ b/yarn-project/simulator/src/mocks/fixtures.ts @@ -117,6 +117,7 @@ export class PublicExecutionResultBuilder { contractStorageReads: [], unencryptedLogsHashes: [], unencryptedLogs: UnencryptedFunctionL2Logs.empty(), + unencryptedLogPreimagesLength: new Fr(4n), // empty logs have len 4 startSideEffectCounter: Fr.ZERO, endSideEffectCounter: Fr.ZERO, reverted: this._reverted, diff --git a/yarn-project/simulator/src/public/abstract_phase_manager.ts b/yarn-project/simulator/src/public/abstract_phase_manager.ts index fbd4d95435c6..3b8c60d3112f 100644 --- a/yarn-project/simulator/src/public/abstract_phase_manager.ts +++ b/yarn-project/simulator/src/public/abstract_phase_manager.ts @@ -359,8 +359,6 @@ export abstract class AbstractPhaseManager { MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, ); - const unencryptedLogPreimagesLength = new Fr(result.unencryptedLogs.getSerializedLength()); - return PublicCircuitPublicInputs.from({ callContext: result.execution.callContext, proverAddress: AztecAddress.ZERO, @@ -397,7 +395,7 @@ export abstract class AbstractPhaseManager { SideEffect.empty(), MAX_UNENCRYPTED_LOGS_PER_CALL, ), - unencryptedLogPreimagesLength, + unencryptedLogPreimagesLength: result.unencryptedLogPreimagesLength, historicalHeader: this.historicalHeader, // TODO(@just-mitch): need better mapping from simulator to revert code. revertCode: result.reverted ? RevertCode.REVERTED : RevertCode.OK, diff --git a/yarn-project/simulator/src/public/execution.ts b/yarn-project/simulator/src/public/execution.ts index 871002e96356..565c061e62d1 100644 --- a/yarn-project/simulator/src/public/execution.ts +++ b/yarn-project/simulator/src/public/execution.ts @@ -53,6 +53,10 @@ export interface PublicExecutionResult { * Note: These are preimages to `unencryptedLogsHashes`. */ unencryptedLogs: UnencryptedFunctionL2Logs; + /** + * Length of the unencrypted log preimages emitted in this function call. + */ + unencryptedLogPreimagesLength: Fr; /** * Whether the execution reverted. */ diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index 57710e7c5a85..80cabbc2acee 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -150,6 +150,7 @@ async function executePublicFunctionAcvm( nestedExecutions: [], unencryptedLogsHashes: [], unencryptedLogs: UnencryptedFunctionL2Logs.empty(), + unencryptedLogPreimagesLength: new Fr(4n), // empty logs have len 4 reverted, revertReason, gasLeft: Gas.empty(), @@ -171,6 +172,7 @@ async function executePublicFunctionAcvm( startSideEffectCounter, endSideEffectCounter, unencryptedLogsHashes: unencryptedLogsHashesPadded, + unencryptedLogPreimagesLength, } = PublicCircuitPublicInputs.fromFields(returnWitness); const returnValues = await context.unpackReturns(returnsHash); @@ -213,6 +215,7 @@ async function executePublicFunctionAcvm( nestedExecutions, unencryptedLogsHashes, unencryptedLogs, + unencryptedLogPreimagesLength, reverted: false, revertReason: undefined, gasLeft, diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index d15122153850..08d6eca344d4 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -336,6 +336,9 @@ describe('ACIR public execution simulator', () => { expect(Fr.fromBuffer(childExecutionResult.unencryptedLogs.logs[0].hash())).toEqual( childExecutionResult.unencryptedLogsHashes[0].value, ); + expect(childExecutionResult.unencryptedLogPreimagesLength).toEqual( + new Fr(childExecutionResult.unencryptedLogs.getSerializedLength()), + ); expect(result.returnValues[0]).toEqual(new Fr(newValue)); }, 20_000); }); diff --git a/yarn-project/simulator/src/public/transitional_adaptors.ts b/yarn-project/simulator/src/public/transitional_adaptors.ts index 204db746fd8f..51f68a62d325 100644 --- a/yarn-project/simulator/src/public/transitional_adaptors.ts +++ b/yarn-project/simulator/src/public/transitional_adaptors.ts @@ -168,6 +168,7 @@ export async function convertAvmResults( nestedExecutions, unencryptedLogsHashes, unencryptedLogs, + unencryptedLogPreimagesLength: new Fr(unencryptedLogs.getSerializedLength()), reverted: result.reverted, revertReason: result.revertReason ? createSimulationError(result.revertReason) : undefined, gasLeft: endMachineState.gasLeft, From 7b594acf2d800fd84c1d9855ba5c8e4c82995766 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Tue, 23 Apr 2024 11:31:38 +0000 Subject: [PATCH 04/21] fix: fix deploy.nr after portal removal --- noir-projects/aztec-nr/aztec/src/deploy.nr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/deploy.nr b/noir-projects/aztec-nr/aztec/src/deploy.nr index 53fda2bee032..9b138875d5b8 100644 --- a/noir-projects/aztec-nr/aztec/src/deploy.nr +++ b/noir-projects/aztec-nr/aztec/src/deploy.nr @@ -16,7 +16,7 @@ pub fn deploy_contract(context: &mut PrivateContext, target: AztecAddress) { // Adapted from noir-contracts/contracts/contract_instance_deployer_contract/src/interface/ContractInstanceDeployer.nr // That file was autogenerated running the following command from noir-projects/noir-contracts: // ../../yarn-project/node_modules/.bin/aztec-cli codegen target/contract_instance_deployer_contract-ContractInstanceDeployer.json --nr -o ./contracts/contract_instance_deployer_contract/src/interface - let mut serialized_args = [0; 6]; + let mut serialized_args = [0; 5]; serialized_args[0] = instance.salt; serialized_args[1] = instance.contract_class_id.to_field(); serialized_args[2] = instance.initialization_hash; @@ -25,7 +25,7 @@ pub fn deploy_contract(context: &mut PrivateContext, target: AztecAddress) { let _call_result = context.call_private_function( AztecAddress::from_field(DEPLOYER_CONTRACT_ADDRESS), - FunctionSelector::from_field(0x883355ab), + FunctionSelector::from_field(0x7ebd3690), serialized_args ); } From a163af62bbcca31939bb8361917cb52e5638d0f6 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 24 Apr 2024 10:22:24 +0000 Subject: [PATCH 05/21] fix: strs as logs, generic size enc log returns --- .../aztec/src/context/private_context.nr | 54 ++++++++++++++++-- noir-projects/aztec-nr/aztec/src/hash.nr | 56 ++++++++++++++++--- .../aztec-nr/aztec/src/oracle/logs.nr | 11 ++-- .../simulator/src/acvm/oracle/oracle.ts | 5 +- 4 files changed, 106 insertions(+), 20 deletions(-) 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 f98307d2503b..387646fbcf82 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -299,17 +299,17 @@ impl PrivateContext { self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + N*32; } - pub fn emit_encrypted_log( + pub fn emit_encrypted_log( &mut self, contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, encryption_pub_key: GrumpkinPoint, preimage: [Field; N] - ) { + ) where [Field; N]: Add7ForEncryptedLog { // TODO(1139): perform encryption in the circuit // The oracle call should come last, but we require the encrypted value for now - let encrypted_log = emit_encrypted_log( + let encrypted_log: [Field; M] = emit_encrypted_log( contract_address, storage_slot, note_type_id, @@ -320,8 +320,9 @@ impl PrivateContext { let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.encrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; - // 308 = encrypted log (304) + processed log len (4) - self.encrypted_log_preimages_length = self.encrypted_log_preimages_length + 308; + let encrypted_log_byte_len = 112 + 32*(N + 3); + // + processed log len (4) + self.encrypted_log_preimages_length = self.encrypted_log_preimages_length + encrypted_log_byte_len + 4; } pub fn call_private_function( @@ -671,3 +672,46 @@ unconstrained pub fn emit_contract_class_unencrypted_log_private_internal( ) -> Field { emit_contract_class_unencrypted_log_private(contract_address, event_selector, message) } + +// 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 +// The fn is never used, it's just to tell the compiler what N+7 is + +// 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 Add7ForEncryptedLog { + fn add7(self: [Field; N]) -> [Field; M]; +} + +impl Add7ForEncryptedLog<1, 8> for [Field; 1] { + fn add7(self) -> [Field; 8] { + [self[0]; 8] + } +} +impl Add7ForEncryptedLog<2, 9> for [Field; 2] { + fn add7(self) -> [Field; 9] { + [self[0]; 9] + } +} +impl Add7ForEncryptedLog<3, 10> for [Field; 3] { + fn add7(self) -> [Field; 10] { + [self[0]; 10] + } +} +impl Add7ForEncryptedLog<4, 11> for [Field; 4] { + fn add7(self) -> [Field; 11] { + [self[0]; 11] + } +} +impl Add7ForEncryptedLog<5, 12> for [Field; 5] { + fn add7(self) -> [Field; 12] { + [self[0]; 12] + } +} +impl Add7ForEncryptedLog<6, 13> for [Field; 6] { + fn add7(self) -> [Field; 13] { + [self[0]; 13] + } +} diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index cb8201420431..ca183636e5fe 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -12,20 +12,23 @@ pub fn compute_secret_hash(secret: Field) -> Field { pedersen_hash([secret], GENERATOR_INDEX__L1_TO_L2_MESSAGE_SECRET) } -pub fn compute_encrypted_log_hash( - encrypted_log: [Field; 10] +pub fn compute_encrypted_log_hash( + encrypted_log: [Field; M] ) -> Field { let mut bytes: [u8] = &[]; // Note that bytes.append(encrypted_log[i].to_be_bytes(31)) results in bound error - for i in 0..9 { + for i in 0..M-1 { let to_add = encrypted_log[i].to_be_bytes(31); for j in 0..31 { bytes = bytes.push_back(to_add[j]); } } - // messy hack to ensure we add the 304 bytes exactly - let to_add = encrypted_log[9].to_be_bytes(25); - for j in 0..25 { + // enc log num bytes = 112 + 32 * (N + 3), where N is len of preimage in fields + // N = M + 7 => we can work out bytes remaining + // = ( 112 + 32 * (M - 4) ) - 31 * (M - 1) + let bytes_left = ( 112 + 32 * (M-4) ) - 31 * (M-1); + let to_add = encrypted_log[M-1].to_be_bytes(bytes_left); + for j in 0..bytes_left { bytes = bytes.push_back(to_add[j]); } slice_sha256_to_field(bytes) @@ -64,7 +67,15 @@ impl ToBytesSlice for [Field; N] { impl ToBytesSlice for str { fn to_be_bytes_slice(self) -> [u8] { - self.as_bytes() + // each character of a string is converted into a byte + // then an ACVM field via the oracle => we recreate here + let chars_bytes = self.as_bytes(); + let mut chars_padded: [u8] = &[]; + for i in 0..N { + chars_padded = chars_padded.append(&[0; 31]); + chars_padded = chars_padded.push_back(chars_bytes[i]); + } + chars_padded } } @@ -212,7 +223,7 @@ fn compute_var_args_hash() { } #[test] -fn compute_enc_log_hash() { +fn compute_enc_log_hash_304() { let input = [ 0x0000000000000000000000000000000000000000000000000000000000000000, 0x0021a0d4aa9989656b592187cf6da1965df53ab2ff2277421e663465cf20d3e9, @@ -229,6 +240,26 @@ fn compute_enc_log_hash() { 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); @@ -252,3 +283,12 @@ fn compute_unenc_log_hash_addr() { let hash = compute_unencrypted_log_hash(contract_address, event_selector, log.to_be_bytes_slice()); assert(hash == 0x00880a801230ea08c98a802a11b4786cba474513875f0fc69a615e81c5f9f21c); } + +#[test] +fn compute_unenc_log_hash_str() { + let contract_address = AztecAddress::from_field(0x1b401e1146c5c507962287065c81f0ef7590adae3802c533d7549d6bf0a41bd8); + let event_selector = 5; + let log = "dummy"; + let hash = compute_unencrypted_log_hash(contract_address, event_selector, log.to_be_bytes_slice()); + assert(hash == 0x00a78b5347813624ecfd26e5b8bc6146f418b0cfcc8296b5112d09b8ebba9496); +} diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index c7fe0e34cb58..f98fc82cc1a2 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -1,23 +1,24 @@ use dep::protocol_types::{address::AztecAddress, grumpkin_point::GrumpkinPoint}; // TODO(1139): Should take encrypted data. -// Currently returns encrypted data to be hashed (304 bytes) +// Currently returns encrypted data to be hashed +// = 112 + 32 * (N + 3) bytes = N + 7 fields #[oracle(emitEncryptedLog)] -fn emit_encrypted_log_oracle( +fn emit_encrypted_log_oracle( _contract_address: AztecAddress, _storage_slot: Field, _note_type_id: Field, _encryption_pub_key: GrumpkinPoint, _preimage: [Field; N] -) -> [Field; 10] {} +) -> [Field; M] {} -unconstrained pub fn emit_encrypted_log( +unconstrained pub fn emit_encrypted_log( contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, encryption_pub_key: GrumpkinPoint, preimage: [Field; N] -) -> [Field; 10] { +) -> [Field; M] { emit_encrypted_log_oracle( contract_address, storage_slot, diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index fcd27b0d7758..5f55b323e44c 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -298,9 +298,10 @@ export class Oracle { log.map(fromACVMField), ); // TODO(1139): We should encrypt in the circuit, but instead we inject here - // encryption output is 304 bytes, so split into 10 fields (gross but avoids 304 ACVMFields) + // 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 < 10; i++) { + 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)))); } From ae9eae4c41f6a2ced986522eea19bfa016fedac3 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 24 Apr 2024 16:27:54 +0000 Subject: [PATCH 06/21] fix: make enc log len generic, replace slices with arrs --- .../aztec-nr/aztec/src/context/avm_context.nr | 2 +- .../aztec-nr/aztec/src/context/interface.nr | 2 +- .../aztec/src/context/private_context.nr | 56 +--- .../aztec/src/context/public_context.nr | 10 +- noir-projects/aztec-nr/aztec/src/hash.nr | 126 +++------ noir-projects/aztec-nr/aztec/src/oracle.nr | 1 + .../aztec-nr/aztec/src/oracle/logs_traits.nr | 255 ++++++++++++++++++ .../crates/types/src/hash.nr | 12 +- 8 files changed, 308 insertions(+), 156 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr diff --git a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr index 16d7de0223b3..a6f2fe3e0ab8 100644 --- a/noir-projects/aztec-nr/aztec/src/context/avm_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/avm_context.nr @@ -90,7 +90,7 @@ impl PublicContextInterface for AvmContext { nullifier_exists(unsiloed_nullifier, address.to_field()) == 1 } - fn emit_unencrypted_log(&mut self, log: T) { + fn emit_unencrypted_log(&mut self, log: T) { let event_selector = 5; // Matches current PublicContext. self.emit_unencrypted_log(event_selector, log); } diff --git a/noir-projects/aztec-nr/aztec/src/context/interface.nr b/noir-projects/aztec-nr/aztec/src/context/interface.nr index f13e5d6df5ef..914da21d5513 100644 --- a/noir-projects/aztec-nr/aztec/src/context/interface.nr +++ b/noir-projects/aztec-nr/aztec/src/context/interface.nr @@ -33,7 +33,7 @@ trait PublicContextInterface { fn fee_per_l2_gas(self) -> Field; fn message_portal(&mut self, recipient: EthAddress, content: Field); fn consume_l1_to_l2_message(&mut self, content: Field, secret: Field, sender: EthAddress, leaf_index: Field); - fn emit_unencrypted_log(&mut self, log: T); + fn emit_unencrypted_log(&mut self, log: T); fn call_public_function( self: &mut Self, contract_address: AztecAddress, 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 387646fbcf82..c280cf3e61b1 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -1,11 +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, ToBytesSlice}, + hash::{hash_args_array, ArgsHasher, compute_encrypted_log_hash, compute_unencrypted_log_hash}, oracle::{ arguments, returns, call_private_function::call_private_function_internal, enqueue_public_function_call::enqueue_public_function_call_internal, header::get_header_at, - logs::emit_encrypted_log, + logs::emit_encrypted_log, logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}, nullifier_key::{get_nullifier_keys, NullifierKeys} } }; @@ -259,14 +259,14 @@ impl PrivateContext { // --> might be a better approach to force devs to make a public function call that emits the log if needed then // it would be less easy to accidentally leak information. // If we decide to keep this function around would make sense to wait for traits and then merge it with emit_unencrypted_log. - pub fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesSlice { + pub fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesForUnencryptedLog { let event_selector = 5; // TODO: compute actual event selector. let contract_address = self.this_address(); - let log_slice = log.to_be_bytes_slice(); + let log_slice = log.to_be_bytes_arr(); let log_hash = compute_unencrypted_log_hash( contract_address, event_selector, - log_slice, + log, ); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); @@ -299,14 +299,14 @@ impl PrivateContext { self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + N*32; } - pub fn emit_encrypted_log( + pub fn emit_encrypted_log( &mut self, contract_address: AztecAddress, storage_slot: Field, note_type_id: Field, encryption_pub_key: GrumpkinPoint, preimage: [Field; N] - ) where [Field; N]: Add7ForEncryptedLog { + ) 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 let encrypted_log: [Field; M] = emit_encrypted_log( @@ -673,45 +673,3 @@ unconstrained pub fn emit_contract_class_unencrypted_log_private_internal( emit_contract_class_unencrypted_log_private(contract_address, event_selector, message) } -// 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 -// The fn is never used, it's just to tell the compiler what N+7 is - -// 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 Add7ForEncryptedLog { - fn add7(self: [Field; N]) -> [Field; M]; -} - -impl Add7ForEncryptedLog<1, 8> for [Field; 1] { - fn add7(self) -> [Field; 8] { - [self[0]; 8] - } -} -impl Add7ForEncryptedLog<2, 9> for [Field; 2] { - fn add7(self) -> [Field; 9] { - [self[0]; 9] - } -} -impl Add7ForEncryptedLog<3, 10> for [Field; 3] { - fn add7(self) -> [Field; 10] { - [self[0]; 10] - } -} -impl Add7ForEncryptedLog<4, 11> for [Field; 4] { - fn add7(self) -> [Field; 11] { - [self[0]; 11] - } -} -impl Add7ForEncryptedLog<5, 12> for [Field; 5] { - fn add7(self) -> [Field; 12] { - [self[0]; 12] - } -} -impl Add7ForEncryptedLog<6, 13> for [Field; 6] { - fn add7(self) -> [Field; 13] { - [self[0]; 13] - } -} diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 2052505cd560..50337cc6be02 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -4,8 +4,8 @@ use crate::{ gas::GasOpts }, messaging::process_l1_to_l2_message, - oracle::{arguments, public_call::call_public_function_internal, returns}, - hash::{hash_args, ArgsHasher, compute_unencrypted_log_hash, ToBytesSlice} + oracle::{arguments, public_call::call_public_function_internal, returns, logs_traits::ToBytesForUnencryptedLog}, + hash::{hash_args, ArgsHasher, compute_unencrypted_log_hash} }; use dep::protocol_types::{ abis::{ @@ -276,14 +276,14 @@ impl PublicContextInterface for PublicContext { self.push_new_nullifier(nullifier, 0) } - fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesSlice { + fn emit_unencrypted_log(&mut self, log: T) where T: ToBytesForUnencryptedLog { let event_selector = 5; // TODO: compute actual event selector. let contract_address = self.this_address(); - let log_slice = log.to_be_bytes_slice(); + let log_slice = log.to_be_bytes_arr(); let log_hash = compute_unencrypted_log_hash( contract_address, event_selector, - log_slice + log ); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index b89ac8bb7970..9a8a9181902d 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -4,113 +4,61 @@ use dep::protocol_types::{ GENERATOR_INDEX__SECRET_HASH, GENERATOR_INDEX__MESSAGE_NULLIFIER, ARGS_HASH_CHUNK_COUNT, GENERATOR_INDEX__FUNCTION_ARGS, ARGS_HASH_CHUNK_LENGTH }, - traits::Hash, hash::{pedersen_hash, silo_nullifier, sha256_to_field, slice_sha256_to_field} + traits::Hash, hash::{pedersen_hash, poseidon2_hash, silo_nullifier, sha256_to_field} }; +use crate::oracle::logs_traits::{LensForEncryptedLog, ToBytesForUnencryptedLog}; pub fn compute_secret_hash(secret: Field) -> Field { pedersen_hash([secret], GENERATOR_INDEX__SECRET_HASH) } -pub fn compute_encrypted_log_hash( +pub fn compute_encrypted_log_hash( encrypted_log: [Field; M] -) -> Field { - let mut bytes: [u8] = &[]; +) -> 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 = bytes.push_back(to_add[j]); - } - } - // enc log num bytes = 112 + 32 * (N + 3), where N is len of preimage in fields - // N = M + 7 => we can work out bytes remaining - // = ( 112 + 32 * (M - 4) ) - 31 * (M - 1) - let bytes_left = ( 112 + 32 * (M-4) ) - 31 * (M-1); - let to_add = encrypted_log[M-1].to_be_bytes(bytes_left); - for j in 0..bytes_left { - bytes = bytes.push_back(to_add[j]); - } - slice_sha256_to_field(bytes) -} - -// TODO(Miranda) move trait def -trait ToBytesSlice { - fn to_be_bytes_slice(self) -> [u8]; -} - -impl ToBytesSlice for Field { - fn to_be_bytes_slice(self) -> [u8] { - self.to_be_bytes(32) - } -} - -impl ToBytesSlice for AztecAddress { - fn to_be_bytes_slice(self) -> [u8] { - self.to_field().to_be_bytes(32) - } -} - -impl ToBytesSlice for [Field; N] { - fn to_be_bytes_slice(self) -> [u8] { - let mut bytes: [u8] = &[]; - for i in 0..N { - // Note that bytes.append() results in bound error - let to_add = self[i].to_be_bytes(32); - for j in 0..32 { - bytes = bytes.push_back(to_add[j]); - } + bytes[i*31 + j] = to_add[j]; } - bytes } -} - -impl ToBytesSlice for str { - fn to_be_bytes_slice(self) -> [u8] { - // each character of a string is converted into a byte - // then an ACVM field via the oracle => we recreate here - let chars_bytes = self.as_bytes(); - let mut chars_padded: [u8] = &[]; - for i in 0..N { - chars_padded = chars_padded.append(&[0; 31]); - chars_padded = chars_padded.push_back(chars_bytes[i]); - } - chars_padded + // 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( +pub fn compute_unencrypted_log_hash( contract_address: AztecAddress, event_selector: Field, - message_bytes: [u8] -) -> Field { - let mut hash_bytes: [u8] = message_bytes; - let len = message_bytes.len().to_field(); - let len_bytes = len.to_be_bytes(4); + log: T, +) -> Field where T: ToBytesForUnencryptedLog { + let message_bytes: [u8; N] = log.to_be_bytes_arr(); + // can't use N - not in scope error + let n = message_bytes.len(); + let mut hash_bytes = [0; M]; // Address is converted to 32 bytes in ts - let mut prepend = contract_address.to_be_bytes_slice(); - // Event selector is 4 bytes long, see foundation/../selector.ts - prepend = prepend.append(event_selector.to_be_bytes(4)); - // prefix message with length of data in 4 bytes - // Note that .append(len_bytes) fails with bound error when compiling noir-contracts - prepend = prepend.append([len_bytes[0], len_bytes[1], len_bytes[2], len_bytes[3]].as_slice()); - // Note that hash_bytes.append() results in bound error - // So does prepend.append(hash_bytes) when compiling noir-contracts - for i in 0..40 { - // prepend always has 40 bytes - hash_bytes = hash_bytes.push_front(prepend[39 - i]); + let address_bytes = contract_address.to_be_bytes_arr(); + for i in 0..32 { + hash_bytes[i] = address_bytes[i]; } - // HACK: when calling via real context, sha256_slice gives the wrong digest - // Appears to fail only for shorter inputs and with a long trace - // TODO(Miranda): remove slices here? - if (len == 32) { - let mut debug = [0; 72]; - for i in 0..72 { - debug[i] = hash_bytes[i]; - } - sha256_to_field(debug) - } else { - slice_sha256_to_field(hash_bytes) + let event_bytes = event_selector.to_be_bytes(4); + for i in 0..4 { + hash_bytes[32 + i] = event_bytes[i]; + } + let len_bytes = (n as Field).to_be_bytes(4); + for i in 0..4 { + hash_bytes[36 + i] = len_bytes[i]; } + for i in 0..n { + hash_bytes[40 + i] = message_bytes[i]; + } + + sha256_to_field(hash_bytes) } pub fn compute_message_hash( @@ -270,7 +218,7 @@ fn compute_unenc_log_hash_array() { 0x25d0f689c4a4178a29d59306f2675824d19be6d25e44fa03b03f49c263053dd2, 0x2d513a722d6f352dc0961f156afdc5e31495b9f0e35cb069261a8e55e2df67fd ]; - let hash = compute_unencrypted_log_hash(contract_address, event_selector, log.to_be_bytes_slice()); + let hash = compute_unencrypted_log_hash(contract_address, event_selector, log); assert(hash == 0x00846d6969c8c2f61d39cd2762efcb0abb14f88d59c2675910251ef2bcffe9a7); } @@ -279,7 +227,7 @@ fn compute_unenc_log_hash_addr() { let contract_address = AztecAddress::from_field(0x233a3e0df23b2b15b324194cb4a151f26c0b7333250781d34cc269d85dc334c6); let event_selector = 5; let log = AztecAddress::from_field(0x26aa302d4715fd8a687453cb26d616b0768027bd54bcae56b09d908ecd9f8303); - let hash = compute_unencrypted_log_hash(contract_address, event_selector, log.to_be_bytes_slice()); + let hash = compute_unencrypted_log_hash(contract_address, event_selector, log); assert(hash == 0x00880a801230ea08c98a802a11b4786cba474513875f0fc69a615e81c5f9f21c); } @@ -288,6 +236,6 @@ fn compute_unenc_log_hash_str() { let contract_address = AztecAddress::from_field(0x1b401e1146c5c507962287065c81f0ef7590adae3802c533d7549d6bf0a41bd8); let event_selector = 5; let log = "dummy"; - let hash = compute_unencrypted_log_hash(contract_address, event_selector, log.to_be_bytes_slice()); + let hash = compute_unencrypted_log_hash(contract_address, event_selector, log); assert(hash == 0x00a78b5347813624ecfd26e5b8bc6146f418b0cfcc8296b5112d09b8ebba9496); } diff --git a/noir-projects/aztec-nr/aztec/src/oracle.nr b/noir-projects/aztec-nr/aztec/src/oracle.nr index 57415dc95759..6929d9019521 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle.nr @@ -19,6 +19,7 @@ mod public_call; mod notes; mod storage; mod logs; +mod logs_traits; mod returns; // debug_log oracle is used by both noir-protocol-circuits and this crate and for this reason we just re-export it diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr new file mode 100644 index 000000000000..15fa76185e65 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr @@ -0,0 +1,255 @@ +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 +// 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 + +// 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 { + // 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]; +} + +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<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<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<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<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<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]} +} + +// This trait defines the length of the inputs in bytes to +// the unencrypted log hash fn, where the log can be any type T +// as long as the ACVM can convert to fields. +trait ToBytesForUnencryptedLog { + // N = preimage input in bytes + // M = full log input in bytes ( = N + 40 = N + 32 for addr, + 4 for selector, + 4 for len) + fn to_be_bytes_arr(self) -> [u8; N]; + fn output_bytes(self) -> [u8; M]; +} + +impl ToBytesForUnencryptedLog<32, 72> for Field { + fn to_be_bytes_arr(self) -> [u8; 32] { + self.to_be_bytes(32).as_array() + } + fn output_bytes(self) -> [u8; 72] {[self as u8; 72]} +} + +impl ToBytesForUnencryptedLog<32, 72> for AztecAddress { + fn to_be_bytes_arr(self) -> [u8; 32] { + self.to_field().to_be_bytes(32).as_array() + } + fn output_bytes(self) -> [u8; 72] {[self.to_field() as u8; 72]} +} + +fn arr_to_be_bytes_arr(fields: [Field; L]) -> [u8; N] { + let mut bytes: [u8] = &[]; + for i in 0..L { + // Note that bytes.append() results in bound error + let to_add = fields[i].to_be_bytes(32); + for j in 0..32 { + bytes = bytes.push_back(to_add[j]); + } + } + bytes.as_array() +} + +// each character of a string is converted into a byte +// then an ACVM field via the oracle => we recreate here +fn str_to_be_bytes_arr(string: str) -> [u8; N] { + let chars_bytes = string.as_bytes(); + let mut bytes: [u8] = &[]; + for i in 0..L { + let to_add = (chars_bytes[i] as Field).to_be_bytes(32); + for j in 0..32 { + bytes = bytes.push_back(to_add[j]); + } + } + bytes.as_array() +} + +impl ToBytesForUnencryptedLog<32, 72> for [Field; 1] { + fn to_be_bytes_arr(self) -> [u8; 32] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 72] { + [self[0] as u8; 72] + } +} + +impl ToBytesForUnencryptedLog<64, 104> for [Field; 2] { + fn to_be_bytes_arr(self) -> [u8; 64] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 104] { + [self[0] as u8; 104] + } +} + +impl ToBytesForUnencryptedLog<96, 136> for [Field; 3] { + fn to_be_bytes_arr(self) -> [u8; 96] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 136] { + [self[0] as u8; 136] + } +} + +impl ToBytesForUnencryptedLog<128, 168> for [Field; 4] { + fn to_be_bytes_arr(self) -> [u8; 128] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 168] { + [self[0] as u8; 168] + } +} + +impl ToBytesForUnencryptedLog<160, 200> for [Field; 5] { + fn to_be_bytes_arr(self) -> [u8; 160] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 200] { + [self[0] as u8; 200] + } +} + +impl ToBytesForUnencryptedLog<192, 232> for [Field; 6] { + fn to_be_bytes_arr(self) -> [u8; 192] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 232] { + [self[0] as u8; 232] + } +} + +impl ToBytesForUnencryptedLog<224, 264> for [Field; 7] { + fn to_be_bytes_arr(self) -> [u8; 224] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 264] { + [self[0] as u8; 264] + } +} + +impl ToBytesForUnencryptedLog<256, 296> for [Field; 8] { + fn to_be_bytes_arr(self) -> [u8; 256] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 296] { + [self[0] as u8; 296] + } +} + +// below gives 'No method named 'to_be_bytes_arr' found for type '[Field; L]'' error +// impl ToBytesForUnencryptedLog for str where [Field; L]: ToBytesForUnencryptedLog { +// fn to_be_bytes_arr(self) -> [u8; N] { +// // each character of a string is converted into a byte +// // then an ACVM field via the oracle => we recreate here +// let chars_bytes = self.as_bytes(); +// let mut chars_padded: [Field; L] = [0; L]; +// for i in 0..L { +// chars_padded[i] = chars_bytes[i] as Field; +// } +// chars_padded.to_be_bytes_arr() +// } +// fn output_bytes(self) -> [u8; M] { +// [0; M] +// } +// } + +impl ToBytesForUnencryptedLog<32, 72> for str<1> { + fn to_be_bytes_arr(self) -> [u8; 32] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 72] { + [0; 72] + } +} + +impl ToBytesForUnencryptedLog<64, 104> for str<2> { + fn to_be_bytes_arr(self) -> [u8; 64] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 104] { + [0; 104] + } +} + +impl ToBytesForUnencryptedLog<96, 136> for str<3> { + fn to_be_bytes_arr(self) -> [u8; 96] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 136] { + [0; 136] + } +} + +impl ToBytesForUnencryptedLog<128, 168> for str<4> { + fn to_be_bytes_arr(self) -> [u8; 128] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 168] { + [0; 168] + } +} + +impl ToBytesForUnencryptedLog<160, 200> for str<5> { + fn to_be_bytes_arr(self) -> [u8; 160] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 200] { + [0; 200] + } +} + +impl ToBytesForUnencryptedLog<192, 232> for str<6> { + fn to_be_bytes_arr(self) -> [u8; 192] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 232] { + [0; 232] + } +} + +impl ToBytesForUnencryptedLog<224, 264> for str<7> { + fn to_be_bytes_arr(self) -> [u8; 224] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 264] { + [0; 264] + } +} + +impl ToBytesForUnencryptedLog<256, 296> for str<8> { + fn to_be_bytes_arr(self) -> [u8; 256] { + str_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 296] { + [0; 296] + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index 342a7b3e77a3..ef7bc7f62bd0 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -13,7 +13,7 @@ use crate::constants::{ use crate::traits::Hash; use crate::messaging::l2_to_l1_message::L2ToL1Message; use crate::merkle_tree::root::root_from_sibling_path; -use dep::std::hash::{pedersen_hash_with_separator, sha256, sha256_slice}; +use dep::std::hash::{pedersen_hash_with_separator, sha256}; pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { let sha256_hashed = sha256(bytes_to_hash); @@ -22,13 +22,6 @@ pub fn sha256_to_field(bytes_to_hash: [u8; N]) -> Field { hash_in_a_field } -pub fn slice_sha256_to_field(bytes_to_hash: [u8]) -> Field { - let sha256_hashed = sha256_slice(bytes_to_hash); - let hash_in_a_field = field_from_bytes_32_trunc(sha256_hashed); - - hash_in_a_field -} - pub fn private_functions_root_from_siblings( selector: FunctionSelector, vk_hash: Field, @@ -198,9 +191,6 @@ fn smoke_sha256_to_field() { assert(result == 0x448ebbc9e1a31220a2f3830c18eef61b9bd070e5084b7fa2a359fe729184c7); - let result_slice = slice_sha256_to_field(full_buffer.as_slice()); - assert(result_slice == result); - // to show correctness of the current ver (truncate one byte) vs old ver (mod full bytes): let result_bytes = sha256(full_buffer); let truncated_field = crate::utils::field::field_from_bytes_32_trunc(result_bytes); From ab4157257ad409f71bd0eeed39e03ad603f3a32b Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 24 Apr 2024 17:26:59 +0000 Subject: [PATCH 07/21] feat: impl strs for unenc logs, generate more trait impls --- noir-projects/aztec-nr/aztec/src/hash.nr | 9 + .../aztec-nr/aztec/src/oracle/logs_traits.nr | 178 ++++++++++++------ 2 files changed, 130 insertions(+), 57 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/hash.nr b/noir-projects/aztec-nr/aztec/src/hash.nr index 9a8a9181902d..db600f4f542c 100644 --- a/noir-projects/aztec-nr/aztec/src/hash.nr +++ b/noir-projects/aztec-nr/aztec/src/hash.nr @@ -239,3 +239,12 @@ fn compute_unenc_log_hash_str() { let hash = compute_unencrypted_log_hash(contract_address, event_selector, log); assert(hash == 0x00a78b5347813624ecfd26e5b8bc6146f418b0cfcc8296b5112d09b8ebba9496); } + +#[test] +fn compute_unenc_log_hash_longer_str() { + let contract_address = AztecAddress::from_field(0x1b401e1146c5c507962287065c81f0ef7590adae3802c533d7549d6bf0a41bd8); + let event_selector = 5; + let log = "Hello this is a string"; + let hash = compute_unencrypted_log_hash(contract_address, event_selector, log); + assert(hash == 0x001f3390ea242afee7ce46dafdbdc4bd4f1cf20cd63850d12d60ff9956712c4f); +} 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 15fa76185e65..267979da364c 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs_traits.nr @@ -47,7 +47,7 @@ impl LensForEncryptedLog<6, 13, 400> for [Field; 6] { // the unencrypted log hash fn, where the log can be any type T // as long as the ACVM can convert to fields. trait ToBytesForUnencryptedLog { - // N = preimage input in bytes + // N = preimage input in bytes (32 * num fields or chars) // M = full log input in bytes ( = N + 40 = N + 32 for addr, + 4 for selector, + 4 for len) fn to_be_bytes_arr(self) -> [u8; N]; fn output_bytes(self) -> [u8; M]; @@ -165,91 +165,155 @@ impl ToBytesForUnencryptedLog<256, 296> for [Field; 8] { } } -// below gives 'No method named 'to_be_bytes_arr' found for type '[Field; L]'' error -// impl ToBytesForUnencryptedLog for str where [Field; L]: ToBytesForUnencryptedLog { -// fn to_be_bytes_arr(self) -> [u8; N] { -// // each character of a string is converted into a byte -// // then an ACVM field via the oracle => we recreate here -// let chars_bytes = self.as_bytes(); -// let mut chars_padded: [Field; L] = [0; L]; -// for i in 0..L { -// chars_padded[i] = chars_bytes[i] as Field; -// } -// chars_padded.to_be_bytes_arr() -// } -// fn output_bytes(self) -> [u8; M] { -// [0; M] -// } -// } - -impl ToBytesForUnencryptedLog<32, 72> for str<1> { - fn to_be_bytes_arr(self) -> [u8; 32] { - str_to_be_bytes_arr(self) +impl ToBytesForUnencryptedLog<288, 328> for [Field; 9] { + fn to_be_bytes_arr(self) -> [u8; 288] { + arr_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 72] { - [0; 72] + fn output_bytes(self) -> [u8; 328] { + [self[0] as u8; 328] } } -impl ToBytesForUnencryptedLog<64, 104> for str<2> { - fn to_be_bytes_arr(self) -> [u8; 64] { - str_to_be_bytes_arr(self) +impl ToBytesForUnencryptedLog<320, 360> for [Field; 10] { + fn to_be_bytes_arr(self) -> [u8; 320] { + arr_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 104] { - [0; 104] + fn output_bytes(self) -> [u8; 360] { + [self[0] as u8; 360] } } -impl ToBytesForUnencryptedLog<96, 136> for str<3> { - fn to_be_bytes_arr(self) -> [u8; 96] { - str_to_be_bytes_arr(self) +impl ToBytesForUnencryptedLog<352, 392> for [Field; 11] { + fn to_be_bytes_arr(self) -> [u8; 352] { + arr_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 136] { - [0; 136] + fn output_bytes(self) -> [u8; 392] { + [self[0] as u8; 392] } } -impl ToBytesForUnencryptedLog<128, 168> for str<4> { - fn to_be_bytes_arr(self) -> [u8; 128] { - str_to_be_bytes_arr(self) +impl ToBytesForUnencryptedLog<384, 424> for [Field; 12] { + fn to_be_bytes_arr(self) -> [u8; 384] { + arr_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 168] { - [0; 168] + fn output_bytes(self) -> [u8; 424] { + [self[0] as u8; 424] } } -impl ToBytesForUnencryptedLog<160, 200> for str<5> { - fn to_be_bytes_arr(self) -> [u8; 160] { - str_to_be_bytes_arr(self) +impl ToBytesForUnencryptedLog<416, 456> for [Field; 13] { + fn to_be_bytes_arr(self) -> [u8; 416] { + arr_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 200] { - [0; 200] + fn output_bytes(self) -> [u8; 456] { + [self[0] as u8; 456] } } -impl ToBytesForUnencryptedLog<192, 232> for str<6> { - fn to_be_bytes_arr(self) -> [u8; 192] { - str_to_be_bytes_arr(self) +impl ToBytesForUnencryptedLog<448, 488> for [Field; 14] { + fn to_be_bytes_arr(self) -> [u8; 448] { + arr_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 232] { - [0; 232] + fn output_bytes(self) -> [u8; 488] { + [self[0] as u8; 488] } } -impl ToBytesForUnencryptedLog<224, 264> for str<7> { - fn to_be_bytes_arr(self) -> [u8; 224] { - str_to_be_bytes_arr(self) +impl ToBytesForUnencryptedLog<480, 520> for [Field; 15] { + fn to_be_bytes_arr(self) -> [u8; 480] { + arr_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 264] { - [0; 264] + fn output_bytes(self) -> [u8; 520] { + [self[0] as u8; 520] } } -impl ToBytesForUnencryptedLog<256, 296> for str<8> { - fn to_be_bytes_arr(self) -> [u8; 256] { +impl ToBytesForUnencryptedLog<512, 552> for [Field; 16] { + fn to_be_bytes_arr(self) -> [u8; 512] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 552] { + [self[0] as u8; 552] + } +} + +impl ToBytesForUnencryptedLog<544, 584> for [Field; 17] { + fn to_be_bytes_arr(self) -> [u8; 544] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 584] { + [self[0] as u8; 584] + } +} + +impl ToBytesForUnencryptedLog<576, 616> for [Field; 18] { + fn to_be_bytes_arr(self) -> [u8; 576] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 616] { + [self[0] as u8; 616] + } +} + +impl ToBytesForUnencryptedLog<608, 648> for [Field; 19] { + fn to_be_bytes_arr(self) -> [u8; 608] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 648] { + [self[0] as u8; 648] + } +} + +impl ToBytesForUnencryptedLog<640, 680> for [Field; 20] { + fn to_be_bytes_arr(self) -> [u8; 640] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 680] { + [self[0] as u8; 680] + } +} + +impl ToBytesForUnencryptedLog<672, 712> for [Field; 21] { + fn to_be_bytes_arr(self) -> [u8; 672] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 712] { + [self[0] as u8; 712] + } +} + +impl ToBytesForUnencryptedLog<704, 744> for [Field; 22] { + fn to_be_bytes_arr(self) -> [u8; 704] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 744] { + [self[0] as u8; 744] + } +} + +impl ToBytesForUnencryptedLog<736, 776> for [Field; 23] { + fn to_be_bytes_arr(self) -> [u8; 736] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 776] { + [self[0] as u8; 776] + } +} + +impl ToBytesForUnencryptedLog<768, 808> for [Field; 24] { + fn to_be_bytes_arr(self) -> [u8; 768] { + arr_to_be_bytes_arr(self) + } + fn output_bytes(self) -> [u8; 808] { + [self[0] as u8; 808] + } +} + +impl ToBytesForUnencryptedLog for str where [Field; L]: ToBytesForUnencryptedLog { + fn to_be_bytes_arr(self) -> [u8; N] { str_to_be_bytes_arr(self) } - fn output_bytes(self) -> [u8; 296] { - [0; 296] + fn output_bytes(self) -> [u8; M] { + [0; M] } } From b5227f95daef90fd49c58b4de13a376460651785 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 26 Apr 2024 12:42:49 +0000 Subject: [PATCH 08/21] feat: track nested public logs with arr, link counter issue --- .../aztec/src/context/public_context.nr | 2 +- .../aztec-nr/aztec/src/oracle/public_call.nr | 2 +- .../end-to-end/src/e2e_ordering.test.ts | 6 +++- .../src/kernel_prover/kernel_prover.test.ts | 1 + .../src/client/execution_result.test.ts | 1 + yarn-project/simulator/src/mocks/fixtures.ts | 1 + .../src/public/abstract_phase_manager.ts | 5 ++-- .../simulator/src/public/execution.ts | 5 ++++ yarn-project/simulator/src/public/executor.ts | 3 ++ .../src/public/public_execution_context.ts | 13 +++++++++ .../src/public/tail_phase_manager.ts | 28 +------------------ .../src/public/transitional_adaptors.ts | 2 ++ 12 files changed, 37 insertions(+), 32 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 50337cc6be02..cdb04f1175a3 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -111,7 +111,7 @@ impl PublicContext { is_delegate_call: bool ) -> FunctionReturns { let side_effect_counter = self.side_effect_counter; - // TODO get next value from output of `call_public_function_internal` + // TODO(6052): get next value from output of `call_public_function_internal` self.side_effect_counter += 1; let raw_returns = call_public_function_internal( diff --git a/noir-projects/aztec-nr/aztec/src/oracle/public_call.nr b/noir-projects/aztec-nr/aztec/src/oracle/public_call.nr index 682c917cd095..6aecbdf6104f 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/public_call.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/public_call.nr @@ -1,5 +1,5 @@ use dep::protocol_types::{abis::function_selector::FunctionSelector, address::AztecAddress}; - +// TODO(6052): get new side effect counter from this call #[oracle(callPublicFunction)] fn call_public_function_oracle( _contract_address: AztecAddress, diff --git a/yarn-project/end-to-end/src/e2e_ordering.test.ts b/yarn-project/end-to-end/src/e2e_ordering.test.ts index 16ce4309f7dd..f9e8225481e3 100644 --- a/yarn-project/end-to-end/src/e2e_ordering.test.ts +++ b/yarn-project/end-to-end/src/e2e_ordering.test.ts @@ -90,8 +90,11 @@ describe('e2e_ordering', () => { const expectedOrders = { set_value_twice_with_nested_first: [nestedValue, directValue] as bigint[], // eslint-disable-line camelcase set_value_twice_with_nested_last: [directValue, nestedValue] as bigint[], // eslint-disable-line camelcase + // TODO(6052) + // set_value_with_nested_calls: [nestedValue, directValue, directValue, nestedValue, directValue] as bigint[], // eslint-disable-line camelcase } as const; + // TODO(6052): Once resolved, add 'set_value_with_nested_calls' it.each(['set_value_twice_with_nested_first', 'set_value_twice_with_nested_last'] as const)( 'orders public state updates in %s (and ensures final state value is correct)', async method => { @@ -100,7 +103,7 @@ describe('e2e_ordering', () => { await child.methods[method]().send().wait(); const value = await pxe.getPublicStorageAt(child.address, new Fr(1)); - expect(value.value).toBe(expectedOrder[1]); // final state should match last value set + expect(value.value).toBe(expectedOrder[expectedOrder.length - 1]); // final state should match last value set }, ); @@ -111,6 +114,7 @@ describe('e2e_ordering', () => { // in reverse order. More info in this thread: https://discourse.aztec.network/t/identifying-the-ordering-of-state-access-across-contract-calls/382/12#transition-counters-for-private-calls-2 // The below only works due to a hack which sorts the logs in ts // See tail_phase_manager.ts + // TODO(6052): Once resolved, add 'set_value_with_nested_calls' it.each(['set_value_twice_with_nested_first', 'set_value_twice_with_nested_last'] as const)( 'orders unencrypted logs in %s', async method => { diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index c8638d6dc952..9b01840e4a15 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -77,6 +77,7 @@ describe('Kernel Prover', () => { enqueuedPublicFunctionCalls: [], encryptedLogs: EncryptedFunctionL2Logs.empty(), unencryptedLogs: UnencryptedFunctionL2Logs.empty(), + allEncryptedLogs: EncryptedFunctionL2Logs.empty(), }; }; diff --git a/yarn-project/simulator/src/client/execution_result.test.ts b/yarn-project/simulator/src/client/execution_result.test.ts index 0938c50af58f..74faef1e58f2 100644 --- a/yarn-project/simulator/src/client/execution_result.test.ts +++ b/yarn-project/simulator/src/client/execution_result.test.ts @@ -22,6 +22,7 @@ function emptyExecutionResult(): ExecutionResult { enqueuedPublicFunctionCalls: [], encryptedLogs: EncryptedFunctionL2Logs.empty(), unencryptedLogs: UnencryptedFunctionL2Logs.empty(), + allEncryptedLogs: EncryptedFunctionL2Logs.empty(), }; } diff --git a/yarn-project/simulator/src/mocks/fixtures.ts b/yarn-project/simulator/src/mocks/fixtures.ts index 314810c81a57..721cd2d4a1f4 100644 --- a/yarn-project/simulator/src/mocks/fixtures.ts +++ b/yarn-project/simulator/src/mocks/fixtures.ts @@ -117,6 +117,7 @@ export class PublicExecutionResultBuilder { unencryptedLogsHashes: [], unencryptedLogs: UnencryptedFunctionL2Logs.empty(), unencryptedLogPreimagesLength: new Fr(4n), // empty logs have len 4 + allUnencryptedLogs: UnencryptedFunctionL2Logs.empty(), startSideEffectCounter: Fr.ZERO, endSideEffectCounter: Fr.ZERO, reverted: this._reverted, diff --git a/yarn-project/simulator/src/public/abstract_phase_manager.ts b/yarn-project/simulator/src/public/abstract_phase_manager.ts index 47f8e51a71e2..e5693c9e869b 100644 --- a/yarn-project/simulator/src/public/abstract_phase_manager.ts +++ b/yarn-project/simulator/src/public/abstract_phase_manager.ts @@ -242,6 +242,7 @@ export abstract class AbstractPhaseManager { while (executionStack.length) { const current = executionStack.pop()!; const isExecutionRequest = !isPublicExecutionResult(current); + // TODO(6052): Extract correct new counter from nested calls const sideEffectCounter = lastSideEffectCounter(tx) + 1; const result = isExecutionRequest @@ -257,8 +258,8 @@ export abstract class AbstractPhaseManager { ); throw result.revertReason; } - - newUnencryptedFunctionLogs.push(result.unencryptedLogs); + + if (isExecutionRequest) newUnencryptedFunctionLogs.push(result.allUnencryptedLogs); this.log.debug( `Running public kernel circuit for ${result.execution.contractAddress.toString()}:${functionSelector}`, diff --git a/yarn-project/simulator/src/public/execution.ts b/yarn-project/simulator/src/public/execution.ts index 565c061e62d1..eea6b5eb9480 100644 --- a/yarn-project/simulator/src/public/execution.ts +++ b/yarn-project/simulator/src/public/execution.ts @@ -57,6 +57,11 @@ export interface PublicExecutionResult { * Length of the unencrypted log preimages emitted in this function call. */ unencryptedLogPreimagesLength: Fr; + /** + * Unencrypted logs emitted during this call AND any nested calls. + * Useful for maintaining correct ordering in ts. + */ + allUnencryptedLogs: UnencryptedFunctionL2Logs; /** * Whether the execution reverted. */ diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index 80cabbc2acee..7d1cee77714d 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -151,6 +151,7 @@ async function executePublicFunctionAcvm( unencryptedLogsHashes: [], unencryptedLogs: UnencryptedFunctionL2Logs.empty(), unencryptedLogPreimagesLength: new Fr(4n), // empty logs have len 4 + allUnencryptedLogs: UnencryptedFunctionL2Logs.empty(), reverted, revertReason, gasLeft: Gas.empty(), @@ -198,6 +199,7 @@ async function executePublicFunctionAcvm( const nestedExecutions = context.getNestedExecutions(); const unencryptedLogs = context.getUnencryptedLogs(); + const allUnencryptedLogs = context.getAllUnencryptedLogs(); const gasLeft = Gas.test(); // TODO(palla/gas): Set proper value return { @@ -216,6 +218,7 @@ async function executePublicFunctionAcvm( unencryptedLogsHashes, unencryptedLogs, unencryptedLogPreimagesLength, + allUnencryptedLogs, reverted: false, revertReason: undefined, gasLeft, diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index e464e40b7560..ea91a58b753a 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -40,6 +40,9 @@ export class PublicExecutionContext extends TypedOracle { public readonly stateDb: PublicStateDB, public readonly contractsDb: PublicContractsDB, public readonly commitmentsDb: CommitmentsDB, + // Unencrypted logs emitted during this call AND any nested calls + // Useful for maintaining correct ordering in ts + private allUnencryptedLogs: UnencryptedL2Log[] = [], private log = createDebugLogger('aztec:simulator:public_execution_context'), ) { super(); @@ -83,6 +86,14 @@ export class PublicExecutionContext extends TypedOracle { return new UnencryptedFunctionL2Logs(this.unencryptedLogs); } + /** + * Return the encrypted logs emitted during this execution, including nested calls. + */ + public getAllUnencryptedLogs() { + return new UnencryptedFunctionL2Logs(this.allUnencryptedLogs); + } + + /** * Return the data read and updated during this execution. */ @@ -133,6 +144,7 @@ export class PublicExecutionContext extends TypedOracle { */ public override emitUnencryptedLog(log: UnencryptedL2Log) { this.unencryptedLogs.push(log); + this.allUnencryptedLogs.push(log); this.log.verbose(`Emitted unencrypted log: "${log.toHumanReadable()}"`); } @@ -220,6 +232,7 @@ export class PublicExecutionContext extends TypedOracle { this.stateDb, this.contractsDb, this.commitmentsDb, + this.allUnencryptedLogs, this.log, ); diff --git a/yarn-project/simulator/src/public/tail_phase_manager.ts b/yarn-project/simulator/src/public/tail_phase_manager.ts index c5c3487f1a9c..cca1aa69a2e4 100644 --- a/yarn-project/simulator/src/public/tail_phase_manager.ts +++ b/yarn-project/simulator/src/public/tail_phase_manager.ts @@ -56,8 +56,6 @@ export class TailPhaseManager extends AbstractPhaseManager { throw err; }, ); - // Temporary hack. Should sort them in the tail circuit. - this.patchLogsOrdering(tx, previousPublicKernelOutput); // commit the state updates from this transaction await this.publicStateDB.commit(); @@ -165,33 +163,9 @@ export class TailPhaseManager extends AbstractPhaseManager { } private sortLogsHashes(unencryptedLogsHashes: Tuple): Tuple { + // TODO(6052): logs here may have duplicate counters from nested calls return sortByCounter(unencryptedLogsHashes.map(n => ({ ...n, counter: n.counter.toNumber() }))).map( h => new SideEffect(h.value, new Fr(h.counter)), ) as Tuple; } - - // As above, this is a hack for unencrypted logs ordering, now they are sorted. Since the public kernel - // cannot keep track of side effects that happen after or before a nested call, we override the gathered logs. - // As a sanity check, we at least verify that the elements are the same, so we are only tweaking their ordering. - // See same fn in pxe_service.ts - // Added as part of resolving #5017 - private patchLogsOrdering(tx: Tx, publicInputs: PublicKernelCircuitPublicInputs) { - const unencLogs = tx.unencryptedLogs.unrollLogs(); - const sortedUnencLogs = publicInputs.end.unencryptedLogsHashes; - - const finalUnencLogs: UnencryptedL2Log[] = []; - sortedUnencLogs.forEach((sideEffect: SideEffect) => { - if (!sideEffect.isEmpty()) { - const isLog = (log: UnencryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value); - const thisLogIndex = unencLogs.findIndex(isLog); - finalUnencLogs.push(unencLogs[thisLogIndex]); - } - }); - const unencryptedLogs = new UnencryptedFunctionL2Logs(finalUnencLogs); - - tx.unencryptedLogs.functionLogs[0] = unencryptedLogs; - for (let i = 1; i < tx.unencryptedLogs.functionLogs.length; i++) { - tx.unencryptedLogs.functionLogs[i] = UnencryptedFunctionL2Logs.empty(); - } - } } diff --git a/yarn-project/simulator/src/public/transitional_adaptors.ts b/yarn-project/simulator/src/public/transitional_adaptors.ts index efe3c2110616..b41a55a820e4 100644 --- a/yarn-project/simulator/src/public/transitional_adaptors.ts +++ b/yarn-project/simulator/src/public/transitional_adaptors.ts @@ -147,6 +147,7 @@ export async function convertAvmResults( // TODO: Support nested executions. const nestedExecutions: PublicExecutionResult[] = []; + const allUnencryptedLogs = unencryptedLogs; // TODO keep track of side effect counters const startSideEffectCounter = Fr.ZERO; const endSideEffectCounter = Fr.ZERO; @@ -167,6 +168,7 @@ export async function convertAvmResults( unencryptedLogsHashes, unencryptedLogs, unencryptedLogPreimagesLength: new Fr(unencryptedLogs.getSerializedLength()), + allUnencryptedLogs, reverted: result.reverted, revertReason: result.revertReason ? createSimulationError(result.revertReason) : undefined, gasLeft: endMachineState.gasLeft, From beddeb9fdefb5d7519bd552d4f21569f0dd1db1f Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Fri, 26 Apr 2024 13:12:57 +0000 Subject: [PATCH 09/21] fix: merge fixes --- yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts | 1 - .../simulator/src/client/execution_result.test.ts | 1 - yarn-project/simulator/src/public/index.test.ts | 2 +- .../simulator/src/public/public_execution_context.ts | 8 ++++---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index 9b01840e4a15..c8638d6dc952 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -77,7 +77,6 @@ describe('Kernel Prover', () => { enqueuedPublicFunctionCalls: [], encryptedLogs: EncryptedFunctionL2Logs.empty(), unencryptedLogs: UnencryptedFunctionL2Logs.empty(), - allEncryptedLogs: EncryptedFunctionL2Logs.empty(), }; }; diff --git a/yarn-project/simulator/src/client/execution_result.test.ts b/yarn-project/simulator/src/client/execution_result.test.ts index 74faef1e58f2..0938c50af58f 100644 --- a/yarn-project/simulator/src/client/execution_result.test.ts +++ b/yarn-project/simulator/src/client/execution_result.test.ts @@ -22,7 +22,6 @@ function emptyExecutionResult(): ExecutionResult { enqueuedPublicFunctionCalls: [], encryptedLogs: EncryptedFunctionL2Logs.empty(), unencryptedLogs: UnencryptedFunctionL2Logs.empty(), - allEncryptedLogs: EncryptedFunctionL2Logs.empty(), }; } diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index f535999e6db7..9974763eb481 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -333,7 +333,7 @@ describe('ACIR public execution simulator', () => { const execution: PublicExecution = { contractAddress: parentContractAddress, functionData, args, callContext }; - const result = await executor.simulate(execution, globalVariables); + const result = await simulate(execution, globalVariables); const childExecutionResult = result.nestedExecutions[0]; expect(Fr.fromBuffer(childExecutionResult.unencryptedLogs.logs[0].data)).toEqual(new Fr(newValue)); expect(Fr.fromBuffer(childExecutionResult.unencryptedLogs.logs[0].hash())).toEqual( diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index ac542d848c77..43d49b015945 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -41,12 +41,12 @@ export class PublicExecutionContext extends TypedOracle { public readonly stateDb: PublicStateDB, public readonly contractsDb: PublicContractsDB, public readonly commitmentsDb: CommitmentsDB, - // Unencrypted logs emitted during this call AND any nested calls - // Useful for maintaining correct ordering in ts - private allUnencryptedLogs: UnencryptedL2Log[] = [], public readonly availableGas: Gas, public readonly transactionFee: Fr, public readonly gasSettings: GasSettings, + // Unencrypted logs emitted during this call AND any nested calls + // Useful for maintaining correct ordering in ts + private allUnencryptedLogs: UnencryptedL2Log[] = [], private log = createDebugLogger('aztec:simulator:public_execution_context'), ) { super(); @@ -236,10 +236,10 @@ export class PublicExecutionContext extends TypedOracle { this.stateDb, this.contractsDb, this.commitmentsDb, - this.allUnencryptedLogs, this.availableGas, this.transactionFee, this.gasSettings, + this.allUnencryptedLogs, this.log, ); From 247de1ed58914786ea4c713998b4ab55b9ad4fcf Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Mon, 29 Apr 2024 15:51:26 +0000 Subject: [PATCH 10/21] feat: add logscache, tests, remove old hacks for logs sorting --- .../contracts/child_contract/src/main.nr | 10 + .../contracts/test_contract/src/main.nr | 18 ++ .../src/e2e_nested_contract.test.ts | 65 +++++- .../end-to-end/src/e2e_ordering.test.ts | 4 +- .../src/kernel_prover/kernel_prover.test.ts | 2 + .../pxe/src/pxe_service/pxe_service.ts | 65 +----- .../src/client/client_execution_context.ts | 20 ++ .../src/client/execution_result.test.ts | 221 ------------------ .../simulator/src/client/execution_result.ts | 30 +-- .../simulator/src/client/logs_cache.ts | 67 ++++++ .../src/client/private_execution.test.ts | 5 +- .../simulator/src/client/private_execution.ts | 2 + .../simulator/src/client/simulator.ts | 2 + .../src/public/abstract_phase_manager.ts | 6 +- .../src/public/public_execution_context.ts | 1 - .../src/public/tail_phase_manager.ts | 8 +- 16 files changed, 206 insertions(+), 320 deletions(-) delete mode 100644 yarn-project/simulator/src/client/execution_result.test.ts create mode 100644 yarn-project/simulator/src/client/logs_cache.ts diff --git a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr index ff8e270a2a07..75cfe1a16730 100644 --- a/noir-projects/noir-contracts/contracts/child_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/child_contract/src/main.nr @@ -102,4 +102,14 @@ contract Child { context.emit_unencrypted_log(20); let _result = Child::at(context.this_address()).pub_set_value(10).call(&mut context); } + + #[aztec(public)] + // TODO(6052): The logs emitted are currently in the wrong order as we don't update + // counters for nested public calls + fn set_value_with_two_nested_calls() { + Child::at(context.this_address()).set_value_twice_with_nested_first().call(&mut context); + Child::at(context.this_address()).set_value_twice_with_nested_last().call(&mut context); + storage.current_value.write(20); + context.emit_unencrypted_log(20); + } } diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index 1c14b54c96a2..25cc04d25980 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -231,6 +231,24 @@ contract Test { context.emit_unencrypted_log(fields); } + #[aztec(private)] + fn emit_unencrypted_logs_nested(fields: [Field; 5]) { + Test::at(context.this_address()).emit_msg_sender().call(&mut context); + Test::at(context.this_address()).emit_array_as_unencrypted_log(fields).call(&mut context); + context.emit_unencrypted_log("test"); + } + + #[aztec(private)] + fn emit_encrypted_logs_nested(value: Field, owner: AztecAddress) { + let mut storage_slot = storage.example_constant.get_storage_slot() + 1; + Test::at(context.this_address()).call_create_note(value, owner, storage_slot).call(&mut context); + storage_slot += 1; + let mut note = ValueNote::new(value + 1, owner); + create_note(&mut context, storage_slot, &mut note, true); + storage_slot += 1; + Test::at(context.this_address()).call_create_note(value + 2, owner, storage_slot).call(&mut context); + } + // docs:start:is-time-equal #[aztec(public)] fn is_time_equal(time: u64) -> u64 { diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index 661978ce75ce..3babd236090c 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -1,6 +1,18 @@ -import { type AztecAddress, BatchCall, type DebugLogger, Fr, type PXE, type Wallet, toBigIntBE } from '@aztec/aztec.js'; +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; +import { + type AztecAddress, + BatchCall, + type DebugLogger, + Fr, + Grumpkin, + type PXE, + type Wallet, + deriveKeys, + toBigIntBE, +} from '@aztec/aztec.js'; import { ChildContract, ImportTestContract, ParentContract, TestContract } from '@aztec/noir-contracts.js'; +import { TaggedNote } from '../../circuit-types/src/logs/index.js'; import { setup } from './fixtures/utils.js'; describe('e2e_nested_contract', () => { @@ -155,4 +167,55 @@ describe('e2e_nested_contract', () => { await importerContract.methods.pub_call_open_fn(testContract.address).send().wait(); }, 30_000); }); + + describe('logs in nested calls are ordered as expected', () => { + let testContract: TestContract; + + beforeEach(async () => { + logger.info(`Deploying test contract`); + testContract = await TestContract.deploy(wallet).send().deployed(); + }, 30_000); + + it('calls a method with nested unencrypted logs', async () => { + const tx = await testContract.methods.emit_unencrypted_logs_nested([1, 2, 3, 4, 5]).send().wait(); + const logs = (await pxe.getUnencryptedLogs({ txHash: tx.txHash })).logs.map(l => l.log); + + // First log should be contract address + expect(logs[0].data).toEqual(testContract.address.toBuffer()); + + // Second log should be array of fields + let expectedBuffer = Buffer.concat([1, 2, 3, 4, 5].map(num => new Fr(num).toBuffer())); + expect(logs[1].data.subarray(-32 * 5)).toEqual(expectedBuffer); + + // Third log should be string "test" + expectedBuffer = Buffer.concat( + ['t', 'e', 's', 't'].map(num => Buffer.concat([Buffer.alloc(31), Buffer.from(num)])), + ); + expect(logs[2].data.subarray(-32 * 5)).toEqual(expectedBuffer); + }, 30_000); + + it('calls a method with nested encrypted logs', async () => { + // account setup + const privateKey = new Fr(7n); + const keys = deriveKeys(privateKey); + const account = getSchnorrAccount(pxe, privateKey, keys.masterIncomingViewingSecretKey); + await account.deploy().wait(); + const thisWallet = await account.getWallet(); + + // call test contract + const action = testContract.methods.emit_encrypted_logs_nested(10, thisWallet.getAddress()); + const tx = await action.prove(); + const rct = await action.send().wait(); + + // compare logs + expect(rct.status).toEqual('mined'); + const decryptedLogs = tx.encryptedLogs + .unrollLogs() + .map(l => TaggedNote.fromEncryptedBuffer(l.data, keys.masterIncomingViewingSecretKey, new Grumpkin())); + const notevalues = decryptedLogs.map(l => l?.notePayload.note.items[0]); + expect(notevalues[0]).toEqual(new Fr(10)); + expect(notevalues[1]).toEqual(new Fr(11)); + expect(notevalues[2]).toEqual(new Fr(12)); + }, 30_000); + }); }); diff --git a/yarn-project/end-to-end/src/e2e_ordering.test.ts b/yarn-project/end-to-end/src/e2e_ordering.test.ts index f9e8225481e3..e30dc55a770a 100644 --- a/yarn-project/end-to-end/src/e2e_ordering.test.ts +++ b/yarn-project/end-to-end/src/e2e_ordering.test.ts @@ -91,7 +91,7 @@ describe('e2e_ordering', () => { set_value_twice_with_nested_first: [nestedValue, directValue] as bigint[], // eslint-disable-line camelcase set_value_twice_with_nested_last: [directValue, nestedValue] as bigint[], // eslint-disable-line camelcase // TODO(6052) - // set_value_with_nested_calls: [nestedValue, directValue, directValue, nestedValue, directValue] as bigint[], // eslint-disable-line camelcase + // set_value_with_two_nested_calls: [nestedValue, directValue, directValue, nestedValue, directValue] as bigint[], // eslint-disable-line camelcase } as const; // TODO(6052): Once resolved, add 'set_value_with_nested_calls' @@ -114,7 +114,7 @@ describe('e2e_ordering', () => { // in reverse order. More info in this thread: https://discourse.aztec.network/t/identifying-the-ordering-of-state-access-across-contract-calls/382/12#transition-counters-for-private-calls-2 // The below only works due to a hack which sorts the logs in ts // See tail_phase_manager.ts - // TODO(6052): Once resolved, add 'set_value_with_nested_calls' + // TODO(6052): Once resolved, add 'set_value_with_two_nested_calls' it.each(['set_value_twice_with_nested_first', 'set_value_twice_with_nested_last'] as const)( 'orders unencrypted logs in %s', async method => { diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index c8638d6dc952..77a1da73ab77 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -77,6 +77,8 @@ describe('Kernel Prover', () => { enqueuedPublicFunctionCalls: [], encryptedLogs: EncryptedFunctionL2Logs.empty(), unencryptedLogs: UnencryptedFunctionL2Logs.empty(), + allEncryptedLogs: EncryptedFunctionL2Logs.empty(), + allUnencryptedLogs: UnencryptedFunctionL2Logs.empty(), }; }; diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index e87a447f81b1..a9a9e35dc247 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -1,8 +1,6 @@ import { type AuthWitness, type AztecNode, - EncryptedFunctionL2Logs, - type EncryptedL2Log, EncryptedTxL2Logs, ExtendedNote, type FunctionCall, @@ -20,8 +18,6 @@ import { type TxExecutionRequest, type TxHash, type TxReceipt, - UnencryptedFunctionL2Logs, - type UnencryptedL2Log, UnencryptedTxL2Logs, isNoirCallStackUnresolved, } from '@aztec/circuit-types'; @@ -35,7 +31,6 @@ import { type PartialAddress, type PrivateKernelTailCircuitPublicInputs, type PublicCallRequest, - type SideEffect, computeContractClassId, getContractClassFromArtifact, } from '@aztec/circuits.js'; @@ -49,9 +44,7 @@ import { Timer } from '@aztec/foundation/timer'; import { type AcirSimulator, type ExecutionResult, - collectEncryptedLogs, collectEnqueuedPublicFunctionCalls, - collectUnencryptedLogs, resolveOpcodeLocations, } from '@aztec/simulator'; import { type ContractClassWithId, type ContractInstanceWithAddress } from '@aztec/types/contracts'; @@ -658,7 +651,8 @@ export class PXEService implements PXE { this.log.debug(`Executing kernel prover...`); const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult); - const { encryptedLogs, unencryptedLogs } = this.patchLogsOrdering(executionResult); + const unencryptedLogs = new UnencryptedTxL2Logs([executionResult.allUnencryptedLogs]); + const encryptedLogs = new EncryptedTxL2Logs([executionResult.allEncryptedLogs]); const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult); // HACK(#1639): Manually patches the ordering of the public call stack @@ -776,61 +770,6 @@ export class PXEService implements PXE { ); } - // As above, this is a hack for encrypted/unencrypted logs ordering, now they are sorted. Since the private kernel - // cannot keep track of side effects that happen after or before a nested call, we override the gathered logs. - // As a sanity check, we at least verify that the elements are the same, so we are only tweaking their ordering. - // See yarn-project/end-to-end/src/e2e_ordering.test.ts - // See https://github.com/AztecProtocol/aztec-packages/issues/1641 - // Added as part of resolving #5017 - private patchLogsOrdering(execResult: ExecutionResult) { - const encLogs = collectEncryptedLogs(execResult).flatMap(l => l.logs); - const unencLogs = collectUnencryptedLogs(execResult).flatMap(l => l.logs); - const getLogs = (res: ExecutionResult, enc: boolean) => { - const logs: SideEffect[] = enc - ? res.callStackItem.publicInputs.encryptedLogsHashes.concat(res.nestedExecutions.flatMap(e => getLogs(e, true))) - : res.callStackItem.publicInputs.unencryptedLogsHashes.concat( - res.nestedExecutions.flatMap(e => getLogs(e, false)), - ); - - return logs; - }; - - const sortSEs = (a: SideEffect, b: SideEffect) => { - if (a.isEmpty()) { - return 1; - } else if (b.isEmpty()) { - return -1; - } else { - return Number(a.counter.toBigInt() - b.counter.toBigInt()); - } - }; - - const sortedEncLogs = getLogs(execResult, true).sort(sortSEs); - const sortedUnencLogs = getLogs(execResult, false).sort(sortSEs); - - const finalEncLogs: EncryptedL2Log[] = []; - sortedEncLogs.forEach((sideEffect: SideEffect) => { - if (!sideEffect.isEmpty()) { - const isLog = (log: EncryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value); - const thisLogIndex = encLogs.findIndex(isLog); - finalEncLogs.push(encLogs[thisLogIndex]); - } - }); - - const finalUnencLogs: UnencryptedL2Log[] = []; - sortedUnencLogs.forEach((sideEffect: SideEffect) => { - if (!sideEffect.isEmpty()) { - const isLog = (log: UnencryptedL2Log) => Fr.fromBuffer(log.hash()).equals(sideEffect.value); - const thisLogIndex = unencLogs.findIndex(isLog); - finalUnencLogs.push(unencLogs[thisLogIndex]); - } - }); - - const encryptedLogs = new EncryptedTxL2Logs([new EncryptedFunctionL2Logs(finalEncLogs)]); - const unencryptedLogs = new UnencryptedTxL2Logs([new UnencryptedFunctionL2Logs(finalUnencLogs)]); - return { encryptedLogs, unencryptedLogs }; - } - public async isGlobalStateSynchronized() { return await this.synchronizer.isGlobalStateSynchronized(); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index bffa72d05c30..686cf3925859 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -33,6 +33,7 @@ import { type PackedValuesCache } from '../common/packed_values_cache.js'; import { type DBOracle } from './db_oracle.js'; import { type ExecutionNoteCache } from './execution_note_cache.js'; import { type ExecutionResult, type NoteAndSlot } from './execution_result.js'; +import { type LogsCache } from './logs_cache.js'; import { pickNotes } from './pick_notes.js'; import { executePrivateFunction } from './private_execution.js'; import { ViewDataOracle } from './view_data_oracle.js'; @@ -75,6 +76,7 @@ export class ClientExecutionContext extends ViewDataOracle { authWitnesses: AuthWitness[], private readonly packedValuesCache: PackedValuesCache, private readonly noteCache: ExecutionNoteCache, + private readonly logsCache: LogsCache, db: DBOracle, private readonly curve: Grumpkin, private node: AztecNode, @@ -146,6 +148,13 @@ export class ClientExecutionContext extends ViewDataOracle { return new EncryptedFunctionL2Logs(this.encryptedLogs); } + /** + * Return the encrypted logs emitted during this execution and nested executions. + */ + public getAllEncryptedLogs() { + return new EncryptedFunctionL2Logs(this.logsCache.getEncryptedLogs()); + } + /** * Return the encrypted logs emitted during this execution. */ @@ -153,6 +162,13 @@ export class ClientExecutionContext extends ViewDataOracle { return new UnencryptedFunctionL2Logs(this.unencryptedLogs); } + /** + * Return the unencrypted logs emitted during this execution and nested executions. + */ + public getAllUnencryptedLogs() { + return new UnencryptedFunctionL2Logs(this.logsCache.getUnencryptedLogs()); + } + /** * Return the nested execution results during this execution. */ @@ -327,6 +343,7 @@ export class ClientExecutionContext extends ViewDataOracle { const encryptedNote = taggedNote.toEncryptedBuffer(publicKey, this.curve); const encryptedLog = new EncryptedL2Log(encryptedNote); this.encryptedLogs.push(encryptedLog); + this.logsCache.addEncryptedLog(encryptedLog); return encryptedNote; } @@ -336,6 +353,7 @@ export class ClientExecutionContext extends ViewDataOracle { */ public override emitUnencryptedLog(log: UnencryptedL2Log) { this.unencryptedLogs.push(log); + this.logsCache.addUnencryptedLog(log); const text = log.toHumanReadable(); this.log.verbose(`Emitted unencrypted log: "${text.length > 100 ? text.slice(0, 100) + '...' : text}"`); } @@ -349,6 +367,7 @@ export class ClientExecutionContext extends ViewDataOracle { */ public override emitContractClassUnencryptedLog(log: UnencryptedL2Log) { this.unencryptedLogs.push(log); + this.logsCache.addUnencryptedLog(log); const text = log.toHumanReadable(); this.log.verbose( `Emitted unencrypted log from ContractClassRegisterer: "${ @@ -416,6 +435,7 @@ export class ClientExecutionContext extends ViewDataOracle { this.authWitnesses, this.packedValuesCache, this.noteCache, + this.logsCache, this.db, this.curve, this.node, diff --git a/yarn-project/simulator/src/client/execution_result.test.ts b/yarn-project/simulator/src/client/execution_result.test.ts deleted file mode 100644 index 0938c50af58f..000000000000 --- a/yarn-project/simulator/src/client/execution_result.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { - EncryptedFunctionL2Logs, - EncryptedL2Log, - UnencryptedFunctionL2Logs, - UnencryptedL2Log, -} from '@aztec/circuit-types'; -import { AztecAddress, PrivateCallStackItem } from '@aztec/circuits.js'; -import { EventSelector } from '@aztec/foundation/abi'; - -import { type ExecutionResult, collectEncryptedLogs, collectUnencryptedLogs } from './execution_result.js'; - -function emptyExecutionResult(): ExecutionResult { - return { - acir: Buffer.from(''), - vk: Buffer.from(''), - partialWitness: new Map(), - callStackItem: PrivateCallStackItem.empty(), - noteHashReadRequestPartialWitnesses: [], - newNotes: [], - returnValues: [], - nestedExecutions: [], - enqueuedPublicFunctionCalls: [], - encryptedLogs: EncryptedFunctionL2Logs.empty(), - unencryptedLogs: UnencryptedFunctionL2Logs.empty(), - }; -} - -describe('Execution Result test suite - collect encrypted logs', () => { - function emptyExecutionResultWithEncryptedLogs(encryptedLogs = EncryptedFunctionL2Logs.empty()): ExecutionResult { - const executionResult = emptyExecutionResult(); - executionResult.encryptedLogs = encryptedLogs; - return executionResult; - } - - function makeEncryptedFunctionLogs(contents: string[]) { - return new EncryptedFunctionL2Logs(contents.map(s => new EncryptedL2Log(Buffer.from(s)))); - } - - it('collect encrypted logs with nested fn calls', () => { - /* - Create the following executionResult object: - fnA (log1) - |---------->fnB (log2) - |---------->fnC (log3) -> fnD (log4) - |---------->fnE (log5) - |-------->fnF (log6) - |-------->fnG (log7) - Circuits and ACVM process in a DFS + stack like format: [fnA, fnE, fnG, fnF, fnC, fnD, fnB] - */ - const executionResult: ExecutionResult = emptyExecutionResultWithEncryptedLogs( - makeEncryptedFunctionLogs(['Log 1']), - ); - const fnB = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 2'])); - const fnC = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 3'])); - const fnD = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 4'])); - const fnE = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 5'])); - const fnF = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 6'])); - const fnG = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 7'])); - - fnE.nestedExecutions.push(fnF, fnG); - - fnC.nestedExecutions.push(fnD); - - executionResult.nestedExecutions.push(fnB, fnC, fnE); - - const encryptedLogs = collectEncryptedLogs(executionResult); - expect(encryptedLogs).toEqual([ - makeEncryptedFunctionLogs(['Log 1']), - makeEncryptedFunctionLogs(['Log 5']), - makeEncryptedFunctionLogs(['Log 7']), - makeEncryptedFunctionLogs(['Log 6']), - makeEncryptedFunctionLogs(['Log 3']), - makeEncryptedFunctionLogs(['Log 4']), - makeEncryptedFunctionLogs(['Log 2']), - ]); - }); - - it('collect encrypted logs with multiple logs each function call', () => { - /* - Create the following executionResult object: - fnA (log1, log2) - |---------->fnB (log3, log4) - |---------->fnC (log5) -> fnD (log6) - Circuits and ACVM process in a DFS + stack like format: [fnA, fnC, fnD, fnB] - */ - const executionResult: ExecutionResult = emptyExecutionResultWithEncryptedLogs( - makeEncryptedFunctionLogs(['Log 1', 'Log 2']), - ); - const fnB = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 3', 'Log 4'])); - const fnC = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 5'])); - const fnD = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 6'])); - fnC.nestedExecutions.push(fnD); - executionResult.nestedExecutions.push(fnB, fnC); - const encryptedLogs = collectEncryptedLogs(executionResult); - expect(encryptedLogs).toEqual([ - makeEncryptedFunctionLogs(['Log 1', 'Log 2']), - makeEncryptedFunctionLogs(['Log 5']), - makeEncryptedFunctionLogs(['Log 6']), - makeEncryptedFunctionLogs(['Log 3', 'Log 4']), - ]); - }); - - it('collect encrypted logs with nested functions where some have no logs', () => { - /* - Create the following executionResult object: - fnA () - |----------> fnB (log1) -> fnC () - Circuits and ACVM process in a DFS + stack like format: [fnA, fnB, fnC] - */ - const executionResult: ExecutionResult = emptyExecutionResult(); - const fnB = emptyExecutionResultWithEncryptedLogs(makeEncryptedFunctionLogs(['Log 1'])); - const fnC = emptyExecutionResult(); - fnB.nestedExecutions.push(fnC); - executionResult.nestedExecutions.push(fnB); - const encryptedLogs = collectEncryptedLogs(executionResult); - expect(encryptedLogs).toEqual([ - EncryptedFunctionL2Logs.empty(), - makeEncryptedFunctionLogs(['Log 1']), - EncryptedFunctionL2Logs.empty(), - ]); - }); - - it('collect encrypted logs with no logs in any nested calls', () => { - /* - Create the following executionResult object: - fnA () - |----------> fnB () -> fnC () - |----------> fnD () -> fnE () - Circuits and ACVM process in a DFS + stack like format: [fnA, fnD, fnE, fnB, fnC] - */ - const executionResult: ExecutionResult = emptyExecutionResult(); - const fnB = emptyExecutionResult(); - const fnC = emptyExecutionResult(); - const fnD = emptyExecutionResult(); - const fnE = emptyExecutionResult(); - - fnB.nestedExecutions.push(fnC); - fnD.nestedExecutions.push(fnE); - - executionResult.nestedExecutions.push(fnB, fnD); - - const encryptedLogs = collectEncryptedLogs(executionResult); - expect(encryptedLogs).toEqual([ - EncryptedFunctionL2Logs.empty(), - EncryptedFunctionL2Logs.empty(), - EncryptedFunctionL2Logs.empty(), - EncryptedFunctionL2Logs.empty(), - EncryptedFunctionL2Logs.empty(), - ]); - }); -}); - -describe('collect unencrypted logs', () => { - // collection of unencrypted logs work similar to encrypted logs, so lets write other kinds of test cases: - - function emptyExecutionResultWithUnencryptedLogs( - unencryptedLogs = UnencryptedFunctionL2Logs.empty(), - ): ExecutionResult { - const executionResult = emptyExecutionResult(); - executionResult.unencryptedLogs = unencryptedLogs; - return executionResult; - } - - function makeUnencryptedFunctionLogs(contents: string[]) { - return new UnencryptedFunctionL2Logs( - contents.map(s => new UnencryptedL2Log(AztecAddress.ZERO, EventSelector.empty(), Buffer.from(s))), - ); - } - - it('collect unencrypted logs even when no logs and no recursion', () => { - // fnA() - const executionResult: ExecutionResult = emptyExecutionResult(); - const unencryptedLogs = collectUnencryptedLogs(executionResult); - expect(unencryptedLogs).toEqual([UnencryptedFunctionL2Logs.empty()]); - }); - - it('collect unencrypted logs with no logs in some nested calls', () => { - /* - Create the following executionResult object: - fnA () - |----------> fnB () -> fnC (log1, log2, log3) - Circuits and ACVM process in a DFS + stack like format: [fnA, fnC, fnB] - */ - const executionResult: ExecutionResult = emptyExecutionResult(); - const fnB = emptyExecutionResult(); - const fnC = emptyExecutionResultWithUnencryptedLogs(makeUnencryptedFunctionLogs(['Log 1', 'Log 2', 'Log 3'])); - - executionResult.nestedExecutions.push(fnB, fnC); - - const unencryptedLogs = collectUnencryptedLogs(executionResult); - expect(unencryptedLogs).toEqual([ - UnencryptedFunctionL2Logs.empty(), - makeUnencryptedFunctionLogs(['Log 1', 'Log 2', 'Log 3']), - UnencryptedFunctionL2Logs.empty(), - ]); - }); - - it('collect unencrypted logs with multiple logs in each function call leaves', () => { - /* - Create the following executionResult object: - fnA() - |->fnB - |->fnC(log1, log2, log3) - |->fnD(log4, log5, log6) - Circuits and ACVM process in a DFS + stack like format: [fnA, fnB, fnD, fnC] - */ - const executionResult: ExecutionResult = emptyExecutionResult(); - const fnB = emptyExecutionResult(); - const fnC = emptyExecutionResultWithUnencryptedLogs(makeUnencryptedFunctionLogs(['Log 1', 'Log 2', 'Log 3'])); - const fnD = emptyExecutionResultWithUnencryptedLogs(makeUnencryptedFunctionLogs(['Log 4', 'Log 5', 'Log 6'])); - fnB.nestedExecutions.push(fnC, fnD); - executionResult.nestedExecutions.push(fnB); - const unencryptedLogs = collectUnencryptedLogs(executionResult); - expect(unencryptedLogs).toEqual([ - UnencryptedFunctionL2Logs.empty(), - UnencryptedFunctionL2Logs.empty(), - makeUnencryptedFunctionLogs(['Log 4', 'Log 5', 'Log 6']), - makeUnencryptedFunctionLogs(['Log 1', 'Log 2', 'Log 3']), - ]); - }); -}); diff --git a/yarn-project/simulator/src/client/execution_result.ts b/yarn-project/simulator/src/client/execution_result.ts index 845386e98076..afdd5a5d9483 100644 --- a/yarn-project/simulator/src/client/execution_result.ts +++ b/yarn-project/simulator/src/client/execution_result.ts @@ -55,26 +55,16 @@ export interface ExecutionResult { * Note: These are preimages to `unencryptedLogsHashes`. */ unencryptedLogs: UnencryptedFunctionL2Logs; -} - -/** - * Collect all encrypted logs across all nested executions. - * @param execResult - The topmost execution result. - * @returns All encrypted logs. - */ -export function collectEncryptedLogs(execResult: ExecutionResult): EncryptedFunctionL2Logs[] { - // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. - return [execResult.encryptedLogs, ...[...execResult.nestedExecutions].reverse().flatMap(collectEncryptedLogs)]; -} - -/** - * Collect all unencrypted logs across all nested executions. - * @param execResult - The topmost execution result. - * @returns All unencrypted logs. - */ -export function collectUnencryptedLogs(execResult: ExecutionResult): UnencryptedFunctionL2Logs[] { - // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. - return [execResult.unencryptedLogs, ...[...execResult.nestedExecutions].reverse().flatMap(collectUnencryptedLogs)]; + /** + * Encrypted logs emitted during execution of this function call + * AND any nested calls. + */ + allEncryptedLogs: EncryptedFunctionL2Logs; + /** + * Unencrypted logs emitted during execution of this function call + * AND any nested calls. + */ + allUnencryptedLogs: UnencryptedFunctionL2Logs; } /** diff --git a/yarn-project/simulator/src/client/logs_cache.ts b/yarn-project/simulator/src/client/logs_cache.ts new file mode 100644 index 000000000000..d4d355dbcf06 --- /dev/null +++ b/yarn-project/simulator/src/client/logs_cache.ts @@ -0,0 +1,67 @@ +import { type EncryptedL2Log, type UnencryptedL2Log } from '@aztec/circuit-types'; + +/** + * Log data that's accessible by all the function calls in an execution. + * This class exists to: + * 1. Keep track of logs emitted through nested calls in the correct order. + * 2. TODO(1641): Remove encrypted logs based on notes nullified in the same scope. + */ +export class LogsCache { + /** + * Logs notes created in this transaction. + */ + private encryptedLogs: EncryptedL2Log[] = []; + private unencryptedLogs: UnencryptedL2Log[] = []; + + // TODO Separate encrypted logs linked to note hashes and arbitrary logs: + + // Maps from note hash to encrypted log - useful for removing transient logs + // private encryptedLogsLinkedToNotes: Map = new Map(); + + // /** + // * Remove the encrypted log for a nullified note. + // * @param innerNoteHash - Inner note hash of the note. If this value equals 0, it means the + // * note being nullified is from a previous transaction (and thus not a new note). + // */ + // public nullifyNote(innerNoteHash: Fr) { + // if (!innerNoteHash.equals(Fr.ZERO)) { + // // Find and remove the matching new note if the emitted innerNoteHash is not empty. + // const log = this.encryptedLogsLinkedToNotes.get(innerNoteHash.toBigInt()) ?? false; + // // TODO: throw here? Will the log always be here? + // if (!log) { + // throw new Error('Attempt to remove a pending note log that does not exist.'); + // } + // this.encryptedLogsLinkedToNotes.delete(innerNoteHash.toBigInt()); + // } + // } + + /** + * Add a new encrypted log to cache. + * @param log - New log created during execution. + */ + public addEncryptedLog(log: EncryptedL2Log) { + this.encryptedLogs.push(log); + } + + /** + * Add a new unencrypted log to cache. + * @param log - New log created during execution. + */ + public addUnencryptedLog(log: UnencryptedL2Log) { + this.unencryptedLogs.push(log); + } + + /** + * Return the encrypted logs. + */ + public getEncryptedLogs() { + return this.encryptedLogs; + } + + /** + * Return the encrypted logs. + */ + public getUnencryptedLogs() { + return this.unencryptedLogs; + } +} diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 0f9fe1a66a87..b4853b5bee7d 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -57,7 +57,6 @@ import { MessageLoadOracleInputs } from '../acvm/index.js'; import { buildL1ToL2Message } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; import { type DBOracle } from './db_oracle.js'; -import { collectUnencryptedLogs } from './execution_result.js'; import { AcirSimulator } from './simulator.js'; jest.setTimeout(60_000); @@ -219,7 +218,7 @@ describe('Private Execution test suite', () => { ); expect(newUnencryptedLogs).toHaveLength(1); - const [functionLogs] = collectUnencryptedLogs(result); + const functionLogs = result.allUnencryptedLogs; expect(functionLogs.logs).toHaveLength(1); const [unencryptedLog] = newUnencryptedLogs; @@ -240,7 +239,7 @@ describe('Private Execution test suite', () => { nonEmptySideEffects(result.callStackItem.publicInputs.unencryptedLogsHashes), ); expect(newUnencryptedLogs).toHaveLength(1); - const [functionLogs] = collectUnencryptedLogs(result); + const functionLogs = result.allUnencryptedLogs; expect(functionLogs.logs).toHaveLength(1); const [unencryptedLog] = newUnencryptedLogs; diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index eb12a2213e27..5f7e8fb70b55 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -70,5 +70,7 @@ export async function executePrivateFunction( enqueuedPublicFunctionCalls, encryptedLogs, unencryptedLogs, + allEncryptedLogs: context.getAllEncryptedLogs(), + allUnencryptedLogs: context.getAllUnencryptedLogs(), }; } diff --git a/yarn-project/simulator/src/client/simulator.ts b/yarn-project/simulator/src/client/simulator.ts index a0499982a292..a542f075d9ac 100644 --- a/yarn-project/simulator/src/client/simulator.ts +++ b/yarn-project/simulator/src/client/simulator.ts @@ -20,6 +20,7 @@ import { ClientExecutionContext } from './client_execution_context.js'; import { type DBOracle } from './db_oracle.js'; import { ExecutionNoteCache } from './execution_note_cache.js'; import { type ExecutionResult } from './execution_result.js'; +import { LogsCache } from './logs_cache.js'; import { executePrivateFunction } from './private_execution.js'; import { executeUnconstrainedFunction } from './unconstrained_execution.js'; import { ViewDataOracle } from './view_data_oracle.js'; @@ -103,6 +104,7 @@ export class AcirSimulator { request.authWitnesses, PackedValuesCache.create(request.argsOfCalls), new ExecutionNoteCache(), + new LogsCache(), this.db, curve, this.node, diff --git a/yarn-project/simulator/src/public/abstract_phase_manager.ts b/yarn-project/simulator/src/public/abstract_phase_manager.ts index 90d12636dc7a..469aa3346b66 100644 --- a/yarn-project/simulator/src/public/abstract_phase_manager.ts +++ b/yarn-project/simulator/src/public/abstract_phase_manager.ts @@ -269,8 +269,10 @@ export abstract class AbstractPhaseManager { ); throw result.revertReason; } - - if (isExecutionRequest) newUnencryptedFunctionLogs.push(result.allUnencryptedLogs); + + if (isExecutionRequest) { + newUnencryptedFunctionLogs.push(result.allUnencryptedLogs); + } this.log.debug( `Running public kernel circuit for ${result.execution.contractAddress.toString()}:${functionSelector}`, diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index 43d49b015945..de61b19356c7 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -96,7 +96,6 @@ export class PublicExecutionContext extends TypedOracle { public getAllUnencryptedLogs() { return new UnencryptedFunctionL2Logs(this.allUnencryptedLogs); } - /** * Return the data read and updated during this execution. diff --git a/yarn-project/simulator/src/public/tail_phase_manager.ts b/yarn-project/simulator/src/public/tail_phase_manager.ts index cca1aa69a2e4..d62ebe20cd4a 100644 --- a/yarn-project/simulator/src/public/tail_phase_manager.ts +++ b/yarn-project/simulator/src/public/tail_phase_manager.ts @@ -1,10 +1,4 @@ -import { - type PublicKernelRequest, - PublicKernelType, - type Tx, - UnencryptedFunctionL2Logs, - type UnencryptedL2Log, -} from '@aztec/circuit-types'; +import { type PublicKernelRequest, PublicKernelType, type Tx } from '@aztec/circuit-types'; import { Fr, type GlobalVariables, From 19c7801a82089bdfb13882a40541ae15c780c06d Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Mon, 29 Apr 2024 16:26:59 +0000 Subject: [PATCH 11/21] fix: merge issues --- .../src/client/private_execution.test.ts | 56 ++++++------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 854b9cd2c960..6857f4b72d17 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -212,16 +212,14 @@ describe('Private Execution test suite', () => { const artifact = getFunctionArtifact(TestContractArtifact, 'emit_msg_sender'); const result = await runSimulator({ artifact, msgSender: owner }); - const newUnencryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.unencryptedLogsHashes), - ); + const newUnencryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.unencryptedLogsHashes); expect(newUnencryptedLogs).toHaveLength(1); const functionLogs = result.allUnencryptedLogs; expect(functionLogs.logs).toHaveLength(1); const [unencryptedLog] = newUnencryptedLogs; - expect(unencryptedLog).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); + expect(unencryptedLog.value).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); expect(result.callStackItem.publicInputs.unencryptedLogPreimagesLength).toEqual( new Fr(functionLogs.getSerializedLength()), ); @@ -234,15 +232,13 @@ describe('Private Execution test suite', () => { const args = [times(5, () => Fr.random())]; const result = await runSimulator({ artifact, msgSender: owner, args }); - const newUnencryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.unencryptedLogsHashes), - ); + const newUnencryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.unencryptedLogsHashes); expect(newUnencryptedLogs).toHaveLength(1); const functionLogs = result.allUnencryptedLogs; expect(functionLogs.logs).toHaveLength(1); const [unencryptedLog] = newUnencryptedLogs; - expect(unencryptedLog).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); + expect(unencryptedLog.value).toEqual(Fr.fromBuffer(functionLogs.logs[0].hash())); expect(result.callStackItem.publicInputs.unencryptedLogPreimagesLength).toEqual( new Fr(functionLogs.getSerializedLength()), ); @@ -326,13 +322,11 @@ describe('Private Execution test suite', () => { ), ); - const newEncryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), - ); + const newEncryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.encryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( new Fr(result.encryptedLogs.getSerializedLength()), ); @@ -359,13 +353,11 @@ describe('Private Execution test suite', () => { ), ); - const newEncryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), - ); + const newEncryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.encryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( new Fr(result.encryptedLogs.getSerializedLength()), ); @@ -417,22 +409,16 @@ describe('Private Execution test suite', () => { expect(recipientNote.note.items[0]).toEqual(new Fr(amountToTransfer)); expect(changeNote.note.items[0]).toEqual(new Fr(40n)); - const newEncryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), - ); + const newEncryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.encryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(2); const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; - expect(encryptedChangeLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); - expect(encryptedRecipientLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); + expect(encryptedChangeLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedRecipientLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( new Fr(result.encryptedLogs.getSerializedLength()), ); - const readRequests = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.noteHashReadRequests), - ); - const readRequests = getNonEmptyItems(result.callStackItem.publicInputs.noteHashReadRequests).map(r => r.value); expect(readRequests).toHaveLength(consumedNotes.length); expect(readRequests).toEqual(expect.arrayContaining(consumedNotes.map(n => n.uniqueSiloedNoteHash))); @@ -465,13 +451,11 @@ describe('Private Execution test suite', () => { expect(recipientNote.note.items[0]).toEqual(new Fr(amountToTransfer)); expect(changeNote.note.items[0]).toEqual(new Fr(balance - amountToTransfer)); - const newEncryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), - ); + const newEncryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.encryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(2); const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; - expect(encryptedChangeLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); - expect(encryptedRecipientLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); + expect(encryptedChangeLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedRecipientLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( new Fr(result.encryptedLogs.getSerializedLength()), ); @@ -920,13 +904,11 @@ describe('Private Execution test suite', () => { ); expect(noteHash).toEqual(innerNoteHash); - const newEncryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(result.callStackItem.publicInputs.encryptedLogsHashes), - ); + const newEncryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.encryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( new Fr(result.encryptedLogs.getSerializedLength()), ); @@ -1001,13 +983,11 @@ describe('Private Execution test suite', () => { ); expect(noteHash).toEqual(innerNoteHash); - const newEncryptedLogs = sideEffectArrayToValueArray( - nonEmptySideEffects(execInsert.callStackItem.publicInputs.encryptedLogsHashes), - ); + const newEncryptedLogs = getNonEmptyItems(execInsert.callStackItem.publicInputs.encryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog).toEqual(Fr.fromBuffer(execInsert.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(execInsert.encryptedLogs.logs[0].hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( new Fr(result.encryptedLogs.getSerializedLength()), ); From fd92b48ce1626874adb548c69a8422f22fff5aea Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Tue, 30 Apr 2024 08:59:40 +0000 Subject: [PATCH 12/21] chore: increase test timeout + clarify comments --- .../private_accumulated_data_builder.nr | 2 +- .../crates/types/src/hash.nr | 17 +---------------- .../end-to-end/src/e2e_state_vars.test.ts | 2 +- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr index 7f686715f25f..8675febf3a23 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr @@ -132,7 +132,7 @@ impl PrivateAccumulatedDataBuilder { } revertible_builder.new_l2_to_l1_msgs = self.new_l2_to_l1_msgs; - // TODO(1165): Once we have individual lens, split here + // TODO(1641) & TODO(4712): Once we track logs with more info, including individual lens, split here revertible_builder.encrypted_log_preimages_length = self.encrypted_log_preimages_length; revertible_builder.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index ef7bc7f62bd0..d7f6e82603d5 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -119,22 +119,7 @@ pub fn accumulate_sha256(input: [Field; 2]) -> Field { } pub fn compute_tx_logs_hash(logs: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX]) -> Field { - // TODO(Miranda): Below is flat hashing which would reduce constraints (we now only hash once in tail) - convert to this? - - // // Convert each field element into a byte array and append the bytes to `hash_input_flattened` - // // Ideally we would define a new global here but for now we assert in case MAX_LOGS changes - // assert(MAX_ENCRYPTED_LOGS_PER_TX * 32 == 256); - // let mut hash_input_flattened = [0; 256]; - // for offset in 0..MAX_ENCRYPTED_LOGS_PER_TX { - // let input_as_bytes = logs[offset].value.to_be_bytes(32); - // for byte_index in 0..32 { - // hash_input_flattened[offset * 32 + byte_index] = input_as_bytes[byte_index]; - // } - // } - // // This differs from accumulate_sha256 as we could increase MAX_LOGS and - // // ideally we would push to a slice then hash, but in practice compilation was very slow - // // Hardcode to 256 bytes for now - // sha256_to_field(hash_input_flattened) + // TODO(Miranda): Consider flat hashing logs once we have a slice method to push to // Assuming logs are pre-sorted let mut accumulated_logs_hash = 0; diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index 8a6ed6dc23ea..434cb090b452 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -22,7 +22,7 @@ describe('e2e_state_vars', () => { beforeAll(async () => { ({ teardown, wallet, pxe } = await setup(2)); contract = await DocsExampleContract.deploy(wallet).send().deployed(); - }, 30_000); + }, 50_000); afterAll(() => teardown()); From 57f8b115e8c52150b1158b0e2fcce6a52738efb7 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Tue, 30 Apr 2024 14:12:05 +0000 Subject: [PATCH 13/21] fix: merge fix --- yarn-project/end-to-end/src/e2e_nested_contract.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index 3babd236090c..c6ee656bdd12 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -211,7 +211,7 @@ describe('e2e_nested_contract', () => { expect(rct.status).toEqual('mined'); const decryptedLogs = tx.encryptedLogs .unrollLogs() - .map(l => TaggedNote.fromEncryptedBuffer(l.data, keys.masterIncomingViewingSecretKey, new Grumpkin())); + .map(l => TaggedNote.fromEncryptedBuffer(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)); From a9226405b780941fca4455017f03c55568abab8a Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Tue, 30 Apr 2024 14:38:42 +0000 Subject: [PATCH 14/21] feat: address comments + fmt --- .../aztec/src/context/private_context.nr | 6 ++--- .../aztec/src/context/public_context.nr | 4 +--- .../src/e2e_nested_contract.test.ts | 1 - .../simulator/src/client/logs_cache.ts | 22 +++++++++---------- 4 files changed, 14 insertions(+), 19 deletions(-) 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 844c3dc5042b..05ee5a7703bb 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -270,7 +270,7 @@ impl PrivateContext { self.unencrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4) - self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + log_slice.len().to_field(); + self.unencrypted_log_preimages_length += 44 + log_slice.len().to_field(); // call oracle let _void = emit_unencrypted_log_private_internal(contract_address, event_selector, log); } @@ -294,7 +294,7 @@ impl PrivateContext { self.unencrypted_logs_hashes.push(side_effect); self.side_effect_counter = self.side_effect_counter + 1; // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4) - self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + N*32; + self.unencrypted_log_preimages_length += 44 + N*32; } pub fn emit_encrypted_log( @@ -320,7 +320,7 @@ impl PrivateContext { self.side_effect_counter = self.side_effect_counter + 1; let encrypted_log_byte_len = 112 + 32*(N + 3); // + processed log len (4) - self.encrypted_log_preimages_length = self.encrypted_log_preimages_length + encrypted_log_byte_len + 4; + self.encrypted_log_preimages_length += encrypted_log_byte_len + 4; } pub fn call_private_function( diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 4ec8816293db..54246424b65a 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -72,9 +72,7 @@ impl PublicContext { unencrypted_logs_hashes: BoundedVec::new(), unencrypted_log_preimages_length: 0, historical_header: inputs.historical_header, - prover_address: AztecAddress::zero() - // encrypted_logs_preimages: Vec::new(), - // unencrypted_logs_preimages: Vec::new(), + prover_address: AztecAddress::zero(), } } diff --git a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts index c6ee656bdd12..c20ac8147190 100644 --- a/yarn-project/end-to-end/src/e2e_nested_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_nested_contract.test.ts @@ -4,7 +4,6 @@ import { BatchCall, type DebugLogger, Fr, - Grumpkin, type PXE, type Wallet, deriveKeys, diff --git a/yarn-project/simulator/src/client/logs_cache.ts b/yarn-project/simulator/src/client/logs_cache.ts index d4d355dbcf06..c1e52ae7d8ec 100644 --- a/yarn-project/simulator/src/client/logs_cache.ts +++ b/yarn-project/simulator/src/client/logs_cache.ts @@ -20,19 +20,17 @@ export class LogsCache { // /** // * Remove the encrypted log for a nullified note. - // * @param innerNoteHash - Inner note hash of the note. If this value equals 0, it means the - // * note being nullified is from a previous transaction (and thus not a new note). + // * This fn should only be called if the note's innerNoteHash != 0. + // * @param noteHashCounter - Side effect counter of the note. // */ - // public nullifyNote(innerNoteHash: Fr) { - // if (!innerNoteHash.equals(Fr.ZERO)) { - // // Find and remove the matching new note if the emitted innerNoteHash is not empty. - // const log = this.encryptedLogsLinkedToNotes.get(innerNoteHash.toBigInt()) ?? false; - // // TODO: throw here? Will the log always be here? - // if (!log) { - // throw new Error('Attempt to remove a pending note log that does not exist.'); - // } - // this.encryptedLogsLinkedToNotes.delete(innerNoteHash.toBigInt()); - // } + // public nullifyNote(noteHashCounter: Fr) { + // // Find and remove the matching new note if the emitted innerNoteHash is not empty. + // const log = this.encryptedLogsLinkedToNotes.get(noteHashCounter.toBigInt()) ?? false; + // // TODO: throw here? Will the log always be here? + // if (!log) { + // throw new Error('Attempt to remove a pending note log that does not exist.'); + // } + // this.encryptedLogsLinkedToNotes.delete(noteHashCounter.toBigInt()); // } /** From 5d465fd1f5dd334879622871ea93f3703be5f2b8 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 1 May 2024 10:19:55 +0000 Subject: [PATCH 15/21] feat: flat hash logs in tail/L1 --- docs/docs/protocol-specs/logs/index.md | 15 +++------ .../core/libraries/decoders/TxsDecoder.sol | 18 ++++++++-- l1-contracts/test/decoders/Decoders.t.sol | 27 ++++++++------- .../src/private_kernel_tail.nr | 20 ++++++++--- .../src/public_kernel_tail.nr | 20 ++++++++--- .../crates/types/src/hash.nr | 33 ++++++++++++------- yarn-project/circuit-types/src/body.ts | 6 ++-- .../circuit-types/src/l2_block.test.ts | 6 ++-- .../circuit-types/src/logs/tx_l2_logs.ts | 19 ++++++----- yarn-project/circuit-types/src/mocks.ts | 4 +-- yarn-project/circuit-types/src/tx_effect.ts | 11 ++----- 11 files changed, 106 insertions(+), 73 deletions(-) diff --git a/docs/docs/protocol-specs/logs/index.md b/docs/docs/protocol-specs/logs/index.md index 5722307c0048..4d51971e6ef2 100644 --- a/docs/docs/protocol-specs/logs/index.md +++ b/docs/docs/protocol-specs/logs/index.md @@ -227,10 +227,7 @@ Following the iterations for all private or public calls, the tail kernel circui 2. Accumulate all the hashes and output the final hash to the public inputs: - - _`accumulated_logs_hash = hash(logs_hash_a, logs_hash_b)`_ - - For tail public kernel circuit, it begins with _`accumulated_logs_hash = hash(accumulated_logs_hash, logs_hash_a)`_ if the _accumulated_logs_hash_ outputted from the tail private kernel circuit is not empty. - - _`accumulated_logs_hash = hash(accumulated_logs_hash, logs_hash_c)`_ - - Repeat the process until all _logs_hashes_ are collectively hashed. + - `accumulated_logs_hash = hash(log_hash[0], log_hash[1], ..., log_hash[N - 1])` for N logs. ### Encoding @@ -273,11 +270,9 @@ After successfully decrypting an encrypted log, one can use the _randomness_ in - _`log_hash_a = hash(log_hash_a, contract_address_tag_a)`_ - Repeat the process for all _log_hashes_ in the transaction. -2. Accumulate all the hashes and outputs the final hash to the public inputs: +2. Accumulate all the hashes in the tail and outputs the final hash to the public inputs: - - _`accumulated_logs_hash = hash(log_hash_a, log_hash_b)`_ - - _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash_c)`_ - - Repeat the process until all _logs_hashes_ are collectively hashed. + - `accumulated_logs_hash = hash(log_hash[0], log_hash[1], ..., log_hash[N - 1])` for N logs, with hashes defined above. ### Encoding @@ -310,9 +305,7 @@ As each encrypted note preimage can be associated with a note in the same transa The kernel circuit simply accumulates all the hashes: -- _`accumulated_logs_hash = hash(log_hash_a, log_hash_b)`_ -- _`accumulated_logs_hash = hash(accumulated_logs_hash, log_hash_c)`_ -- Repeat the process until all _logs_hashes_ are collectively hashed. +- `accumulated_logs_hash = hash(log_hash[0], log_hash[1], ..., log_hash[N - 1])` for N logs. ### Encoding diff --git a/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol b/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol index dac894c870b0..b19b2065d9b9 100644 --- a/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol +++ b/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol @@ -227,7 +227,7 @@ library TxsDecoder { uint256 remainingLogsLength = read4(_body, offset); offset += 0x4; - bytes32 kernelPublicInputsLogsHash; // The hash on the output of kernel iteration + bytes memory flattenedLogHashes; // The hash input // Iterate until all the logs were processed while (remainingLogsLength > 0) { @@ -245,13 +245,25 @@ library TxsDecoder { bytes32 singleLogHash = Hash.sha256ToField(slice(_body, offset, singleCallLogsLength)); offset += singleCallLogsLength; - kernelPublicInputsLogsHash = - Hash.sha256ToField(bytes.concat(kernelPublicInputsLogsHash, singleLogHash)); + flattenedLogHashes = bytes.concat(flattenedLogHashes, singleLogHash); privateCircuitPublicInputLogsLength -= (singleCallLogsLength + 0x4); } } + // Not having a 0 value hash for empty logs causes issues with empty txs used for padding. + if (flattenedLogHashes.length == 0) { + return (0, offset); + } + + // padded to MAX_LOGS * 32 bytes + // NB: this assumes MAX_ENCRYPTED_LOGS_PER_TX == MAX_UNENCRYPTED_LOGS_PER_TX + flattenedLogHashes = bytes.concat( + flattenedLogHashes, new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - flattenedLogHashes.length) + ); + + bytes32 kernelPublicInputsLogsHash = Hash.sha256ToField(flattenedLogHashes); + return (kernelPublicInputsLogsHash, offset); } diff --git a/l1-contracts/test/decoders/Decoders.t.sol b/l1-contracts/test/decoders/Decoders.t.sol index accec36ab519..74ae5449f310 100644 --- a/l1-contracts/test/decoders/Decoders.t.sol +++ b/l1-contracts/test/decoders/Decoders.t.sol @@ -5,15 +5,11 @@ pragma solidity >=0.8.18; import {DecoderBase} from "./Base.sol"; import {Hash} from "../../src/core/libraries/Hash.sol"; -import {DataStructures} from "../../src/core/libraries/DataStructures.sol"; import {HeaderLibHelper} from "./helpers/HeaderLibHelper.sol"; import {TxsDecoderHelper} from "./helpers/TxsDecoderHelper.sol"; import {HeaderLib} from "../../src/core/libraries/HeaderLib.sol"; - -import {TxsDecoder} from "../../src/core/libraries/decoders/TxsDecoder.sol"; - -import {AvailabilityOracle} from "../../src/core/availability_oracle/AvailabilityOracle.sol"; +import {Constants} from "../../src/core/libraries/ConstantsGen.sol"; /** * Blocks are generated using the `integration_l1_publisher.test.ts` tests. @@ -196,13 +192,12 @@ contract DecodersTest is DecoderBase { abi.encodePacked(hex"0000000c00000008", hex"00000004", firstFunctionCallLogs); (bytes32 logsHash, uint256 bytesAdvanced) = txsHelper.computeKernelLogsHash(encodedLogs); - // Zero because this is the first iteration - bytes32 previousKernelPublicInputsLogsHash = bytes32(0); bytes32 privateCircuitPublicInputsLogsHashFirstCall = Hash.sha256ToField(firstFunctionCallLogs); bytes32 referenceLogsHash = Hash.sha256ToField( abi.encodePacked( - previousKernelPublicInputsLogsHash, privateCircuitPublicInputsLogsHashFirstCall + privateCircuitPublicInputsLogsHashFirstCall, + new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - 32) ) ); @@ -229,15 +224,16 @@ contract DecodersTest is DecoderBase { ); (bytes32 logsHash, uint256 bytesAdvanced) = txsHelper.computeKernelLogsHash(encodedLogs); - bytes32 referenceLogsHashFromIteration1 = - Hash.sha256ToField(abi.encodePacked(bytes32(0), Hash.sha256ToField(firstFunctionCallLogs))); + bytes32 referenceLogsHashFromIteration1 = Hash.sha256ToField(firstFunctionCallLogs); bytes32 privateCircuitPublicInputsLogsHashSecondCall = Hash.sha256ToField(secondFunctionCallLogs); bytes32 referenceLogsHashFromIteration2 = Hash.sha256ToField( abi.encodePacked( - referenceLogsHashFromIteration1, privateCircuitPublicInputsLogsHashSecondCall + referenceLogsHashFromIteration1, + privateCircuitPublicInputsLogsHashSecondCall, + new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - 64) ) ); @@ -269,8 +265,7 @@ contract DecodersTest is DecoderBase { ); (bytes32 logsHash, uint256 bytesAdvanced) = txsHelper.computeKernelLogsHash(encodedLogs); - bytes32 referenceLogsHashFromIteration1 = - Hash.sha256ToField(abi.encodePacked(bytes32(0), Hash.sha256ToField(firstFunctionCallLogs))); + bytes32 referenceLogsHashFromIteration1 = Hash.sha256ToField(firstFunctionCallLogs); // Note: as of resolving #5017, we now hash logs inside the circuits // Following the YP, we skip any zero length logs, hence no use of secondFunctionCallLogs here @@ -278,7 +273,11 @@ contract DecodersTest is DecoderBase { bytes32 privateCircuitPublicInputsLogsHashThirdCall = Hash.sha256ToField(thirdFunctionCallLogs); bytes32 referenceLogsHashFromIteration3 = Hash.sha256ToField( - abi.encodePacked(referenceLogsHashFromIteration1, privateCircuitPublicInputsLogsHashThirdCall) + abi.encodePacked( + referenceLogsHashFromIteration1, + privateCircuitPublicInputsLogsHashThirdCall, + new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - 64) + ) ); assertEq(bytesAdvanced, encodedLogs.length, "Advanced by an incorrect number of bytes"); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr index 551acfa19839..b4b53d060c25 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr @@ -89,7 +89,8 @@ mod tests { }; use dep::types::constants::{ MAX_NOTE_HASH_READ_REQUESTS_PER_TX, MAX_NEW_NOTE_HASHES_PER_TX, MAX_NEW_NULLIFIERS_PER_TX, - MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX + MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX, + MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, }; use dep::types::{ abis::{ @@ -97,7 +98,7 @@ mod tests { note_hash::NoteHashContext, nullifier::Nullifier, side_effect::{SideEffect, Ordered}, gas::Gas }, grumpkin_private_key::GrumpkinPrivateKey, - hash::{compute_note_hash_nonce, compute_unique_siloed_note_hash, accumulate_sha256}, + hash::{compute_note_hash_nonce, compute_unique_siloed_note_hash, sha256_to_field}, tests::{fixture_builder::FixtureBuilder, sort::sort_get_sorted_hints}, utils::{arrays::{array_eq, array_length}}, traits::{Empty, is_empty, is_empty_array} }; @@ -316,10 +317,19 @@ mod tests { public_inputs.end.unencrypted_log_preimages_length, unencrypted_log_preimages_length + prev_unencrypted_log_preimages_length ); - let expected_encrypted_logs_hash = accumulate_sha256([0, prev_encrypted_logs_hash]); + let hash_bytes: [u8; MAX_ENCRYPTED_LOGS_PER_TX * 32] = prev_encrypted_logs_hash + .to_be_bytes(32) + .append(&[0; MAX_ENCRYPTED_LOGS_PER_TX * 32 - 32]) + .as_array(); + let expected_encrypted_logs_hash = sha256_to_field(hash_bytes); assert_eq(public_inputs.end.encrypted_logs_hash, expected_encrypted_logs_hash); - let mut expected_unencrypted_logs_hash = accumulate_sha256([0, prev_unencrypted_logs_hash]); - expected_unencrypted_logs_hash = accumulate_sha256([expected_unencrypted_logs_hash, unencrypted_logs_hash]); + + let hash_bytes: [u8; MAX_UNENCRYPTED_LOGS_PER_TX * 32] = prev_unencrypted_logs_hash + .to_be_bytes(32) + .append(unencrypted_logs_hash.to_be_bytes(32)) + .append(&[0; MAX_UNENCRYPTED_LOGS_PER_TX * 32 - 64]) + .as_array(); + let expected_unencrypted_logs_hash = sha256_to_field(hash_bytes); assert_eq(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index c2804fd67bb1..ed4cd1fd49d7 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -121,9 +121,10 @@ mod tests { MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_HINTS, MAX_PUBLIC_DATA_READS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NULLIFIER_TREE_HEIGHT, NULLIFIER_SUBTREE_SIBLING_PATH_LENGTH, NULLIFIER_SUBTREE_HEIGHT, PUBLIC_DATA_SUBTREE_HEIGHT, - PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT + PUBLIC_DATA_SUBTREE_SIBLING_PATH_LENGTH, PUBLIC_DATA_TREE_HEIGHT, MAX_ENCRYPTED_LOGS_PER_TX, + MAX_UNENCRYPTED_LOGS_PER_TX, }, - hash::{silo_nullifier, accumulate_sha256}, + hash::{silo_nullifier, sha256_to_field}, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, tests::{fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree}, partial_state_reference::PartialStateReference, utils::arrays::array_merge @@ -359,10 +360,19 @@ mod tests { public_inputs.end.unencrypted_log_preimages_length, unencrypted_log_preimages_length + prev_unencrypted_log_preimages_length ); - let expected_encrypted_logs_hash = accumulate_sha256([0, prev_encrypted_logs_hash]); + let hash_bytes: [u8; MAX_ENCRYPTED_LOGS_PER_TX * 32] = prev_encrypted_logs_hash + .to_be_bytes(32) + .append(&[0; MAX_ENCRYPTED_LOGS_PER_TX * 32 - 32]) + .as_array(); + let expected_encrypted_logs_hash = sha256_to_field(hash_bytes); assert_eq(public_inputs.end.encrypted_logs_hash, expected_encrypted_logs_hash); - let mut expected_unencrypted_logs_hash = accumulate_sha256([0, prev_unencrypted_logs_hash]); - expected_unencrypted_logs_hash = accumulate_sha256([expected_unencrypted_logs_hash, unencrypted_logs_hash]); + + let hash_bytes: [u8; MAX_UNENCRYPTED_LOGS_PER_TX * 32] = prev_unencrypted_logs_hash + .to_be_bytes(32) + .append(unencrypted_logs_hash.to_be_bytes(32)) + .append(&[0; MAX_UNENCRYPTED_LOGS_PER_TX * 32 - 64]) + .as_array(); + let expected_unencrypted_logs_hash = sha256_to_field(hash_bytes); assert_eq(public_inputs.end.unencrypted_logs_hash, expected_unencrypted_logs_hash); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr index d7f6e82603d5..31740a66be75 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/hash.nr @@ -8,7 +8,8 @@ use crate::traits::is_empty; use crate::utils::{uint256::U256, field::field_from_bytes_32_trunc}; use crate::constants::{ FUNCTION_TREE_HEIGHT, GENERATOR_INDEX__SILOED_NOTE_HASH, GENERATOR_INDEX__OUTER_NULLIFIER, - GENERATOR_INDEX__VK, GENERATOR_INDEX__NOTE_HASH_NONCE, GENERATOR_INDEX__UNIQUE_NOTE_HASH + GENERATOR_INDEX__VK, GENERATOR_INDEX__NOTE_HASH_NONCE, GENERATOR_INDEX__UNIQUE_NOTE_HASH, + MAX_ENCRYPTED_LOGS_PER_TX }; use crate::traits::Hash; use crate::messaging::l2_to_l1_message::L2ToL1Message; @@ -118,18 +119,28 @@ pub fn accumulate_sha256(input: [Field; 2]) -> Field { sha256_to_field(hash_input_flattened) } -pub fn compute_tx_logs_hash(logs: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX]) -> Field { - // TODO(Miranda): Consider flat hashing logs once we have a slice method to push to - - // Assuming logs are pre-sorted - let mut accumulated_logs_hash = 0; - for i in 0..MAX_ENCRYPTED_LOGS_PER_TX { - if !is_empty(logs[i]) { - accumulated_logs_hash = accumulate_sha256([accumulated_logs_hash, logs[i].value]); +// Computes the final logs hash for a tx. +// NB: this assumes MAX_ENCRYPTED_LOGS_PER_TX == MAX_UNENCRYPTED_LOGS_PER_TX +// to avoid doubling code, since we can't define the byte len to be 32*N directly. +pub fn compute_tx_logs_hash(logs: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX]) -> Field { + // Convert each field element into a byte array and append the bytes to `hash_input_flattened` + let mut hash_input_flattened = [0; MAX_ENCRYPTED_LOGS_PER_TX * 32]; + for offset in 0..MAX_ENCRYPTED_LOGS_PER_TX { + let input_as_bytes = logs[offset].value.to_be_bytes(32); + for byte_index in 0..32 { + hash_input_flattened[offset * 32 + byte_index] = input_as_bytes[byte_index]; } } - - accumulated_logs_hash + // Ideally we would push to a slice then hash, but there is no sha_slice + // Hardcode to 256 bytes for now + let mut hash = sha256_to_field(hash_input_flattened); + // Not having a 0 value hash for empty logs causes issues with empty txs + // used for padding. Returning early is currently unsupported. + // We always provide sorted logs here, so 0 being empty means all are empty. + if is_empty(logs[0]) { + hash = 0; + } + hash } pub fn compute_note_hash_nonce(first_nullifier: Field, commitment_index: u64) -> Field { diff --git a/yarn-project/circuit-types/src/body.ts b/yarn-project/circuit-types/src/body.ts index 03a983d95da3..6bb1146c3959 100644 --- a/yarn-project/circuit-types/src/body.ts +++ b/yarn-project/circuit-types/src/body.ts @@ -1,7 +1,7 @@ import { EncryptedL2BlockL2Logs, TxEffect, UnencryptedL2BlockL2Logs } from '@aztec/circuit-types'; import { padArrayEnd } from '@aztec/foundation/collection'; -import { sha256 } from '@aztec/foundation/crypto'; -import { BufferReader, serializeToBuffer, truncateAndPad } from '@aztec/foundation/serialize'; +import { sha256Trunc } from '@aztec/foundation/crypto'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { inspect } from 'util'; @@ -56,7 +56,7 @@ export class Body { const left = layers[activeLayer][i]; const right = layers[activeLayer][i + 1]; - layer.push(truncateAndPad(sha256(Buffer.concat([left, right])))); + layer.push(sha256Trunc(Buffer.concat([left, right]))); } layers.push(layer); diff --git a/yarn-project/circuit-types/src/l2_block.test.ts b/yarn-project/circuit-types/src/l2_block.test.ts index 5a1a39c0699a..64546f8ed37a 100644 --- a/yarn-project/circuit-types/src/l2_block.test.ts +++ b/yarn-project/circuit-types/src/l2_block.test.ts @@ -27,7 +27,7 @@ describe('L2Block', () => { // The following 2 values are copied from `testComputeKernelLogs1Iteration` in `Decoder.t.sol` const encodedLogs = Buffer.from('0000000c000000080000000493e78a70', 'hex'); const logs = EncryptedTxL2Logs.fromBuffer(encodedLogs, true); - const referenceLogsHash = Buffer.from('0020f9217a7218a377a78d0e8929b87d31c32d270817fe8f5fe876c61b741024', 'hex'); + const referenceLogsHash = Buffer.from('0044339f3cafeb22de0d76423142797f1d4520c6cad559de5d1390bb7ab4c812', 'hex'); const logsHash = logs.hash(); expect(logsHash).toEqual(referenceLogsHash); @@ -41,7 +41,7 @@ describe('L2Block', () => { 'hex', ); const logs = EncryptedTxL2Logs.fromBuffer(encodedLogs, true); - const referenceLogsHash = Buffer.from('007e066525b587fdfb3704301ffcfa4b6a585d95491926d0fd5698f3ae603b18', 'hex'); + const referenceLogsHash = Buffer.from('00ebc16f83abc50c57496375353bf377b06bef23880bd3e9975ea1f7f5a0e8b1', 'hex'); const logsHash = logs.hash(); expect(logsHash).toEqual(referenceLogsHash); @@ -56,7 +56,7 @@ describe('L2Block', () => { 'hex', ); const logs = EncryptedTxL2Logs.fromBuffer(encodedLogs, true); - const referenceLogsHash = Buffer.from('007e066525b587fdfb3704301ffcfa4b6a585d95491926d0fd5698f3ae603b18', 'hex'); + const referenceLogsHash = Buffer.from('00ebc16f83abc50c57496375353bf377b06bef23880bd3e9975ea1f7f5a0e8b1', 'hex'); const logsHash = logs.hash(); expect(logsHash).toEqual(referenceLogsHash); diff --git a/yarn-project/circuit-types/src/logs/tx_l2_logs.ts b/yarn-project/circuit-types/src/logs/tx_l2_logs.ts index e0814eafa4e5..c18bec7c3c50 100644 --- a/yarn-project/circuit-types/src/logs/tx_l2_logs.ts +++ b/yarn-project/circuit-types/src/logs/tx_l2_logs.ts @@ -84,18 +84,21 @@ export abstract class TxL2Logs { * for more details. */ public hash(): Buffer { - const logsHashes: [Buffer, Buffer] = [Buffer.alloc(32), Buffer.alloc(32)]; - let kernelPublicInputsLogsHash = Buffer.alloc(32); + if (this.unrollLogs().length == 0) { + return Buffer.alloc(32); + } + let flattenedLogs = Buffer.alloc(0); for (const logsFromSingleFunctionCall of this.unrollLogs()) { - logsHashes[0] = kernelPublicInputsLogsHash; - logsHashes[1] = logsFromSingleFunctionCall.hash(); // privateCircuitPublicInputsLogsHash - - // Hash logs hash from the public inputs of previous kernel iteration and logs hash from private circuit public inputs - kernelPublicInputsLogsHash = sha256Trunc(Buffer.concat(logsHashes)); + flattenedLogs = Buffer.concat([flattenedLogs, logsFromSingleFunctionCall.hash()]); + } + // pad the end of logs with 0s + // NB - This assumes MAX_ENCRYPTED_LOGS_PER_TX == MAX_UNENCRYPTED_LOGS_PER_TX + for (let i = 0; i < MAX_ENCRYPTED_LOGS_PER_TX - this.unrollLogs().length; i++) { + flattenedLogs = Buffer.concat([flattenedLogs, Buffer.alloc(32)]); } - return kernelPublicInputsLogsHash; + return sha256Trunc(flattenedLogs); } } diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 8adb56862578..395346f4a761 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -103,8 +103,8 @@ export const mockTx = ( ); } else { data.forRollup!.end.newNullifiers[0] = firstNullifier.value; - data.forRollup!.end.encryptedLogsHash = hasLogs ? Fr.fromBuffer(encryptedLogs.hash()) : Fr.ZERO; - data.forRollup!.end.unencryptedLogsHash = hasLogs ? Fr.fromBuffer(unencryptedLogs.hash()) : Fr.ZERO; + data.forRollup!.end.encryptedLogsHash = Fr.fromBuffer(encryptedLogs.hash()); + data.forRollup!.end.unencryptedLogsHash = Fr.fromBuffer(unencryptedLogs.hash()); } const tx = new Tx(data, new Proof(Buffer.alloc(0)), encryptedLogs, unencryptedLogs, publicCallRequests); diff --git a/yarn-project/circuit-types/src/tx_effect.ts b/yarn-project/circuit-types/src/tx_effect.ts index 7d2e991602fc..ad70f4e29749 100644 --- a/yarn-project/circuit-types/src/tx_effect.ts +++ b/yarn-project/circuit-types/src/tx_effect.ts @@ -8,13 +8,8 @@ import { RevertCode, } from '@aztec/circuits.js'; import { makeTuple } from '@aztec/foundation/array'; -import { sha256 } from '@aztec/foundation/crypto'; -import { - BufferReader, - serializeArrayOfBufferableToVector, - serializeToBuffer, - truncateAndPad, -} from '@aztec/foundation/serialize'; +import { sha256Trunc } from '@aztec/foundation/crypto'; +import { BufferReader, serializeArrayOfBufferableToVector, serializeToBuffer } from '@aztec/foundation/serialize'; import { inspect } from 'util'; @@ -157,7 +152,7 @@ export class TxEffect { unencryptedLogsHashKernel0, ]); - return truncateAndPad(sha256(inputValue)); + return sha256Trunc(inputValue); } static random( From d7a68bd025a4325dfc7e9aedae4b8f243b3777a3 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 1 May 2024 10:24:27 +0000 Subject: [PATCH 16/21] chore: fmt --- l1-contracts/src/core/libraries/decoders/TxsDecoder.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol b/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol index b19b2065d9b9..bc0b21c41106 100644 --- a/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol +++ b/l1-contracts/src/core/libraries/decoders/TxsDecoder.sol @@ -259,7 +259,8 @@ library TxsDecoder { // padded to MAX_LOGS * 32 bytes // NB: this assumes MAX_ENCRYPTED_LOGS_PER_TX == MAX_UNENCRYPTED_LOGS_PER_TX flattenedLogHashes = bytes.concat( - flattenedLogHashes, new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - flattenedLogHashes.length) + flattenedLogHashes, + new bytes(Constants.MAX_ENCRYPTED_LOGS_PER_TX * 32 - flattenedLogHashes.length) ); bytes32 kernelPublicInputsLogsHash = Hash.sha256ToField(flattenedLogHashes); From e7cc4c42d4cbbdcea2ba9a4548161b813dd11ab2 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 1 May 2024 13:42:56 +0000 Subject: [PATCH 17/21] feat: revert arr, track logs with counters, sort in exec. res. --- .../aztec/src/context/private_context.nr | 25 +++--- .../aztec/src/context/public_context.nr | 5 +- .../aztec-nr/aztec/src/oracle/logs.nr | 9 ++- .../src/kernel_prover/kernel_prover.test.ts | 8 +- .../pxe/src/pxe_service/pxe_service.ts | 6 +- .../simulator/src/acvm/oracle/oracle.ts | 14 +++- .../simulator/src/acvm/oracle/typed_oracle.ts | 5 +- .../src/client/client_execution_context.ts | 26 +++--- .../simulator/src/client/execution_result.ts | 79 ++++++++++++++++--- .../src/client/private_execution.test.ts | 47 +++++++---- .../simulator/src/client/private_execution.ts | 2 - .../src/public/public_execution_context.ts | 2 +- 12 files changed, 158 insertions(+), 70 deletions(-) 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 05ee5a7703bb..7b606ce3360b 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -272,7 +272,7 @@ impl PrivateContext { // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4) self.unencrypted_log_preimages_length += 44 + log_slice.len().to_field(); // call oracle - let _void = emit_unencrypted_log_private_internal(contract_address, event_selector, log); + let _void = emit_unencrypted_log_private_internal(contract_address, event_selector, log, side_effect.counter); } // This fn exists separately from emit_unencrypted_log because sha hashing the preimage @@ -288,7 +288,8 @@ impl PrivateContext { let log_hash = emit_contract_class_unencrypted_log_private_internal( contract_address, event_selector, - log + log, + self.side_effect_counter ); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; self.unencrypted_logs_hashes.push(side_effect); @@ -312,7 +313,8 @@ impl PrivateContext { storage_slot, note_type_id, encryption_pub_key, - preimage + preimage, + self.side_effect_counter ); let log_hash = compute_encrypted_log_hash(encrypted_log); let side_effect = SideEffect { value: log_hash, counter: self.side_effect_counter }; @@ -644,31 +646,34 @@ impl PackedReturns { fn emit_unencrypted_log_oracle_private( _contract_address: AztecAddress, _event_selector: Field, - _message: T + _message: T, + _counter: u32 ) -> Field {} unconstrained pub fn emit_unencrypted_log_private_internal( contract_address: AztecAddress, event_selector: Field, - message: T + message: T, + counter: u32 ) -> Field { - // https://github.com/AztecProtocol/aztec-packages/issues/885 - emit_unencrypted_log_oracle_private(contract_address, event_selector, message) + emit_unencrypted_log_oracle_private(contract_address, event_selector, message, counter) } #[oracle(emitContractClassUnencryptedLog)] fn emit_contract_class_unencrypted_log_private( contract_address: AztecAddress, event_selector: Field, - message: [Field; N] + message: [Field; N], + counter: u32 ) -> Field {} unconstrained pub fn emit_contract_class_unencrypted_log_private_internal( contract_address: AztecAddress, event_selector: Field, - message: [Field; N] + message: [Field; N], + counter: u32 ) -> Field { - emit_contract_class_unencrypted_log_private(contract_address, event_selector, message) + emit_contract_class_unencrypted_log_private(contract_address, event_selector, message, counter) } diff --git a/noir-projects/aztec-nr/aztec/src/context/public_context.nr b/noir-projects/aztec-nr/aztec/src/context/public_context.nr index 54246424b65a..33765f8f3ec5 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -284,7 +284,7 @@ impl PublicContextInterface for PublicContext { // 44 = addr (32) + selector (4) + raw log len (4) + processed log len (4) self.unencrypted_log_preimages_length = self.unencrypted_log_preimages_length + 44 + log_slice.len().to_field(); // Call oracle to broadcast log - let _void = emit_unencrypted_log_oracle(contract_address, event_selector, log); + let _void = emit_unencrypted_log_oracle(contract_address, event_selector, log, side_effect.counter); } fn call_public_function( @@ -353,7 +353,8 @@ fn nullifier_exists_oracle(nullifier: Field) -> Field {} fn emit_unencrypted_log_oracle( _contract_address: AztecAddress, _event_selector: Field, - _message: T + _message: T, + _counter: u32, ) -> Field {} struct FunctionReturns { diff --git a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr index f98fc82cc1a2..48df110c32a7 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/logs.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/logs.nr @@ -9,7 +9,8 @@ fn emit_encrypted_log_oracle( _storage_slot: Field, _note_type_id: Field, _encryption_pub_key: GrumpkinPoint, - _preimage: [Field; N] + _preimage: [Field; N], + _counter: u32, ) -> [Field; M] {} unconstrained pub fn emit_encrypted_log( @@ -17,13 +18,15 @@ unconstrained pub fn emit_encrypted_log( storage_slot: Field, note_type_id: Field, encryption_pub_key: GrumpkinPoint, - preimage: [Field; N] + preimage: [Field; N], + counter: u32 ) -> [Field; M] { emit_encrypted_log_oracle( contract_address, storage_slot, note_type_id, encryption_pub_key, - preimage + preimage, + counter ) } diff --git a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts index f94f59b41fd9..bf95927e70df 100644 --- a/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts +++ b/yarn-project/pxe/src/kernel_prover/kernel_prover.test.ts @@ -1,4 +1,4 @@ -import { EncryptedFunctionL2Logs, Note, UnencryptedFunctionL2Logs } from '@aztec/circuit-types'; +import { Note } from '@aztec/circuit-types'; import { FunctionData, FunctionSelector, @@ -77,10 +77,8 @@ describe('Kernel Prover', () => { acir: Buffer.alloc(0), partialWitness: new Map(), enqueuedPublicFunctionCalls: [], - encryptedLogs: EncryptedFunctionL2Logs.empty(), - unencryptedLogs: UnencryptedFunctionL2Logs.empty(), - allEncryptedLogs: EncryptedFunctionL2Logs.empty(), - allUnencryptedLogs: UnencryptedFunctionL2Logs.empty(), + encryptedLogs: [], + unencryptedLogs: [], }; }; diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index a9a9e35dc247..6994ea926c64 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -45,6 +45,8 @@ import { type AcirSimulator, type ExecutionResult, collectEnqueuedPublicFunctionCalls, + collectSortedEncryptedLogs, + collectSortedUnencryptedLogs, resolveOpcodeLocations, } from '@aztec/simulator'; import { type ContractClassWithId, type ContractInstanceWithAddress } from '@aztec/types/contracts'; @@ -651,8 +653,8 @@ export class PXEService implements PXE { this.log.debug(`Executing kernel prover...`); const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult); - const unencryptedLogs = new UnencryptedTxL2Logs([executionResult.allUnencryptedLogs]); - const encryptedLogs = new EncryptedTxL2Logs([executionResult.allEncryptedLogs]); + const unencryptedLogs = new UnencryptedTxL2Logs([collectSortedUnencryptedLogs(executionResult)]); + const encryptedLogs = new EncryptedTxL2Logs([collectSortedEncryptedLogs(executionResult)]); const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult); // HACK(#1639): Manually patches the ordering of the public call stack diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index af1444da7611..f4438d7d0af2 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -294,6 +294,7 @@ export class Oracle { [publicKeyX]: ACVMField[], [publicKeyY]: ACVMField[], log: ACVMField[], + [counter]: ACVMField[], ): ACVMField[] { const publicKey = new Point(fromACVMField(publicKeyX), fromACVMField(publicKeyY)); const encLog = this.typedOracle.emitEncryptedLog( @@ -302,6 +303,7 @@ export class Oracle { Fr.fromString(noteTypeId), publicKey, log.map(fromACVMField), + +counter, ); // 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 @@ -314,7 +316,12 @@ export class Oracle { return encLogFields; } - emitUnencryptedLog([contractAddress]: ACVMField[], [eventSelector]: ACVMField[], message: ACVMField[]): ACVMField { + emitUnencryptedLog( + [contractAddress]: ACVMField[], + [eventSelector]: ACVMField[], + message: ACVMField[], + [counter]: ACVMField[], + ): ACVMField { const logPayload = Buffer.concat(message.map(fromACVMField).map(f => f.toBuffer())); const log = new UnencryptedL2Log( AztecAddress.fromString(contractAddress), @@ -322,7 +329,7 @@ export class Oracle { logPayload, ); - this.typedOracle.emitUnencryptedLog(log); + this.typedOracle.emitUnencryptedLog(log, +counter); return toACVMField(0); } @@ -330,6 +337,7 @@ export class Oracle { [contractAddress]: ACVMField[], [eventSelector]: ACVMField[], message: ACVMField[], + [counter]: ACVMField[], ): ACVMField { const logPayload = Buffer.concat(message.map(fromACVMField).map(f => f.toBuffer())); const log = new UnencryptedL2Log( @@ -338,7 +346,7 @@ export class Oracle { logPayload, ); - const logHash = this.typedOracle.emitContractClassUnencryptedLog(log); + const logHash = this.typedOracle.emitContractClassUnencryptedLog(log, +counter); return toACVMField(logHash); } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 4e5e50a74f4a..f56b84dcd37b 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -193,15 +193,16 @@ export abstract class TypedOracle { _noteTypeId: Fr, _publicKey: PublicKey, _log: Fr[], + _counter: number, ): Buffer { throw new OracleMethodNotAvailableError('emitEncryptedLog'); } - emitUnencryptedLog(_log: UnencryptedL2Log): void { + emitUnencryptedLog(_log: UnencryptedL2Log, _counter: number): void { throw new OracleMethodNotAvailableError('emitUnencryptedLog'); } - emitContractClassUnencryptedLog(_log: UnencryptedL2Log): Fr { + emitContractClassUnencryptedLog(_log: UnencryptedL2Log, _counter: number): Fr { throw new OracleMethodNotAvailableError('emitContractClassUnencryptedLog'); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 9a30465352f4..e6ed679910cf 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -32,7 +32,12 @@ import { type NoteData, toACVMWitness } from '../acvm/index.js'; import { type PackedValuesCache } from '../common/packed_values_cache.js'; import { type DBOracle } from './db_oracle.js'; import { type ExecutionNoteCache } from './execution_note_cache.js'; -import { type ExecutionResult, type NoteAndSlot, type NullifiedNoteHashCounter } from './execution_result.js'; +import { + CountedLog, + type ExecutionResult, + type NoteAndSlot, + type NullifiedNoteHashCounter, +} from './execution_result.js'; import { type LogsCache } from './logs_cache.js'; import { pickNotes } from './pick_notes.js'; import { executePrivateFunction } from './private_execution.js'; @@ -61,8 +66,8 @@ export class ClientExecutionContext extends ViewDataOracle { */ private gotNotes: Map = new Map(); private nullifiedNoteHashCounters: NullifiedNoteHashCounter[] = []; - private encryptedLogs: EncryptedL2Log[] = []; - private unencryptedLogs: UnencryptedL2Log[] = []; + private encryptedLogs: CountedLog[] = []; + private unencryptedLogs: CountedLog[] = []; private nestedExecutions: ExecutionResult[] = []; private enqueuedPublicFunctionCalls: PublicCallRequest[] = []; @@ -149,7 +154,7 @@ export class ClientExecutionContext extends ViewDataOracle { * Return the encrypted logs emitted during this execution. */ public getEncryptedLogs() { - return new EncryptedFunctionL2Logs(this.encryptedLogs); + return this.encryptedLogs; } /** @@ -163,7 +168,7 @@ export class ClientExecutionContext extends ViewDataOracle { * Return the encrypted logs emitted during this execution. */ public getUnencryptedLogs() { - return new UnencryptedFunctionL2Logs(this.unencryptedLogs); + return this.unencryptedLogs; } /** @@ -356,13 +361,14 @@ export class ClientExecutionContext extends ViewDataOracle { noteTypeId: Fr, publicKey: Point, log: Fr[], + counter: number, ) { const note = new Note(log); 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(encryptedLog); + this.encryptedLogs.push(new CountedLog(encryptedLog, counter)); this.logsCache.addEncryptedLog(encryptedLog); return encryptedNote; } @@ -371,8 +377,8 @@ export class ClientExecutionContext extends ViewDataOracle { * Emit an unencrypted log. * @param log - The unencrypted log to be emitted. */ - public override emitUnencryptedLog(log: UnencryptedL2Log) { - this.unencryptedLogs.push(log); + public override emitUnencryptedLog(log: UnencryptedL2Log, counter: number) { + this.unencryptedLogs.push(new CountedLog(log, counter)); this.logsCache.addUnencryptedLog(log); const text = log.toHumanReadable(); this.log.verbose(`Emitted unencrypted log: "${text.length > 100 ? text.slice(0, 100) + '...' : text}"`); @@ -385,8 +391,8 @@ export class ClientExecutionContext extends ViewDataOracle { * See private_context.nr * @param log - The unencrypted log to be emitted. */ - public override emitContractClassUnencryptedLog(log: UnencryptedL2Log) { - this.unencryptedLogs.push(log); + public override emitContractClassUnencryptedLog(log: UnencryptedL2Log, counter: number) { + this.unencryptedLogs.push(new CountedLog(log, counter)); this.logsCache.addUnencryptedLog(log); const text = log.toHumanReadable(); this.log.verbose( diff --git a/yarn-project/simulator/src/client/execution_result.ts b/yarn-project/simulator/src/client/execution_result.ts index ab5a4dcedec4..2973a2ad8dbf 100644 --- a/yarn-project/simulator/src/client/execution_result.ts +++ b/yarn-project/simulator/src/client/execution_result.ts @@ -1,8 +1,16 @@ -import { type EncryptedFunctionL2Logs, type Note, type UnencryptedFunctionL2Logs } from '@aztec/circuit-types'; import { + EncryptedFunctionL2Logs, + type EncryptedL2Log, + type Note, + UnencryptedFunctionL2Logs, + type UnencryptedL2Log, +} from '@aztec/circuit-types'; +import { + type IsEmpty, type NoteHashReadRequestMembershipWitness, type PrivateCallStackItem, type PublicCallRequest, + sortByCounter, } from '@aztec/circuits.js'; import { type Fr } from '@aztec/foundation/fields'; @@ -20,6 +28,14 @@ export interface NoteAndSlot { noteTypeId: Fr; } +export class CountedLog implements IsEmpty { + constructor(public log: TLog, public counter: number) {} + + isEmpty(): boolean { + return !this.log.data.length && !this.counter; + } +} + export interface NullifiedNoteHashCounter { noteHashCounter: number; nullifierCounter: number; @@ -54,22 +70,12 @@ export interface ExecutionResult { * Encrypted logs emitted during execution of this function call. * Note: These are preimages to `encryptedLogsHashes`. */ - encryptedLogs: EncryptedFunctionL2Logs; + encryptedLogs: CountedLog[]; /** * Unencrypted logs emitted during execution of this function call. * Note: These are preimages to `unencryptedLogsHashes`. */ - unencryptedLogs: UnencryptedFunctionL2Logs; - /** - * Encrypted logs emitted during execution of this function call - * AND any nested calls. - */ - allEncryptedLogs: EncryptedFunctionL2Logs; - /** - * Unencrypted logs emitted during execution of this function call - * AND any nested calls. - */ - allUnencryptedLogs: UnencryptedFunctionL2Logs; + unencryptedLogs: CountedLog[]; } export function collectNullifiedNoteHashCounters(execResult: ExecutionResult): NullifiedNoteHashCounter[] { @@ -79,6 +85,53 @@ export function collectNullifiedNoteHashCounters(execResult: ExecutionResult): N ].flat(); } +/** + * Collect all encrypted logs across all nested executions. + * @param execResult - The topmost execution result. + * @returns All encrypted logs. + */ +function collectEncryptedLogs(execResult: ExecutionResult): CountedLog[] { + // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. + return [execResult.encryptedLogs, ...[...execResult.nestedExecutions].reverse().flatMap(collectEncryptedLogs)].flat(); +} + +/** + * Collect all encrypted logs across all nested executions and sorts by counter. + * @param execResult - The topmost execution result. + * @returns All encrypted logs. + */ +export function collectSortedEncryptedLogs(execResult: ExecutionResult): EncryptedFunctionL2Logs { + // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. + const allLogs = collectEncryptedLogs(execResult); + const sortedLogs = sortByCounter(allLogs); + return new EncryptedFunctionL2Logs(sortedLogs.map(l => l.log)); +} + +/** + * Collect all unencrypted logs across all nested executions. + * @param execResult - The topmost execution result. + * @returns All unencrypted logs. + */ +function collectUnencryptedLogs(execResult: ExecutionResult): CountedLog[] { + // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. + return [ + execResult.unencryptedLogs, + ...[...execResult.nestedExecutions].reverse().flatMap(collectUnencryptedLogs), + ].flat(); +} + +/** + * Collect all unencrypted logs across all nested executions and sorts by counter. + * @param execResult - The topmost execution result. + * @returns All unencrypted logs. + */ +export function collectSortedUnencryptedLogs(execResult: ExecutionResult): UnencryptedFunctionL2Logs { + // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. + const allLogs = collectUnencryptedLogs(execResult); + const sortedLogs = sortByCounter(allLogs); + return new UnencryptedFunctionL2Logs(sortedLogs.map(l => l.log)); +} + /** * Collect all enqueued public function calls across all nested executions. * @param execResult - The topmost execution result. diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 6857f4b72d17..f82a4c265c8a 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -1,4 +1,11 @@ -import { type AztecNode, type L1ToL2Message, Note, PackedValues, TxExecutionRequest } from '@aztec/circuit-types'; +import { + type AztecNode, + EncryptedFunctionL2Logs, + type L1ToL2Message, + Note, + PackedValues, + TxExecutionRequest, +} from '@aztec/circuit-types'; import { AppendOnlyTreeSnapshot, CallContext, @@ -56,6 +63,7 @@ import { MessageLoadOracleInputs } from '../acvm/index.js'; import { buildL1ToL2Message } from '../test/utils.js'; import { computeSlotForMapping } from '../utils.js'; import { type DBOracle } from './db_oracle.js'; +import { type ExecutionResult, collectSortedUnencryptedLogs } from './execution_result.js'; import { AcirSimulator } from './simulator.js'; jest.setTimeout(60_000); @@ -163,6 +171,11 @@ describe('Private Execution test suite', () => { return trees[name]; }; + const getEncryptedSerializedLength = (result: ExecutionResult) => { + const fnLogs = new EncryptedFunctionL2Logs(result.encryptedLogs.map(l => l.log)); + return fnLogs.getSerializedLength(); + }; + beforeAll(() => { logger = createDebugLogger('aztec:test:private_execution'); @@ -215,7 +228,7 @@ describe('Private Execution test suite', () => { const newUnencryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.unencryptedLogsHashes); expect(newUnencryptedLogs).toHaveLength(1); - const functionLogs = result.allUnencryptedLogs; + const functionLogs = collectSortedUnencryptedLogs(result); expect(functionLogs.logs).toHaveLength(1); const [unencryptedLog] = newUnencryptedLogs; @@ -234,7 +247,7 @@ describe('Private Execution test suite', () => { const newUnencryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.unencryptedLogsHashes); expect(newUnencryptedLogs).toHaveLength(1); - const functionLogs = result.allUnencryptedLogs; + const functionLogs = collectSortedUnencryptedLogs(result); expect(functionLogs.logs).toHaveLength(1); const [unencryptedLog] = newUnencryptedLogs; @@ -326,9 +339,9 @@ describe('Private Execution test suite', () => { expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs[0].log.hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( - new Fr(result.encryptedLogs.getSerializedLength()), + new Fr(getEncryptedSerializedLength(result)), ); }); @@ -357,9 +370,9 @@ describe('Private Execution test suite', () => { expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs[0].log.hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( - new Fr(result.encryptedLogs.getSerializedLength()), + new Fr(getEncryptedSerializedLength(result)), ); }); @@ -413,10 +426,10 @@ describe('Private Execution test suite', () => { expect(newEncryptedLogs).toHaveLength(2); const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; - expect(encryptedChangeLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); - expect(encryptedRecipientLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); + expect(encryptedChangeLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs[0].log.hash())); + expect(encryptedRecipientLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs[1].log.hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( - new Fr(result.encryptedLogs.getSerializedLength()), + new Fr(getEncryptedSerializedLength(result)), ); const readRequests = getNonEmptyItems(result.callStackItem.publicInputs.noteHashReadRequests).map(r => r.value); @@ -454,10 +467,10 @@ describe('Private Execution test suite', () => { const newEncryptedLogs = getNonEmptyItems(result.callStackItem.publicInputs.encryptedLogsHashes); expect(newEncryptedLogs).toHaveLength(2); const [encryptedChangeLog, encryptedRecipientLog] = newEncryptedLogs; - expect(encryptedChangeLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); - expect(encryptedRecipientLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[1].hash())); + expect(encryptedChangeLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs[0].log.hash())); + expect(encryptedRecipientLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs[1].log.hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( - new Fr(result.encryptedLogs.getSerializedLength()), + new Fr(getEncryptedSerializedLength(result)), ); }); }); @@ -908,9 +921,9 @@ describe('Private Execution test suite', () => { expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(result.encryptedLogs[0].log.hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( - new Fr(result.encryptedLogs.getSerializedLength()), + new Fr(getEncryptedSerializedLength(result)), ); // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) @@ -987,9 +1000,9 @@ describe('Private Execution test suite', () => { expect(newEncryptedLogs).toHaveLength(1); const [encryptedLog] = newEncryptedLogs; - expect(encryptedLog.value).toEqual(Fr.fromBuffer(execInsert.encryptedLogs.logs[0].hash())); + expect(encryptedLog.value).toEqual(Fr.fromBuffer(execInsert.encryptedLogs[0].log.hash())); expect(result.callStackItem.publicInputs.encryptedLogPreimagesLength).toEqual( - new Fr(result.encryptedLogs.getSerializedLength()), + new Fr(getEncryptedSerializedLength(result)), ); // read request should match innerNoteHash for pending notes (there is no nonce, so can't compute "unique" hash) diff --git a/yarn-project/simulator/src/client/private_execution.ts b/yarn-project/simulator/src/client/private_execution.ts index e5170a4bab4c..75509e6578c0 100644 --- a/yarn-project/simulator/src/client/private_execution.ts +++ b/yarn-project/simulator/src/client/private_execution.ts @@ -72,7 +72,5 @@ export async function executePrivateFunction( enqueuedPublicFunctionCalls, encryptedLogs, unencryptedLogs, - allEncryptedLogs: context.getAllEncryptedLogs(), - allUnencryptedLogs: context.getAllUnencryptedLogs(), }; } diff --git a/yarn-project/simulator/src/public/public_execution_context.ts b/yarn-project/simulator/src/public/public_execution_context.ts index de61b19356c7..5998df359979 100644 --- a/yarn-project/simulator/src/public/public_execution_context.ts +++ b/yarn-project/simulator/src/public/public_execution_context.ts @@ -145,7 +145,7 @@ export class PublicExecutionContext extends TypedOracle { * Emit an unencrypted log. * @param log - The unencrypted log to be emitted. */ - public override emitUnencryptedLog(log: UnencryptedL2Log) { + public override emitUnencryptedLog(log: UnencryptedL2Log, _counter: number) { this.unencryptedLogs.push(log); this.allUnencryptedLogs.push(log); this.log.verbose(`Emitted unencrypted log: "${log.toHumanReadable()}"`); From 4050be3180ad434ecb492d462c12867a3f1e5d7b Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Wed, 1 May 2024 15:37:04 +0000 Subject: [PATCH 18/21] chore: remove redundant check + comments --- .../aztec-nr/aztec/src/context/private_context.nr | 4 ---- yarn-project/circuit-types/src/tx/tx.ts | 12 ------------ 2 files changed, 16 deletions(-) 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 7b606ce3360b..9641090481ca 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -67,8 +67,6 @@ struct PrivateContext { unencrypted_logs_hashes: BoundedVec, encrypted_log_preimages_length: Field, unencrypted_log_preimages_length: Field, - // encrypted_logs_preimages: Vec, - // unencrypted_logs_preimages: Vec, nullifier_key: Option, } @@ -136,8 +134,6 @@ impl PrivateContext { unencrypted_logs_hashes: BoundedVec::new(), encrypted_log_preimages_length: 0, unencrypted_log_preimages_length: 0, - // encrypted_logs_preimages: Vec::new(), - // unencrypted_logs_preimages: Vec::new(), nullifier_key: Option::none() } } diff --git a/yarn-project/circuit-types/src/tx/tx.ts b/yarn-project/circuit-types/src/tx/tx.ts index 1bf14f809be8..63e2c62fdd75 100644 --- a/yarn-project/circuit-types/src/tx/tx.ts +++ b/yarn-project/circuit-types/src/tx/tx.ts @@ -39,18 +39,6 @@ export class Tx { */ public readonly enqueuedPublicFunctionCalls: PublicCallRequest[], ) { - if (this.unencryptedLogs.functionLogs.length < this.encryptedLogs.functionLogs.length) { - // TODO(Miranda): This error was not throwing in some cases, as logs are nested objects which would show len 1 even if no logs existed - // Many tests produce enc logs and no unenc logs, so this error should have been throwing even in good cases - // This check is present because each private function invocation creates encrypted FunctionL2Logs object and - // both public and private function invocations create unencrypted FunctionL2Logs object. Hence "num unencrypted" - // >= "num encrypted". - throw new Error( - `Number of function logs in unencrypted logs (${this.unencryptedLogs.functionLogs.length}) has to be equal - or larger than number function logs in encrypted logs (${this.encryptedLogs.functionLogs.length})`, - ); - } - const kernelPublicCallStackSize = data.numberOfPublicCallRequests(); if (kernelPublicCallStackSize !== enqueuedPublicFunctionCalls.length) { throw new Error( From 1726fb3444ec8e5ccef7285f814a92795511eac7 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Thu, 2 May 2024 10:49:12 +0000 Subject: [PATCH 19/21] chore: remove comments, remove redundant .reverse --- yarn-project/simulator/src/client/execution_result.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/yarn-project/simulator/src/client/execution_result.ts b/yarn-project/simulator/src/client/execution_result.ts index 2973a2ad8dbf..f2572878e0f8 100644 --- a/yarn-project/simulator/src/client/execution_result.ts +++ b/yarn-project/simulator/src/client/execution_result.ts @@ -91,8 +91,7 @@ export function collectNullifiedNoteHashCounters(execResult: ExecutionResult): N * @returns All encrypted logs. */ function collectEncryptedLogs(execResult: ExecutionResult): CountedLog[] { - // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. - return [execResult.encryptedLogs, ...[...execResult.nestedExecutions].reverse().flatMap(collectEncryptedLogs)].flat(); + return [execResult.encryptedLogs, ...[...execResult.nestedExecutions].flatMap(collectEncryptedLogs)].flat(); } /** @@ -101,7 +100,6 @@ function collectEncryptedLogs(execResult: ExecutionResult): CountedLog l.log)); @@ -113,11 +111,7 @@ export function collectSortedEncryptedLogs(execResult: ExecutionResult): Encrypt * @returns All unencrypted logs. */ function collectUnencryptedLogs(execResult: ExecutionResult): CountedLog[] { - // without the .reverse(), the logs will be in a queue like fashion which is wrong as the kernel processes it like a stack. - return [ - execResult.unencryptedLogs, - ...[...execResult.nestedExecutions].reverse().flatMap(collectUnencryptedLogs), - ].flat(); + return [execResult.unencryptedLogs, ...[...execResult.nestedExecutions].flatMap(collectUnencryptedLogs)].flat(); } /** @@ -126,7 +120,6 @@ function collectUnencryptedLogs(execResult: ExecutionResult): CountedLog l.log)); From e591c87c905463fc9b34164f22c04a8f4ca3c130 Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Thu, 2 May 2024 11:12:19 +0000 Subject: [PATCH 20/21] chore: moved nested logs tests now e2e_nested has been refactored --- .../end-to-end/src/e2e_block_building.test.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) 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 31698d5516bb..5481b5687b90 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 @@ -1,3 +1,4 @@ +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type AztecAddress, type AztecNode, @@ -11,6 +12,7 @@ import { type TxReceipt, TxStatus, type Wallet, + deriveKeys, } from '@aztec/aztec.js'; import { times } from '@aztec/foundation/collection'; import { pedersenHash } from '@aztec/foundation/crypto'; @@ -19,6 +21,7 @@ import { TestContract } from '@aztec/noir-contracts.js/Test'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { setup } from './fixtures/utils.js'; +import { TaggedNote } from '../../circuit-types/src/logs/l1_note_payload/tagged_note.js'; describe('e2e_block_building', () => { let pxe: PXE; @@ -184,6 +187,60 @@ describe('e2e_block_building', () => { }); }); }); + + describe('logs in nested calls are ordered as expected', () => { + // This test was originally writted for e2e_nested, but it was refactored + // to not use TestContract. + let testContract: TestContract; + + beforeEach(async () => { + ({ teardown, pxe, logger, wallet: owner } = await setup(1)); + logger.info(`Deploying test contract`); + testContract = await TestContract.deploy(owner).send().deployed(); + }, 30_000); + + it('calls a method with nested unencrypted logs', async () => { + const tx = await testContract.methods.emit_unencrypted_logs_nested([1, 2, 3, 4, 5]).send().wait(); + const logs = (await pxe.getUnencryptedLogs({ txHash: tx.txHash })).logs.map(l => l.log); + + // First log should be contract address + expect(logs[0].data).toEqual(testContract.address.toBuffer()); + + // Second log should be array of fields + let expectedBuffer = Buffer.concat([1, 2, 3, 4, 5].map(num => new Fr(num).toBuffer())); + expect(logs[1].data.subarray(-32 * 5)).toEqual(expectedBuffer); + + // Third log should be string "test" + expectedBuffer = Buffer.concat( + ['t', 'e', 's', 't'].map(num => Buffer.concat([Buffer.alloc(31), Buffer.from(num)])), + ); + expect(logs[2].data.subarray(-32 * 5)).toEqual(expectedBuffer); + }, 30_000); + + it('calls a method with nested encrypted logs', async () => { + // account setup + const privateKey = new Fr(7n); + const keys = deriveKeys(privateKey); + const account = getSchnorrAccount(pxe, privateKey, keys.masterIncomingViewingSecretKey); + await account.deploy().wait(); + const thisWallet = await account.getWallet(); + + // call test contract + const action = testContract.methods.emit_encrypted_logs_nested(10, thisWallet.getAddress()); + const tx = await action.prove(); + const rct = await action.send().wait(); + + // compare logs + expect(rct.status).toEqual('mined'); + const decryptedLogs = tx.encryptedLogs + .unrollLogs() + .map(l => TaggedNote.fromEncryptedBuffer(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)); + expect(notevalues[2]).toEqual(new Fr(12)); + }, 30_000); + }); }); /** From 5cf5b5a1b01c9d8b16a2a633ac298ae0c5d1cedb Mon Sep 17 00:00:00 2001 From: MirandaWood Date: Thu, 2 May 2024 11:30:58 +0000 Subject: [PATCH 21/21] chore: fmt + update docs for 0.37 release --- docs/docs/misc/migration_notes.md | 4 +++- yarn-project/end-to-end/src/e2e_block_building.test.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index ba5a0a5230b5..c792470b0b5c 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -6,7 +6,7 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. -## 0.36.0 +## 0.38.0 ### [Aztec.nr] Emmiting encrypted logs @@ -20,6 +20,8 @@ The `emit_encrypted_log` function is now a context method. + context.emit_encrypted_log(log1); ``` +## 0.36.0 + ### `FieldNote` removed `FieldNote` only existed for testing purposes, and was not a note type that should be used in any real application. Its name unfortunately led users to think that it was a note type suitable to store a `Field` value, which it wasn't. 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 5481b5687b90..3ad1d2c9f2fb 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 @@ -20,8 +20,8 @@ import { StatefulTestContractArtifact } from '@aztec/noir-contracts.js'; import { TestContract } from '@aztec/noir-contracts.js/Test'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; -import { setup } from './fixtures/utils.js'; import { TaggedNote } from '../../circuit-types/src/logs/l1_note_payload/tagged_note.js'; +import { setup } from './fixtures/utils.js'; describe('e2e_block_building', () => { let pxe: PXE;