diff --git a/docs/docs/protocol-specs/gas-and-fees/fee-schedule.md b/docs/docs/protocol-specs/gas-and-fees/fee-schedule.md index 05c139b48a6..3bb448e906d 100644 --- a/docs/docs/protocol-specs/gas-and-fees/fee-schedule.md +++ b/docs/docs/protocol-specs/gas-and-fees/fee-schedule.md @@ -52,6 +52,8 @@ A side effect of the above calculation is that all transactions will have a non- L2 gas is consumed to cover the costs associated with executing the public VM, proving the public VM circuit, and proving the public kernel circuit. +It is also consumed to perform fixed, mandatory computation that must be performed per transaction by the sequencer, regardless of what the transaction actually does; examples are TX validation and updating state roots in trees. + The public vm has an [instruction set](../public-vm/instruction-set.mdx) with opcode level gas metering to cover the cost of actions performed within the public VM. Additionally, there is a fixed cost associated with each iteration of the public VM (i.e. the number of enqueued public function calls, plus 1 if there is a teardown function), which is used to cover the cost of proving the public VM circuit. @@ -59,23 +61,24 @@ Additionally, there is a fixed cost associated with each iteration of the public The L2 gas used is then calculated as: ``` -AVM_STARTUP_L2_GAS = 1024 +FIXED_L2_GAS = 512 +FIXED_AVM_STARTUP_L2_GAS = 1024 num_avm_invocations = (number of enqueued public function calls) + (is there a teardown function ? 1 : 0) -l2_gas_used = AVM_STARTUP_L2_GAS * num_avm_invocations + - teardown_l2_gas + - (gas reported as consumed by the public VM) +l2_gas_used = FIXED_L2_GAS + + FIXED_AVM_STARTUP_L2_GAS * num_avm_invocations + + teardown_l2_gas + + (gas reported as consumed by the public VM) ``` -:::warning L2 Gas from Private -In the current implementation, private execution does not consume L2 gas. This will change in future versions of the protocol, because there is still work that needs to be performed by the sequencer correspondent to the private outputs, which is effectively L2 gas. The following operations performed in private execution will likely consume L2 gas in future versions of the protocol: -- emitting note hashes (due to tree insertion) -- emitting nullifiers (due to tree insertion) -- possibly emitting logs (due to validation checks) -::: +### L2 Gas from Private +Private execution also consumes L2 gas, because there is still work that needs to be performed by the sequencer correspondent to the private outputs, which is effectively L2 gas. The following operations performed in private execution will consume L2 gas: +- 32 L2 gas per note hash +- 64 L2 gas per nullifier +- 4 L2 gas per byte of logs (note encrypted, encrypted, and unencrypted) ## Max Inclusion Fee diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 8059d997c24..d30f01b1377 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -98,6 +98,11 @@ library Constants { uint256 internal constant DA_BYTES_PER_FIELD = 32; uint256 internal constant DA_GAS_PER_BYTE = 16; uint256 internal constant FIXED_DA_GAS = 512; + uint256 internal constant FIXED_L2_GAS = 512; + uint256 internal constant FIXED_AVM_STARTUP_L2_GAS = 1024; + uint256 internal constant L2_GAS_PER_LOG_BYTE = 4; + uint256 internal constant L2_GAS_PER_NOTE_HASH = 32; + uint256 internal constant L2_GAS_PER_NULLIFIER = 64; uint256 internal constant CANONICAL_KEY_REGISTRY_ADDRESS = 2153455745675440165069577621832684870696142028027528497509357256345838682961; uint256 internal constant CANONICAL_AUTH_REGISTRY_ADDRESS = diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr index c958fab4182..f20103b878a 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr @@ -6,7 +6,7 @@ use dep::types::{ log_hash::{ScopedEncryptedLogHash, NoteLogHash, ScopedLogHash}, note_hash::ScopedNoteHash, nullifier::ScopedNullifier }, - constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE}, + constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, L2_GAS_PER_NOTE_HASH, L2_GAS_PER_NULLIFIER, L2_GAS_PER_LOG_BYTE}, hash::{compute_tx_logs_hash, compute_tx_note_logs_hash}, messaging::l2_to_l1_message::ScopedL2ToL1Message }; @@ -50,20 +50,32 @@ impl TailOutputComposer { } fn meter_gas_used(self, data: CombinedAccumulatedData) -> Gas { - let mut metered_bytes = 0; + let mut metered_da_bytes = 0; + let mut metered_l2_gas = 0; let data_builder = self.output_composer.public_inputs.end; // IMPORTANT: Must use data_builder.__.len(), which is the the number of items pushed to the BoundedVec. // Do not use data.__.len(), which is the array's max length. - metered_bytes += data_builder.note_hashes.len() * DA_BYTES_PER_FIELD; - metered_bytes += data_builder.nullifiers.len() * DA_BYTES_PER_FIELD; - metered_bytes += data_builder.l2_to_l1_msgs.len() * DA_BYTES_PER_FIELD; + metered_da_bytes += data_builder.note_hashes.len() * DA_BYTES_PER_FIELD; + metered_l2_gas += data_builder.note_hashes.len() * L2_GAS_PER_NOTE_HASH; - metered_bytes += data.note_encrypted_log_preimages_length as u32; - metered_bytes += data.encrypted_log_preimages_length as u32; - metered_bytes += data.unencrypted_log_preimages_length as u32; + metered_da_bytes += data_builder.nullifiers.len() * DA_BYTES_PER_FIELD; + metered_l2_gas += data_builder.nullifiers.len() * L2_GAS_PER_NULLIFIER; + + metered_da_bytes += data_builder.l2_to_l1_msgs.len() * DA_BYTES_PER_FIELD; + + metered_da_bytes += data.note_encrypted_log_preimages_length as u32; + metered_l2_gas += data.note_encrypted_log_preimages_length as u32 * L2_GAS_PER_LOG_BYTE; + + metered_da_bytes += data.encrypted_log_preimages_length as u32; + metered_l2_gas += data.encrypted_log_preimages_length as u32 * L2_GAS_PER_LOG_BYTE; + + metered_da_bytes += data.unencrypted_log_preimages_length as u32; + metered_l2_gas += data.unencrypted_log_preimages_length as u32 * L2_GAS_PER_LOG_BYTE; let teardown_gas = self.output_composer.public_inputs.constants.tx_context.gas_settings.teardown_gas_limits; - Gas::new(metered_bytes * DA_GAS_PER_BYTE, 0) + Gas::tx_overhead() + teardown_gas + Gas::new(metered_da_bytes * DA_GAS_PER_BYTE, metered_l2_gas) + + Gas::tx_overhead() + + teardown_gas } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/meter_gas_used.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/meter_gas_used.nr index 3a2ad4ac4ac..eb3a21da7ec 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/meter_gas_used.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer/meter_gas_used.nr @@ -3,27 +3,39 @@ use dep::types::{ accumulated_data::{public_accumulated_data_builder::PublicAccumulatedDataBuilder}, gas::Gas, log_hash::LogHash }, - constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE} + constants::{ + DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, FIXED_AVM_STARTUP_L2_GAS, L2_GAS_PER_NOTE_HASH, + L2_GAS_PER_NULLIFIER, L2_GAS_PER_LOG_BYTE +} }; fn meter_gas_used(data: PublicAccumulatedDataBuilder) -> Gas { - let mut metered_bytes = 0; - metered_bytes += data.note_hashes.len() * DA_BYTES_PER_FIELD; - metered_bytes += data.nullifiers.len() * DA_BYTES_PER_FIELD; - metered_bytes += data.l2_to_l1_msgs.len() * DA_BYTES_PER_FIELD; + let mut metered_da_bytes = 0; + let mut metered_l2_gas = 0; + + metered_da_bytes += data.note_hashes.len() * DA_BYTES_PER_FIELD; + metered_l2_gas += data.note_hashes.len() * L2_GAS_PER_NOTE_HASH; + + metered_da_bytes += data.nullifiers.len() * DA_BYTES_PER_FIELD; + metered_l2_gas += data.nullifiers.len() * L2_GAS_PER_NULLIFIER; + + metered_da_bytes += data.l2_to_l1_msgs.len() * DA_BYTES_PER_FIELD; let note_encrypted_log_preimages_length = data.note_encrypted_logs_hashes.storage.fold(0, |len, l: LogHash| len + l.length); - metered_bytes += note_encrypted_log_preimages_length as u32; + metered_da_bytes += note_encrypted_log_preimages_length as u32; + metered_l2_gas += note_encrypted_log_preimages_length as u32 * L2_GAS_PER_LOG_BYTE; let encrypted_log_preimages_length = data.encrypted_logs_hashes.storage.fold(0, |len, l: LogHash| len + l.length); - metered_bytes += encrypted_log_preimages_length as u32; + metered_da_bytes += encrypted_log_preimages_length as u32; + metered_l2_gas += encrypted_log_preimages_length as u32 * L2_GAS_PER_LOG_BYTE; let unencrypted_log_preimages_length = data.unencrypted_logs_hashes.storage.fold(0, |len, l: LogHash| len + l.length); - metered_bytes += unencrypted_log_preimages_length as u32; + metered_da_bytes += unencrypted_log_preimages_length as u32; + metered_l2_gas += unencrypted_log_preimages_length as u32 * L2_GAS_PER_LOG_BYTE; - // TODO(gas): add AVM_STARTUP_L2_GAS + metered_l2_gas += data.public_call_stack.len() * FIXED_AVM_STARTUP_L2_GAS; - Gas::new(metered_bytes * DA_GAS_PER_BYTE, 0) + Gas::new(metered_da_bytes * DA_GAS_PER_BYTE, metered_l2_gas) } pub fn meter_gas_used_non_revertible(data: PublicAccumulatedDataBuilder) -> Gas { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr index f21f036f67e..496be7841d0 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail.nr @@ -37,14 +37,14 @@ mod tests { use crate::private_kernel_tail::PrivateKernelTailCircuitPrivateInputs; use dep::types::constants::{ MAX_ENCRYPTED_LOGS_PER_TX, MAX_UNENCRYPTED_LOGS_PER_TX, DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, - GENERATOR_INDEX__IVSK_M + GENERATOR_INDEX__IVSK_M, L2_GAS_PER_LOG_BYTE, L2_GAS_PER_NULLIFIER, L2_GAS_PER_NOTE_HASH }; use dep::types::{ abis::{ kernel_circuit_public_inputs::KernelCircuitPublicInputs, max_block_number::MaxBlockNumber, note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, gas::Gas }, - address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, + address::{AztecAddress, EthAddress}, grumpkin_private_key::GrumpkinPrivateKey, hash::{ sha256_to_field, silo_note_hash, silo_nullifier, compute_siloed_encrypted_log_hash, compute_siloed_unencrypted_log_hash @@ -261,10 +261,18 @@ mod tests { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.append_note_hashes(2); builder.previous_kernel.append_nullifiers(2); + builder.previous_kernel.add_note_encrypted_log_hash( + 42, + 5, + builder.previous_kernel.note_hashes.get(0).note_hash.counter + ); let public_inputs = builder.execute(); assert_eq(array_length(public_inputs.end.note_hashes), 2); assert_eq(array_length(public_inputs.end.nullifiers), 3); - let expected_gas = Gas::tx_overhead() + Gas::new(DA_GAS_PER_BYTE * DA_BYTES_PER_FIELD * 5, 0); + let expected_gas = Gas::tx_overhead() + Gas::new( + DA_GAS_PER_BYTE * DA_BYTES_PER_FIELD * 5 + DA_GAS_PER_BYTE * 5, + L2_GAS_PER_NOTE_HASH * 2 + L2_GAS_PER_NULLIFIER * 3 + L2_GAS_PER_LOG_BYTE * 5 + ); assert_eq(public_inputs.end.gas_used, expected_gas); } @@ -321,13 +329,53 @@ mod tests { // addition follows the form: // teardown gas // tx overhead - // tx nullifier - let expected_gas_consumed = Gas::new(300, 300) - + Gas::tx_overhead() - + Gas::new(DA_GAS_PER_BYTE * DA_BYTES_PER_FIELD * 1, 0); + // tx nullifier (which has DA and L2 gas) + let expected_gas_consumed = Gas::new(300, 300) + Gas::tx_overhead() + Gas::new( + DA_GAS_PER_BYTE * DA_BYTES_PER_FIELD * 1, + L2_GAS_PER_NULLIFIER * 1 + ); assert_eq(public_inputs.end.gas_used, expected_gas_consumed); } + #[test] + unconstrained fn tx_consumes_gas_from_l2_l1_msgs() { + let mut builder = PrivateKernelTailInputsBuilder::new(); + + builder.previous_kernel.add_l2_to_l1_message(42, EthAddress::zero()); + builder.previous_kernel.add_l2_to_l1_message(42, EthAddress::zero()); + builder.previous_kernel.end_setup(); + builder.previous_kernel.add_l2_to_l1_message(42, EthAddress::zero()); + + let public_inputs = builder.execute(); + + assert_eq( + Gas::tx_overhead() + Gas::new( + 4 * DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, + 1 * L2_GAS_PER_NULLIFIER + ), public_inputs.end.gas_used + ); + } + + #[test] + unconstrained fn tx_consumed_gas_from_logs() { + let mut builder = PrivateKernelTailInputsBuilder::new(); + builder.previous_kernel.add_encrypted_log_hash(42, 3); + builder.previous_kernel.add_encrypted_log_hash(42, 4); + builder.previous_kernel.add_unencrypted_log_hash(42, 5); + builder.previous_kernel.end_setup(); + builder.previous_kernel.add_encrypted_log_hash(42, 6); + builder.previous_kernel.add_unencrypted_log_hash(42, 7); + + let public_inputs = builder.execute(); + + assert_eq( + Gas::tx_overhead() + Gas::new( + (1 * DA_BYTES_PER_FIELD + 25) * DA_GAS_PER_BYTE , + 1 * L2_GAS_PER_NULLIFIER + 25 * L2_GAS_PER_LOG_BYTE + ), public_inputs.end.gas_used + ); + } + #[test(should_fail_with="The gas used exceeds the gas limits")] fn gas_limits_are_enforced() { let mut builder = PrivateKernelTailInputsBuilder::new(); diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr index 3f00cfe4974..fe914554ef0 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_tail_to_public.nr @@ -36,14 +36,17 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs { mod tests { use crate::private_kernel_tail_to_public::PrivateKernelTailToPublicCircuitPrivateInputs; - use dep::types::constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, GENERATOR_INDEX__TSK_M}; + use dep::types::constants::{ + DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, GENERATOR_INDEX__TSK_M, L2_GAS_PER_LOG_BYTE, + L2_GAS_PER_NOTE_HASH, L2_GAS_PER_NULLIFIER, FIXED_AVM_STARTUP_L2_GAS + }; use dep::types::{ abis::{ kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, gas::Gas, note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, log_hash::{LogHash, NoteLogHash} }, - address::AztecAddress, hash::{silo_note_hash, silo_nullifier}, + address::{AztecAddress, EthAddress}, hash::{silo_note_hash, silo_nullifier}, tests::fixture_builder::FixtureBuilder, utils::{arrays::array_eq}, grumpkin_point::GrumpkinPoint }; @@ -195,9 +198,18 @@ mod tests { ) ); - assert_eq(public_inputs.end.gas_used, Gas::new(2 * DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, 0)); assert_eq( - public_inputs.end_non_revertible.gas_used, Gas::new(3 * DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, 0) + Gas::tx_overhead() + public_inputs.end.gas_used, Gas::new( + 2 * DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, + 2 * L2_GAS_PER_NULLIFIER + ) + ); + assert_eq( + public_inputs.end_non_revertible.gas_used, Gas::new( + 3 * DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, + FIXED_AVM_STARTUP_L2_GAS + 3 * L2_GAS_PER_NULLIFIER + ) + + Gas::tx_overhead() ); } @@ -253,13 +265,16 @@ mod tests { assert_eq( public_inputs.end.gas_used, Gas::new( (2 * DA_BYTES_PER_FIELD + revertible_logs_len) * DA_GAS_PER_BYTE, - 0 + 2 * L2_GAS_PER_NOTE_HASH + revertible_logs_len * L2_GAS_PER_LOG_BYTE ) ); assert_eq( public_inputs.end_non_revertible.gas_used, Gas::new( (3 * DA_BYTES_PER_FIELD + non_revertible_logs_len) * DA_GAS_PER_BYTE, - 0 + FIXED_AVM_STARTUP_L2_GAS + + L2_GAS_PER_NULLIFIER + + 2 * L2_GAS_PER_NOTE_HASH + + non_revertible_logs_len * L2_GAS_PER_LOG_BYTE ) + Gas::tx_overhead() ); @@ -292,13 +307,83 @@ mod tests { fn empty_tx_consumes_teardown_limits_plus_fixed_gas() { let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300); + builder.previous_kernel.end_setup(); + let public_inputs = builder.execute(); + + let expected_revertible_gas_used = Gas::new(300, 300); + assert_eq(public_inputs.end.gas_used, expected_revertible_gas_used); + + let expected_non_revertible_gas_used = Gas::tx_overhead() + Gas::new( + DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE * 1, + L2_GAS_PER_NULLIFIER * 1 + FIXED_AVM_STARTUP_L2_GAS + ); + + assert_eq(public_inputs.end_non_revertible.gas_used, expected_non_revertible_gas_used); + } + + #[test] + unconstrained fn enqueued_public_calls_consume_startup_gas() { + let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + // add an extra non-revertible call + builder.previous_kernel.push_public_call_request(42, false); + builder.previous_kernel.end_setup(); + // add some revertible calls + builder.previous_kernel.push_public_call_request(42, false); + builder.previous_kernel.push_public_call_request(42, false); + builder.previous_kernel.push_public_call_request(42, false); + let public_inputs = builder.execute(); + + let expected_revertible_gas_used = Gas::new(0, 3 * FIXED_AVM_STARTUP_L2_GAS); + assert_eq(public_inputs.end.gas_used, expected_revertible_gas_used); + + let expected_non_revertible_gas_used = Gas::tx_overhead() + Gas::new( + DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE * 1, + L2_GAS_PER_NULLIFIER * 1 + 2 * FIXED_AVM_STARTUP_L2_GAS + ); + + assert_eq(public_inputs.end_non_revertible.gas_used, expected_non_revertible_gas_used); + } + + #[test] + unconstrained fn tx_consumes_gas_from_l2_l1_msgs() { + let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + + builder.previous_kernel.add_l2_to_l1_message(42, EthAddress::zero()); + builder.previous_kernel.add_l2_to_l1_message(42, EthAddress::zero()); + builder.previous_kernel.end_setup(); + builder.previous_kernel.add_l2_to_l1_message(42, EthAddress::zero()); + let public_inputs = builder.execute(); - let gas_for_first_nullifier = Gas::new(DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, 0); - let expected_non_revertible_gas_consumed = Gas::tx_overhead() + gas_for_first_nullifier; - assert_eq(public_inputs.end_non_revertible.gas_used, expected_non_revertible_gas_consumed); - let expected_revertible_gas_consumed = Gas::new(300, 300); - assert_eq(public_inputs.end.gas_used, expected_revertible_gas_consumed); + assert_eq(Gas::new(1 * DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, 0), public_inputs.end.gas_used); + assert_eq( + Gas::tx_overhead() + Gas::new( + 3 * DA_BYTES_PER_FIELD * DA_GAS_PER_BYTE, + FIXED_AVM_STARTUP_L2_GAS + 1 * L2_GAS_PER_NULLIFIER + ), public_inputs.end_non_revertible.gas_used + ); + } + + #[test] + unconstrained fn tx_consumed_gas_from_logs() { + let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + builder.previous_kernel.add_encrypted_log_hash(42, 3); + builder.previous_kernel.add_encrypted_log_hash(42, 4); + builder.previous_kernel.add_unencrypted_log_hash(42, 5); + builder.previous_kernel.end_setup(); + builder.previous_kernel.add_encrypted_log_hash(42, 6); + builder.previous_kernel.add_unencrypted_log_hash(42, 7); + + let public_inputs = builder.execute(); + + assert_eq(Gas::new(13 * DA_GAS_PER_BYTE, 13 * L2_GAS_PER_LOG_BYTE), public_inputs.end.gas_used); + + assert_eq( + Gas::tx_overhead() + Gas::new( + (1 * DA_BYTES_PER_FIELD + 12) * DA_GAS_PER_BYTE , + FIXED_AVM_STARTUP_L2_GAS + 1 * L2_GAS_PER_NULLIFIER + 12 * L2_GAS_PER_LOG_BYTE + ), public_inputs.end_non_revertible.gas_used + ); } #[test(should_fail_with="The gas used exceeds the gas limits")] diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/meter_gas_used.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/meter_gas_used.nr index 98b998134ee..bab21a8b090 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/meter_gas_used.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/meter_gas_used.nr @@ -1,6 +1,10 @@ use crate::components::tail_to_public_output_composer::meter_gas_used::{meter_gas_used_non_revertible, meter_gas_used_revertible}; use dep::types::{ - abis::gas::Gas, constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE}, + abis::gas::Gas, + constants::{ + DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, FIXED_AVM_STARTUP_L2_GAS, L2_GAS_PER_NOTE_HASH, + L2_GAS_PER_NULLIFIER, L2_GAS_PER_LOG_BYTE +}, tests::fixture_builder::FixtureBuilder }; @@ -36,8 +40,12 @@ fn meter_gas_used_non_revertible_everything_succeeds() { + 2 + 6 // encrypted_log_hash + 51; // unencrypted_log_hash let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; + let computed_l2_gas = 4 * L2_GAS_PER_NOTE_HASH + + 3 * L2_GAS_PER_NULLIFIER + + total_log_length * L2_GAS_PER_LOG_BYTE + + 2 * FIXED_AVM_STARTUP_L2_GAS; - assert_eq(gas, Gas::new(computed_da_gas, 0) + Gas::tx_overhead()); + assert_eq(gas, Gas::new(computed_da_gas, computed_l2_gas) + Gas::tx_overhead()); } #[test] @@ -74,6 +82,10 @@ fn meter_gas_used_revertible_everything_succeeds() { + 2 + 6 // encrypted_log_hash + 51; // unencrypted_log_hash let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; + let computed_l2_gas = 4 * L2_GAS_PER_NOTE_HASH + + 3 * L2_GAS_PER_NULLIFIER + + total_log_length * L2_GAS_PER_LOG_BYTE + + 2 * FIXED_AVM_STARTUP_L2_GAS; - assert_eq(gas, Gas::new(computed_da_gas, 0) + teardown_gas); + assert_eq(gas, Gas::new(computed_da_gas, computed_l2_gas) + teardown_gas); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr index 8f9bb886ae4..ddbc2e36998 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/tail_to_public_output_composer_builder/tail_to_public_output_composer.nr @@ -1,6 +1,10 @@ use crate::tests::tail_to_public_output_composer_builder::TailToPublicOutputComposerBuilder; use dep::types::{ - abis::gas::Gas, constants::{DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE}, + abis::gas::Gas, + constants::{ + DA_BYTES_PER_FIELD, DA_GAS_PER_BYTE, FIXED_AVM_STARTUP_L2_GAS, L2_GAS_PER_NOTE_HASH, + L2_GAS_PER_NULLIFIER, L2_GAS_PER_LOG_BYTE +}, tests::utils::{assert_array_eq, swap_items} }; @@ -132,7 +136,13 @@ fn tail_to_public_output_composer_succeeds() { + 2 // encrypted_log_hash + 51; // unencrypted_log_hash let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; - assert_eq(output.end_non_revertible.gas_used, Gas::new(computed_da_gas, 0) + Gas::tx_overhead()); + let computed_l2_gas = 4 * L2_GAS_PER_NOTE_HASH + + 3 * L2_GAS_PER_NULLIFIER + + total_log_length * L2_GAS_PER_LOG_BYTE + + 2 * FIXED_AVM_STARTUP_L2_GAS; + assert_eq( + output.end_non_revertible.gas_used, Gas::new(computed_da_gas, computed_l2_gas) + Gas::tx_overhead() + ); // Gas: revertible let total_num_side_effects = 2 + 1 + 1; @@ -140,5 +150,9 @@ fn tail_to_public_output_composer_succeeds() { + 6 + 24 // encrypted_log_hash + 4; // unencrypted_log_hash let computed_da_gas = (total_num_side_effects * DA_BYTES_PER_FIELD + total_log_length) * DA_GAS_PER_BYTE; - assert_eq(output.end.gas_used, Gas::new(computed_da_gas, 0) + teardown_gas); + let computed_l2_gas = 2 * L2_GAS_PER_NOTE_HASH + + 1 * L2_GAS_PER_NULLIFIER + + total_log_length * L2_GAS_PER_LOG_BYTE + + 3 * FIXED_AVM_STARTUP_L2_GAS; + assert_eq(output.end.gas_used, Gas::new(computed_da_gas, computed_l2_gas) + teardown_gas); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr index 5b90203eed5..5828ec37193 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/accumulated_data/private_accumulated_data_builder.nr @@ -8,7 +8,8 @@ use crate::{ constants::{ MAX_NOTE_HASHES_PER_TX, MAX_NULLIFIERS_PER_TX, MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, MAX_L2_TO_L1_MSGS_PER_TX, MAX_ENCRYPTED_LOGS_PER_TX, - MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX + MAX_UNENCRYPTED_LOGS_PER_TX, MAX_NOTE_ENCRYPTED_LOGS_PER_TX, DA_GAS_PER_BYTE, L2_GAS_PER_LOG_BYTE, + L2_GAS_PER_NOTE_HASH, L2_GAS_PER_NULLIFIER, FIXED_AVM_STARTUP_L2_GAS }, messaging::l2_to_l1_message::ScopedL2ToL1Message, traits::Empty }; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas.nr index cf2652e7fed..7cc44c36585 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas.nr @@ -1,6 +1,6 @@ use crate::{ abis::function_selector::FunctionSelector, address::{EthAddress, AztecAddress}, - constants::{GAS_LENGTH, FIXED_DA_GAS}, hash::pedersen_hash, + constants::{GAS_LENGTH, FIXED_DA_GAS, FIXED_L2_GAS}, hash::pedersen_hash, traits::{Deserialize, Hash, Serialize, Empty}, abis::side_effect::Ordered, utils::reader::Reader, abis::gas_fees::GasFees }; @@ -17,7 +17,7 @@ impl Gas { } pub fn tx_overhead() -> Self { - Self { da_gas: FIXED_DA_GAS, l2_gas: 0 } + Self { da_gas: FIXED_DA_GAS, l2_gas: FIXED_L2_GAS } } pub fn compute_fee(self, fees: GasFees) -> Field { 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 b830d210c51..716bb056394 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -142,7 +142,19 @@ global DEFAULT_MAX_FEE_PER_GAS: Field = 10; global DEFAULT_INCLUSION_FEE: Field = 0; global DA_BYTES_PER_FIELD: u32 = 32; global DA_GAS_PER_BYTE: u32 = 16; +// pays for preamble information in TX Effects global FIXED_DA_GAS: u32 = 512; +// pays for fixed tx costs like validation, and updating state roots +global FIXED_L2_GAS: u32 = 512; +// base cost for a single public call +global FIXED_AVM_STARTUP_L2_GAS = 1024; +// pays for hashing/validating logs +global L2_GAS_PER_LOG_BYTE = 4; +// pays for tree insertions. +global L2_GAS_PER_NOTE_HASH = 32; +// nullifiers are more expensive to insert/validate: +// they are inserted into an indexed tree and must be checked for duplicates +global L2_GAS_PER_NULLIFIER = 64; // CANONICAL CONTRACT ADDRESSES global CANONICAL_KEY_REGISTRY_ADDRESS = 0x04c2d010f88e8c238882fbbcbce5c81fdc1dc8ece85e8dbf3f602b4d81ec0351; diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index c8bb1f971cd..c24e70488ed 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -84,6 +84,11 @@ export const DEFAULT_INCLUSION_FEE = 0; export const DA_BYTES_PER_FIELD = 32; export const DA_GAS_PER_BYTE = 16; export const FIXED_DA_GAS = 512; +export const FIXED_L2_GAS = 512; +export const FIXED_AVM_STARTUP_L2_GAS = 1024; +export const L2_GAS_PER_LOG_BYTE = 4; +export const L2_GAS_PER_NOTE_HASH = 32; +export const L2_GAS_PER_NULLIFIER = 64; export const CANONICAL_KEY_REGISTRY_ADDRESS = 2153455745675440165069577621832684870696142028027528497509357256345838682961n; export const CANONICAL_AUTH_REGISTRY_ADDRESS = diff --git a/yarn-project/simulator/src/avm/avm_simulator.test.ts b/yarn-project/simulator/src/avm/avm_simulator.test.ts index b8475277283..40be737bcf7 100644 --- a/yarn-project/simulator/src/avm/avm_simulator.test.ts +++ b/yarn-project/simulator/src/avm/avm_simulator.test.ts @@ -13,7 +13,6 @@ import { mock } from 'jest-mock-extended'; import { type PublicSideEffectTraceInterface } from '../public/side_effect_trace_interface.js'; import { type AvmContext } from './avm_context.js'; import { type AvmExecutionEnvironment } from './avm_execution_environment.js'; -import { AvmMachineState } from './avm_machine_state.js'; import { type MemoryValue, TypeTag, type Uint8 } from './avm_memory_types.js'; import { AvmSimulator } from './avm_simulator.js'; import { isAvmBytecode, markBytecodeAsAvm } from './bytecode_utils.js'; @@ -63,7 +62,7 @@ describe('AVM simulator: injected bytecode', () => { it('Should execute bytecode that performs basic addition', async () => { const context = initContext({ env: initExecutionEnvironment({ calldata }) }); - const { l2GasLeft: initialL2GasLeft } = AvmMachineState.fromState(context.machineState); + const { l2Gas: initialL2GasLeft } = context.machineState.gasLeft; const results = await new AvmSimulator(context).executeBytecode(markBytecodeAsAvm(bytecode)); expect(results.reverted).toBe(false);