From a02fd20aecf5216d8ebe27155f6ea712abc2391c Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 4 Jul 2024 14:38:08 +0000 Subject: [PATCH 1/3] refactor: optimize public call stack item hashing --- .../src/core/libraries/ConstantsGen.sol | 1 + .../aztec/src/context/private_context.nr | 4 +- .../crates/public-kernel-lib/src/common.nr | 2 +- .../src/public_kernel_app_logic.nr | 6 +- .../src/public_kernel_setup.nr | 10 +-- .../src/public_kernel_teardown.nr | 12 +-- .../crates/types/src/abis/mod.nr | 1 + .../types/src/abis/public_call_stack_item.nr | 38 ++++---- .../abis/public_call_stack_item_compressed.nr | 75 ++++++++++++++++ .../crates/types/src/constants.nr | 1 + yarn-project/circuits.js/src/constants.gen.ts | 1 + .../public_call_stack_item.test.ts.snap | 8 +- .../src/structs/public_call_request.ts | 2 +- .../structs/public_call_stack_item.test.ts | 8 +- .../src/structs/public_call_stack_item.ts | 38 +++----- .../public_call_stack_item_compressed.ts | 88 +++++++++++++++++++ .../circuits.js/src/tests/factories.ts | 2 +- .../src/client/private_execution.test.ts | 2 +- .../src/public/abstract_phase_manager.ts | 2 +- 19 files changed, 224 insertions(+), 77 deletions(-) create mode 100644 noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr create mode 100644 yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 81d2dd41d83f..aab158b8fd3c 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -165,6 +165,7 @@ library Constants { uint256 internal constant PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; uint256 internal constant COMBINED_ACCUMULATED_DATA_LENGTH = 333; uint256 internal constant COMBINED_CONSTANT_DATA_LENGTH = 40; + uint256 internal constant PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = 10; uint256 internal constant CALL_REQUEST_LENGTH = 7; uint256 internal constant PRIVATE_ACCUMULATED_DATA_LENGTH = 1232; uint256 internal constant PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2307; 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 dc03685f8428..de534ce55c23 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -506,7 +506,7 @@ impl PrivateContext { ); self.side_effect_counter = self.side_effect_counter + 1; - self.public_call_stack_hashes.push(item.hash()); + self.public_call_stack_hashes.push(item.get_compressed().hash()); } pub fn set_public_teardown_function( @@ -549,7 +549,7 @@ impl PrivateContext { ); self.side_effect_counter = self.side_effect_counter + 1; - self.public_teardown_function_hash = item.hash(); + self.public_teardown_function_hash = item.get_compressed().hash(); } fn validate_call_stack_item_from_oracle( diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr index 4dfe96d6083c..a5fe93486098 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/common.nr @@ -554,7 +554,7 @@ pub fn validate_call_against_request(public_call: PublicCallData, request: CallR let call_stack_item = public_call.call_stack_item; assert( - request.hash == call_stack_item.hash(), "calculated public_kernel_inputs_hash does not match provided public_kernel_inputs_hash at the top of the call stack" + request.hash == call_stack_item.get_compressed().hash(), "calculated public_kernel_inputs_hash does not match provided public_kernel_inputs_hash at the top of the call stack" ); let call_context = call_stack_item.public_inputs.call_context; diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr index da8ea3554bf8..2cb9d9f733ac 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_app_logic.nr @@ -83,7 +83,7 @@ impl PublicKernelAppLogicCircuitPrivateInputs { // and we aren't updating the public end values, so we want this kernel circuit to solve. // So just check that the call request is the same as the one we expected. assert( - reverted_call_request.hash == self.public_call.call_stack_item.hash(), "calculated public_kernel_inputs_hash does not match provided public_kernel_inputs_hash at the top of the call stack" + reverted_call_request.hash == self.public_call.call_stack_item.get_compressed().hash(), "calculated public_kernel_inputs_hash does not match provided public_kernel_inputs_hash at the top of the call stack" ); } @@ -150,7 +150,7 @@ mod tests { pub fn execute(&mut self) -> PublicKernelCircuitPublicInputs { let public_call = self.public_call.finish(); // Adjust the call stack item hash for the current call in the previous iteration. - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); let is_delegate_call = public_call.call_stack_item.public_inputs.call_context.is_delegate_call; self.previous_kernel.push_public_call_request(hash, is_delegate_call); let previous_kernel = self.previous_kernel.to_public_kernel_data(true); @@ -598,7 +598,7 @@ mod tests { let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); builder.public_call.public_inputs.revert_code = 1; let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); builder.previous_kernel.push_public_call_request(hash, false); builder.previous_kernel.push_public_call_request(hash, false); diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr index a041fc2a56f7..a434a3ecc3e1 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_setup.nr @@ -117,13 +117,13 @@ mod tests { pub fn stub_teardown_call(&mut self) { let teardown_call = PublicCallDataBuilder::new(); let teardown_call = teardown_call.finish(); - let teardown_call_hash = teardown_call.call_stack_item.hash(); + let teardown_call_hash = teardown_call.call_stack_item.get_compressed().hash(); let teardown_is_delegate_call = teardown_call.call_stack_item.public_inputs.call_context.is_delegate_call; self.previous_kernel.push_public_call_request(teardown_call_hash, teardown_is_delegate_call); } pub fn push_public_call(&mut self, public_call: PublicCallData) { - let public_call_hash = public_call.call_stack_item.hash(); + let public_call_hash = public_call.call_stack_item.get_compressed().hash(); let setup_is_delegate_call = public_call.call_stack_item.public_inputs.call_context.is_delegate_call; self.previous_kernel.push_public_call_request(public_call_hash, setup_is_delegate_call); } @@ -242,7 +242,7 @@ mod tests { builder.stub_teardown_call(); let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); // Tweak the call stack item hash. builder.previous_kernel.push_public_call_request(hash + 1, false); let previous_kernel = builder.previous_kernel.to_public_kernel_data(false); @@ -285,7 +285,7 @@ mod tests { let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); // Caller context is empty for regular calls. let is_delegate_call = false; builder.previous_kernel.push_public_call_request(hash, is_delegate_call); @@ -591,7 +591,7 @@ mod tests { let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new(); builder.public_call.public_inputs.revert_code = 0; let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); builder.previous_kernel.push_public_call_request(hash, false); builder.previous_kernel.push_public_call_request(hash, false); diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr index 1063374ec22e..e748e57c8569 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_teardown.nr @@ -131,7 +131,7 @@ impl PublicKernelTeardownCircuitPrivateInputs { let reverted_call_request = remaining_calls.pop(); assert( - reverted_call_request.hash == self.public_call.call_stack_item.hash(), "calculated public_kernel_inputs_hash does not match provided public_kernel_inputs_hash at the top of the teardown call stack" + reverted_call_request.hash == self.public_call.call_stack_item.get_compressed().hash(), "calculated public_kernel_inputs_hash does not match provided public_kernel_inputs_hash at the top of the teardown call stack" ); } @@ -198,7 +198,7 @@ mod tests { pub fn execute(&mut self) -> PublicKernelCircuitPublicInputs { let public_call = self.public_call.finish(); // Adjust the call stack item hash for the current call in the previous iteration. - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); let is_delegate_call = public_call.call_stack_item.public_inputs.call_context.is_delegate_call; self.previous_kernel.push_public_teardown_call_request(hash, is_delegate_call); let mut previous_kernel = self.previous_kernel.to_public_kernel_data(true); @@ -254,7 +254,7 @@ mod tests { let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); // Tweak the call stack item hash. builder.previous_kernel.push_public_teardown_call_request(hash + 1, false); let previous_kernel = builder.previous_kernel.to_public_kernel_data(true); @@ -295,7 +295,7 @@ mod tests { let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new().is_delegate_call(); let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); // Caller context is empty for regular calls. let is_delegate_call = false; builder.previous_kernel.push_public_teardown_call_request(hash, is_delegate_call); @@ -517,7 +517,7 @@ mod tests { let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); builder.public_call.public_inputs.revert_code = 1; let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); builder.previous_kernel.push_public_teardown_call_request(hash, false); // push again to check that the stack is cleared builder.previous_kernel.push_public_teardown_call_request(hash, false); @@ -535,7 +535,7 @@ mod tests { let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); builder.previous_kernel.revert_code = 1; let public_call = builder.public_call.finish(); - let hash = public_call.call_stack_item.hash(); + let hash = public_call.call_stack_item.get_compressed().hash(); builder.previous_kernel.push_public_teardown_call_request(hash, false); // push again to check that we keep one item after popping the current call builder.previous_kernel.push_public_teardown_call_request(hash, false); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr index 2434f8ffdeb4..ee604875a951 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr @@ -35,6 +35,7 @@ mod call_request; mod private_call_request; mod private_call_stack_item; mod public_call_stack_item; +mod public_call_stack_item_compressed; mod call_context; mod caller_context; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr index 4c12a77d0d3b..5c8023e8bb82 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr @@ -1,4 +1,7 @@ -use crate::abis::{function_data::FunctionData, public_circuit_public_inputs::PublicCircuitPublicInputs}; +use crate::abis::{ + function_data::FunctionData, public_circuit_public_inputs::PublicCircuitPublicInputs, + public_call_stack_item_compressed::PublicCallStackItemCompressed +}; use crate::address::AztecAddress; use crate::constants::GENERATOR_INDEX__CALL_STACK_ITEM; use crate::traits::Hash; @@ -12,22 +15,6 @@ struct PublicCallStackItem { is_execution_request: bool, } -impl Hash for PublicCallStackItem { - fn hash(self) -> Field { - let item = if self.is_execution_request { - self.as_execution_request() - } else { - self - }; - - std::hash::pedersen_hash_with_separator([ - item.contract_address.to_field(), - item.function_data.hash(), - item.public_inputs.hash(), - ], GENERATOR_INDEX__CALL_STACK_ITEM) - } -} - impl PublicCallStackItem { fn as_execution_request(self) -> Self { // WARNING: if updating, see comment in public_call_stack_item.ts's `PublicCallStackItem.hash()` @@ -44,6 +31,15 @@ impl PublicCallStackItem { }; call_stack_item } + + fn get_compressed(self) -> PublicCallStackItemCompressed { + PublicCallStackItemCompressed { + contract_address: self.contract_address, + call_context: self.public_inputs.call_context, + function_data: self.function_data, + args_hash: self.public_inputs.args_hash + } + } } mod tests { @@ -70,8 +66,8 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: true, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item request hash" test - let test_data_call_stack_item_request_hash = 0x022a2b82af83606ae5a8d4955ef6215e54025193356318aefbde3b5026952953; - assert_eq(call_stack_item.hash(), test_data_call_stack_item_request_hash); + let test_data_call_stack_item_request_hash = 0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e; + assert_eq(call_stack_item.get_compressed().hash(), test_data_call_stack_item_request_hash); } #[test] @@ -88,7 +84,7 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: false, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item hash" test - let test_data_call_stack_item_hash = 0x23a1d22e7bf37df7d68e8fcbfb7e016c060194b7915e3771e2dcd72cea26e427; - assert_eq(call_stack_item.hash(), test_data_call_stack_item_hash); + let test_data_call_stack_item_hash = 0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e; + assert_eq(call_stack_item.get_compressed().hash(), test_data_call_stack_item_hash); } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr new file mode 100644 index 000000000000..2f5ff29d83ac --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr @@ -0,0 +1,75 @@ +use crate::abis::{call_context::CallContext, function_data::FunctionData}; +use crate::address::AztecAddress; +use crate::constants::{GENERATOR_INDEX__CALL_STACK_ITEM, PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH}; +use crate::traits::{Hash, Empty, Serialize, Deserialize}; +use crate::utils::reader::Reader; + +struct PublicCallStackItemCompressed { + contract_address: AztecAddress, + call_context: CallContext, + function_data: FunctionData, + args_hash: Field, +} + +impl Eq for PublicCallStackItemCompressed { + fn eq(self, other: PublicCallStackItemCompressed) -> bool { + (self.contract_address == other.contract_address) + & (self.call_context == other.call_context) + & (self.function_data == other.function_data) + & (self.args_hash == other.args_hash) + } +} + +impl Hash for PublicCallStackItemCompressed { + fn hash(self) -> Field { + let item = self; + std::hash::pedersen_hash_with_separator([ + item.contract_address.to_field(), + item.call_context.hash(), + item.function_data.hash(), + item.args_hash, + ], GENERATOR_INDEX__CALL_STACK_ITEM) + } +} + +impl Empty for PublicCallStackItemCompressed { + + fn empty() -> Self { + PublicCallStackItemCompressed { + contract_address: AztecAddress::empty(), + call_context: CallContext::empty(), + function_data: FunctionData::empty(), + args_hash: 0, + } + } +} + +impl Serialize for PublicCallStackItemCompressed { + fn serialize(self) -> [Field; PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH] { + let mut fields: BoundedVec = BoundedVec::new(); + + fields.push(self.contract_address.to_field()); + fields.extend_from_array(self.call_context.serialize()); + fields.extend_from_array(self.function_data.serialize()); + fields.push(self.args_hash); + + assert_eq(fields.len(), PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH); + + fields.storage + } +} + +impl Deserialize for PublicCallStackItemCompressed { + fn deserialize(fields: [Field; PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH]) -> PublicCallStackItemCompressed { + let mut reader = Reader::new(fields); + + let item = PublicCallStackItemCompressed { + contract_address: reader.read_struct(AztecAddress::deserialize), + call_context: reader.read_struct(CallContext::deserialize), + function_data: reader.read_struct(FunctionData::deserialize), + args_hash: reader.read(), + }; + reader.finish(); + item + } +} 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 3b2ea1e741fc..34607a5ed91a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -220,6 +220,7 @@ global PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; global COMBINED_ACCUMULATED_DATA_LENGTH = MAX_NOTE_HASHES_PER_TX + MAX_NULLIFIERS_PER_TX + MAX_L2_TO_L1_MSGS_PER_TX + 6 + (MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * PUBLIC_DATA_UPDATE_REQUEST_LENGTH) + GAS_LENGTH; global COMBINED_CONSTANT_DATA_LENGTH = HEADER_LENGTH + TX_CONTEXT_LENGTH + GLOBAL_VARIABLES_LENGTH; +global PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = AZTEC_ADDRESS_LENGTH + CALL_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH + 1; global CALL_REQUEST_LENGTH = 1 + AZTEC_ADDRESS_LENGTH + CALLER_CONTEXT_LENGTH + 2; global PRIVATE_ACCUMULATED_DATA_LENGTH = (SCOPED_NOTE_HASH_LENGTH * MAX_NOTE_HASHES_PER_TX) + (SCOPED_NULLIFIER_LENGTH * MAX_NULLIFIERS_PER_TX) + (MAX_L2_TO_L1_MSGS_PER_TX * SCOPED_L2_TO_L1_MESSAGE_LENGTH) + (NOTE_LOG_HASH_LENGTH * MAX_NOTE_ENCRYPTED_LOGS_PER_TX) + (SCOPED_ENCRYPTED_LOG_HASH_LENGTH * MAX_ENCRYPTED_LOGS_PER_TX) + (SCOPED_LOG_HASH_LENGTH * MAX_UNENCRYPTED_LOGS_PER_TX) + (SCOPED_PRIVATE_CALL_REQUEST_LENGTH * MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX) + (CALL_REQUEST_LENGTH * MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX); global PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 1 + VALIDATION_REQUESTS_LENGTH + PRIVATE_ACCUMULATED_DATA_LENGTH + COMBINED_CONSTANT_DATA_LENGTH + CALL_REQUEST_LENGTH + AZTEC_ADDRESS_LENGTH; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index a4ec30313330..9c0a5ced21a8 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -149,6 +149,7 @@ export const VALIDATION_REQUESTS_LENGTH = 1026; export const PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; export const COMBINED_ACCUMULATED_DATA_LENGTH = 333; export const COMBINED_CONSTANT_DATA_LENGTH = 40; +export const PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = 10; export const CALL_REQUEST_LENGTH = 7; export const PRIVATE_ACCUMULATED_DATA_LENGTH = 1232; export const PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2307; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap index 4db396c18cd1..bc13dbec3bc7 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x23a1d22e7bf37df7d68e8fcbfb7e016c060194b7915e3771e2dcd72cea26e427"`; +exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e"`; -exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x022a2b82af83606ae5a8d4955ef6215e54025193356318aefbde3b5026952953"`; +exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e"`; -exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x211932b705c9a5dbbaf2433d2ee4b0a896ef9fb720a4efbe7c1e783747c36588>`; +exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x00ccf05f0449b7f9f0e4b20585334b860596780a70e41b939f67731e6276fba1>`; -exports[`PublicCallStackItem computes hash 1`] = `Fr<0x2d9862fe4fb3db6fe24996cbc3064346aa4551ccd41246c1ca6b002b8b0201fd>`; +exports[`PublicCallStackItem computes hash 1`] = `Fr<0x153cad9cc1e6d97618fd6f920ebe80a1e1cfc8a1f336aab769397e66729b1b33>`; diff --git a/yarn-project/circuits.js/src/structs/public_call_request.ts b/yarn-project/circuits.js/src/structs/public_call_request.ts index 7c9384c95bd4..4aa0f51b09cd 100644 --- a/yarn-project/circuits.js/src/structs/public_call_request.ts +++ b/yarn-project/circuits.js/src/structs/public_call_request.ts @@ -132,7 +132,7 @@ export class PublicCallRequest { ) : CallerContext.empty(); return new CallRequest( - item.hash(), + item.getCompressed().hash(), this.parentCallContext.storageContractAddress, callerContext, new Fr(this.callContext.sideEffectCounter), diff --git a/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts b/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts index 2ffbbcf8a4e3..3f966b416a94 100644 --- a/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts +++ b/yarn-project/circuits.js/src/structs/public_call_stack_item.test.ts @@ -18,13 +18,13 @@ describe('PublicCallStackItem', () => { it('computes hash', () => { const seed = 9870243; const item = makePublicCallStackItem(seed); - const hash = item.hash(); + const hash = item.getCompressed().hash(); expect(hash).toMatchSnapshot(); }); it('computes empty item hash', () => { const item = PublicCallStackItem.empty(); - const hash = item.hash(); + const hash = item.getCompressed().hash(); expect(hash).toMatchSnapshot(); }); @@ -36,7 +36,7 @@ describe('PublicCallStackItem', () => { callStack.isExecutionRequest = true; callStack.publicInputs.noteHashes[0] = new NoteHash(new Fr(1), 0); - const hash = callStack.hash(); + const hash = callStack.getCompressed().hash(); expect(hash.toString()).toMatchSnapshot(); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data @@ -54,7 +54,7 @@ describe('PublicCallStackItem', () => { callStack.functionData = new FunctionData(new FunctionSelector(2), /*isPrivate=*/ false); callStack.publicInputs.noteHashes[0] = new NoteHash(new Fr(1), 0); - const hash = callStack.hash(); + const hash = callStack.getCompressed().hash(); expect(hash.toString()).toMatchSnapshot(); // Run with AZTEC_GENERATE_TEST_DATA=1 to update noir test data diff --git a/yarn-project/circuits.js/src/structs/public_call_stack_item.ts b/yarn-project/circuits.js/src/structs/public_call_stack_item.ts index 170f3dc84b65..2f6cdbd6637a 100644 --- a/yarn-project/circuits.js/src/structs/public_call_stack_item.ts +++ b/yarn-project/circuits.js/src/structs/public_call_stack_item.ts @@ -1,14 +1,13 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; -import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; -import { GeneratorIndex } from '../constants.gen.js'; import { type CallContext } from './call_context.js'; import { CallRequest } from './call_request.js'; import { CallerContext } from './caller_context.js'; import { FunctionData } from './function_data.js'; +import { PublicCallStackItemCompressed } from './public_call_stack_item_compressed.js'; import { PublicCircuitPublicInputs } from './public_circuit_public_inputs.js'; /** @@ -85,30 +84,12 @@ export class PublicCallStackItem { return this.contractAddress.isZero() && this.functionData.isEmpty() && this.publicInputs.isEmpty(); } - /** - * Computes this call stack item hash. - * @returns Hash. - */ - public hash() { - let publicInputsToHash = this.publicInputs; - if (this.isExecutionRequest) { - // An execution request (such as an enqueued call from private) is hashed with - // only the publicInput members present in a PublicCallRequest. - // This allows us to check that the request (which is created/hashed before - // side-effects and output info are unknown for public calls) matches the call - // being processed by a kernel iteration. - // WARNING: This subset of publicInputs that is set here must align with - // `parse_public_call_stack_item_from_oracle` in enqueue_public_function_call.nr - // and `PublicCallStackItem::as_execution_request()` in public_call_stack_item.ts - const { callContext, argsHash } = this.publicInputs; - publicInputsToHash = PublicCircuitPublicInputs.empty(); - publicInputsToHash.callContext = callContext; - publicInputsToHash.argsHash = argsHash; - } - - return pedersenHash( - [this.contractAddress, this.functionData.hash(), publicInputsToHash.hash()], - GeneratorIndex.CALL_STACK_ITEM, + getCompressed(): PublicCallStackItemCompressed { + return new PublicCallStackItemCompressed( + this.contractAddress, + this.publicInputs.callContext, + this.functionData, + this.publicInputs.argsHash, ); } @@ -130,6 +111,9 @@ export class PublicCallStackItem { ) : CallerContext.empty(); // todo: populate side effect counters correctly - return new CallRequest(this.hash(), parentCallContext.storageContractAddress, callerContext, Fr.ZERO, Fr.ZERO); + + const hash = this.getCompressed().hash(); + + return new CallRequest(hash, parentCallContext.storageContractAddress, callerContext, Fr.ZERO, Fr.ZERO); } } diff --git a/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts b/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts new file mode 100644 index 000000000000..1148a189c12a --- /dev/null +++ b/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts @@ -0,0 +1,88 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { pedersenHash } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { type FieldsOf } from '@aztec/foundation/types'; + +import { GeneratorIndex } from '../constants.gen.js'; +import { CallContext } from './call_context.js'; +import { FunctionData } from './function_data.js'; + +/** + * Call stack item on a public call. + */ +export class PublicCallStackItemCompressed { + constructor( + public contractAddress: AztecAddress, + public callContext: CallContext, + public functionData: FunctionData, + public argsHash: Fr, + ) {} + + static getFields(fields: FieldsOf) { + return [fields.contractAddress, fields.callContext, fields.functionData, fields.argsHash] as const; + } + + toBuffer() { + return serializeToBuffer(...PublicCallStackItemCompressed.getFields(this)); + } + + /** + * Deserializes from a buffer or reader. + * @param buffer - Buffer or reader to read from. + * @returns The deserialized instance. + */ + static fromBuffer(buffer: Buffer | BufferReader): PublicCallStackItemCompressed { + const reader = BufferReader.asReader(buffer); + return new PublicCallStackItemCompressed( + reader.readObject(AztecAddress), + reader.readObject(CallContext), + reader.readObject(FunctionData), + reader.readObject(Fr), + ); + } + + static fromFields(fields: Fr[] | FieldReader): PublicCallStackItemCompressed { + const reader = FieldReader.asReader(fields); + + const contractAddress = AztecAddress.fromFields(reader); + const callContext = CallContext.fromFields(reader); + const functionData = FunctionData.fromFields(reader); + const argsHash = reader.readField(); + + return new PublicCallStackItemCompressed(contractAddress, callContext, functionData, argsHash); + } + + /** + * Returns a new instance of PublicCallStackItem with zero contract address, function data and public inputs. + * @returns A new instance of PublicCallStackItem with zero contract address, function data and public inputs. + */ + public static empty(): PublicCallStackItemCompressed { + return new PublicCallStackItemCompressed( + AztecAddress.ZERO, + CallContext.empty(), + FunctionData.empty({ isPrivate: false }), + Fr.ZERO, + ); + } + + isEmpty() { + return ( + this.contractAddress.isZero() && + this.callContext.isEmpty() && + this.functionData.isEmpty() && + this.argsHash.isEmpty() + ); + } + + /** + * Computes this call stack item hash. + * @returns Hash. + */ + public hash() { + return pedersenHash( + [this.contractAddress, this.callContext.hash(), this.functionData.hash(), this.argsHash], + GeneratorIndex.CALL_STACK_ITEM, + ); + } +} diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 968a491ca786..dc15c1de7005 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -747,7 +747,7 @@ export function makePublicKernelInputsWithTweak( // Set the call stack item for this circuit iteration at the top of the call stack publicKernelInputs.previousKernel.publicInputs.end.publicCallStack[MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX - 1] = new CallRequest( - publicCall.callStackItem.hash(), + publicCall.callStackItem.getCompressed().hash(), publicCall.callStackItem.publicInputs.callContext.msgSender, makeCallerContext(seed + 0x100), Fr.ZERO, diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 2c6bf39ea2c3..d64978882946 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -903,7 +903,7 @@ describe('Private Execution test suite', () => { }), }); - const publicCallRequestHash = publicCallRequest.toPublicCallStackItem().hash(); + const publicCallRequestHash = publicCallRequest.toPublicCallStackItem().getCompressed().hash(); expect(result.enqueuedPublicFunctionCalls).toHaveLength(1); expect(result.enqueuedPublicFunctionCalls[0]).toEqual(publicCallRequest); diff --git a/yarn-project/simulator/src/public/abstract_phase_manager.ts b/yarn-project/simulator/src/public/abstract_phase_manager.ts index ec278a3c9a55..3a056339b679 100644 --- a/yarn-project/simulator/src/public/abstract_phase_manager.ts +++ b/yarn-project/simulator/src/public/abstract_phase_manager.ts @@ -424,7 +424,7 @@ export abstract class AbstractPhaseManager { const callStackPreimages = await this.getPublicCallStackPreimages(result); const publicCallStackHashes = padArrayEnd( - callStackPreimages.map(c => c.hash()), + callStackPreimages.map(c => c.getCompressed().hash()), Fr.ZERO, MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, ); From 1834b8b9196c6a8c4fac90291c849b38f241ac50 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Thu, 4 Jul 2024 16:06:51 +0000 Subject: [PATCH 2/3] chore: add comment public_call_stack_item_compressed --- .../src/abis/public_call_stack_item_compressed.nr | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr index 2f5ff29d83ac..7c9f43a616e1 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr @@ -4,6 +4,20 @@ use crate::constants::{GENERATOR_INDEX__CALL_STACK_ITEM, PUBLIC_CALL_STACK_ITEM_ use crate::traits::{Hash, Empty, Serialize, Deserialize}; use crate::utils::reader::Reader; +/** + * A compressed version of the PublicCallStackItem struct used to compute the "hash" + * of a PublicCallStackItem. + * + * Historically, we have been zeroing most values in the PublicCallStackItem struct + * to compute the hash involved when adding a PublicCallStackItem to the PublicCallStack. + * + * This struct is used to store the values that we did not zero out, and allow us to hash + * only these, thereby skipping a lot of computation and saving us a lot of constraints + * + * Essentially this struct exists such that we don't have a `hash` function in the + * PublicCallStackItem struct that practically throws away some values of the struct + * without clearly indicating that it does so. + */ struct PublicCallStackItemCompressed { contract_address: AztecAddress, call_context: CallContext, From a77b6187a7483a14094aecbaa499e47c46bca2b8 Mon Sep 17 00:00:00 2001 From: LHerskind Date: Fri, 5 Jul 2024 09:30:48 +0000 Subject: [PATCH 3/3] fix: include return, revert and gas in compressed call stack item --- .../src/core/libraries/ConstantsGen.sol | 8 +-- .../types/src/abis/public_call_stack_item.nr | 24 +++++-- .../abis/public_call_stack_item_compressed.nr | 31 +++++--- .../crates/types/src/constants.nr | 2 +- yarn-project/circuits.js/src/constants.gen.ts | 8 +-- .../public_call_stack_item.test.ts.snap | 8 +-- .../src/structs/public_call_stack_item.ts | 24 ++++++- .../public_call_stack_item_compressed.ts | 70 +++++++++++++++---- 8 files changed, 129 insertions(+), 46 deletions(-) diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index aab158b8fd3c..527ae5c969b2 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -142,7 +142,7 @@ library Constants { uint256 internal constant SCOPED_ENCRYPTED_LOG_HASH_LENGTH = 5; uint256 internal constant NOTE_LOG_HASH_LENGTH = 4; uint256 internal constant NOTE_HASH_LENGTH = 2; - uint256 internal constant SCOPED_NOTE_HASH_LENGTH = 4; + uint256 internal constant SCOPED_NOTE_HASH_LENGTH = 3; uint256 internal constant NULLIFIER_LENGTH = 3; uint256 internal constant SCOPED_NULLIFIER_LENGTH = 4; uint256 internal constant CALLER_CONTEXT_LENGTH = 3; @@ -165,10 +165,10 @@ library Constants { uint256 internal constant PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; uint256 internal constant COMBINED_ACCUMULATED_DATA_LENGTH = 333; uint256 internal constant COMBINED_CONSTANT_DATA_LENGTH = 40; - uint256 internal constant PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = 10; + uint256 internal constant PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = 16; uint256 internal constant CALL_REQUEST_LENGTH = 7; - uint256 internal constant PRIVATE_ACCUMULATED_DATA_LENGTH = 1232; - uint256 internal constant PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2307; + uint256 internal constant PRIVATE_ACCUMULATED_DATA_LENGTH = 1168; + uint256 internal constant PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2243; uint256 internal constant PUBLIC_ACCUMULATED_DATA_LENGTH = 983; uint256 internal constant PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3258; uint256 internal constant KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 383; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr index 5c8023e8bb82..a1a49f670588 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item.nr @@ -17,7 +17,7 @@ struct PublicCallStackItem { impl PublicCallStackItem { fn as_execution_request(self) -> Self { - // WARNING: if updating, see comment in public_call_stack_item.ts's `PublicCallStackItem.hash()` + // WARNING: if updating, see comment in public_call_stack_item.ts's `PublicCallStackItem.getCompressed()` let public_inputs = self.public_inputs; let mut request_public_inputs = PublicCircuitPublicInputs::empty(); request_public_inputs.call_context = public_inputs.call_context; @@ -33,11 +33,21 @@ impl PublicCallStackItem { } fn get_compressed(self) -> PublicCallStackItemCompressed { + let item = if self.is_execution_request { + self.as_execution_request() + } else { + self + }; + PublicCallStackItemCompressed { - contract_address: self.contract_address, - call_context: self.public_inputs.call_context, - function_data: self.function_data, - args_hash: self.public_inputs.args_hash + contract_address: item.contract_address, + call_context: item.public_inputs.call_context, + function_data: item.function_data, + args_hash: item.public_inputs.args_hash, + returns_hash: item.public_inputs.returns_hash, + revert_code: item.public_inputs.revert_code, + start_gas_left: item.public_inputs.start_gas_left, + end_gas_left: item.public_inputs.end_gas_left } } } @@ -66,7 +76,7 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: true, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item request hash" test - let test_data_call_stack_item_request_hash = 0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e; + let test_data_call_stack_item_request_hash = 0x1331fdec4ec7bd6bb23447c47753659b68e3a285d812ab6eaf9258a902d16e8e; assert_eq(call_stack_item.get_compressed().hash(), test_data_call_stack_item_request_hash); } @@ -84,7 +94,7 @@ mod tests { let call_stack_item = PublicCallStackItem { contract_address, public_inputs, is_execution_request: false, function_data }; // Value from public_call_stack_item.test.ts "Computes a callstack item hash" test - let test_data_call_stack_item_hash = 0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e; + let test_data_call_stack_item_hash = 0x1331fdec4ec7bd6bb23447c47753659b68e3a285d812ab6eaf9258a902d16e8e; assert_eq(call_stack_item.get_compressed().hash(), test_data_call_stack_item_hash); } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr index 7c9f43a616e1..f31b62975488 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr @@ -1,4 +1,4 @@ -use crate::abis::{call_context::CallContext, function_data::FunctionData}; +use crate::abis::{call_context::CallContext, function_data::FunctionData, gas::Gas}; use crate::address::AztecAddress; use crate::constants::{GENERATOR_INDEX__CALL_STACK_ITEM, PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH}; use crate::traits::{Hash, Empty, Serialize, Deserialize}; @@ -23,6 +23,10 @@ struct PublicCallStackItemCompressed { call_context: CallContext, function_data: FunctionData, args_hash: Field, + returns_hash: Field, + revert_code: u8, + start_gas_left: Gas, + end_gas_left: Gas, } impl Eq for PublicCallStackItemCompressed { @@ -31,29 +35,30 @@ impl Eq for PublicCallStackItemCompressed { & (self.call_context == other.call_context) & (self.function_data == other.function_data) & (self.args_hash == other.args_hash) + & (self.returns_hash == other.returns_hash) + & (self.revert_code == other.revert_code) + & (self.start_gas_left == other.start_gas_left) + & (self.end_gas_left == other.end_gas_left) } } impl Hash for PublicCallStackItemCompressed { fn hash(self) -> Field { - let item = self; - std::hash::pedersen_hash_with_separator([ - item.contract_address.to_field(), - item.call_context.hash(), - item.function_data.hash(), - item.args_hash, - ], GENERATOR_INDEX__CALL_STACK_ITEM) + std::hash::pedersen_hash_with_separator(self.serialize(), GENERATOR_INDEX__CALL_STACK_ITEM) } } impl Empty for PublicCallStackItemCompressed { - fn empty() -> Self { PublicCallStackItemCompressed { contract_address: AztecAddress::empty(), call_context: CallContext::empty(), function_data: FunctionData::empty(), args_hash: 0, + returns_hash: 0, + revert_code: 0, + start_gas_left: Gas::empty(), + end_gas_left: Gas::empty(), } } } @@ -66,6 +71,10 @@ impl Serialize for PublicCallStackItem fields.extend_from_array(self.call_context.serialize()); fields.extend_from_array(self.function_data.serialize()); fields.push(self.args_hash); + fields.push(self.returns_hash); + fields.push(self.revert_code as Field); + fields.extend_from_array(self.start_gas_left.serialize()); + fields.extend_from_array(self.end_gas_left.serialize()); assert_eq(fields.len(), PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH); @@ -82,6 +91,10 @@ impl Deserialize for PublicCallStackIt call_context: reader.read_struct(CallContext::deserialize), function_data: reader.read_struct(FunctionData::deserialize), args_hash: reader.read(), + returns_hash: reader.read(), + revert_code: reader.read() as u8, + start_gas_left: reader.read_struct(Gas::deserialize), + end_gas_left: reader.read_struct(Gas::deserialize), }; reader.finish(); item 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 34607a5ed91a..eca27679e02a 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -220,7 +220,7 @@ global PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; global COMBINED_ACCUMULATED_DATA_LENGTH = MAX_NOTE_HASHES_PER_TX + MAX_NULLIFIERS_PER_TX + MAX_L2_TO_L1_MSGS_PER_TX + 6 + (MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX * PUBLIC_DATA_UPDATE_REQUEST_LENGTH) + GAS_LENGTH; global COMBINED_CONSTANT_DATA_LENGTH = HEADER_LENGTH + TX_CONTEXT_LENGTH + GLOBAL_VARIABLES_LENGTH; -global PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = AZTEC_ADDRESS_LENGTH + CALL_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH + 1; +global PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = AZTEC_ADDRESS_LENGTH + CALL_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH + 3 + 2 * GAS_LENGTH; global CALL_REQUEST_LENGTH = 1 + AZTEC_ADDRESS_LENGTH + CALLER_CONTEXT_LENGTH + 2; global PRIVATE_ACCUMULATED_DATA_LENGTH = (SCOPED_NOTE_HASH_LENGTH * MAX_NOTE_HASHES_PER_TX) + (SCOPED_NULLIFIER_LENGTH * MAX_NULLIFIERS_PER_TX) + (MAX_L2_TO_L1_MSGS_PER_TX * SCOPED_L2_TO_L1_MESSAGE_LENGTH) + (NOTE_LOG_HASH_LENGTH * MAX_NOTE_ENCRYPTED_LOGS_PER_TX) + (SCOPED_ENCRYPTED_LOG_HASH_LENGTH * MAX_ENCRYPTED_LOGS_PER_TX) + (SCOPED_LOG_HASH_LENGTH * MAX_UNENCRYPTED_LOGS_PER_TX) + (SCOPED_PRIVATE_CALL_REQUEST_LENGTH * MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX) + (CALL_REQUEST_LENGTH * MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX); global PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 1 + VALIDATION_REQUESTS_LENGTH + PRIVATE_ACCUMULATED_DATA_LENGTH + COMBINED_CONSTANT_DATA_LENGTH + CALL_REQUEST_LENGTH + AZTEC_ADDRESS_LENGTH; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 9c0a5ced21a8..bc4a5a91d2f2 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -126,7 +126,7 @@ export const ENCRYPTED_LOG_HASH_LENGTH = 4; export const SCOPED_ENCRYPTED_LOG_HASH_LENGTH = 5; export const NOTE_LOG_HASH_LENGTH = 4; export const NOTE_HASH_LENGTH = 2; -export const SCOPED_NOTE_HASH_LENGTH = 4; +export const SCOPED_NOTE_HASH_LENGTH = 3; export const NULLIFIER_LENGTH = 3; export const SCOPED_NULLIFIER_LENGTH = 4; export const CALLER_CONTEXT_LENGTH = 3; @@ -149,10 +149,10 @@ export const VALIDATION_REQUESTS_LENGTH = 1026; export const PUBLIC_DATA_UPDATE_REQUEST_LENGTH = 3; export const COMBINED_ACCUMULATED_DATA_LENGTH = 333; export const COMBINED_CONSTANT_DATA_LENGTH = 40; -export const PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = 10; +export const PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH = 16; export const CALL_REQUEST_LENGTH = 7; -export const PRIVATE_ACCUMULATED_DATA_LENGTH = 1232; -export const PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2307; +export const PRIVATE_ACCUMULATED_DATA_LENGTH = 1168; +export const PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 2243; export const PUBLIC_ACCUMULATED_DATA_LENGTH = 983; export const PUBLIC_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 3258; export const KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 383; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap index bc13dbec3bc7..bfbe51f2a092 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/public_call_stack_item.test.ts.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e"`; +exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x1331fdec4ec7bd6bb23447c47753659b68e3a285d812ab6eaf9258a902d16e8e"`; -exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x00ae3637b279ce8be67fd283808915bce72cb2943cb9bdbcb19d35cf8ca8fe9e"`; +exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x1331fdec4ec7bd6bb23447c47753659b68e3a285d812ab6eaf9258a902d16e8e"`; -exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x00ccf05f0449b7f9f0e4b20585334b860596780a70e41b939f67731e6276fba1>`; +exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x077ecad5709816865f4c65422d74c646739f4177691a5d87c716219e72da0705>`; -exports[`PublicCallStackItem computes hash 1`] = `Fr<0x153cad9cc1e6d97618fd6f920ebe80a1e1cfc8a1f336aab769397e66729b1b33>`; +exports[`PublicCallStackItem computes hash 1`] = `Fr<0x2799d225e955c0d1db6fa7adb9be5cbd2ca760a48996443a629e01ce28a3919d>`; diff --git a/yarn-project/circuits.js/src/structs/public_call_stack_item.ts b/yarn-project/circuits.js/src/structs/public_call_stack_item.ts index 2f6cdbd6637a..17010b247df3 100644 --- a/yarn-project/circuits.js/src/structs/public_call_stack_item.ts +++ b/yarn-project/circuits.js/src/structs/public_call_stack_item.ts @@ -85,11 +85,31 @@ export class PublicCallStackItem { } getCompressed(): PublicCallStackItemCompressed { + let publicInputsToHash = this.publicInputs; + if (this.isExecutionRequest) { + // An execution request (such as an enqueued call from private) is hashed with + // only the publicInput members present in a PublicCallRequest. + // This allows us to check that the request (which is created/hashed before + // side-effects and output info are unknown for public calls) matches the call + // being processed by a kernel iteration. + // WARNING: This subset of publicInputs that is set here must align with + // `parse_public_call_stack_item_from_oracle` in enqueue_public_function_call.nr + // and `PublicCallStackItem::as_execution_request()` in public_call_stack_item.ts + const { callContext, argsHash } = this.publicInputs; + publicInputsToHash = PublicCircuitPublicInputs.empty(); + publicInputsToHash.callContext = callContext; + publicInputsToHash.argsHash = argsHash; + } + return new PublicCallStackItemCompressed( this.contractAddress, - this.publicInputs.callContext, + publicInputsToHash.callContext, this.functionData, - this.publicInputs.argsHash, + publicInputsToHash.argsHash, + publicInputsToHash.returnsHash, + publicInputsToHash.revertCode, + publicInputsToHash.startGasLeft, + publicInputsToHash.endGasLeft, ); } diff --git a/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts b/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts index 1148a189c12a..49f6181b8590 100644 --- a/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts +++ b/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts @@ -1,15 +1,17 @@ import { AztecAddress } from '@aztec/foundation/aztec-address'; import { pedersenHash } from '@aztec/foundation/crypto'; import { Fr } from '@aztec/foundation/fields'; -import { BufferReader, FieldReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { BufferReader, FieldReader, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; -import { GeneratorIndex } from '../constants.gen.js'; +import { GeneratorIndex, PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH } from '../constants.gen.js'; import { CallContext } from './call_context.js'; import { FunctionData } from './function_data.js'; +import { Gas } from './gas.js'; +import { RevertCode } from './revert_code.js'; /** - * Call stack item on a public call. + * Compressed call stack item on a public call. */ export class PublicCallStackItemCompressed { constructor( @@ -17,10 +19,35 @@ export class PublicCallStackItemCompressed { public callContext: CallContext, public functionData: FunctionData, public argsHash: Fr, + public returnsHash: Fr, + public revertCode: RevertCode, + /** How much gas was available for execution. */ + public startGasLeft: Gas, + /** How much gas was left after execution. */ + public endGasLeft: Gas, ) {} static getFields(fields: FieldsOf) { - return [fields.contractAddress, fields.callContext, fields.functionData, fields.argsHash] as const; + return [ + fields.contractAddress, + fields.callContext, + fields.functionData, + fields.argsHash, + fields.returnsHash, + fields.revertCode, + fields.startGasLeft, + fields.endGasLeft, + ] as const; + } + + toFields(): Fr[] { + const fields = serializeToFields(...PublicCallStackItemCompressed.getFields(this)); + if (fields.length !== PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH) { + throw new Error( + `Invalid number of fields for PublicCallStackItemCompressed. Expected ${PUBLIC_CALL_STACK_ITEM_COMPRESSED_LENGTH}, got ${fields.length}`, + ); + } + return fields; } toBuffer() { @@ -39,18 +66,26 @@ export class PublicCallStackItemCompressed { reader.readObject(CallContext), reader.readObject(FunctionData), reader.readObject(Fr), + reader.readObject(Fr), + reader.readObject(RevertCode), + reader.readObject(Gas), + reader.readObject(Gas), ); } static fromFields(fields: Fr[] | FieldReader): PublicCallStackItemCompressed { const reader = FieldReader.asReader(fields); - const contractAddress = AztecAddress.fromFields(reader); - const callContext = CallContext.fromFields(reader); - const functionData = FunctionData.fromFields(reader); - const argsHash = reader.readField(); - - return new PublicCallStackItemCompressed(contractAddress, callContext, functionData, argsHash); + return new PublicCallStackItemCompressed( + AztecAddress.fromFields(reader), + CallContext.fromFields(reader), + FunctionData.fromFields(reader), + reader.readField(), + reader.readField(), + RevertCode.fromFields(reader), + Gas.fromFields(reader), + Gas.fromFields(reader), + ); } /** @@ -63,6 +98,10 @@ export class PublicCallStackItemCompressed { CallContext.empty(), FunctionData.empty({ isPrivate: false }), Fr.ZERO, + Fr.ZERO, + RevertCode.OK, + Gas.empty(), + Gas.empty(), ); } @@ -71,7 +110,11 @@ export class PublicCallStackItemCompressed { this.contractAddress.isZero() && this.callContext.isEmpty() && this.functionData.isEmpty() && - this.argsHash.isEmpty() + this.argsHash.isEmpty() && + this.returnsHash.isEmpty() && + this.revertCode === RevertCode.OK && + this.startGasLeft.isEmpty() && + this.endGasLeft.isEmpty() ); } @@ -80,9 +123,6 @@ export class PublicCallStackItemCompressed { * @returns Hash. */ public hash() { - return pedersenHash( - [this.contractAddress, this.callContext.hash(), this.functionData.hash(), this.argsHash], - GeneratorIndex.CALL_STACK_ITEM, - ); + return pedersenHash(this.toFields(), GeneratorIndex.CALL_STACK_ITEM); } }