Skip to content

Commit

Permalink
charge l2 gas for:
Browse files Browse the repository at this point in the history
- avm startup
- note hashes in private
- nullifiers in private
- logs in private
  • Loading branch information
just-mitch committed Jul 2, 2024
1 parent d127338 commit 3c2ee6c
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 29 deletions.
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 @@ -44,7 +44,7 @@ mod tests {
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_new_note_hashes(2);
builder.previous_kernel.append_new_nullifiers(2);
builder.previous_kernel.add_note_encrypted_log_hash(
42,
5,
builder.previous_kernel.new_note_hashes.get(0).note_hash.counter
);
let public_inputs = builder.execute();
assert_eq(array_length(public_inputs.end.new_note_hashes), 2);
assert_eq(array_length(public_inputs.end.new_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 @@ -195,9 +195,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 +262,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 +304,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 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);
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();

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

0 comments on commit 3c2ee6c

Please sign in to comment.