diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr index 40a7a09f8fba..0c5f8647c089 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/base_or_merge_rollup_public_inputs.nr @@ -1,5 +1,5 @@ -use crate::abis::constant_rollup_data::ConstantRollupData; use dep::types::{ + abis::constant_rollup_data::ConstantRollupData, constants::BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH, partial_state_reference::PartialStateReference, traits::{Deserialize, Empty, Serialize}, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr index 716b67bf953e..0bcaaa80b27d 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/mod.nr @@ -1,4 +1,3 @@ -pub(crate) mod constant_rollup_data; pub(crate) mod base_or_merge_rollup_public_inputs; pub(crate) mod block_root_or_block_merge_public_inputs; pub(crate) mod previous_rollup_data; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr index 0e7f459fecdf..dbe2eb34fd6d 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/constants.nr @@ -1,5 +1,6 @@ -use crate::abis::constant_rollup_data::ConstantRollupData; -use types::abis::combined_constant_data::CombinedConstantData; +use types::abis::{ + combined_constant_data::CombinedConstantData, constant_rollup_data::ConstantRollupData, +}; pub(crate) fn validate_combined_constant_data( constants: CombinedConstantData, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr index 9e3e70f9433b..128c7bed5bdf 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/mod.nr @@ -3,3 +3,10 @@ pub(crate) mod constants; pub(crate) mod fees; pub(crate) mod nullifier_tree; pub(crate) mod public_data_tree; + +mod private_tube_data_validator; +mod public_tube_data_validator; +pub(crate) mod validate_tube_data; + +pub(crate) use private_tube_data_validator::PrivateTubeDataValidator; +pub(crate) use public_tube_data_validator::PublicTubeDataValidator; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data_validator.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data_validator.nr new file mode 100644 index 000000000000..64f27feb546a --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/private_tube_data_validator.nr @@ -0,0 +1,28 @@ +use super::validate_tube_data::validate_max_fees_per_gas; +use dep::types::abis::{constant_rollup_data::ConstantRollupData, tube::PrivateTubeData}; + +pub struct PrivateTubeDataValidator { + pub data: PrivateTubeData, +} + +// TODO: Move relevant verifications here. +impl PrivateTubeDataValidator { + pub fn new(data: PrivateTubeData) -> Self { + PrivateTubeDataValidator { data } + } + + pub fn verify_proof(self, _allowed_previous_circuits: [u32; N]) { + if !dep::std::runtime::is_unconstrained() { + self.data.verify(); + // TODO(#7410) + // self.tube_data.vk_data.validate_in_vk_tree(self.tube_data.public_inputs.constants.vk_tree_root, ALLOWED_PREVIOUS_CIRCUITS); + } + } + + pub fn validate_with_rollup_data(self, constants: ConstantRollupData) { + validate_max_fees_per_gas( + self.data.public_inputs.constants.tx_context.gas_settings.max_fees_per_gas, + constants.global_variables.gas_fees, + ); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data_validator.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data_validator.nr new file mode 100644 index 000000000000..6edeb1ad88b9 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/public_tube_data_validator.nr @@ -0,0 +1,28 @@ +use super::validate_tube_data::validate_max_fees_per_gas; +use dep::types::abis::{constant_rollup_data::ConstantRollupData, tube::PublicTubeData}; + +pub struct PublicTubeDataValidator { + pub data: PublicTubeData, +} + +// TODO: Move relevant verifications here. +impl PublicTubeDataValidator { + pub fn new(data: PublicTubeData) -> Self { + PublicTubeDataValidator { data } + } + + pub fn verify_proof(self) { + if !dep::std::runtime::is_unconstrained() { + self.data.verify(); + // TODO(#7410) + // self.tube_data.vk_data.validate_in_vk_tree(self.tube_data.public_inputs.constants.vk_tree_root, ALLOWED_PREVIOUS_CIRCUITS); + } + } + + pub fn validate_with_rollup_data(self, constants: ConstantRollupData) { + validate_max_fees_per_gas( + self.data.public_inputs.constants.tx_context.gas_settings.max_fees_per_gas, + constants.global_variables.gas_fees, + ); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validate_tube_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validate_tube_data.nr new file mode 100644 index 000000000000..1a443ee53494 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/components/validate_tube_data.nr @@ -0,0 +1,12 @@ +use dep::types::abis::gas_fees::GasFees; + +pub fn validate_max_fees_per_gas(max_fees_per_gas: GasFees, gas_fees: GasFees) { + assert( + !max_fees_per_gas.fee_per_da_gas.lt(gas_fees.fee_per_da_gas), + "max fee_per_da_gas in settings must not be less than the global value", + ); + assert( + !max_fees_per_gas.fee_per_l2_gas.lt(gas_fees.fee_per_l2_gas), + "max fee_per_l2_gas in settings must not be less than the global value", + ); +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr index 03b442b4eaa0..9b9bc52e9bd8 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/mod.nr @@ -2,6 +2,7 @@ pub(crate) mod components; pub(crate) mod state_diff_hints; mod private_base_rollup; mod public_base_rollup; +mod tests; pub use crate::abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs; pub use private_base_rollup::PrivateBaseRollupInputs; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr index e1c000a7b781..b5d2c1b649d2 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/private_base_rollup.nr @@ -1,13 +1,11 @@ use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, base::{ components::{ archive::perform_archive_membership_check, constants::validate_combined_constant_data, fees::compute_fee_payer_fee_juice_balance_leaf_slot, - nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, + nullifier_tree::nullifier_tree_batch_insert, PrivateTubeDataValidator, + public_data_tree::public_data_tree_insert, }, state_diff_hints::PrivateBaseStateDiffHints, }, @@ -15,7 +13,7 @@ use crate::{ }; use dep::types::{ abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, constant_rollup_data::ConstantRollupData, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, tube::PrivateTubeData, }, @@ -55,11 +53,9 @@ impl PrivateBaseRollupInputs { } pub fn execute(self) -> BaseOrMergeRollupPublicInputs { - if !dep::std::runtime::is_unconstrained() { - self.tube_data.verify(); - // TODO(#7410) - // self.tube_data.vk_data.validate_in_vk_tree(self.tube_data.public_inputs.constants.vk_tree_root, ALLOWED_PREVIOUS_CIRCUITS); - } + let tube_data_validator = PrivateTubeDataValidator::new(self.tube_data); + tube_data_validator.verify_proof(ALLOWED_PREVIOUS_CIRCUITS); + tube_data_validator.validate_with_rollup_data(self.constants); let transaction_fee = self.compute_transaction_fee(); @@ -221,10 +217,7 @@ impl PrivateBaseRollupInputs { mod tests { use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, base::{ components::fees::compute_fee_payer_fee_juice_balance_leaf_slot, private_base_rollup::PrivateBaseRollupInputs, @@ -234,7 +227,8 @@ mod tests { }; use dep::types::{ abis::{ - append_only_tree_snapshot::AppendOnlyTreeSnapshot, gas::Gas, gas_fees::GasFees, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, + constant_rollup_data::ConstantRollupData, gas::Gas, gas_fees::GasFees, kernel_circuit_public_inputs::KernelCircuitPublicInputs, nullifier_leaf_preimage::NullifierLeafPreimage, }, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr index c7a689749da8..ee97bd479c91 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/public_base_rollup.nr @@ -1,13 +1,11 @@ use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::{BASE_ROLLUP_TYPE, BaseOrMergeRollupPublicInputs}, base::{ components::{ archive::perform_archive_membership_check, constants::validate_combined_constant_data, fees::compute_fee_payer_fee_juice_balance_leaf_slot, nullifier_tree::nullifier_tree_batch_insert, public_data_tree::public_data_tree_insert, + PublicTubeDataValidator, }, state_diff_hints::PublicBaseStateDiffHints, }, @@ -19,6 +17,7 @@ use dep::types::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, avm_circuit_public_inputs::AvmProofData, combined_constant_data::CombinedConstantData, + constant_rollup_data::ConstantRollupData, log_hash::{LogHash, ScopedLogHash}, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, @@ -109,11 +108,9 @@ impl PublicBaseRollupInputs { } pub fn execute(self) -> BaseOrMergeRollupPublicInputs { - if !dep::std::runtime::is_unconstrained() { - self.tube_data.verify(); - // TODO(#7410) - // self.tube_data.vk_data.validate_in_vk_tree([TUBE_VK_INDEX]); - } + let tube_data_validator = PublicTubeDataValidator::new(self.tube_data); + tube_data_validator.verify_proof(); + tube_data_validator.validate_with_rollup_data(self.constants); // TODO(#8470) // if !dep::std::runtime::is_unconstrained() { @@ -363,10 +360,7 @@ impl PublicBaseRollupInputs { mod tests { use crate::{ - abis::{ - base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, - constant_rollup_data::ConstantRollupData, - }, + abis::base_or_merge_rollup_public_inputs::BaseOrMergeRollupPublicInputs, base::{ components::fees::compute_fee_payer_fee_juice_balance_leaf_slot, public_base_rollup::PublicBaseRollupInputs, state_diff_hints::PublicBaseStateDiffHints, @@ -376,6 +370,7 @@ mod tests { use dep::types::{ abis::{ append_only_tree_snapshot::AppendOnlyTreeSnapshot, + constant_rollup_data::ConstantRollupData, nullifier_leaf_preimage::NullifierLeafPreimage, public_data_write::PublicDataWrite, }, address::{AztecAddress, EthAddress}, diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/mod.nr new file mode 100644 index 000000000000..7e88fe33e7df --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/mod.nr @@ -0,0 +1 @@ +mod private_tube_data_validator_builder; diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/mod.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/mod.nr new file mode 100644 index 000000000000..cefbf64d37db --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/mod.nr @@ -0,0 +1,24 @@ +mod validate_with_rollup_data; + +use crate::base::components::PrivateTubeDataValidator; +use dep::types::tests::fixture_builder::FixtureBuilder; + +pub struct PrivateTubeDataValidatorBuilder { + pub tube_data: FixtureBuilder, + pub rollup_data: FixtureBuilder, +} + +impl PrivateTubeDataValidatorBuilder { + pub fn new() -> Self { + PrivateTubeDataValidatorBuilder { + tube_data: FixtureBuilder::new(), + rollup_data: FixtureBuilder::new(), + } + } + + pub fn validate_with_rollup_data(self) { + let tube_data = self.tube_data.to_private_tube_data(); + let rollup_data = self.rollup_data.to_constant_rollup_data(); + PrivateTubeDataValidator::new(tube_data).validate_with_rollup_data(rollup_data); + } +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/validate_with_rollup_data.nr b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/validate_with_rollup_data.nr new file mode 100644 index 000000000000..72bbb8e2d041 --- /dev/null +++ b/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/base/tests/private_tube_data_validator_builder/validate_with_rollup_data.nr @@ -0,0 +1,27 @@ +use super::PrivateTubeDataValidatorBuilder; + +#[test] +fn validate_with_rollup_data_success() { + let builder = PrivateTubeDataValidatorBuilder::new(); + builder.validate_with_rollup_data(); +} + +#[test(should_fail_with = "max fee_per_da_gas in settings must not be less than the global value")] +fn validate_with_rollup_data_not_enough_fee_per_da_gas_fails() { + let mut builder = PrivateTubeDataValidatorBuilder::new(); + + builder.tube_data.tx_context.gas_settings.max_fees_per_gas.fee_per_da_gas = 3; + builder.rollup_data.global_variables.gas_fees.fee_per_da_gas = 4; + + builder.validate_with_rollup_data(); +} + +#[test(should_fail_with = "max fee_per_l2_gas in settings must not be less than the global value")] +fn validate_with_rollup_data_not_enough_fee_per_l2_gas_fails() { + let mut builder = PrivateTubeDataValidatorBuilder::new(); + + builder.tube_data.tx_context.gas_settings.max_fees_per_gas.fee_per_l2_gas = 3; + builder.rollup_data.global_variables.gas_fees.fee_per_l2_gas = 4; + + builder.validate_with_rollup_data(); +} diff --git a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/constant_rollup_data.nr similarity index 99% rename from noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr rename to noir-projects/noir-protocol-circuits/crates/types/src/abis/constant_rollup_data.nr index 9eab56a2092d..0a8d6919164b 100644 --- a/noir-projects/noir-protocol-circuits/crates/rollup-lib/src/abis/constant_rollup_data.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/constant_rollup_data.nr @@ -1,4 +1,4 @@ -use dep::types::{ +use crate::{ abis::{append_only_tree_snapshot::AppendOnlyTreeSnapshot, global_variables::GlobalVariables}, constants::CONSTANT_ROLLUP_DATA_LENGTH, traits::{Deserialize, Empty, Serialize}, diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_settings.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_settings.nr index 457f1fb5a8af..42301eb096d5 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_settings.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/gas_settings.nr @@ -1,14 +1,14 @@ use crate::{ abis::{gas::Gas, gas_fees::GasFees}, - constants::{DEFAULT_GAS_LIMIT, DEFAULT_TEARDOWN_GAS_LIMIT, GAS_SETTINGS_LENGTH}, + constants::GAS_SETTINGS_LENGTH, traits::{Deserialize, Empty, Serialize}, utils::reader::Reader, }; pub struct GasSettings { - gas_limits: Gas, - teardown_gas_limits: Gas, - max_fees_per_gas: GasFees, + pub gas_limits: Gas, + pub teardown_gas_limits: Gas, + pub max_fees_per_gas: GasFees, } impl GasSettings { diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr index 890d2d4429de..fb8e97d5de9b 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/mod.nr @@ -13,6 +13,7 @@ pub mod nullifier_leaf_preimage; pub mod tx_constant_data; pub mod combined_constant_data; +pub mod constant_rollup_data; pub mod side_effect; pub mod read_request; diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr b/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr index e36c2ead478d..3638c6bd3683 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/abis/tube.nr @@ -25,7 +25,7 @@ impl Verifiable for PublicTubeData { } pub struct PrivateTubeData { - public_inputs: KernelCircuitPublicInputs, + pub public_inputs: KernelCircuitPublicInputs, proof: TubeProof, vk_data: VkData, } 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 94294b65c160..092911b5934b 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 @@ -4,9 +4,11 @@ use crate::{ avm_accumulated_data::AvmAccumulatedData, CombinedAccumulatedData, PrivateAccumulatedData, PrivateAccumulatedDataBuilder, PrivateToPublicAccumulatedData, }, + append_only_tree_snapshot::AppendOnlyTreeSnapshot, avm_circuit_public_inputs::AvmProofData, call_context::CallContext, combined_constant_data::CombinedConstantData, + constant_rollup_data::ConstantRollupData, function_data::FunctionData, gas::Gas, gas_settings::GasSettings, @@ -159,6 +161,9 @@ pub struct FixtureBuilder { pub protocol_contract_tree_root: Field, pub protocol_contract_sibling_path: [Field; PROTOCOL_CONTRACT_TREE_HEIGHT], + // Tree snapshots. + pub archive_tree: AppendOnlyTreeSnapshot, + // Counters. pub min_revertible_side_effect_counter: u32, pub counter_start: u32, @@ -291,6 +296,15 @@ impl FixtureBuilder { } } + pub fn to_constant_rollup_data(self) -> ConstantRollupData { + ConstantRollupData { + last_archive: self.archive_tree, + vk_tree_root: self.vk_tree_root, + protocol_contract_tree_root: self.protocol_contract_tree_root, + global_variables: self.global_variables, + } + } + pub fn build_tx_request(self) -> TxRequest { TxRequest { origin: self.contract_address, @@ -1047,13 +1061,13 @@ impl FixtureBuilder { fixtures::vk_tree::get_vk_merkle_tree().get_root() } - fn to_private_tube_data(self) -> PrivateTubeData { + pub fn to_private_tube_data(self) -> PrivateTubeData { let mut result: PrivateTubeData = std::mem::zeroed(); result.public_inputs = self.to_kernel_circuit_public_inputs(); result } - fn to_public_tube_data(self) -> PublicTubeData { + pub fn to_public_tube_data(self) -> PublicTubeData { let mut result: PublicTubeData = std::mem::zeroed(); result.public_inputs = self.to_private_to_public_kernel_circuit_public_inputs(true); result @@ -1133,6 +1147,7 @@ impl Empty for FixtureBuilder { vk_tree_root: FixtureBuilder::vk_tree_root(), protocol_contract_tree_root: 0, protocol_contract_sibling_path: [0; PROTOCOL_CONTRACT_TREE_HEIGHT], + archive_tree: AppendOnlyTreeSnapshot::zero(), revert_code: 0, min_revertible_side_effect_counter: 0, counter_start: 0, diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts index 95210a1b69a9..aa1672c42f92 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.test.ts @@ -1,6 +1,7 @@ import { type Tx, mockTx } from '@aztec/circuit-types'; import { AztecAddress, Fr, FunctionSelector, GasFees, GasSettings, PUBLIC_DISPATCH_SELECTOR } from '@aztec/circuits.js'; import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { type Writeable } from '@aztec/foundation/types'; import { FeeJuiceContract } from '@aztec/noir-contracts.js'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; @@ -10,17 +11,16 @@ import { GasTxValidator, type PublicStateSource } from './gas_validator.js'; import { patchNonRevertibleFn, patchRevertibleFn } from './test_utils.js'; describe('GasTxValidator', () => { - let validator: GasTxValidator; let publicStateSource: MockProxy; let feeJuiceAddress: AztecAddress; + let enforceFees: boolean; + let gasFees: Writeable; beforeEach(() => { feeJuiceAddress = ProtocolContractAddress.FeeJuice; publicStateSource = mock({ storageRead: mockFn().mockImplementation((_address: AztecAddress, _slot: Fr) => Fr.ZERO), }); - - validator = new GasTxValidator(publicStateSource, feeJuiceAddress, false); }); let tx: Tx; @@ -31,10 +31,12 @@ describe('GasTxValidator', () => { beforeEach(() => { tx = mockTx(1, { numberOfNonRevertiblePublicCallRequests: 2 }); tx.data.feePayer = AztecAddress.random(); - tx.data.constants.txContext.gasSettings = GasSettings.default({ maxFeesPerGas: new GasFees(10, 10) }); + gasFees = new GasFees(11, 22); + tx.data.constants.txContext.gasSettings = GasSettings.default({ maxFeesPerGas: gasFees }); payer = tx.data.feePayer; expectedBalanceSlot = poseidon2Hash([FeeJuiceContract.storage.balances.slot, payer]); feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit().toBigInt(); + enforceFees = false; }); const mockBalance = (balance: bigint) => { @@ -44,12 +46,14 @@ describe('GasTxValidator', () => { }; const expectValidateSuccess = async (tx: Tx) => { + const validator = new GasTxValidator(publicStateSource, feeJuiceAddress, enforceFees, gasFees); const result = await validator.validateTxs([tx]); expect(result[0].length).toEqual(1); expect(result).toEqual([[tx], []]); }; const expectValidateFail = async (tx: Tx) => { + const validator = new GasTxValidator(publicStateSource, feeJuiceAddress, enforceFees, gasFees); const result = await validator.validateTxs([tx]); expect(result[1].length).toEqual(1); expect(result).toEqual([[], [tx]]); @@ -96,8 +100,18 @@ describe('GasTxValidator', () => { }); it('rejects txs with no fee payer if fees are enforced', async () => { - validator.enforceFees = true; + enforceFees = true; tx.data.feePayer = AztecAddress.ZERO; await expectValidateFail(tx); }); + + it('rejects txs with not enough fee per da gas', async () => { + gasFees.feePerDaGas = gasFees.feePerDaGas.sub(new Fr(1)); + await expectValidateFail(tx); + }); + + it('rejects txs with not enough fee per l2 gas', async () => { + gasFees.feePerL2Gas = gasFees.feePerL2Gas.sub(new Fr(1)); + await expectValidateFail(tx); + }); }); diff --git a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts index 58d92c7ce1a7..54d9f0650ed2 100644 --- a/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts +++ b/yarn-project/sequencer-client/src/tx_validator/gas_validator.ts @@ -1,5 +1,5 @@ import { type Tx, TxExecutionPhase, type TxValidator } from '@aztec/circuit-types'; -import { type AztecAddress, type Fr, FunctionSelector } from '@aztec/circuits.js'; +import { type AztecAddress, type Fr, FunctionSelector, type GasFees } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { computeFeePayerBalanceStorageSlot, getExecutionRequestsByPhase } from '@aztec/simulator'; @@ -13,7 +13,12 @@ export class GasTxValidator implements TxValidator { #publicDataSource: PublicStateSource; #feeJuiceAddress: AztecAddress; - constructor(publicDataSource: PublicStateSource, feeJuiceAddress: AztecAddress, public enforceFees: boolean) { + constructor( + publicDataSource: PublicStateSource, + feeJuiceAddress: AztecAddress, + private enforceFees: boolean, + private gasFees: GasFees, + ) { this.#publicDataSource = publicDataSource; this.#feeJuiceAddress = feeJuiceAddress; } @@ -48,8 +53,20 @@ export class GasTxValidator implements TxValidator { } } + const gasSettings = tx.data.constants.txContext.gasSettings; + + // Check that the user is willing to pay enough fee per gas. + const maxFeesPerGas = gasSettings.maxFeesPerGas; + if ( + maxFeesPerGas.feePerDaGas.lt(this.gasFees.feePerDaGas) || + maxFeesPerGas.feePerL2Gas.lt(this.gasFees.feePerL2Gas) + ) { + this.#log.warn(`Rejecting transaction ${tx.getTxHash()} due to insufficient fee per gas`); + return false; + } + // Compute the maximum fee that this tx may pay, based on its gasLimits and maxFeePerGas - const feeLimit = tx.data.constants.txContext.gasSettings.getFeeLimit(); + const feeLimit = gasSettings.getFeeLimit(); // Read current balance of the feePayer const initialBalance = await this.#publicDataSource.storageRead( diff --git a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts index d0bab6af43ef..4d69b417c0da 100644 --- a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts +++ b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts @@ -45,7 +45,12 @@ export class TxValidatorFactory { new MetadataTxValidator(globalVariables.chainId, globalVariables.blockNumber), new DoubleSpendTxValidator(this.nullifierSource), new PhasesTxValidator(this.contractDataSource, setupAllowList), - new GasTxValidator(this.publicStateSource, ProtocolContractAddress.FeeJuice, this.enforceFees), + new GasTxValidator( + this.publicStateSource, + ProtocolContractAddress.FeeJuice, + this.enforceFees, + globalVariables.gasFees, + ), ); }