From 9852c8465b437d1859c9d8754b2353baf0324fe7 Mon Sep 17 00:00:00 2001 From: iAmMichaelConnor <42943676+iAmMichaelConnor@users.noreply.github.com> Date: Mon, 16 Feb 2026 22:20:50 +0000 Subject: [PATCH] fix: min expiration timestamp instead of assert Thanks to Nico for the suggestion. Gracefully takes the min of the tx's expiry timestamp and the max tx lifetime. --- .../src/components/tail_output_composer.nr | 10 +++++-- .../src/components/tail_output_validator.nr | 30 +++++++++++-------- .../validate_expiration_timestamp.nr | 9 ------ .../tail_to_public_output_composer.nr | 16 +++++++--- .../tail_to_public_output_validator.nr | 28 +++++++++++------ .../validate_propagated_values_tests.nr | 6 ++-- .../validate_propagated_values_tests.nr | 6 ++-- 7 files changed, 63 insertions(+), 42 deletions(-) delete mode 100644 noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_expiration_timestamp.nr diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr index 79376cc24384..b3c1d2151eca 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_composer.nr @@ -12,6 +12,7 @@ use types::{ PrivateKernelCircuitPublicInputs, PrivateToRollupKernelCircuitPublicInputs, }, }, + constants::MAX_TX_LIFETIME, traits::Empty, }; @@ -38,9 +39,14 @@ impl TailOutputComposer { let gas_used = meter_gas_used(self.previous_kernel, false /* is_for_public */); + let max_timestamp = + source.constants.anchor_block_header.global_variables.timestamp + MAX_TX_LIFETIME; let expiration_timestamp = std::cmp::min( - source.expiration_timestamp, - self.expiration_timestamp_upper_bound, + std::cmp::min( + source.expiration_timestamp, + self.expiration_timestamp_upper_bound, + ), + max_timestamp, ); PrivateToRollupKernelCircuitPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr index 38d6a3bb138b..abe84c3c7b72 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator.nr @@ -1,14 +1,13 @@ -mod validate_expiration_timestamp; - -pub(crate) use validate_expiration_timestamp::validate_expiration_timestamp; - use crate::{ accumulated_data::{assert_sorted_transformed_array, assert_transformed_array}, components::gas_meter::meter_gas_used, }; -use types::abis::kernel_circuit_public_inputs::{ - PrivateKernelCircuitPublicInputs, PrivateToRollupKernelCircuitPublicInputs, +use types::{ + abis::kernel_circuit_public_inputs::{ + PrivateKernelCircuitPublicInputs, PrivateToRollupKernelCircuitPublicInputs, + }, + constants::MAX_TX_LIFETIME, }; pub struct TailOutputValidator { @@ -62,14 +61,21 @@ impl TailOutputValidator { // This value can only decrease with each iteration. Here we take the minimum of: // - The value from the previous kernel iteration // - An upper bound value set by the user + let max_timestamp = self + .previous_kernel + .constants + .anchor_block_header + .global_variables + .timestamp + + MAX_TX_LIFETIME; let expiration_timestamp = std::cmp::min( - self.previous_kernel.expiration_timestamp, - self.expiration_timestamp_upper_bound, - ); - validate_expiration_timestamp( - expiration_timestamp, - self.previous_kernel.constants.anchor_block_header.global_variables, + std::cmp::min( + self.previous_kernel.expiration_timestamp, + self.expiration_timestamp_upper_bound, + ), + max_timestamp, ); + assert_eq( self.output.expiration_timestamp, expiration_timestamp, diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_expiration_timestamp.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_expiration_timestamp.nr deleted file mode 100644 index 0b511fed1d09..000000000000 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_output_validator/validate_expiration_timestamp.nr +++ /dev/null @@ -1,9 +0,0 @@ -use types::{abis::global_variables::GlobalVariables, constants::MAX_TX_LIFETIME}; - -/// We enforce in the tail circuit that the `include_by_timestamp` is at most MAX_TX_LIFETIME after the -/// tx's anchor timestamp. This restriction was motivated by wanting to prune network nodes' -/// mempools of all txs that are longer than a day old. -pub fn validate_expiration_timestamp(expiration_timestamp: u64, global_variables: GlobalVariables) { - let max_timestamp = global_variables.timestamp + MAX_TX_LIFETIME; - assert(expiration_timestamp <= max_timestamp, "expiration_timestamp exceeds max tx lifetime"); -} diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr index 672c98e02f5f..8371ea5e95c0 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_composer.nr @@ -9,8 +9,11 @@ use crate::{ private_kernel_circuit_output_composer::PrivateKernelCircuitOutputComposer, }, }; -use types::abis::kernel_circuit_public_inputs::{ - PrivateKernelCircuitPublicInputs, PrivateToPublicKernelCircuitPublicInputs, +use types::{ + abis::kernel_circuit_public_inputs::{ + PrivateKernelCircuitPublicInputs, PrivateToPublicKernelCircuitPublicInputs, + }, + constants::MAX_TX_LIFETIME, }; pub struct TailToPublicOutputComposer { @@ -46,9 +49,14 @@ impl TailToPublicOutputComposer { let gas_used = meter_gas_used(self.previous_kernel, true /* is_for_public */); + let max_timestamp = + source.constants.anchor_block_header.global_variables.timestamp + MAX_TX_LIFETIME; let expiration_timestamp = std::cmp::min( - source.expiration_timestamp, - self.expiration_timestamp_upper_bound, + std::cmp::min( + source.expiration_timestamp, + self.expiration_timestamp_upper_bound, + ), + max_timestamp, ); let mut output = PrivateToPublicKernelCircuitPublicInputs { diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr index f11ad814a0b2..df9108f0bd62 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/components/tail_to_public_output_validator.nr @@ -4,10 +4,13 @@ use crate::{ assert_split_sorted_transformed_arrays, assert_split_transformed_arrays_from_sorted_padded_array, }, - components::{gas_meter::meter_gas_used, tail_output_validator::validate_expiration_timestamp}, + components::gas_meter::meter_gas_used, }; -use types::abis::kernel_circuit_public_inputs::{ - PrivateKernelCircuitPublicInputs, PrivateToPublicKernelCircuitPublicInputs, +use types::{ + abis::kernel_circuit_public_inputs::{ + PrivateKernelCircuitPublicInputs, PrivateToPublicKernelCircuitPublicInputs, + }, + constants::MAX_TX_LIFETIME, }; pub struct TailToPublicOutputValidator { @@ -52,14 +55,21 @@ impl TailToPublicOutputValidator { // This value can only decrease with each iteration. Here we take the minimum of: // - The value from the previous kernel iteration // - An upper bound value set by the user + let max_timestamp = self + .previous_kernel + .constants + .anchor_block_header + .global_variables + .timestamp + + MAX_TX_LIFETIME; let expiration_timestamp = std::cmp::min( - self.previous_kernel.expiration_timestamp, - self.expiration_timestamp_upper_bound, - ); - validate_expiration_timestamp( - expiration_timestamp, - self.previous_kernel.constants.anchor_block_header.global_variables, + std::cmp::min( + self.previous_kernel.expiration_timestamp, + self.expiration_timestamp_upper_bound, + ), + max_timestamp, ); + assert_eq( self.output.expiration_timestamp, expiration_timestamp, diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail/tail_output_validator/validate_propagated_values_tests.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail/tail_output_validator/validate_propagated_values_tests.nr index 6a0f906b7a33..a61d441be996 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail/tail_output_validator/validate_propagated_values_tests.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail/tail_output_validator/validate_propagated_values_tests.nr @@ -127,7 +127,7 @@ fn expiration_timestamp_custom_equals_max_duration() { builder.validate(); } -#[test(should_fail_with = "expiration_timestamp exceeds max tx lifetime")] +#[test] fn expiration_timestamp_custom_exceeds_max_tx_lifetime() { let mut builder = TestBuilder::new(); @@ -138,8 +138,8 @@ fn expiration_timestamp_custom_exceeds_max_tx_lifetime() { let max_timestamp = block_timestamp + MAX_TX_LIFETIME; builder.expiration_timestamp_upper_bound = max_timestamp + 1; builder.previous_kernel.expiration_timestamp = max_timestamp + 1; - // Output should be the max timestamp. - builder.output.expiration_timestamp = max_timestamp + 1; + // Output should be clamped to the max timestamp. + builder.output.expiration_timestamp = max_timestamp; builder.validate(); } diff --git a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail_to_public/tail_to_public_output_validator/validate_propagated_values_tests.nr b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail_to_public/tail_to_public_output_validator/validate_propagated_values_tests.nr index a6c3cfcf0046..869c257a3f04 100644 --- a/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail_to_public/tail_to_public_output_validator/validate_propagated_values_tests.nr +++ b/noir-projects/noir-protocol-circuits/crates/private-kernel-lib/src/tests/private_kernel_tail_to_public/tail_to_public_output_validator/validate_propagated_values_tests.nr @@ -127,7 +127,7 @@ fn expiration_timestamp_custom_equals_max_tx_lifetime() { builder.validate(); } -#[test(should_fail_with = "expiration_timestamp exceeds max tx lifetime")] +#[test] fn expiration_timestamp_custom_exceeds_max_tx_lifetime() { let mut builder = TestBuilder::new(); @@ -138,8 +138,8 @@ fn expiration_timestamp_custom_exceeds_max_tx_lifetime() { let max_timestamp = block_timestamp + MAX_TX_LIFETIME; builder.expiration_timestamp_upper_bound = max_timestamp + 1; builder.previous_kernel.expiration_timestamp = max_timestamp + 1; - // Output should be the max timestamp. - builder.output.expiration_timestamp = max_timestamp + 1; + // Output should be clamped to the max timestamp. + builder.output.expiration_timestamp = max_timestamp; builder.validate(); }