diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 81d2dd41d83f..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,9 +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 = 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/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..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 @@ -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,25 +15,9 @@ 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()` + // 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; @@ -44,6 +31,25 @@ impl PublicCallStackItem { }; call_stack_item } + + fn get_compressed(self) -> PublicCallStackItemCompressed { + let item = if self.is_execution_request { + self.as_execution_request() + } else { + self + }; + + PublicCallStackItemCompressed { + 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 + } + } } mod tests { @@ -70,8 +76,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 = 0x1331fdec4ec7bd6bb23447c47753659b68e3a285d812ab6eaf9258a902d16e8e; + assert_eq(call_stack_item.get_compressed().hash(), test_data_call_stack_item_request_hash); } #[test] @@ -88,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 = 0x23a1d22e7bf37df7d68e8fcbfb7e016c060194b7915e3771e2dcd72cea26e427; - assert_eq(call_stack_item.hash(), test_data_call_stack_item_hash); + 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 new file mode 100644 index 000000000000..f31b62975488 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_call_stack_item_compressed.nr @@ -0,0 +1,102 @@ +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}; +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, + function_data: FunctionData, + args_hash: Field, + returns_hash: Field, + revert_code: u8, + start_gas_left: Gas, + end_gas_left: Gas, +} + +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) + & (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 { + 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(), + } + } +} + +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); + 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); + + 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(), + 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 3b2ea1e741fc..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,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 + 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 a4ec30313330..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,9 +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 = 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 4db396c18cd1..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`] = `"0x23a1d22e7bf37df7d68e8fcbfb7e016c060194b7915e3771e2dcd72cea26e427"`; +exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x1331fdec4ec7bd6bb23447c47753659b68e3a285d812ab6eaf9258a902d16e8e"`; -exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x022a2b82af83606ae5a8d4955ef6215e54025193356318aefbde3b5026952953"`; +exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x1331fdec4ec7bd6bb23447c47753659b68e3a285d812ab6eaf9258a902d16e8e"`; -exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x211932b705c9a5dbbaf2433d2ee4b0a896ef9fb720a4efbe7c1e783747c36588>`; +exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x077ecad5709816865f4c65422d74c646739f4177691a5d87c716219e72da0705>`; -exports[`PublicCallStackItem computes hash 1`] = `Fr<0x2d9862fe4fb3db6fe24996cbc3064346aa4551ccd41246c1ca6b002b8b0201fd>`; +exports[`PublicCallStackItem computes hash 1`] = `Fr<0x2799d225e955c0d1db6fa7adb9be5cbd2ca760a48996443a629e01ce28a3919d>`; 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..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 @@ -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,11 +84,7 @@ export class PublicCallStackItem { return this.contractAddress.isZero() && this.functionData.isEmpty() && this.publicInputs.isEmpty(); } - /** - * Computes this call stack item hash. - * @returns Hash. - */ - public hash() { + getCompressed(): PublicCallStackItemCompressed { let publicInputsToHash = this.publicInputs; if (this.isExecutionRequest) { // An execution request (such as an enqueued call from private) is hashed with @@ -106,9 +101,15 @@ export class PublicCallStackItem { publicInputsToHash.argsHash = argsHash; } - return pedersenHash( - [this.contractAddress, this.functionData.hash(), publicInputsToHash.hash()], - GeneratorIndex.CALL_STACK_ITEM, + return new PublicCallStackItemCompressed( + this.contractAddress, + publicInputsToHash.callContext, + this.functionData, + publicInputsToHash.argsHash, + publicInputsToHash.returnsHash, + publicInputsToHash.revertCode, + publicInputsToHash.startGasLeft, + publicInputsToHash.endGasLeft, ); } @@ -130,6 +131,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..49f6181b8590 --- /dev/null +++ b/yarn-project/circuits.js/src/structs/public_call_stack_item_compressed.ts @@ -0,0 +1,128 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { pedersenHash } from '@aztec/foundation/crypto'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, FieldReader, serializeToBuffer, serializeToFields } from '@aztec/foundation/serialize'; +import { type FieldsOf } from '@aztec/foundation/types'; + +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'; + +/** + * Compressed call stack item on a public call. + */ +export class PublicCallStackItemCompressed { + constructor( + public contractAddress: AztecAddress, + 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, + 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() { + 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), + reader.readObject(Fr), + reader.readObject(RevertCode), + reader.readObject(Gas), + reader.readObject(Gas), + ); + } + + static fromFields(fields: Fr[] | FieldReader): PublicCallStackItemCompressed { + const reader = FieldReader.asReader(fields); + + 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), + ); + } + + /** + * 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, + Fr.ZERO, + RevertCode.OK, + Gas.empty(), + Gas.empty(), + ); + } + + isEmpty() { + return ( + this.contractAddress.isZero() && + this.callContext.isEmpty() && + this.functionData.isEmpty() && + this.argsHash.isEmpty() && + this.returnsHash.isEmpty() && + this.revertCode === RevertCode.OK && + this.startGasLeft.isEmpty() && + this.endGasLeft.isEmpty() + ); + } + + /** + * Computes this call stack item hash. + * @returns Hash. + */ + public hash() { + return pedersenHash(this.toFields(), 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, );