Skip to content

Commit

Permalink
feat: Wire gas from public execution to kernels (#5941)
Browse files Browse the repository at this point in the history
Sets gas used and transaction fee in the public executor and public
kernels. Implements changes as defined in
#5855 except for
those related to enshrining fee payment (eg `fee_payer`).

Suggested reviewing per-commit.
  • Loading branch information
spalladino authored Apr 24, 2024
1 parent e2842a6 commit 6894fc7
Show file tree
Hide file tree
Showing 37 changed files with 565 additions and 135 deletions.
3 changes: 1 addition & 2 deletions noir-projects/noir-contracts/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ NARGO=${NARGO:-../../noir/noir-repo/target/release/nargo}
$NARGO compile --silence-warnings

echo "Transpiling avm contracts... (only '#[aztec(public-vm)]')"
TRANSPILER=${TRANSPILER:-../../avm-transpiler/target/release/avm-transpiler}
ls target/avm_*.json | parallel "$TRANSPILER {} {}"
scripts/transpile.sh
5 changes: 5 additions & 0 deletions noir-projects/noir-contracts/scripts/transpile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -eu

TRANSPILER=${TRANSPILER:-../../avm-transpiler/target/release/avm-transpiler}
ls target/avm_*.json | parallel "$TRANSPILER {} {}"
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ fn asc_sort_by_counters<T>(a: T, b: T) -> bool where T: Ordered {
a.counter() < b.counter()
}

// Builds:
// .finish -> KernelCircuitPublicInputs (from PrivateKernelTailCircuitPrivateInputs)
// .finish_to_public -> PublicKernelCircuitPublicInputs (from PrivateKernelTailToPublicCircuitPrivateInputs)
struct KernelCircuitPublicInputsComposer {
public_inputs: PrivateKernelCircuitPublicInputsBuilder,
previous_kernel: PrivateKernelData,
Expand All @@ -47,7 +50,7 @@ impl KernelCircuitPublicInputsComposer {
sorted_encrypted_log_hashes: [SideEffect; MAX_ENCRYPTED_LOGS_PER_TX],
sorted_encrypted_log_hashes_indexes: [u64; MAX_ENCRYPTED_LOGS_PER_TX],
sorted_unencrypted_log_hashes: [SideEffect; MAX_UNENCRYPTED_LOGS_PER_TX],
sorted_unencrypted_log_hashes_indexes: [u64; MAX_UNENCRYPTED_LOGS_PER_TX],
sorted_unencrypted_log_hashes_indexes: [u64; MAX_UNENCRYPTED_LOGS_PER_TX]
) -> Self {
let public_inputs = PrivateKernelCircuitPublicInputsBuilder::empty();

Expand All @@ -62,7 +65,7 @@ impl KernelCircuitPublicInputsComposer {
sorted_encrypted_log_hashes,
sorted_encrypted_log_hashes_indexes,
sorted_unencrypted_log_hashes,
sorted_unencrypted_log_hashes_indexes,
sorted_unencrypted_log_hashes_indexes
}
}

Expand All @@ -82,6 +85,8 @@ impl KernelCircuitPublicInputsComposer {

self.silo_values();

self.set_gas_used();

*self
}

Expand All @@ -102,6 +107,12 @@ impl KernelCircuitPublicInputsComposer {
self.public_inputs.finish_to_public(min_revertible_side_effect_counter)
}

fn set_gas_used(&mut self) {
// TODO(gas): Compute DA gas used here and add to public_inputs.(end,end_non_revertible).gas_used
let teardown_gas = self.previous_kernel.public_inputs.constants.tx_context.gas_settings.teardown_gas_limits;
self.public_inputs.end.gas_used = teardown_gas;
}

fn silo_values(&mut self) {
self.silo_note_hashes();
// TODO: Move siloing from init/inner circuits to here.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ impl PrivateKernelInitCircuitPrivateInputs {
// Ensure we are passing the correct arguments to the function.
let args_match = tx_request.args_hash == call_stack_item.public_inputs.args_hash;
assert(args_match, "noir function args passed to tx_request must match args in the call_stack_item");
//
// Ensure we are passing the correct tx context
let tx_context_matches = tx_request.tx_context == call_stack_item.public_inputs.tx_context;
assert(tx_context_matches, "tx_context in tx_request must match tx_context in call_stack_item");
}

fn validate_inputs(self) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl PrivateKernelTailCircuitPrivateInputs {
self.sorted_encrypted_log_hashes,
self.sorted_encrypted_log_hashes_indexes,
self.sorted_unencrypted_log_hashes,
self.sorted_unencrypted_log_hashes_indexes,
self.sorted_unencrypted_log_hashes_indexes
);
composer.compose().finish()
}
Expand All @@ -76,7 +76,7 @@ mod tests {
use dep::types::{
abis::{
kernel_circuit_public_inputs::KernelCircuitPublicInputs, max_block_number::MaxBlockNumber,
side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered}
side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered}, gas::Gas
},
grumpkin_private_key::GrumpkinPrivateKey,
hash::{compute_note_hash_nonce, compute_unique_siloed_note_hash, accumulate_sha256},
Expand Down Expand Up @@ -189,7 +189,7 @@ mod tests {
sorted_encrypted_log_hashes_indexes,
sorted_unencrypted_log_hashes,
sorted_unencrypted_log_hashes_indexes,
master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX],
master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX]
};
kernel.native_private_kernel_circuit_tail()
}
Expand Down Expand Up @@ -488,4 +488,14 @@ mod tests {
builder.previous_kernel.new_nullifiers = BoundedVec::new();
builder.failed();
}

#[test]
unconstrained fn set_teardown_gas_as_gas_used() {
// TODO(gas): When we compute DA gas used, we'll have to include it here as well.
let mut builder = PrivateKernelTailInputsBuilder::new();
builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300, 300);
let public_inputs = builder.execute();

assert_eq(public_inputs.end.gas_used, Gas::new(300, 300, 300));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl PrivateKernelTailToPublicCircuitPrivateInputs {
self.sorted_encrypted_log_hashes,
self.sorted_encrypted_log_hashes_indexes,
self.sorted_unencrypted_log_hashes,
self.sorted_unencrypted_log_hashes_indexes,
self.sorted_unencrypted_log_hashes_indexes
);
composer.compose_public().finish_to_public()
}
Expand All @@ -75,7 +75,7 @@ mod tests {
};
use dep::types::{
abis::{
kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs,
kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, gas::Gas,
side_effect::{SideEffect, SideEffectLinkedToNoteHash, Ordered}
},
grumpkin_private_key::GrumpkinPrivateKey,
Expand Down Expand Up @@ -196,7 +196,7 @@ mod tests {
sorted_encrypted_log_hashes_indexes,
sorted_unencrypted_log_hashes,
sorted_unencrypted_log_hashes_indexes,
master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX],
master_nullifier_secret_keys: [GrumpkinPrivateKey::empty(); MAX_NULLIFIER_KEY_VALIDATION_REQUESTS_PER_TX]
};
kernel.execute()
}
Expand Down Expand Up @@ -538,4 +538,14 @@ mod tests {
assert(is_empty_array(public_inputs.end.new_note_hashes));
assert(is_empty_array(public_inputs.end.new_nullifiers));
}

#[test]
unconstrained fn set_teardown_gas_as_gas_used() {
// TODO(gas): When we compute DA gas used, we'll have to include it here as well.
let mut builder = PrivateKernelTailToPublicInputsBuilder::new();
builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300, 300);
let public_inputs = builder.execute();

assert_eq(public_inputs.end.gas_used, Gas::new(300, 300, 300));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,22 @@ pub fn update_non_revertible_gas_used(public_call: PublicCallData, circuit_outpu
.sub(accum_end_gas_used);
}

// Validates that the start gas injected into the app circuit matches the remaining gas
pub fn validate_start_gas(public_call: PublicCallData, previous_kernel: PublicKernelData) {
let public_call_start_gas = public_call.call_stack_item.public_inputs.start_gas_left;
let tx_gas_limits = previous_kernel.public_inputs.constants.tx_context.gas_settings.gas_limits;
let computed_start_gas = tx_gas_limits.sub(previous_kernel.public_inputs.end.gas_used).sub(previous_kernel.public_inputs.end_non_revertible.gas_used);
assert(
public_call_start_gas == computed_start_gas, "Start gas for public phase does not match transaction gas left"
);
}

// Validates the transaction fee injected into the app circuit is zero for non-teardown phases
pub fn validate_transaction_fee_is_zero(public_call: PublicCallData) {
let transaction_fee = public_call.call_stack_item.public_inputs.transaction_fee;
assert(transaction_fee == 0, "Transaction fee must be zero on setup and app phases");
}

pub fn update_public_end_non_revertible_values(
public_call: PublicCallData,
circuit_outputs: &mut PublicKernelCircuitPublicInputsBuilder
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ impl PublicKernelAppLogicCircuitPrivateInputs {
// validate the inputs unique to having a previous public kernel
self.validate_inputs();

common::validate_start_gas(self.public_call, self.previous_kernel);
common::validate_transaction_fee_is_zero(self.public_call);

common::update_validation_requests(self.public_call, &mut public_inputs);

common::update_revertible_gas_used(self.public_call, &mut public_inputs);
Expand Down Expand Up @@ -464,4 +467,22 @@ mod tests {
assert_eq(output.end.gas_used, Gas::new(500, 500, 500));
assert_eq(output.end_non_revertible.gas_used, Gas::new(0, 0, 0));
}

#[test(should_fail_with="Start gas for public phase does not match transaction gas left")]
fn validates_start_gas() {
let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new();

builder.public_call.public_inputs.start_gas_left = Gas::new(200, 100, 100);

builder.failed();
}

#[test(should_fail_with="Transaction fee must be zero on setup and app phases")]
fn validates_transaction_fee() {
let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new();

builder.public_call.public_inputs.transaction_fee = 10;

builder.failed();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ impl PublicKernelSetupCircuitPrivateInputs {
// validate the inputs unique to having a previous private kernel
self.validate_inputs();

common::validate_start_gas(self.public_call, self.previous_kernel);
common::validate_transaction_fee_is_zero(self.public_call);

common::update_non_revertible_gas_used(self.public_call, &mut public_inputs);

// Pops the item from the call stack and validates it against the current execution.
Expand Down Expand Up @@ -519,4 +522,22 @@ mod tests {
assert_eq(output.end_non_revertible.gas_used, Gas::new(500, 500, 500));
assert_eq(output.end.gas_used, Gas::new(100, 100, 100));
}

#[test(should_fail_with="Start gas for public phase does not match transaction gas left")]
fn validates_start_gas() {
let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new();

builder.public_call.public_inputs.start_gas_left = Gas::new(200, 100, 100);

builder.failed();
}

#[test(should_fail_with="Transaction fee must be zero on setup and app phases")]
fn validates_transaction_fee() {
let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new();

builder.public_call.public_inputs.transaction_fee = 10;

builder.failed();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::common;
use dep::types::abis::{
kernel_circuit_public_inputs::{PublicKernelCircuitPublicInputs, PublicKernelCircuitPublicInputsBuilder},
kernel_data::PublicKernelData, public_call_data::PublicCallData
kernel_data::PublicKernelData, public_call_data::PublicCallData, gas_fees::GasFees
};

struct PublicKernelTeardownCircuitPrivateInputs {
Expand All @@ -22,6 +22,45 @@ impl PublicKernelTeardownCircuitPrivateInputs {
assert(needs_teardown == true, "Cannot run unnecessary teardown circuit");
}

// Validates that the start gas injected into the app circuit matches the teardown gas limits set by the user
fn validate_start_gas(self) {
let public_call_start_gas = self.public_call.call_stack_item.public_inputs.start_gas_left;
let teardown_gas_limit = self.previous_kernel.public_inputs.constants.tx_context.gas_settings.teardown_gas_limits;
assert(
public_call_start_gas == teardown_gas_limit, "Start gas for teardown phase does not match teardown gas allocation"
);
}

// Validates the transaction fee injected into the app circuit is properly computed from gas_used and block gas_fees
fn validate_transaction_fee(self) {
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 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;

// dep::types::debug_log::debug_log_format(
// "Validating tx fee: total_gas_used.da={0} total_gas_used.l1={1} total_gas_used.l2={2} block_fee_per_gas.da={3} block_fee_per_gas.l1={4} block_fee_per_gas.l2={5} inclusion_fee={6} computed={7} actual={8}",
// [
// total_gas_used.da_gas as Field,
// total_gas_used.l1_gas as Field,
// total_gas_used.l2_gas as Field,
// block_gas_fees.fee_per_da_gas as Field,
// block_gas_fees.fee_per_l1_gas as Field,
// block_gas_fees.fee_per_l2_gas as Field,
// inclusion_fee,
// computed_transaction_fee,
// transaction_fee
// ]
// );

assert(
transaction_fee == computed_transaction_fee, "Transaction fee on teardown phase does not match expected value"
);
}

fn public_kernel_teardown(self) -> PublicKernelCircuitPublicInputs {
// construct the circuit outputs
let mut public_inputs = PublicKernelCircuitPublicInputsBuilder::empty();
Expand All @@ -42,6 +81,9 @@ impl PublicKernelTeardownCircuitPrivateInputs {
let call_request = public_inputs.end_non_revertible.public_call_stack.pop();
common::validate_call_against_request(self.public_call, call_request);

self.validate_start_gas();
self.validate_transaction_fee();

common::update_validation_requests(self.public_call, &mut public_inputs);

common::update_public_end_non_revertible_values(self.public_call, &mut public_inputs);
Expand All @@ -60,7 +102,7 @@ mod tests {
};
use dep::types::{
abis::{
call_request::CallRequest, function_selector::FunctionSelector,
call_request::CallRequest, function_selector::FunctionSelector, gas::Gas,
kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs, public_data_read::PublicDataRead,
public_data_update_request::PublicDataUpdateRequest
},
Expand Down Expand Up @@ -347,12 +389,16 @@ mod tests {

let public_inputs = builder.execute();

assert_eq(public_inputs.end_non_revertible.encrypted_log_preimages_length, prev_encrypted_log_preimages_length);
assert_eq(
public_inputs.end_non_revertible.encrypted_log_preimages_length, prev_encrypted_log_preimages_length
);
assert_eq(
public_inputs.end_non_revertible.unencrypted_log_preimages_length, unencrypted_log_preimages_length + prev_unencrypted_log_preimages_length
);
assert_eq(public_inputs.end_non_revertible.encrypted_logs_hashes[0].value, prev_encrypted_logs_hash);
assert_eq(public_inputs.end_non_revertible.unencrypted_logs_hashes[0].value, prev_unencrypted_logs_hash);
assert_eq(
public_inputs.end_non_revertible.unencrypted_logs_hashes[0].value, prev_unencrypted_logs_hash
);
assert_eq(public_inputs.end_non_revertible.unencrypted_logs_hashes[1].value, unencrypted_logs_hash);
}

Expand All @@ -363,4 +409,20 @@ mod tests {

builder.failed();
}

#[test(should_fail_with="Start gas for teardown phase does not match teardown gas allocation")]
fn validates_start_gas() {
let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new();
builder.public_call.public_inputs.start_gas_left = Gas::new(10, 20, 30);

builder.failed();
}

#[test(should_fail_with="Transaction fee on teardown phase does not match expected value")]
fn validates_transaction_fee() {
let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new();
builder.public_call.public_inputs.transaction_fee = 1234;

builder.failed();
}
}
Loading

0 comments on commit 6894fc7

Please sign in to comment.