Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: charge for more l2 gas costs #7157

Merged
merged 2 commits into from
Jul 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions docs/docs/protocol-specs/gas-and-fees/fee-schedule.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,30 +52,33 @@ 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.

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

Expand Down
5 changes: 5 additions & 0 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down Expand Up @@ -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()
);
}

Expand Down Expand Up @@ -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()
);
Expand Down Expand Up @@ -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")]
Expand Down
Loading
Loading