diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index e491271a5ca..3b9ba9aa014 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -144,8 +144,9 @@ library Constants { + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (SIDE_EFFECT_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 - + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 1 + HEADER_LENGTH + AZTEC_ADDRESS_LENGTH /* revert_code */ - + 1 + 2 * GAS_LENGTH /* transaction_fee */ + 1; + + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 1 + HEADER_LENGTH + + GLOBAL_VARIABLES_LENGTH + AZTEC_ADDRESS_LENGTH /* revert_code */ + 1 + 2 * GAS_LENGTH /* transaction_fee */ + + 1; uint256 internal constant PRIVATE_CALL_STACK_ITEM_LENGTH = AZTEC_ADDRESS_LENGTH + FUNCTION_DATA_LENGTH + PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH; uint256 internal constant ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH = 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 43006332b78..184849fff95 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -9,7 +9,7 @@ use crate::{ }; use dep::protocol_types::{ abis::{ - gas::Gas, call_context::CallContext, function_data::FunctionData, + global_variables::GlobalVariables, gas::Gas, call_context::CallContext, function_data::FunctionData, function_selector::FunctionSelector, max_block_number::MaxBlockNumber, nullifier_key_validation_request::NullifierKeyValidationRequest, private_call_stack_item::PrivateCallStackItem, @@ -500,6 +500,7 @@ impl PrivateContext { unencrypted_logs_hashes: [SideEffect::empty(); MAX_UNENCRYPTED_LOGS_PER_CALL], unencrypted_log_preimages_length: 0, historical_header: Header::empty(), + global_variables: GlobalVariables::empty(), prover_address: AztecAddress::zero(), revert_code: 0, start_gas_left: Gas::empty(), 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 acda01b17b6..80182425b17 100644 --- a/noir-projects/aztec-nr/aztec/src/context/public_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/public_context.nr @@ -167,6 +167,7 @@ impl PublicContext { unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage, unencrypted_log_preimages_length, historical_header: self.inputs.historical_header, + global_variables: self.inputs.public_global_variables, prover_address: self.prover_address, revert_code: 0, start_gas_left: self.inputs.gas_left, diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr index b348adc4ad0..f83df8a71ae 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_init.nr @@ -19,10 +19,10 @@ struct PrivateKernelInitCircuitPrivateInputs { impl PrivateKernelInitCircuitPrivateInputs { fn initialize_end_values(self, public_inputs: &mut PrivateKernelCircuitPublicInputsBuilder) { - public_inputs.constants = CombinedConstantData { - historical_header: self.private_call.call_stack_item.public_inputs.historical_header, - tx_context: self.tx_request.tx_context, - }; + public_inputs.constants = CombinedConstantData::private( + self.private_call.call_stack_item.public_inputs.historical_header, + self.tx_request.tx_context, + ); public_inputs.min_revertible_side_effect_counter = self.private_call.call_stack_item.public_inputs.min_revertible_side_effect_counter; } 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 855da0a6ec4..9004bbec8c8 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 @@ -4,7 +4,8 @@ use dep::types::{ kernel_circuit_public_inputs::PublicKernelCircuitPublicInputsBuilder, kernel_data::PublicKernelData, public_call_data::PublicCallData, public_data_read::PublicDataRead, public_data_update_request::PublicDataUpdateRequest, read_request::ReadRequestContext, - side_effect::{SideEffect, SideEffectLinkedToNoteHash} + side_effect::{SideEffect, SideEffectLinkedToNoteHash}, global_variables::GlobalVariables, + combined_constant_data::CombinedConstantData }, address::AztecAddress, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, @@ -33,6 +34,34 @@ pub fn validate_inputs(public_call: PublicCallData) { assert(public_call.bytecode_hash != 0, "Bytecode hash cannot be zero"); } +pub fn validate_public_call_global_variables(public_call: PublicCallData, constants: CombinedConstantData) { + let public_call_globals = public_call.call_stack_item.public_inputs.global_variables; + assert( + public_call_globals == constants.global_variables, "Global variables injected into the public call do not match constants" + ); +} + +// Validates constants injected into the public call are correct. +// Note that the previous_kernel.public_inputs.constants returned from the private kernel tail +// will be empty, so in the first run on of this circuit we load them from the first public +// call, following the same pattern as in the private_kernel_init. +// TODO(@spalladino): This can be a security risk since it allows a sequencer to run public +// circuits with empty global variables. This must be patched by having a differentiated init public +// circuit that runs only once, or by having a way to differentiate when we're coming from a private +// kernel tail vs from another public run. +pub fn initialize_from_or_validate_public_call_variables( + previous_kernel: PublicKernelData, + public_call: PublicCallData, + public_inputs: &mut PublicKernelCircuitPublicInputsBuilder +) { + if public_inputs.constants.global_variables.is_empty() { + let public_call_global_variables = public_call.call_stack_item.public_inputs.global_variables; + public_inputs.constants.global_variables = public_call_global_variables; + } else { + validate_public_call_global_variables(public_call, previous_kernel.public_inputs.constants); + } +} + pub fn validate_public_call_non_revert(public_call: PublicCallData) { assert(public_call.call_stack_item.public_inputs.revert_code == 0, "Public call cannot be reverted"); } 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 e372e50f43d..07beccedc3c 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 @@ -30,6 +30,9 @@ impl PublicKernelAppLogicCircuitPrivateInputs { // validate the inputs common to all invocation circumstances common::validate_inputs(self.public_call); + // validate constants injected into the public call are correct or set them if this is the first public call + common::initialize_from_or_validate_public_call_variables(self.previous_kernel, self.public_call, &mut public_inputs); + // validate the inputs unique to having a previous public kernel self.validate_inputs(); @@ -485,4 +488,25 @@ mod tests { builder.failed(); } + + #[test] + fn propagates_global_variables_if_empty() { + let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); + + builder.public_call.public_inputs.global_variables.block_number = 11; + + let public_inputs = builder.execute(); + + assert_eq(public_inputs.constants.global_variables.block_number, 11); + } + + #[test(should_fail_with="Global variables injected into the public call do not match constants")] + fn validates_global_variables() { + let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); + + builder.previous_kernel.global_variables.block_number = 10; + builder.public_call.public_inputs.global_variables.block_number = 11; + + builder.failed(); + } } 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 e918d78ad64..d81f22114b7 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 @@ -8,7 +8,8 @@ struct PublicKernelSetupCircuitPrivateInputs { // Note: One might think that our previous_kernel ought to be // a PrivateKernelTailData. However, we instead supply a PublicKernelData. // This is because PrivateKernelTailData is a subset of PublicKernelData. - // And we just initialize the missing values to zero in TS before passing it to the circuit. + // And we just initialize the missing values to zero in TS before passing it to the circuit, + // except for the constants.global_variables which we populate with the current block values. // This is a bit of a hack, but it allows us to reuse the setup circuit until // the setup phase of the public kernel is complete. Maybe in a perfect world we would // have a SetupInit, SetupInner, etc, but this will change anyway once the public VM is able to @@ -36,6 +37,9 @@ impl PublicKernelSetupCircuitPrivateInputs { // validate the inputs common to all invocation circumstances common::validate_inputs(self.public_call); + // validate constants injected into the public call are correct or set them if this is the first public call + common::initialize_from_or_validate_public_call_variables(self.previous_kernel, self.public_call, &mut public_inputs); + // validate the inputs unique to having a previous private kernel self.validate_inputs(); @@ -540,4 +544,25 @@ mod tests { builder.failed(); } + + #[test] + fn propagates_global_variables_if_empty() { + let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new(); + + builder.public_call.public_inputs.global_variables.block_number = 11; + + let public_inputs = builder.execute(); + + assert_eq(public_inputs.constants.global_variables.block_number, 11); + } + + #[test(should_fail_with="Global variables injected into the public call do not match constants")] + fn validates_global_variables() { + let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new(); + + builder.previous_kernel.global_variables.block_number = 10; + builder.public_call.public_inputs.global_variables.block_number = 11; + + builder.failed(); + } } 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 9b19bb71dcc..4b8f57f786e 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 @@ -36,8 +36,7 @@ impl PublicKernelTeardownCircuitPrivateInputs { let transaction_fee = self.public_call.call_stack_item.public_inputs.transaction_fee; // Note that teardown_gas is already included in end.gas_used as it was injected by the private kernel let total_gas_used = self.previous_kernel.public_inputs.end.gas_used.add(self.previous_kernel.public_inputs.end_non_revertible.gas_used); - // TODO(palla/gas): Load gas fees from a PublicConstantData struct that's currently missing - let block_gas_fees = GasFees::default(); + let block_gas_fees = self.previous_kernel.public_inputs.constants.global_variables.gas_fees; let inclusion_fee = self.previous_kernel.public_inputs.constants.tx_context.gas_settings.inclusion_fee; let computed_transaction_fee = total_gas_used.compute_fee(block_gas_fees) + inclusion_fee; @@ -74,6 +73,9 @@ impl PublicKernelTeardownCircuitPrivateInputs { // validate the inputs common to all invocation circumstances common::validate_inputs(self.public_call); + // validate constants injected into the public call are correct or set them if this is the first public call + common::initialize_from_or_validate_public_call_variables(self.previous_kernel, self.public_call, &mut public_inputs); + // validate the inputs unique to having a previous private kernel self.validate_inputs(); @@ -425,4 +427,25 @@ mod tests { builder.failed(); } + + #[test] + fn propagates_global_variables_if_empty() { + let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); + + builder.public_call.public_inputs.global_variables.block_number = 11; + + let public_inputs = builder.execute(); + + assert_eq(public_inputs.constants.global_variables.block_number, 11); + } + + #[test(should_fail_with="Global variables injected into the public call do not match constants")] + fn validates_global_variables() { + let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); + + builder.previous_kernel.global_variables.block_number = 10; + builder.public_call.public_inputs.global_variables.block_number = 11; + + builder.failed(); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr index db3a8cddfb0..b6e59abff55 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/base_rollup_inputs.nr @@ -66,6 +66,14 @@ impl BaseRollupInputs { == self.constants.global_variables.version, "kernel version does not match the rollup version" ); + // Verify the kernel global variables if set, note these can be empty if this is a request coming directly from the private kernel tail. + // TODO(@spalladino) How can we check that this is a request coming from the private kernel tail? + assert( + self.kernel_data.public_inputs.constants.global_variables.is_empty() + | (self.kernel_data.public_inputs.constants.global_variables + == self.constants.global_variables), "kernel global variables do not match the rollup global variables" + ); + self.validate_kernel_start_state(); let rollup_validation_requests = self.kernel_data.public_inputs.rollup_validation_requests; @@ -983,6 +991,14 @@ mod tests { builder.fails(); } + #[test(should_fail_with = "kernel global variables do not match the rollup global variables")] + unconstrained fn constants_global_variables_dont_match_kernels() { + let mut builder = BaseRollupInputsBuilder::new(); + builder.kernel_data.global_variables.block_number = 6; + builder.constants.global_variables.block_number = 7; + builder.fails(); + } + #[test(should_fail_with = "kernel max_block_number is smaller than block number")] unconstrained fn constants_dont_satisfy_smaller_max_block_number() { let mut builder = BaseRollupInputsBuilder::new(); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/combined_constant_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/combined_constant_data.nr index 09655af7058..0d823df58d2 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/combined_constant_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/combined_constant_data.nr @@ -1,6 +1,7 @@ use crate::transaction::tx_context::TxContext; use crate::header::Header; use crate::traits::Empty; +use crate::abis::global_variables::GlobalVariables; struct CombinedConstantData { historical_header: Header, @@ -9,6 +10,14 @@ struct CombinedConstantData { // a situation we could be using header from a block before the upgrade took place but be using the updated // protocol to execute and prove the transaction. tx_context: TxContext, + + global_variables: GlobalVariables, +} + +impl CombinedConstantData { + pub fn private(historical_header: Header, tx_context: TxContext) -> CombinedConstantData { + CombinedConstantData { historical_header, tx_context, global_variables: GlobalVariables::empty() } + } } impl Empty for CombinedConstantData { @@ -16,6 +25,7 @@ impl Empty for CombinedConstantData { CombinedConstantData { historical_header: Header::empty(), tx_context: TxContext::empty(), + global_variables: GlobalVariables::empty() } } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_fees.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_fees.nr index 09d75aae0c8..45e9ed3ab66 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_fees.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_fees.nr @@ -18,6 +18,10 @@ impl GasFees { pub fn default() -> Self { GasFees::new(1, 1, 1) } + + pub fn is_empty(self) -> bool { + (self.fee_per_da_gas == 0) & (self.fee_per_l1_gas == 0) & (self.fee_per_l2_gas == 0) + } } impl Serialize for GasFees { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr index 1a7a5a68315..f08db75bb73 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/global_variables.nr @@ -17,6 +17,18 @@ struct GlobalVariables { } // docs:end:global-variables +impl GlobalVariables { + fn is_empty(self) -> bool { + (self.chain_id == 0) + & (self.version == 0) + & (self.block_number == 0) + & (self.timestamp == 0) + & (self.coinbase.is_zero()) + & (self.fee_recipient.is_zero()) + & (self.gas_fees.is_empty()) + } +} + impl Serialize for GlobalVariables { fn serialize(self) -> [Field; GLOBAL_VARIABLES_LENGTH] { let mut serialized: BoundedVec = BoundedVec::new(); 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 d176bfa53e1..1939b3034f0 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 @@ -69,7 +69,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 = 0x1b06f4a4960455e9f01c20d4cb01afbf8c8f39eb50094c5d1ad6725ced0f7d08; + let test_data_call_stack_item_request_hash = 0x22848497ff97ff3a4517aec32454059030fb5a3ef4f3ca533ee40132d7a63aea; assert_eq(call_stack_item.hash(), test_data_call_stack_item_request_hash); } @@ -87,7 +87,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 = 0x1f3f1902ca41ffd6fd7191fa5a52edd677444a9b6ae8f4448336fa71a4b2d5cc; + let test_data_call_stack_item_hash = 0x0e18ddd9aaddae02d45598f0278d925e289913384d6e15057ce5b4a9e8e7488d; assert_eq(call_stack_item.hash(), test_data_call_stack_item_hash); } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr index 154b3e05a77..2dba18750fd 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/public_circuit_public_inputs.nr @@ -1,7 +1,7 @@ use crate::{ abis::{ call_context::CallContext, read_request::ReadRequest, - side_effect::{SideEffect, SideEffectLinkedToNoteHash}, gas::Gas + side_effect::{SideEffect, SideEffectLinkedToNoteHash}, gas::Gas, global_variables::GlobalVariables }, address::AztecAddress, constants::{ @@ -46,6 +46,9 @@ struct PublicCircuitPublicInputs { // previous to the one in which the tx is included. historical_header: Header, + // Global variables injected into this circuit + global_variables: GlobalVariables, + prover_address: AztecAddress, revert_code: u8, @@ -99,6 +102,7 @@ impl Serialize for PublicCircuitPublicInput } fields.push(self.unencrypted_log_preimages_length); fields.extend_from_array(self.historical_header.serialize()); + fields.extend_from_array(self.global_variables.serialize()); fields.push(self.prover_address.to_field()); fields.push(self.revert_code as Field); fields.extend_from_array(self.start_gas_left.serialize()); @@ -129,6 +133,7 @@ impl Deserialize for PublicCircuitPublicInp unencrypted_logs_hashes: reader.read_struct_array(SideEffect::deserialize, [SideEffect::empty(); MAX_UNENCRYPTED_LOGS_PER_CALL]), unencrypted_log_preimages_length: reader.read(), historical_header: reader.read_struct(Header::deserialize), + global_variables: reader.read_struct(GlobalVariables::deserialize), prover_address: reader.read_struct(AztecAddress::deserialize), revert_code: reader.read() as u8, start_gas_left: reader.read_struct(Gas::deserialize), @@ -166,6 +171,7 @@ impl Empty for PublicCircuitPublicInputs { unencrypted_logs_hashes: [SideEffect::empty(); MAX_UNENCRYPTED_LOGS_PER_CALL], unencrypted_log_preimages_length: 0, historical_header: Header::empty(), + global_variables: GlobalVariables::empty(), prover_address: AztecAddress::zero(), revert_code: 0 as u8, start_gas_left: Gas::empty(), @@ -189,6 +195,6 @@ fn empty_hash() { let hash = inputs.hash(); // Value from public_circuit_public_inputs.test.ts "computes empty item hash" test - let test_data_empty_hash = 0x237c89f8b29c3fb169b889940a714b3c72017cb2941d0724d4668a030794d2fb; + let test_data_empty_hash = 0x2d91debc43bd6354caef4fd152975e7c6dd44e8623b6b62c21b9f547f2fabd32; assert_eq(hash, test_data_empty_hash); } 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 9f454eaeefe..31dec2bda41 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -165,7 +165,7 @@ global TX_CONTEXT_LENGTH: u64 = 2 + GAS_SETTINGS_LENGTH; global TX_REQUEST_LENGTH: u64 = 2 + TX_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH; global HEADER_LENGTH: u64 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + CONTENT_COMMITMENT_LENGTH + STATE_REFERENCE_LENGTH + GLOBAL_VARIABLES_LENGTH; global PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 3 + MAX_BLOCK_NUMBER_LENGTH + (SIDE_EFFECT_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (NULLIFIER_KEY_VALIDATION_REQUEST_LENGTH * MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_CALL) + (SIDE_EFFECT_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (SIDE_EFFECT_LENGTH * MAX_ENCRYPTED_LOGS_PER_CALL) + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 2 + HEADER_LENGTH + TX_CONTEXT_LENGTH; -global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 2 + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_CALL) + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (SIDE_EFFECT_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 1 + HEADER_LENGTH + AZTEC_ADDRESS_LENGTH + /* revert_code */ 1 + 2 * GAS_LENGTH + /* transaction_fee */ 1; +global PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 2 + (READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL) + (READ_REQUEST_LENGTH * MAX_NULLIFIER_NON_EXISTENT_READ_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_UPDATE_REQUEST_LENGTH * MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL) + (CONTRACT_STORAGE_READ_LENGTH * MAX_PUBLIC_DATA_READS_PER_CALL) + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + (SIDE_EFFECT_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (SIDE_EFFECT_LINKED_TO_NOTE_HASH_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + (L2_TO_L1_MESSAGE_LENGTH * MAX_NEW_L2_TO_L1_MSGS_PER_CALL) + 2 + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL) + 1 + HEADER_LENGTH + GLOBAL_VARIABLES_LENGTH + AZTEC_ADDRESS_LENGTH + /* revert_code */ 1 + 2 * GAS_LENGTH + /* transaction_fee */ 1; global PRIVATE_CALL_STACK_ITEM_LENGTH: u64 = AZTEC_ADDRESS_LENGTH + FUNCTION_DATA_LENGTH + PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH; global ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH: u64 = 2 + FUNCTION_DATA_LENGTH + CALL_CONTEXT_LENGTH; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr index a70de06ebfa..4d02ae8cea0 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/fixture_builder.nr @@ -6,7 +6,7 @@ use crate::{ CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PublicAccumulatedData, PublicAccumulatedDataBuilder }, - combined_constant_data::CombinedConstantData, + global_variables::GlobalVariables, combined_constant_data::CombinedConstantData, kernel_circuit_public_inputs::{KernelCircuitPublicInputs, PrivateKernelCircuitPublicInputs, PublicKernelCircuitPublicInputs}, kernel_data::{PrivateKernelData, PublicKernelData, KernelData}, max_block_number::MaxBlockNumber, nullifier_key_validation_request::NullifierKeyValidationRequestContext, @@ -35,6 +35,7 @@ struct FixtureBuilder { // Constant data. historical_header: Header, tx_context: TxContext, + global_variables: GlobalVariables, // Accumulated data. new_note_hashes: BoundedVec, @@ -111,12 +112,17 @@ impl FixtureBuilder { counter: 0, start_state: PartialStateReference::empty(), gas_used: Gas::empty(), - non_revertible_gas_used: Gas::empty() + non_revertible_gas_used: Gas::empty(), + global_variables: GlobalVariables::empty() } } pub fn to_constant_data(self) -> CombinedConstantData { - CombinedConstantData { historical_header: self.historical_header, tx_context: self.tx_context } + CombinedConstantData { + historical_header: self.historical_header, + tx_context: self.tx_context, + global_variables: self.global_variables + } } pub fn to_private_accumulated_data(self) -> PrivateAccumulatedData { @@ -427,6 +433,7 @@ impl Empty for FixtureBuilder { storage_contract_address: AztecAddress::zero(), historical_header: Header::empty(), tx_context: TxContext::empty(), + global_variables: GlobalVariables::empty(), new_note_hashes: BoundedVec::new(), new_nullifiers: BoundedVec::new(), new_l2_to_l1_msgs: BoundedVec::new(), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr index f6d712bb814..db56f3b0852 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/public_circuit_public_inputs_builder.nr @@ -1,7 +1,8 @@ use crate::{ abis::{ gas::Gas, call_context::CallContext, public_circuit_public_inputs::PublicCircuitPublicInputs, - read_request::ReadRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash} + read_request::ReadRequest, side_effect::{SideEffect, SideEffectLinkedToNoteHash}, + global_variables::GlobalVariables }, address::AztecAddress, contrakt::{storage_read::StorageRead, storage_update_request::StorageUpdateRequest}, header::Header, @@ -34,6 +35,7 @@ struct PublicCircuitPublicInputsBuilder { unencrypted_logs_hashes: BoundedVec, unencrypted_log_preimages_length: Field, historical_header: Header, + global_variables: GlobalVariables, prover_address: AztecAddress, revert_code: u8, start_gas_left: Gas, @@ -67,6 +69,7 @@ impl PublicCircuitPublicInputsBuilder { unencrypted_logs_hashes: self.unencrypted_logs_hashes.storage, unencrypted_log_preimages_length: self.unencrypted_log_preimages_length, historical_header: self.historical_header, + global_variables: self.global_variables, prover_address: self.prover_address, revert_code: self.revert_code, start_gas_left: self.start_gas_left, @@ -95,6 +98,7 @@ impl Empty for PublicCircuitPublicInputsBuilder { unencrypted_logs_hashes: BoundedVec::new(), unencrypted_log_preimages_length: 0, historical_header: Header::empty(), + global_variables: GlobalVariables::empty(), prover_address: AztecAddress::zero(), revert_code: 0 as u8, start_gas_left: Gas::empty(), diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index bdac9c533f0..1f4e77af246 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -141,6 +141,7 @@ export const PUBLIC_CIRCUIT_PUBLIC_INPUTS_LENGTH = SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_CALL + 1 + HEADER_LENGTH + + GLOBAL_VARIABLES_LENGTH + AZTEC_ADDRESS_LENGTH + /* revert_code */ 1 + 2 * GAS_LENGTH + 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 18794323156..733ca2607b5 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`] = `"0x1f3f1902ca41ffd6fd7191fa5a52edd677444a9b6ae8f4448336fa71a4b2d5cc"`; +exports[`PublicCallStackItem Computes a callstack item hash 1`] = `"0x0e18ddd9aaddae02d45598f0278d925e289913384d6e15057ce5b4a9e8e7488d"`; -exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x1b06f4a4960455e9f01c20d4cb01afbf8c8f39eb50094c5d1ad6725ced0f7d08"`; +exports[`PublicCallStackItem Computes a callstack item request hash 1`] = `"0x22848497ff97ff3a4517aec32454059030fb5a3ef4f3ca533ee40132d7a63aea"`; -exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x040c3667dd703bad4465ba5d12e7a422959395f76299794aa9eeaf5044d9e157>`; +exports[`PublicCallStackItem computes empty item hash 1`] = `Fr<0x004e1dc292cd5919dcea653efb6d791458a1eee853432e27d385ef56714edce9>`; -exports[`PublicCallStackItem computes hash 1`] = `Fr<0x0f3fde3c615e9d95337fbbf3f835b3e26187de0de9f199320b53355f4089bb88>`; +exports[`PublicCallStackItem computes hash 1`] = `Fr<0x1efb84fc01ae8d6e8c27826100655f2d24344fa6edef9907649243770fc0798d>`; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap index 55e7a236787..7b63132cfd7 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/public_circuit_public_inputs.test.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PublicCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x237c89f8b29c3fb169b889940a714b3c72017cb2941d0724d4668a030794d2fb>`; +exports[`PublicCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x2d91debc43bd6354caef4fd152975e7c6dd44e8623b6b62c21b9f547f2fabd32>`; -exports[`PublicCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x0d22cf387fb73386318033d92d07d203ad5c3d1e332734fa58b9ec08fbf0ceac>`; +exports[`PublicCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x1710f7b36cf97619af020da867f7029d826d45b8237e6205981d1c15307875e0>`; diff --git a/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts b/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts index 189d6a27764..2fea7415bc7 100644 --- a/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts +++ b/yarn-project/circuits.js/src/structs/kernel/combined_constant_data.ts @@ -1,5 +1,7 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { type FieldsOf } from '@aztec/foundation/types'; +import { GlobalVariables } from '../global_variables.js'; import { Header } from '../header.js'; import { TxContext } from '../tx_context.js'; @@ -8,9 +10,7 @@ import { TxContext } from '../tx_context.js'; */ export class CombinedConstantData { constructor( - /** - * Header of a block whose state is used during execution (not the block the transaction is included in). - */ + /** Header of a block whose state is used during execution (not the block the transaction is included in). */ public historicalHeader: Header, /** * Context of the transaction. @@ -21,10 +21,17 @@ export class CombinedConstantData { * protocol to execute and prove the transaction. */ public txContext: TxContext, + + /** Present when output by a public kernel, empty otherwise. */ + public globalVariables: GlobalVariables, ) {} toBuffer() { - return serializeToBuffer(this.historicalHeader, this.txContext); + return serializeToBuffer(this.historicalHeader, this.txContext, this.globalVariables); + } + + static from({ historicalHeader, txContext, globalVariables }: FieldsOf): CombinedConstantData { + return new CombinedConstantData(historicalHeader, txContext, globalVariables); } /** @@ -34,10 +41,14 @@ export class CombinedConstantData { */ static fromBuffer(buffer: Buffer | BufferReader): CombinedConstantData { const reader = BufferReader.asReader(buffer); - return new CombinedConstantData(reader.readObject(Header), reader.readObject(TxContext)); + return new CombinedConstantData( + reader.readObject(Header), + reader.readObject(TxContext), + reader.readObject(GlobalVariables), + ); } static empty() { - return new CombinedConstantData(Header.empty(), TxContext.empty()); + return new CombinedConstantData(Header.empty(), TxContext.empty(), GlobalVariables.empty()); } } diff --git a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts index 220101ff45a..90b2f337de6 100644 --- a/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/public_circuit_public_inputs.ts @@ -29,6 +29,7 @@ import { CallContext } from './call_context.js'; import { ContractStorageRead } from './contract_storage_read.js'; import { ContractStorageUpdateRequest } from './contract_storage_update_request.js'; import { Gas } from './gas.js'; +import { GlobalVariables } from './global_variables.js'; import { Header } from './header.js'; import { L2ToL1Message } from './l2_to_l1_message.js'; import { ReadRequest } from './read_request.js'; @@ -112,6 +113,8 @@ export class PublicCircuitPublicInputs { * previous to the one in which the tx is included. */ public historicalHeader: Header, + /** Global variables for the block. */ + public globalVariables: GlobalVariables, /** * Address of the prover. */ @@ -163,6 +166,7 @@ export class PublicCircuitPublicInputs { makeTuple(MAX_UNENCRYPTED_LOGS_PER_CALL, SideEffect.empty), Fr.ZERO, Header.empty(), + GlobalVariables.empty(), AztecAddress.ZERO, RevertCode.OK, Gas.empty(), @@ -193,6 +197,7 @@ export class PublicCircuitPublicInputs { isArrayEmpty(this.unencryptedLogsHashes, item => item.isEmpty()) && this.unencryptedLogPreimagesLength.isZero() && this.historicalHeader.isEmpty() && + this.globalVariables.isEmpty() && this.proverAddress.isZero() && this.revertCode.isOK() && this.startGasLeft.isEmpty() && @@ -224,6 +229,7 @@ export class PublicCircuitPublicInputs { fields.unencryptedLogsHashes, fields.unencryptedLogPreimagesLength, fields.historicalHeader, + fields.globalVariables, fields.proverAddress, fields.revertCode, fields.startGasLeft, @@ -274,6 +280,7 @@ export class PublicCircuitPublicInputs { reader.readArray(MAX_UNENCRYPTED_LOGS_PER_CALL, SideEffect), reader.readObject(Fr), reader.readObject(Header), + reader.readObject(GlobalVariables), reader.readObject(AztecAddress), reader.readObject(RevertCode), reader.readObject(Gas), @@ -302,6 +309,7 @@ export class PublicCircuitPublicInputs { reader.readArray(MAX_UNENCRYPTED_LOGS_PER_CALL, SideEffect), reader.readField(), Header.fromFields(reader), + GlobalVariables.fromFields(reader), AztecAddress.fromFields(reader), RevertCode.fromFields(reader), Gas.fromFields(reader), diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 52a9b1e1d0a..48244164f09 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -180,7 +180,7 @@ export function makeTxContext(seed: number = 1): TxContext { * @returns A constant data object. */ export function makeConstantData(seed = 1): CombinedConstantData { - return new CombinedConstantData(makeHeader(seed, undefined), makeTxContext(seed + 4)); + return new CombinedConstantData(makeHeader(seed, undefined), makeTxContext(seed + 4), makeGlobalVariables(seed + 5)); } /** @@ -293,7 +293,7 @@ export function makeRollupValidationRequests(seed = 1) { } export function makeCombinedConstantData(seed = 1): CombinedConstantData { - return new CombinedConstantData(makeHeader(seed), makeTxContext(seed + 0x100)); + return new CombinedConstantData(makeHeader(seed), makeTxContext(seed + 0x100), makeGlobalVariables(seed + 0x200)); } /** @@ -456,6 +456,7 @@ export function makePublicCircuitPublicInputs( tupleGenerator(MAX_UNENCRYPTED_LOGS_PER_CALL, sideEffectFromNumber, seed + 0x901, SideEffect.empty), fr(seed + 0x902), makeHeader(seed + 0xa00, undefined), + makeGlobalVariables(seed + 0xa01), makeAztecAddress(seed + 0xb01), RevertCode.OK, makeGas(seed + 0xc00), diff --git a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts index 3b3e0a2385a..c1dd1746365 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -1188,6 +1188,7 @@ export function mapCombinedConstantDataFromNoir(combinedConstantData: CombinedCo return new CombinedConstantData( mapHeaderFromNoir(combinedConstantData.historical_header), mapTxContextFromNoir(combinedConstantData.tx_context), + mapGlobalVariablesFromNoir(combinedConstantData.global_variables), ); } @@ -1200,6 +1201,7 @@ export function mapCombinedConstantDataToNoir(combinedConstantData: CombinedCons return { historical_header: mapHeaderToNoir(combinedConstantData.historicalHeader), tx_context: mapTxContextToNoir(combinedConstantData.txContext), + global_variables: mapGlobalVariablesToNoir(combinedConstantData.globalVariables), }; } @@ -1547,6 +1549,7 @@ export function mapPublicCircuitPublicInputsToNoir( unencrypted_logs_hashes: mapTuple(publicInputs.unencryptedLogsHashes, mapSideEffectToNoir), unencrypted_log_preimages_length: mapFieldToNoir(publicInputs.unencryptedLogPreimagesLength), historical_header: mapHeaderToNoir(publicInputs.historicalHeader), + global_variables: mapGlobalVariablesToNoir(publicInputs.globalVariables), prover_address: mapAztecAddressToNoir(publicInputs.proverAddress), revert_code: mapRevertCodeToNoir(publicInputs.revertCode), start_gas_left: mapGasToNoir(publicInputs.startGasLeft), diff --git a/yarn-project/simulator/src/public/abstract_phase_manager.ts b/yarn-project/simulator/src/public/abstract_phase_manager.ts index 27f513dd4d2..f84298d6dea 100644 --- a/yarn-project/simulator/src/public/abstract_phase_manager.ts +++ b/yarn-project/simulator/src/public/abstract_phase_manager.ts @@ -421,6 +421,7 @@ export abstract class AbstractPhaseManager { ), unencryptedLogPreimagesLength, historicalHeader: this.historicalHeader, + globalVariables: this.globalVariables, startGasLeft: Gas.from(result.startGasLeft), endGasLeft: Gas.from(result.endGasLeft), transactionFee: result.transactionFee, diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index 9e5c3a098c1..a5fd6a08177 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -207,6 +207,8 @@ async function executePublicFunctionAcvm( const nestedExecutions = context.getNestedExecutions(); const unencryptedLogs = context.getUnencryptedLogs(); + + // TODO(palla/gas): We should be loading these values from the returned PublicCircuitPublicInputs const startGasLeft = context.availableGas; const endGasLeft = context.availableGas; // No gas consumption in non-AVM