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 a7120a50612..05c139b48a6 100644 --- a/docs/docs/protocol-specs/gas-and-fees/fee-schedule.md +++ b/docs/docs/protocol-specs/gas-and-fees/fee-schedule.md @@ -2,7 +2,7 @@ The [transaction fee](./specifying-gas-fee-info.md#transaction-fee) is comprised of a DA component, an L2 component, and an inclusion fee. The DA and L2 components are calculated by multiplying the gas consumed in each dimension by the respective `feePerGas` value. The inclusion fee is a fixed cost associated with the transaction, which is used to cover the cost of verifying the encompassing rollup proof on L1. -# DA Gas +## DA Gas DA gas is consumed to cover the costs associated with publishing data associated with a transaction. @@ -48,7 +48,7 @@ da_gas_used = FIXED_DA_GAS + A side effect of the above calculation is that all transactions will have a non-zero `transaction_fee`. ::: -# L2 Gas +## L2 Gas 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. @@ -77,7 +77,7 @@ In the current implementation, private execution does not consume L2 gas. This w - possibly emitting logs (due to validation checks) ::: -# Max Inclusion Fee +## Max Inclusion Fee Each transaction, and each block, has inescapable overhead costs associated with it which are not directly related to the amount of data or computation performed. diff --git a/docs/docs/protocol-specs/gas-and-fees/kernel-tracking.md b/docs/docs/protocol-specs/gas-and-fees/kernel-tracking.md index 0ed89caf481..646e7023275 100644 --- a/docs/docs/protocol-specs/gas-and-fees/kernel-tracking.md +++ b/docs/docs/protocol-specs/gas-and-fees/kernel-tracking.md @@ -6,7 +6,7 @@ title: Kernel Tracking Gas and fees are tracked throughout the kernel circuits to ensure that users are charged correctly for their transactions. -# Private Kernel Circuits Overview +## Private Kernel Circuits Overview On the private side, the ordering of the circuits is: @@ -213,7 +213,7 @@ It must: - set the `public_teardown_call_request` in the `PublicKernelCircuitPublicInputs` - copy the constants from the `PrivateKernelData` to the `PublicKernelCircuitPublicInputs.constants` -# Mempool/Node Validation +## Mempool/Node Validation A `Tx` broadcasted to the network has: @@ -260,7 +260,7 @@ When a node receives a transaction, it must check that: See other [validity conditions](../transactions/validity.md). -# Public Kernel Circuits +## Public Kernel Circuits On the public side, the order of the circuits is: @@ -538,7 +538,7 @@ The interplay between these two `revert_code`s is as follows: | 1 | 1 | 3 | | 2 or 3 | (any) | (unchanged) | -# Base Rollup Kernel Circuit +## Base Rollup Kernel Circuit The base rollup kernel circuit takes in a `KernelData`, which contains a `KernelCircuitPublicInputs`, which it uses to compute the `transaction_fee`. diff --git a/docs/docs/protocol-specs/gas-and-fees/published-gas-and-fee-data.md b/docs/docs/protocol-specs/gas-and-fees/published-gas-and-fee-data.md index f46f8085622..fbdc0899b49 100644 --- a/docs/docs/protocol-specs/gas-and-fees/published-gas-and-fee-data.md +++ b/docs/docs/protocol-specs/gas-and-fees/published-gas-and-fee-data.md @@ -6,7 +6,7 @@ title: Published Gas & Fee Data When a block is published to L1, it includes information about the gas and fees at a block-level, and at a transaction-level. -# Block-level Data +## Block-level Data The block header contains a `GlobalVariables`, which contains a `GasFees` object. This object contains the following fields: - `feePerDaGas`: The fee in [FPA](./fee-payment-asset.md) per unit of DA gas consumed for transactions in the block. @@ -22,7 +22,7 @@ In the future, these values may be updated dynamically based on network conditio Should we move to a 1559-style fee market with block-level gas targets, there is an interesting point where gas "used" presently includes the entire [`teardown_gas_allocation`](./specifying-gas-fee-info.md) regardless of how much of that allocation was spent. In the future, if this becomes a concern, we can update our accounting to reflect the true gas used for the purposes of updating the `GasFees` object, though the user will be charged the full `teardown_gas_allocation` regardless. ::: -# Transaction-level Data +## Transaction-level Data The transaction data which is published to L1 is a `TxEffects` object, which includes - `transaction_fee`: the fee paid by the transaction in FPA diff --git a/docs/docs/protocol-specs/gas-and-fees/specifying-gas-fee-info.md b/docs/docs/protocol-specs/gas-and-fees/specifying-gas-fee-info.md index fca9c67c86f..55db5a1bba3 100644 --- a/docs/docs/protocol-specs/gas-and-fees/specifying-gas-fee-info.md +++ b/docs/docs/protocol-specs/gas-and-fees/specifying-gas-fee-info.md @@ -44,7 +44,7 @@ GasSettings --> GasFees All fees are denominated in the [Fee Payment Asset (FPA)](./fee-payment-asset.md). ::: -# Gas Dimensions and Max Inclusion Fee +## Gas Dimensions and Max Inclusion Fee Transactions are metered for their gas consumption across two dimensions: @@ -58,7 +58,7 @@ Separately, every transaction has overhead costs associated with it, e.g. verify See the [Fee Schedule](./fee-schedule.md) for a detailed breakdown of costs associated with different actions. -# `gasLimits` and `teardownGasLimits` +## `gasLimits` and `teardownGasLimits` Transactions can optionally have a "teardown" phase as part of their public execution, during which the "transaction fee" is available to public functions. This is useful to transactions/contracts that need to compute a "refund", e.g. contracts that facilitate [fee abstraction](./tx-setup-and-teardown.md). @@ -66,7 +66,7 @@ Because the transaction fee must be known at the time teardown is executed, tran For example, if a transaction has `gasLimits` of 1000 DA gas and 2000 L2 gas, and `teardownGasLimits` of 100 DA gas and 200 L2 gas, then the transaction will be able to consume 900 DA gas and 1800 L2 gas during the main execution phase, but 100 DA gas and 200 L2 gas **will be consumed** to cover the teardown phase: even if teardown does not consume that much gas, the transaction will still be charged for it; even if the transaction does not have a teardown phase, the gas will still be consumed. -# `maxFeesPerGas` and `feePerGas` +## `maxFeesPerGas` and `feePerGas` The `maxFeesPerGas` field specifies the maximum fees that the user is willing to pay per gas unit consumed in each dimension. @@ -97,7 +97,7 @@ A transaction cannot be executed if the `maxFeesPerGas` is less than the `feePer The `feePerGas` is presently held constant at `1` for both dimensions, but may be updated in future protocol versions. -# Transaction Fee +## Transaction Fee The transaction fee is calculated as: @@ -111,7 +111,7 @@ Why is the "max" inclusion fee charged? We're working on a mechanism that will a See more on how the "gas consumed" values are calculated in the [Fee Schedule](./fee-schedule.md). -# Maximum Transaction Fee +## Maximum Transaction Fee The final transaction fee cannot be calculated until all public function execution is complete. However, a maximum theoretical fee can be calculated as: @@ -121,7 +121,7 @@ maxTransactionFee = maxInclusionFee + (gasLimits.daGas * maxFeesPerDaGas) + (gas This is useful for imposing [validity conditions](./kernel-tracking.md#mempoolnode-validation). -# `fee_payer` +## `fee_payer` The `fee_payer` is the entity that pays the transaction fee. @@ -129,7 +129,7 @@ It is effectively set in private by the contract that calls `context.set_as_fee_ This manifests as a boolean flag `is_fee_payer` in the `PrivateCircuitPublicInputs`. The private kernel circuits will check this flag for every call stack item. -When a call stack item is found with `is_fee_payer` set, the kernel circuit will set `fee_payer` in its `PrivateKernelCircuitPublicInputs` to be the `callStackItem.contractAddress`. +When a call stack item is found with `is_fee_payer` set, the kernel circuit will set `fee_payer` in its `PrivateKernelCircuitPublicInputs` to be the `callContext.storageContractAddress`. This is subsequently passed through the `PublicKernelCircuitPublicInputs` to the `KernelCircuitPublicInputs`. diff --git a/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md b/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md index 08021198b37..8a2779c6188 100644 --- a/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md +++ b/docs/docs/protocol-specs/gas-and-fees/tx-setup-and-teardown.md @@ -31,7 +31,7 @@ The public teardown phase is the only phase where the final transaction fee is a In the base rollup, the kernel circuit injects a public data write that levies the transaction fee on the `fee_payer`. -# An example: Fee Abstraction +## An example: Fee Abstraction Consider a user, Alice, who does not have FPA but wishes to interact with the network. Suppose she has a private balance of a fictitious asset "BananaCoin" that supports public and private balances. @@ -58,11 +58,11 @@ Suppose there is a Fee Payment Contract (FPC) that has been deployed by another This illustrates the utility of the various phases. In particular, we see why the setup phase must not be revertible: if Alice's public app logic fails, the FPC is still going to pay the fee in the base rollup; if public setup were revertible, the transfer of Alice's BananaCoin would revert so the FPC would be losing money. -# Sequencer Whitelisting +## Sequencer Whitelisting Because a transaction is invalid if it fails in the public setup phase, sequencers are taking a risk by processing them. To mitigate this risk, it is expected that sequencers will only process transactions that use public functions that they have whitelisted. -# Defining Setup +## Defining Setup The private function that is executed first is referred to as the "entrypoint". @@ -76,7 +76,7 @@ Execution of the entrypoint is always verified/processed by the `PrivateKernelIn It is only the `PrivateKernelInit` circuit that looks at the `min_revertible_side_effect_counter` as reported by `PrivateCirclePublicInputs`, and thus it is only the entrypoint that can effectively call `context.end_setup()`. -# Defining Teardown +## Defining Teardown At any point during private execution, a contract may call `context.set_public_teardown_function` to specify a public function that will be called during the public teardown phase. This function takes the same arguments as `context.call_public_function`, but does not have a side effect counter associated with it. @@ -84,7 +84,7 @@ Similar to `call_public_function`, this results in the hash of a `PublicCallStac The private kernel circuits will verify that this hash is set at most once. -# Interpreting the `min_revertible_side_effect_counter` +## Interpreting the `min_revertible_side_effect_counter` Notes, nullifiers, and logs are examples of side effects that are partitioned into setup and app logic. diff --git a/l1-contracts/src/core/libraries/ConstantsGen.sol b/l1-contracts/src/core/libraries/ConstantsGen.sol index 58354ffb7c5..ff662654f9e 100644 --- a/l1-contracts/src/core/libraries/ConstantsGen.sol +++ b/l1-contracts/src/core/libraries/ConstantsGen.sol @@ -135,7 +135,7 @@ library Constants { uint256 internal constant TX_REQUEST_LENGTH = 2 + TX_CONTEXT_LENGTH + FUNCTION_DATA_LENGTH; uint256 internal constant HEADER_LENGTH = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + CONTENT_COMMITMENT_LENGTH + STATE_REFERENCE_LENGTH + GLOBAL_VARIABLES_LENGTH; - uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = CALL_CONTEXT_LENGTH + 3 + uint256 internal constant PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = CALL_CONTEXT_LENGTH + 4 + MAX_BLOCK_NUMBER_LENGTH + (READ_REQUEST_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) 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 f18b33f0f11..a2be4ac607a 100644 --- a/noir-projects/aztec-nr/aztec/src/context/private_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/private_context.nr @@ -40,6 +40,7 @@ struct PrivateContext { side_effect_counter: u32, min_revertible_side_effect_counter: u32, + is_fee_payer: bool, args_hash: Field, return_hash: Field, @@ -115,6 +116,7 @@ impl PrivateContext { inputs, side_effect_counter, min_revertible_side_effect_counter, + is_fee_payer: false, args_hash, return_hash: 0, max_block_number: MaxBlockNumber::empty(), @@ -159,6 +161,7 @@ impl PrivateContext { args_hash: self.args_hash, returns_hash: self.return_hash, min_revertible_side_effect_counter: self.min_revertible_side_effect_counter, + is_fee_payer: self.is_fee_payer, max_block_number: self.max_block_number, note_hash_read_requests: self.note_hash_read_requests.storage, nullifier_read_requests: self.nullifier_read_requests.storage, @@ -608,6 +611,7 @@ impl Empty for PrivateContext { inputs: PrivateContextInputs::empty(), side_effect_counter: 0 as u32, min_revertible_side_effect_counter: 0 as u32, + is_fee_payer: false, args_hash: 0, return_hash: 0, max_block_number: MaxBlockNumber::empty(), diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr index 2dec1046f68..18fb6cce881 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/kernel_circuit_public_inputs_composer.nr @@ -86,6 +86,8 @@ impl KernelCircuitPublicInputsComposer { self.propagate_sorted_arrays(); + self.propagate_fee_payer(); + // TODO: Should be done in a reset circuit. self.squash_transient_data(); @@ -230,6 +232,10 @@ impl KernelCircuitPublicInputsComposer { self.public_inputs.public_teardown_call_request = self.previous_kernel.public_inputs.public_teardown_call_request; } + fn propagate_fee_payer(&mut self) { + self.public_inputs.fee_payer = self.previous_kernel.public_inputs.fee_payer; + } + fn squash_transient_data(&mut self) { verify_squashed_transient_note_hashes_and_nullifiers( self.public_inputs.end.new_note_hashes.storage, diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr index b7f5dc96d85..7073fad466d 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_circuit_public_inputs_composer.nr @@ -52,6 +52,8 @@ impl PrivateKernelCircuitPublicInputsComposer { public_inputs.constants = previous_kernel_public_inputs.constants; public_inputs.min_revertible_side_effect_counter = previous_kernel_public_inputs.min_revertible_side_effect_counter; + public_inputs.fee_payer = previous_kernel_public_inputs.fee_payer; + public_inputs.public_teardown_call_request = previous_kernel_public_inputs.public_teardown_call_request; let start = previous_kernel_public_inputs.validation_requests; public_inputs.validation_requests.max_block_number = start.for_rollup.max_block_number; @@ -71,8 +73,6 @@ impl PrivateKernelCircuitPublicInputsComposer { let _call_request = public_inputs.end.private_call_stack.pop(); public_inputs.end.public_call_stack = array_to_bounded_vec(start.public_call_stack); - public_inputs.public_teardown_call_request = previous_kernel_public_inputs.public_teardown_call_request; - PrivateKernelCircuitPublicInputsComposer { public_inputs } } @@ -105,6 +105,7 @@ impl PrivateKernelCircuitPublicInputsComposer { self.propagate_private_call_requests(source); self.propagate_public_call_requests(source); self.propagate_public_teardown_call_request(source); + self.propagate_fee_payer(source); *self } @@ -220,4 +221,11 @@ impl PrivateKernelCircuitPublicInputsComposer { self.public_inputs.public_teardown_call_request = call_request; } } + + fn propagate_fee_payer(&mut self, source: DataSource) { + if (source.private_call_public_inputs.is_fee_payer) { + assert(self.public_inputs.fee_payer.is_zero(), "Cannot overwrite non-empty fee_payer"); + self.public_inputs.fee_payer = source.storage_contract_address; + } + } } 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 da6ef0cc4ba..2e76640a611 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 @@ -204,4 +204,20 @@ mod tests { request.contract_address, builder.private_call.public_inputs.call_context.storage_contract_address ); } + + #[test] + unconstrained fn propagate_fee_payer() { + let mut builder = PrivateKernelInitInputsBuilder::new(); + let fee_payer = builder.private_call.public_inputs.call_context.storage_contract_address; + builder.private_call.public_inputs.is_fee_payer = true; + + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer is not set if is_fee_payer is false + let mut builder = PrivateKernelInitInputsBuilder::new(); + assert_eq(builder.private_call.public_inputs.is_fee_payer, false); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr index 20376e9b83a..2ac2e0f2e97 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/private_kernel_inner.nr @@ -248,4 +248,37 @@ mod tests { assert_eq(public_inputs.end.encrypted_logs_hashes[1].value, encrypted_logs_hash); assert_eq(public_inputs.end.unencrypted_logs_hashes[1].value, unencrypted_logs_hash); } + + #[test] + unconstrained fn propagate_fee_payer() { + let mut builder = PrivateKernelInnerInputsBuilder::new(); + let fee_payer = builder.private_call.public_inputs.call_context.storage_contract_address; + builder.private_call.public_inputs.is_fee_payer = true; + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer is not set if is_fee_payer is false + let mut builder = PrivateKernelInnerInputsBuilder::new(); + assert_eq(builder.private_call.public_inputs.is_fee_payer, false); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + + // Check that we carry forward if the fee payer is already set + let mut builder = PrivateKernelInnerInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.fee_payer = fee_payer; + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + } + + #[test(should_fail_with="Cannot overwrite non-empty fee_payer")] + unconstrained fn does_not_overwrite_fee_payer() { + let mut builder = PrivateKernelInnerInputsBuilder::new(); + let original_fee_payer = AztecAddress::from_field(123); + + builder.private_call.public_inputs.is_fee_payer = true; + builder.previous_kernel.fee_payer = original_fee_payer; + + builder.failed(); + } } 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 fabb95e9911..b04669d7e36 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 @@ -106,7 +106,7 @@ mod tests { note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, side_effect::SideEffect, gas::Gas }, - grumpkin_private_key::GrumpkinPrivateKey, + address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, hash::{compute_note_hash_nonce, compute_unique_note_hash, sha256_to_field, silo_note_hash, silo_nullifier}, tests::{fixture_builder::FixtureBuilder, sort::sort_get_sorted_hints}, utils::{arrays::{array_eq, array_length}}, traits::{Empty, is_empty, is_empty_array} @@ -276,7 +276,7 @@ mod tests { } #[test] - fn propagate_previous_kernel_max_block_number() { + unconstrained fn propagate_previous_kernel_max_block_number() { let mut builder = PrivateKernelTailInputsBuilder::new(); builder.previous_kernel.max_block_number = MaxBlockNumber::new(13); let public_inputs = builder.execute(); @@ -285,7 +285,7 @@ mod tests { } #[test] - fn logs_are_handled_as_expected() { + unconstrained fn logs_are_handled_as_expected() { let mut builder = PrivateKernelTailInputsBuilder::new(); // Logs for the previous call stack. let prev_encrypted_logs_hash = 80; @@ -587,9 +587,13 @@ mod tests { builder.previous_kernel.tx_context.gas_settings.teardown_gas_limits = Gas::new(300, 300); let public_inputs = builder.execute(); - let expected_gas_consumed = Gas::new(300, 300) // teardown gas - + Gas::tx_overhead() // tx overhead - + Gas::new(DA_GAS_PER_BYTE * DA_BYTES_PER_FIELD * 1, 0); // tx nullifier + // 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); assert_eq(public_inputs.end.gas_used, expected_gas_consumed); } @@ -600,4 +604,19 @@ mod tests { builder.previous_kernel.tx_context.gas_settings.gas_limits = Gas::new(1, 1); builder.failed(); } + + #[test] + unconstrained fn propagate_fee_payer() { + // Check that we carry forward if the fee payer is already set + let mut builder = PrivateKernelTailInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.fee_payer = fee_payer; + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer remains empty if unset + let mut builder = PrivateKernelTailInputsBuilder::new(); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } } 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 fa73aaf18ae..27490b7e12b 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 @@ -108,7 +108,7 @@ mod tests { note_hash::{NoteHash, ScopedNoteHash}, nullifier::{Nullifier, ScopedNullifier}, side_effect::SideEffect }, - grumpkin_private_key::GrumpkinPrivateKey, + address::AztecAddress, grumpkin_private_key::GrumpkinPrivateKey, hash::{compute_note_hash_nonce, compute_unique_note_hash, silo_note_hash, silo_nullifier}, tests::{fixture_builder::FixtureBuilder, sort::sort_get_sorted_hints}, utils::{arrays::{array_eq, array_length}}, traits::is_empty_array @@ -586,4 +586,19 @@ mod tests { builder.previous_kernel.tx_context.gas_settings.gas_limits = Gas::new(1, 1); builder.failed(); } + + #[test] + unconstrained fn propagate_fee_payer() { + // Check that we carry forward if the fee payer is already set + let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.fee_payer = fee_payer; + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer remains empty if unset + let mut builder = PrivateKernelTailToPublicInputsBuilder::new(); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } } 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 83dfd47ef6d..3f9f12ef6a6 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 @@ -85,6 +85,7 @@ pub fn initialize_emitted_end_values( circuit_outputs: &mut PublicKernelCircuitPublicInputsBuilder ) { circuit_outputs.constants = previous_kernel.public_inputs.constants; + circuit_outputs.fee_payer = previous_kernel.public_inputs.fee_payer; if circuit_outputs.revert_code == 0 { let start = previous_kernel.public_inputs.end; 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 9eb7eb4a126..044ba1727a6 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 @@ -87,7 +87,7 @@ mod tests { hash::{compute_l2_to_l1_hash, silo_note_hash, silo_nullifier}, messaging::l2_to_l1_message::L2ToL1Message, tests::{fixture_builder::FixtureBuilder, public_call_data_builder::PublicCallDataBuilder}, - utils::arrays::{array_eq, array_length} + utils::arrays::{array_eq, array_length}, traits::is_empty }; use dep::types::constants::{MAX_PUBLIC_DATA_READS_PER_CALL, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_CALL}; @@ -510,4 +510,19 @@ mod tests { builder.failed(); } + + #[test] + unconstrained fn propagate_fee_payer() { + let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.set_fee_payer(fee_payer); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer is not set if is_fee_payer is false + let mut builder = PublicKernelAppLogicCircuitPrivateInputsBuilder::new(); + assert_eq(is_empty(builder.previous_kernel.fee_payer), true); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } } 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 bd90b42fac0..0b5031f3f99 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 @@ -79,7 +79,7 @@ mod tests { address::{AztecAddress, EthAddress}, contract_class_id::ContractClassId, contrakt::storage_read::StorageRead, tests::{fixture_builder::FixtureBuilder, public_call_data_builder::PublicCallDataBuilder}, - utils::{arrays::{array_eq, array_length}} + utils::{arrays::{array_eq, array_length}}, traits::is_empty }; use dep::types::constants::{ MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, @@ -548,4 +548,19 @@ mod tests { builder.failed(); } + + #[test] + unconstrained fn propagate_fee_payer() { + let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.set_fee_payer(fee_payer); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer is not set if is_fee_payer is false + let mut builder = PublicKernelSetupCircuitPrivateInputsBuilder::new(); + assert_eq(is_empty(builder.previous_kernel.fee_payer), true); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr index e92e4c791a4..e6c2fa5cc68 100644 --- a/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr +++ b/noir-projects/noir-protocol-circuits/crates/public-kernel-lib/src/public_kernel_tail.nr @@ -96,7 +96,8 @@ impl PublicKernelTailCircuitPrivateInputs { end, constants: previous_public_inputs.constants, start_state: self.start_state, - revert_code: previous_public_inputs.revert_code + revert_code: previous_public_inputs.revert_code, + fee_payer: previous_public_inputs.fee_payer } } } @@ -116,6 +117,7 @@ mod tests { kernel_circuit_public_inputs::KernelCircuitPublicInputs, kernel_data::PublicKernelData, nullifier::ScopedNullifier, nullifier_leaf_preimage::NullifierLeafPreimage }, + address::AztecAddress, constants::{ MAX_NEW_NULLIFIERS_PER_TX, MAX_NULLIFIER_READ_REQUESTS_PER_TX, MAX_PUBLIC_DATA_HINTS, MAX_PUBLIC_DATA_READS_PER_TX, MAX_PUBLIC_DATA_UPDATE_REQUESTS_PER_TX, NULLIFIER_TREE_HEIGHT, @@ -126,7 +128,7 @@ mod tests { hash::{silo_nullifier, sha256_to_field}, public_data_tree_leaf_preimage::PublicDataTreeLeafPreimage, tests::{fixture_builder::FixtureBuilder, merkle_tree_utils::NonEmptyMerkleTree}, - partial_state_reference::PartialStateReference, utils::arrays::array_merge, + traits::is_empty, partial_state_reference::PartialStateReference, utils::arrays::array_merge, merkle_tree::MembershipWitness }; @@ -541,4 +543,19 @@ mod tests { builder.failed(); } + + #[test] + unconstrained fn propagate_fee_payer() { + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.set_fee_payer(fee_payer); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer is not set if is_fee_payer is false + let mut builder = PublicKernelTailCircuitPrivateInputsBuilder::new(); + assert_eq(is_empty(builder.previous_kernel.fee_payer), true); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } } 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 981d431d4a7..88657fd1ce3 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 @@ -120,7 +120,7 @@ mod tests { address::{AztecAddress, EthAddress}, contract_class_id::ContractClassId, contrakt::storage_read::StorageRead, tests::{fixture_builder::FixtureBuilder, public_call_data_builder::PublicCallDataBuilder}, - utils::{arrays::{array_eq, array_length}} + utils::{arrays::{array_eq, array_length}}, traits::is_empty }; use dep::types::constants::{ MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL, MAX_PUBLIC_DATA_READS_PER_CALL, @@ -459,4 +459,19 @@ mod tests { builder.failed(); } + + #[test] + unconstrained fn propagate_fee_payer() { + let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); + let fee_payer = AztecAddress::from_field(123); + builder.previous_kernel.set_fee_payer(fee_payer); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, fee_payer); + + // Check that the fee payer is not set if is_fee_payer is false + let mut builder = PublicKernelTeardownCircuitPrivateInputsBuilder::new(); + assert_eq(is_empty(builder.previous_kernel.fee_payer), true); + let public_inputs = builder.execute(); + assert_eq(public_inputs.fee_payer, AztecAddress::empty()); + } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/kernel_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/kernel_circuit_public_inputs.nr index 5256f275a3e..57d14ffbab9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/kernel_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/kernel_circuit_public_inputs.nr @@ -3,7 +3,7 @@ use crate::{ accumulated_data::CombinedAccumulatedData, combined_constant_data::CombinedConstantData, validation_requests::RollupValidationRequests, gas::Gas }, - partial_state_reference::PartialStateReference, traits::Empty + address::AztecAddress, partial_state_reference::PartialStateReference, traits::Empty }; use crate::mocked::AggregationObject; @@ -13,6 +13,7 @@ struct KernelCircuitPublicInputs { constants: CombinedConstantData, start_state: PartialStateReference, revert_code: u8, + fee_payer: AztecAddress } impl KernelCircuitPublicInputs { @@ -32,6 +33,7 @@ impl Empty for KernelCircuitPublicInputs { constants: CombinedConstantData::empty(), start_state: PartialStateReference::empty(), revert_code: 0, + fee_payer: AztecAddress::empty() } } } @@ -42,6 +44,7 @@ mod tests { accumulated_data::CombinedAccumulatedData, combined_constant_data::CombinedConstantData, validation_requests::RollupValidationRequests, gas::Gas, gas_fees::GasFees }; + use crate::address::AztecAddress; use crate::partial_state_reference::PartialStateReference; use crate::mocked::AggregationObject; @@ -58,7 +61,8 @@ mod tests { end: CombinedAccumulatedData::empty(), constants: CombinedConstantData::empty(), start_state: PartialStateReference::empty(), - revert_code: 0 + revert_code: 0, + fee_payer: AztecAddress::empty() }; inputs.end.gas_used = Gas { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs.nr index e7aee9e9d52..0acec33c99c 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs.nr @@ -3,7 +3,8 @@ use crate::abis::{ validation_requests::ValidationRequests, call_request::CallRequest }; use crate::constants::PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH; -use crate::traits::Serialize; +use crate::traits::{Serialize, Deserialize}; +use crate::address::AztecAddress; struct PrivateKernelCircuitPublicInputs { min_revertible_side_effect_counter: u32, @@ -11,6 +12,7 @@ struct PrivateKernelCircuitPublicInputs { end: PrivateAccumulatedData, constants: CombinedConstantData, public_teardown_call_request: CallRequest, + fee_payer: AztecAddress } impl Serialize for PrivateKernelCircuitPublicInputs { @@ -22,6 +24,7 @@ impl Serialize for PrivateKernelCir fields.extend_from_array(self.end.serialize()); fields.extend_from_array(self.constants.serialize()); fields.extend_from_array(self.public_teardown_call_request.serialize()); + fields.extend_from_array(self.fee_payer.serialize()); assert_eq(fields.len(), PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH); diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr index 499d2402609..51e8d6278ac 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/private_kernel_circuit_public_inputs_builder.nr @@ -9,7 +9,7 @@ use crate::{ gas::Gas, validation_requests::validation_requests_builder::ValidationRequestsBuilder, call_request::CallRequest }, - constants::MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, mocked::AggregationObject, + address::AztecAddress, constants::MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, mocked::AggregationObject, partial_state_reference::PartialStateReference, traits::{Empty, is_empty} }; @@ -23,6 +23,7 @@ struct PrivateKernelCircuitPublicInputsBuilder { end: PrivateAccumulatedDataBuilder, constants: CombinedConstantData, public_teardown_call_request: CallRequest, + fee_payer: AztecAddress } impl PrivateKernelCircuitPublicInputsBuilder { @@ -32,7 +33,8 @@ impl PrivateKernelCircuitPublicInputsBuilder { validation_requests: self.validation_requests.finish(), end: self.end.finish(), constants: self.constants, - public_teardown_call_request: self.public_teardown_call_request + public_teardown_call_request: self.public_teardown_call_request, + fee_payer: self.fee_payer } } @@ -42,7 +44,8 @@ impl PrivateKernelCircuitPublicInputsBuilder { end: self.end.to_combined(teardown_gas), constants: self.constants, start_state: PartialStateReference::empty(), - revert_code: 0 + revert_code: 0, + fee_payer: self.fee_payer } } @@ -63,7 +66,8 @@ impl PrivateKernelCircuitPublicInputsBuilder { end, constants: self.constants, revert_code: 0, - public_teardown_call_stack: public_teardown_call_stack.storage + public_teardown_call_stack: public_teardown_call_stack.storage, + fee_payer: self.fee_payer } } } @@ -75,7 +79,8 @@ impl Empty for PrivateKernelCircuitPublicInputsBuilder { validation_requests: ValidationRequestsBuilder::empty(), end: PrivateAccumulatedDataBuilder::empty(), constants: CombinedConstantData::empty(), - public_teardown_call_request: CallRequest::empty() + public_teardown_call_request: CallRequest::empty(), + fee_payer: AztecAddress::empty() } } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs.nr index 06722dfce8e..e5a83da9ac3 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs.nr @@ -2,6 +2,7 @@ use crate::abis::{ accumulated_data::PublicAccumulatedData, combined_constant_data::CombinedConstantData, validation_requests::{RollupValidationRequests, ValidationRequests}, call_request::CallRequest }; +use crate::address::AztecAddress; use crate::constants::MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX; struct PublicKernelCircuitPublicInputs { @@ -11,6 +12,7 @@ struct PublicKernelCircuitPublicInputs { constants: CombinedConstantData, revert_code: u8, public_teardown_call_stack: [CallRequest; MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX], + fee_payer: AztecAddress } impl PublicKernelCircuitPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs_builder.nr index f4228bd8f94..a44f58f8116 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/kernel_circuit_public_inputs/public_kernel_circuit_public_inputs_builder.nr @@ -5,7 +5,7 @@ use crate::{ kernel_circuit_public_inputs::{public_kernel_circuit_public_inputs::PublicKernelCircuitPublicInputs}, validation_requests::ValidationRequestsBuilder, call_request::CallRequest }, - constants::MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, traits::Empty + address::AztecAddress, constants::MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, traits::Empty }; struct PublicKernelCircuitPublicInputsBuilder { @@ -15,6 +15,7 @@ struct PublicKernelCircuitPublicInputsBuilder { constants: CombinedConstantData, revert_code: u8, public_teardown_call_stack: BoundedVec, + fee_payer: AztecAddress, } impl PublicKernelCircuitPublicInputsBuilder { @@ -28,7 +29,8 @@ impl PublicKernelCircuitPublicInputsBuilder { end: self.end.finish(), constants: self.constants, revert_code: self.revert_code, - public_teardown_call_stack: self.public_teardown_call_stack.storage + public_teardown_call_stack: self.public_teardown_call_stack.storage, + fee_payer: self.fee_payer } } } @@ -41,7 +43,8 @@ impl Empty for PublicKernelCircuitPublicInputsBuilder { end: PublicAccumulatedDataBuilder::empty(), constants: CombinedConstantData::empty(), revert_code: 0 as u8, - public_teardown_call_stack: BoundedVec::new() + public_teardown_call_stack: BoundedVec::new(), + fee_payer: AztecAddress::empty(), } } } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_call_stack_item.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_call_stack_item.nr index a24fd6e9881..e3d17abef66 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_call_stack_item.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_call_stack_item.nr @@ -85,6 +85,6 @@ fn empty_hash() { let hash = item.hash(); // Value from private_call_stack_item.test.ts "computes empty item hash" test - let test_data_empty_hash = 0x2a1bab3d40feb5234df51a7a6665998920119fd60f5c1e4d9ff3f1128a5f8f81; + let test_data_empty_hash = 0x138c6ad441864ce43487e99d5e1e122c38b4b55d893edec04a32f5aacecc856c; assert_eq(hash, test_data_empty_hash); } diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr index fe7429aef2a..fd62b5efee9 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/private_circuit_public_inputs.nr @@ -24,6 +24,7 @@ struct PrivateCircuitPublicInputs { returns_hash: Field, min_revertible_side_effect_counter: u32, + is_fee_payer: bool, max_block_number: MaxBlockNumber, @@ -62,6 +63,8 @@ impl Eq for PrivateCircuitPublicInputs { self.call_context.eq(other.call_context) & self.args_hash.eq(other.args_hash) & (self.returns_hash == other.returns_hash) & + (self.min_revertible_side_effect_counter == other.min_revertible_side_effect_counter) & + (self.is_fee_payer == other.is_fee_payer) & (self.max_block_number == other.max_block_number) & (self.note_hash_read_requests == other.note_hash_read_requests) & (self.nullifier_read_requests == other.nullifier_read_requests) & @@ -90,6 +93,7 @@ impl Serialize for PrivateCircuitPublicInp fields.push(self.returns_hash); fields.push(self.min_revertible_side_effect_counter as Field); + fields.push(if self.is_fee_payer { 1 } else { 0 } as Field); fields.extend_from_array(self.max_block_number.serialize()); @@ -142,6 +146,7 @@ impl Deserialize for PrivateCircuitPublicI args_hash: reader.read(), returns_hash: reader.read(), min_revertible_side_effect_counter: reader.read() as u32, + is_fee_payer: reader.read() == 1, max_block_number: reader.read_struct(MaxBlockNumber::deserialize), note_hash_read_requests: reader.read_struct_array(ReadRequest::deserialize, [ReadRequest::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL]), nullifier_read_requests: reader.read_struct_array(ReadRequest::deserialize, [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL]), @@ -180,6 +185,7 @@ impl Empty for PrivateCircuitPublicInputs { args_hash: 0, returns_hash: 0, min_revertible_side_effect_counter: 0 as u32, + is_fee_payer: false, max_block_number: MaxBlockNumber::empty(), note_hash_read_requests: [ReadRequest::empty(); MAX_NOTE_HASH_READ_REQUESTS_PER_CALL], nullifier_read_requests: [ReadRequest::empty(); MAX_NULLIFIER_READ_REQUESTS_PER_CALL], @@ -215,6 +221,6 @@ fn empty_hash() { let inputs = PrivateCircuitPublicInputs::empty(); let hash = inputs.hash(); // Value from private_circuit_public_inputs.test.ts "computes empty item hash" test - let test_data_empty_hash = 0x09cc3ed80b2171f093828087431d66777514912b4e7baddb418ab5f1ddbbfd5a; + let test_data_empty_hash = 0x2517b9a84487bde68e18647e59530c6ffe4a7a88c5c556f013d09fd22b84ba35; 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 32e4ef51d4c..d7c6c6d44dd 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/constants.nr @@ -172,7 +172,7 @@ global STATE_REFERENCE_LENGTH: u64 = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + PARTIAL_ 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 + (READ_REQUEST_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) + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + 1 + (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 PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + 4 + MAX_BLOCK_NUMBER_LENGTH + (READ_REQUEST_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) + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_CALL) + MAX_PRIVATE_CALL_STACK_LENGTH_PER_CALL + MAX_PUBLIC_CALL_STACK_LENGTH_PER_CALL + 1 + (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 + (NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_CALL) + (NULLIFIER_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 PUBLIC_CONTEXT_INPUTS_LENGTH: u64 = CALL_CONTEXT_LENGTH + HEADER_LENGTH + GLOBAL_VARIABLES_LENGTH + GAS_LENGTH + 2; @@ -188,7 +188,7 @@ global COMBINED_CONSTANT_DATA_LENGTH = HEADER_LENGTH + TX_CONTEXT_LENGTH + GLOBA global CALLER_CONTEXT_LENGTH = 2 * AZTEC_ADDRESS_LENGTH; global CALL_REQUEST_LENGTH = 1 + AZTEC_ADDRESS_LENGTH + CALLER_CONTEXT_LENGTH + 2; global PRIVATE_ACCUMULATED_DATA_LENGTH = (SCOPED_NOTE_HASH_LENGTH * MAX_NEW_NOTE_HASHES_PER_TX) + (SCOPED_NULLIFIER_LENGTH * MAX_NEW_NULLIFIERS_PER_TX) + (MAX_NEW_L2_TO_L1_MSGS_PER_TX * SCOPED_L2_TO_L1_MESSAGE_LENGTH) + (SIDE_EFFECT_LENGTH * MAX_ENCRYPTED_LOGS_PER_TX) + (SIDE_EFFECT_LENGTH * MAX_UNENCRYPTED_LOGS_PER_TX) + 2 + (CALL_REQUEST_LENGTH * MAX_PRIVATE_CALL_STACK_LENGTH_PER_TX) + (CALL_REQUEST_LENGTH * MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX); -global PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 1 + VALIDATION_REQUESTS_LENGTH + PRIVATE_ACCUMULATED_DATA_LENGTH + COMBINED_CONSTANT_DATA_LENGTH + CALL_REQUEST_LENGTH; +global PRIVATE_KERNEL_CIRCUIT_PUBLIC_INPUTS_LENGTH = 1 + VALIDATION_REQUESTS_LENGTH + PRIVATE_ACCUMULATED_DATA_LENGTH + COMBINED_CONSTANT_DATA_LENGTH + CALL_REQUEST_LENGTH + AZTEC_ADDRESS_LENGTH; global ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH: u64 = 2 + FUNCTION_DATA_LENGTH + CALL_CONTEXT_LENGTH; global GET_NOTES_ORACLE_RETURN_LENGTH: u64 = 674; 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 105a3fed3bb..2235dc7e7ac 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 @@ -31,12 +31,13 @@ use crate::{ struct FixtureBuilder { contract_address: AztecAddress, storage_contract_address: AztecAddress, + fee_payer: AztecAddress, + public_teardown_call_request: CallRequest, // Constant data. historical_header: Header, tx_context: TxContext, global_variables: GlobalVariables, - public_teardown_call_request: CallRequest, // Accumulated data. new_note_hashes: BoundedVec, @@ -84,6 +85,7 @@ impl FixtureBuilder { FixtureBuilder { contract_address: fixtures::contracts::parent_contract.address, storage_contract_address: fixtures::contracts::parent_contract.address, + fee_payer: AztecAddress::empty(), historical_header: Header::empty(), tx_context, new_note_hashes: BoundedVec::new(), @@ -193,7 +195,8 @@ impl FixtureBuilder { end, validation_requests, constants, - public_teardown_call_request: self.public_teardown_call_request + public_teardown_call_request: self.public_teardown_call_request, + fee_payer: self.fee_payer } } @@ -225,7 +228,8 @@ impl FixtureBuilder { validation_requests, constants, revert_code: self.revert_code, - public_teardown_call_stack: public_teardown_call_stack.storage + public_teardown_call_stack: public_teardown_call_stack.storage, + fee_payer: self.fee_payer } } @@ -245,7 +249,14 @@ impl FixtureBuilder { let end = self.to_combined_accumulated_data(); let constants = self.to_constant_data(); - KernelCircuitPublicInputs { rollup_validation_requests, end, constants, start_state: self.start_state, revert_code: self.revert_code } + KernelCircuitPublicInputs { + rollup_validation_requests, + end, + constants, + start_state: self.start_state, + revert_code: self.revert_code, + fee_payer: self.fee_payer + } } pub fn to_kernel_data(self) -> KernelData { @@ -415,6 +426,10 @@ impl FixtureBuilder { self.public_call_stack.push(call_stack_item); } + pub fn set_fee_payer(&mut self, fee_payer: AztecAddress) { + self.fee_payer = fee_payer; + } + pub fn set_public_teardown_call_request(&mut self, hash: Field, is_delegate_call: bool) { self.public_teardown_call_request = self.generate_call_request(hash, is_delegate_call); } @@ -472,6 +487,7 @@ impl Empty for FixtureBuilder { FixtureBuilder { contract_address: AztecAddress::zero(), storage_contract_address: AztecAddress::zero(), + fee_payer: AztecAddress::zero(), historical_header: Header::empty(), tx_context: TxContext::empty(), global_variables: GlobalVariables::empty(), diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr index a8cc18978b8..0a47abc4014 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/tests/private_circuit_public_inputs_builder.nr @@ -27,6 +27,7 @@ struct PrivateCircuitPublicInputsBuilder { returns_hash: Field, min_revertible_side_effect_counter: u32, + is_fee_payer: bool, max_block_number: MaxBlockNumber, @@ -97,6 +98,7 @@ impl PrivateCircuitPublicInputsBuilder { args_hash: self.args_hash, returns_hash: self.returns_hash, min_revertible_side_effect_counter: self.min_revertible_side_effect_counter, + is_fee_payer: self.is_fee_payer, max_block_number: self.max_block_number, note_hash_read_requests: self.note_hash_read_requests.storage, nullifier_read_requests: self.nullifier_read_requests.storage, @@ -126,6 +128,7 @@ impl Empty for PrivateCircuitPublicInputsBuilder { args_hash: 0, returns_hash: 0, min_revertible_side_effect_counter: 0 as u32, + is_fee_payer: false, max_block_number: MaxBlockNumber::empty(), note_hash_read_requests: BoundedVec::new(), nullifier_read_requests: BoundedVec::new(), diff --git a/yarn-project/circuit-types/src/mocks.ts b/yarn-project/circuit-types/src/mocks.ts index 84d27f5a33e..dba3bca5427 100644 --- a/yarn-project/circuit-types/src/mocks.ts +++ b/yarn-project/circuit-types/src/mocks.ts @@ -47,12 +47,14 @@ export const mockTx = ( numberOfRevertiblePublicCallRequests = MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX / 2, publicCallRequests = [], publicTeardownCallRequest = PublicCallRequest.empty(), + feePayer = AztecAddress.ZERO, }: { hasLogs?: boolean; numberOfNonRevertiblePublicCallRequests?: number; numberOfRevertiblePublicCallRequests?: number; publicCallRequests?: PublicCallRequest[]; publicTeardownCallRequest?: PublicCallRequest; + feePayer?: AztecAddress; } = {}, ) => { const totalPublicCallRequests = @@ -69,6 +71,7 @@ export const mockTx = ( const encryptedLogs = hasLogs ? EncryptedTxL2Logs.random(2, 3) : EncryptedTxL2Logs.empty(); // 2 priv function invocations creating 3 encrypted logs each const unencryptedLogs = hasLogs ? UnencryptedTxL2Logs.random(2, 1) : UnencryptedTxL2Logs.empty(); // 2 priv function invocations creating 1 unencrypted log each data.constants.txContext.gasSettings = GasSettings.default(); + data.feePayer = feePayer; if (isForPublic) { data.forRollup = undefined; diff --git a/yarn-project/circuits.js/package.json b/yarn-project/circuits.js/package.json index 07c5759f8a2..92b5928fc87 100644 --- a/yarn-project/circuits.js/package.json +++ b/yarn-project/circuits.js/package.json @@ -35,7 +35,7 @@ "clean": "rm -rf ./dest .tsbuildinfo", "formatting": "run -T prettier --check ./src && run -T eslint ./src", "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", - "remake-constants": "node --loader ts-node/esm src/scripts/constants.in.ts && prettier -w src/constants.gen.ts && cd ../../l1-contracts && ../foundry/bin/forge fmt", + "remake-constants": "node --loader ts-node/esm src/scripts/constants.in.ts && prettier -w src/constants.gen.ts && cd ../../l1-contracts && forge fmt", "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests" }, "dependencies": { diff --git a/yarn-project/circuits.js/src/constants.gen.ts b/yarn-project/circuits.js/src/constants.gen.ts index 0d5ac6d8742..ff27401376b 100644 --- a/yarn-project/circuits.js/src/constants.gen.ts +++ b/yarn-project/circuits.js/src/constants.gen.ts @@ -118,7 +118,7 @@ export const HEADER_LENGTH = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + CONTENT_COMMITMENT_LENGTH + STATE_REFERENCE_LENGTH + GLOBAL_VARIABLES_LENGTH; export const PRIVATE_CIRCUIT_PUBLIC_INPUTS_LENGTH = CALL_CONTEXT_LENGTH + - 3 + + 4 + MAX_BLOCK_NUMBER_LENGTH + READ_REQUEST_LENGTH * MAX_NOTE_HASH_READ_REQUESTS_PER_CALL + READ_REQUEST_LENGTH * MAX_NULLIFIER_READ_REQUESTS_PER_CALL + diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap index 279a77f9b5f..2de7a0cede8 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/private_call_stack_item.test.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PrivateCallStackItem computes empty item hash 1`] = `Fr<0x2a1bab3d40feb5234df51a7a6665998920119fd60f5c1e4d9ff3f1128a5f8f81>`; +exports[`PrivateCallStackItem computes empty item hash 1`] = `Fr<0x138c6ad441864ce43487e99d5e1e122c38b4b55d893edec04a32f5aacecc856c>`; -exports[`PrivateCallStackItem computes hash 1`] = `Fr<0x1368f96c8d186bfc35d8dc71a0ac006d12e25cfa9fdf12bd3bd5af001049933f>`; +exports[`PrivateCallStackItem computes hash 1`] = `Fr<0x2078c0fe8fa7dc6d0c4623ec068d3297e027e60131ff4b0e333a99f72503aa32>`; diff --git a/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap b/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap index 54ef2021ca4..4ace7377315 100644 --- a/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap +++ b/yarn-project/circuits.js/src/structs/__snapshots__/private_circuit_public_inputs.test.ts.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PrivateCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x09cc3ed80b2171f093828087431d66777514912b4e7baddb418ab5f1ddbbfd5a>`; +exports[`PrivateCircuitPublicInputs computes empty inputs hash 1`] = `Fr<0x2517b9a84487bde68e18647e59530c6ffe4a7a88c5c556f013d09fd22b84ba35>`; -exports[`PrivateCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x03dee3f2b52e26410a7a69b1c67e7aee5012d9acd53c85f72ab83917e1f4a8f6>`; +exports[`PrivateCircuitPublicInputs hash matches snapshot 1`] = `Fr<0x0e570673c6fee73b2c55d8acff12bdd9084820e6448c32cfb2600847f493bec1>`; diff --git a/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.test.ts b/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.test.ts index 57ab2ab0782..b3dfb7b3819 100644 --- a/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.test.ts +++ b/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.test.ts @@ -1,3 +1,4 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { AggregationObject } from '../aggregation_object.js'; @@ -43,6 +44,7 @@ describe('KernelCircuitPublicInputs', () => { ), PartialStateReference.empty(), RevertCode.OK, + AztecAddress.ZERO, ); i.end.gasUsed = Gas.from({ daGas: 10, l2Gas: 20 }); diff --git a/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.ts index 9b88daf2412..32ae9dc91fa 100644 --- a/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/kernel_circuit_public_inputs.ts @@ -1,3 +1,4 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; import type { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -35,6 +36,10 @@ export class KernelCircuitPublicInputs { * Flag indicating whether the transaction reverted. */ public revertCode: RevertCode, + /** + * The address of the fee payer for the transaction. + */ + public feePayer: AztecAddress, ) {} getNonEmptyNullifiers() { @@ -59,6 +64,7 @@ export class KernelCircuitPublicInputs { this.constants, this.startState, this.revertCode, + this.feePayer, ); } @@ -76,6 +82,7 @@ export class KernelCircuitPublicInputs { reader.readObject(CombinedConstantData), reader.readObject(PartialStateReference), reader.readObject(RevertCode), + reader.readObject(AztecAddress), ); } @@ -87,6 +94,7 @@ export class KernelCircuitPublicInputs { CombinedConstantData.empty(), PartialStateReference.empty(), RevertCode.OK, + AztecAddress.ZERO, ); } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_circuit_public_inputs.ts index feabf6397fa..a5ab6dcf362 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_circuit_public_inputs.ts @@ -1,3 +1,4 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { Fr } from '@aztec/foundation/fields'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; @@ -36,6 +37,10 @@ export class PrivateKernelCircuitPublicInputs { * The call request for the public teardown function */ public publicTeardownCallRequest: CallRequest, + /** + * The address of the fee payer for the transaction + */ + public feePayer: AztecAddress, ) {} toBuffer() { @@ -46,6 +51,7 @@ export class PrivateKernelCircuitPublicInputs { this.end, this.constants, this.publicTeardownCallRequest, + this.feePayer, ); } @@ -63,6 +69,7 @@ export class PrivateKernelCircuitPublicInputs { reader.readObject(PrivateAccumulatedData), reader.readObject(CombinedConstantData), reader.readObject(CallRequest), + reader.readObject(AztecAddress), ); } @@ -74,6 +81,7 @@ export class PrivateKernelCircuitPublicInputs { PrivateAccumulatedData.empty(), CombinedConstantData.empty(), CallRequest.empty(), + AztecAddress.ZERO, ); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts index f708a197870..c6fe6e91bb5 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_tail_circuit_public_inputs.ts @@ -1,4 +1,5 @@ import { makeTuple } from '@aztec/foundation/array'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX } from '../../constants.gen.js'; @@ -113,6 +114,11 @@ export class PrivateKernelTailCircuitPublicInputs { * Indicates whether execution of the public circuit reverted. */ public revertCode: RevertCode, + /** + * The address of the fee payer for the transaction. + */ + public feePayer: AztecAddress, + public forPublic?: PartialPrivateTailPublicInputsForPublic, public forRollup?: PartialPrivateTailPublicInputsForRollup, ) { @@ -142,6 +148,7 @@ export class PrivateKernelTailCircuitPublicInputs { this.constants, this.revertCode, this.forPublic.publicTeardownCallStack, + this.feePayer, ); } @@ -156,6 +163,7 @@ export class PrivateKernelTailCircuitPublicInputs { this.constants, PartialStateReference.empty(), this.revertCode, + this.feePayer, ); } @@ -191,6 +199,7 @@ export class PrivateKernelTailCircuitPublicInputs { reader.readObject(AggregationObject), reader.readObject(CombinedConstantData), reader.readObject(RevertCode), + reader.readObject(AztecAddress), isForPublic ? reader.readObject(PartialPrivateTailPublicInputsForPublic) : undefined, !isForPublic ? reader.readObject(PartialPrivateTailPublicInputsForRollup) : undefined, ); @@ -203,6 +212,7 @@ export class PrivateKernelTailCircuitPublicInputs { this.aggregationObject, this.constants, this.revertCode, + this.feePayer, isForPublic ? this.forPublic!.toBuffer() : this.forRollup!.toBuffer(), ); } @@ -212,6 +222,7 @@ export class PrivateKernelTailCircuitPublicInputs { AggregationObject.makeFake(), CombinedConstantData.empty(), RevertCode.OK, + AztecAddress.ZERO, undefined, PartialPrivateTailPublicInputsForRollup.empty(), ); diff --git a/yarn-project/circuits.js/src/structs/kernel/public_kernel_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/public_kernel_circuit_public_inputs.ts index 838a641e279..cb71026c7ac 100644 --- a/yarn-project/circuits.js/src/structs/kernel/public_kernel_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/public_kernel_circuit_public_inputs.ts @@ -1,4 +1,5 @@ import { makeTuple } from '@aztec/foundation/array'; +import { AztecAddress } from '@aztec/foundation/aztec-address'; import { BufferReader, type Tuple, serializeToBuffer } from '@aztec/foundation/serialize'; import { inspect } from 'util'; @@ -45,6 +46,10 @@ export class PublicKernelCircuitPublicInputs { * The call request for the public teardown function */ public publicTeardownCallStack: Tuple, + /** + * The address of the fee payer for the transaction + */ + public feePayer: AztecAddress, ) {} toBuffer() { @@ -56,6 +61,7 @@ export class PublicKernelCircuitPublicInputs { this.constants, this.revertCode, this.publicTeardownCallStack, + this.feePayer, ); } @@ -94,6 +100,7 @@ export class PublicKernelCircuitPublicInputs { reader.readObject(CombinedConstantData), reader.readObject(RevertCode), reader.readArray(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest), + reader.readObject(AztecAddress), ); } @@ -106,6 +113,7 @@ export class PublicKernelCircuitPublicInputs { CombinedConstantData.empty(), RevertCode.OK, makeTuple(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, CallRequest.empty), + AztecAddress.ZERO, ); } @@ -118,6 +126,7 @@ export class PublicKernelCircuitPublicInputs { constants: ${inspect(this.constants)}, revertCode: ${this.revertCode}, publicTeardownCallStack: ${inspect(this.publicTeardownCallStack)} + feePayer: ${this.feePayer} }`; } } diff --git a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts index 03c22846910..7c9da159b58 100644 --- a/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts +++ b/yarn-project/circuits.js/src/structs/private_circuit_public_inputs.ts @@ -58,6 +58,10 @@ export class PrivateCircuitPublicInputs { * The side-effect counter under which all side effects are non-revertible. */ public minRevertibleSideEffectCounter: Fr, + /** + * Whether the caller of the function is the fee payer. + */ + public isFeePayer: boolean, /** * The maximum block number in which this transaction can be included and be valid. */ @@ -164,6 +168,7 @@ export class PrivateCircuitPublicInputs { reader.readObject(Fr), reader.readObject(Fr), reader.readObject(Fr), + reader.readBoolean(), reader.readObject(MaxBlockNumber), reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), @@ -192,6 +197,7 @@ export class PrivateCircuitPublicInputs { reader.readField(), reader.readField(), reader.readField(), + reader.readBoolean(), reader.readObject(MaxBlockNumber), reader.readArray(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, ReadRequest), reader.readArray(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest), @@ -223,6 +229,7 @@ export class PrivateCircuitPublicInputs { Fr.ZERO, Fr.ZERO, Fr.ZERO, + false, MaxBlockNumber.empty(), makeTuple(MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, ReadRequest.empty), makeTuple(MAX_NULLIFIER_READ_REQUESTS_PER_CALL, ReadRequest.empty), @@ -251,6 +258,7 @@ export class PrivateCircuitPublicInputs { this.argsHash.isZero() && this.returnsHash.isZero() && this.minRevertibleSideEffectCounter.isZero() && + !this.isFeePayer && this.maxBlockNumber.isEmpty() && isEmptyArray(this.noteHashReadRequests) && isEmptyArray(this.nullifierReadRequests) && @@ -281,6 +289,7 @@ export class PrivateCircuitPublicInputs { fields.argsHash, fields.returnsHash, fields.minRevertibleSideEffectCounter, + fields.isFeePayer, fields.maxBlockNumber, fields.noteHashReadRequests, fields.nullifierReadRequests, diff --git a/yarn-project/circuits.js/src/tests/factories.ts b/yarn-project/circuits.js/src/tests/factories.ts index 79a4b9b96ee..7cfc28ff701 100644 --- a/yarn-project/circuits.js/src/tests/factories.ts +++ b/yarn-project/circuits.js/src/tests/factories.ts @@ -437,6 +437,7 @@ export function makePublicKernelCircuitPublicInputs( makeConstantData(seed + 0x100), RevertCode.OK, tupleGenerator(MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, makeCallRequest, seed + 0x600, CallRequest.empty), + makeAztecAddress(seed + 0x700), ); } @@ -467,6 +468,7 @@ export function makePrivateKernelTailCircuitPublicInputs( makeAggregationObject(seed), makeConstantData(seed + 0x300), RevertCode.OK, + makeAztecAddress(seed + 0x700), forPublic, forRollup, ); @@ -485,6 +487,7 @@ export function makeKernelCircuitPublicInputs(seed = 1, fullAccumulatedData = tr makeConstantData(seed + 0x100), makePartialStateReference(seed + 0x200), RevertCode.OK, + makeAztecAddress(seed + 0x700), ); } @@ -788,6 +791,7 @@ export function makePrivateCircuitPublicInputs(seed = 0): PrivateCircuitPublicIn unencryptedLogPreimagesLength: fr(seed + 0xc00), historicalHeader: makeHeader(seed + 0xd00, undefined), txContext: makeTxContext(seed + 0x1400), + isFeePayer: false, }); } 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 8172acc8483..1df2a44e2de 100644 --- a/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts +++ b/yarn-project/noir-protocol-circuits-types/src/type_conversion.ts @@ -760,6 +760,7 @@ export function mapPrivateCircuitPublicInputsToNoir( historical_header: mapHeaderToNoir(privateCircuitPublicInputs.historicalHeader), tx_context: mapTxContextToNoir(privateCircuitPublicInputs.txContext), min_revertible_side_effect_counter: mapFieldToNoir(privateCircuitPublicInputs.minRevertibleSideEffectCounter), + is_fee_payer: privateCircuitPublicInputs.isFeePayer, }; } @@ -1242,6 +1243,7 @@ export function mapPublicKernelCircuitPublicInputsToNoir( end_non_revertible: mapPublicAccumulatedDataToNoir(inputs.endNonRevertibleData), revert_code: mapRevertCodeToNoir(inputs.revertCode), public_teardown_call_stack: mapTuple(inputs.publicTeardownCallStack, mapCallRequestToNoir), + fee_payer: mapAztecAddressToNoir(inputs.feePayer), }; } @@ -1253,6 +1255,7 @@ export function mapKernelCircuitPublicInputsFromNoir(inputs: KernelCircuitPublic mapCombinedConstantDataFromNoir(inputs.constants), mapPartialStateReferenceFromNoir(inputs.start_state), mapRevertCodeFromNoir(inputs.revert_code), + mapAztecAddressFromNoir(inputs.fee_payer), ); } @@ -1263,6 +1266,7 @@ export function mapKernelCircuitPublicInputsToNoir(inputs: KernelCircuitPublicIn end: mapCombinedAccumulatedDataToNoir(inputs.end), start_state: mapPartialStateReferenceToNoir(inputs.startState), revert_code: mapRevertCodeToNoir(inputs.revertCode), + fee_payer: mapAztecAddressToNoir(inputs.feePayer), }; } @@ -1308,6 +1312,7 @@ export function mapPrivateKernelCircuitPublicInputsFromNoir( mapPrivateAccumulatedDataFromNoir(inputs.end), mapCombinedConstantDataFromNoir(inputs.constants), mapCallRequestFromNoir(inputs.public_teardown_call_request), + mapAztecAddressFromNoir(inputs.fee_payer), ); } @@ -1320,6 +1325,7 @@ export function mapPrivateKernelCircuitPublicInputsToNoir( end: mapPrivateAccumulatedDataToNoir(inputs.end), min_revertible_side_effect_counter: mapFieldToNoir(inputs.minRevertibleSideEffectCounter), public_teardown_call_request: mapCallRequestToNoir(inputs.publicTeardownCallRequest), + fee_payer: mapAztecAddressToNoir(inputs.feePayer), }; } @@ -1349,6 +1355,7 @@ export function mapPrivateKernelTailCircuitPublicInputsForRollupFromNoir( AggregationObject.makeFake(), mapCombinedConstantDataFromNoir(inputs.constants), mapRevertCodeFromNoir(inputs.revert_code), + mapAztecAddressFromNoir(inputs.fee_payer), undefined, forRollup, ); @@ -1367,6 +1374,7 @@ export function mapPrivateKernelTailCircuitPublicInputsForPublicFromNoir( AggregationObject.makeFake(), mapCombinedConstantDataFromNoir(inputs.constants), mapRevertCodeFromNoir(inputs.revert_code), + mapAztecAddressFromNoir(inputs.fee_payer), forPublic, ); } @@ -1480,6 +1488,7 @@ export function mapPublicKernelCircuitPublicInputsFromNoir( mapCombinedConstantDataFromNoir(inputs.constants), mapRevertCodeFromNoir(inputs.revert_code), mapTupleFromNoir(inputs.public_teardown_call_stack, MAX_PUBLIC_CALL_STACK_LENGTH_PER_TX, mapCallRequestFromNoir), + mapAztecAddressFromNoir(inputs.fee_payer), ); } diff --git a/yarn-project/simulator/src/public/public_processor.test.ts b/yarn-project/simulator/src/public/public_processor.test.ts index a8826c1a041..5495342516e 100644 --- a/yarn-project/simulator/src/public/public_processor.test.ts +++ b/yarn-project/simulator/src/public/public_processor.test.ts @@ -11,6 +11,7 @@ import { } from '@aztec/circuit-types'; import { AppendOnlyTreeSnapshot, + AztecAddress, ContractStorageUpdateRequest, Fr, Gas, @@ -152,12 +153,14 @@ describe('public_processor', () => { numberOfRevertiblePublicCallRequests = 0, publicCallRequests = [], publicTeardownCallRequest = PublicCallRequest.empty(), + feePayer = AztecAddress.ZERO, }: { hasLogs?: boolean; numberOfNonRevertiblePublicCallRequests?: number; numberOfRevertiblePublicCallRequests?: number; publicCallRequests?: PublicCallRequest[]; publicTeardownCallRequest?: PublicCallRequest; + feePayer?: AztecAddress; } = {}, seed = 1, ) => { @@ -167,6 +170,7 @@ describe('public_processor', () => { numberOfRevertiblePublicCallRequests, publicCallRequests, publicTeardownCallRequest, + feePayer, }); }; @@ -220,9 +224,11 @@ describe('public_processor', () => { }); it('runs a tx with enqueued public calls', async function () { + const feePayer = AztecAddress.random(); const tx = mockTxWithPartialState({ numberOfRevertiblePublicCallRequests: 2, publicTeardownCallRequest: PublicCallRequest.empty(), + feePayer, }); publicExecutor.simulate.mockImplementation(execution => { @@ -244,6 +250,7 @@ describe('public_processor', () => { expect(publicExecutor.simulate).toHaveBeenCalledTimes(2); expect(publicWorldStateDB.commit).toHaveBeenCalledTimes(1); expect(publicWorldStateDB.rollbackToCommit).toHaveBeenCalledTimes(0); + expect(processed[0].data.feePayer).toEqual(feePayer); expect(prover.addNewTx).toHaveBeenCalledWith(processed[0]); });