From 5676e678e204e7ad807d71a21e9b3dac81c8c2e2 Mon Sep 17 00:00:00 2001 From: smtmfft Date: Fri, 26 Jun 2026 17:12:21 +0800 Subject: [PATCH 1/2] fix: validate shasta grandparent base fee --- lib/src/utils/shasta.rs | 30 ++--- lib/src/utils/shasta_rules.rs | 122 ++++++++++++++++-- .../config/sgx-guest.docker.manifest.template | 3 + .../config/sgx-guest.local.manifest.template | 1 + 4 files changed, 131 insertions(+), 25 deletions(-) diff --git a/lib/src/utils/shasta.rs b/lib/src/utils/shasta.rs index 08927d2d1..69ec73db3 100644 --- a/lib/src/utils/shasta.rs +++ b/lib/src/utils/shasta.rs @@ -123,23 +123,6 @@ pub fn generate_transactions_for_shasta_blocks( last_anchor_block_number, ) => { - // parent is pacaya means this is the first shasta block - let use_init_base_fee = - guest_batch_input.inputs[0].parent_header.number == 0; - - let min_base_fee = min_base_fee_for_shasta_chain( - guest_batch_input.taiko.chain_spec.chain_id(), - ); - //TODO: move to validate_normal_proposal_manifest - if !validate_shasta_block_base_fee( - &guest_batch_input.inputs, - use_init_base_fee, - guest_batch_input.taiko.l2_grandparent_header.as_ref(), - min_base_fee, - ) { - warn!("shasta block base fee is invalid, need double check"); - panic!("shasta block base fee is invalid"); - } manifest } _ => { @@ -156,6 +139,19 @@ pub fn generate_transactions_for_shasta_blocks( manifest } }; + // parent is pacaya means this is the first shasta block + let use_init_base_fee = guest_batch_input.inputs[0].parent_header.number == 0; + let min_base_fee = + min_base_fee_for_shasta_chain(guest_batch_input.taiko.chain_spec.chain_id()); + if !validate_shasta_block_base_fee( + &guest_batch_input.inputs, + use_init_base_fee, + guest_batch_input.taiko.l2_grandparent_header.as_ref(), + min_base_fee, + ) { + warn!("shasta block base fee is invalid, need double check"); + panic!("shasta block base fee is invalid"); + } protocol_manifest .blocks diff --git a/lib/src/utils/shasta_rules.rs b/lib/src/utils/shasta_rules.rs index 8b8e5a296..6007e0c98 100644 --- a/lib/src/utils/shasta_rules.rs +++ b/lib/src/utils/shasta_rules.rs @@ -519,6 +519,42 @@ pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; /// CHANGE(taiko): add ShastaInitialBaseFee for Shasta fork. pub const SHASTA_INITIAL_BASE_FEE: u64 = 25_000_000; +fn parent_block_time_from_grandparent( + parent: &Header, + l2_grandparent_header: Option<&Header>, +) -> Option { + if parent.number == 0 { + return Some(BLOCK_TIME_TARGET); + } + + let Some(grandparent) = l2_grandparent_header else { + warn!( + "missing L2 grandparent header for parent block {}", + parent.number + ); + return None; + }; + + if grandparent.number + 1 != parent.number { + warn!( + "L2 grandparent number mismatch: grandparent {}, parent {}", + grandparent.number, parent.number + ); + return None; + } + + let grandparent_hash = grandparent.hash_slow(); + if grandparent_hash != parent.parent_hash { + warn!( + "L2 grandparent hash mismatch: expected {}, found {}", + parent.parent_hash, grandparent_hash + ); + return None; + } + + Some(parent.timestamp.saturating_sub(grandparent.timestamp)) +} + pub fn validate_shasta_block_base_fee( block_guest_inputs: &[GuestInput], use_init_base_fee: bool, @@ -537,14 +573,11 @@ pub fn validate_shasta_block_base_fee( } else { // Calculate parent_block_time = parent.timestamp - grandparent.timestamp // According to EIP-4396, we need the time between parent and grandparent - let parent_block_time = if let Some(grandparent) = l2_grandparent_header { - block_guest_inputs[0] - .parent_header - .timestamp - .saturating_sub(grandparent.timestamp) - } else { - // Fallback: if no parent's parent (e.g., first block ever), use default block time target - BLOCK_TIME_TARGET + let Some(parent_block_time) = parent_block_time_from_grandparent( + &block_guest_inputs[0].parent_header, + l2_grandparent_header, + ) else { + return false; }; let first_block_base_fee = calc_next_shasta_base_fee( block_guest_inputs[0].parent_header.gas_limit, @@ -630,6 +663,15 @@ mod tests { use super::calc_next_shasta_base_fee; + fn base_fee_guest_input(parent_header: Header, block_base_fee: u64) -> GuestInput { + let mut input = GuestInput { + parent_header, + ..Default::default() + }; + input.block.header.base_fee_per_gas = Some(block_base_fee); + input + } + #[test] fn test_calc_next_shasta_base_fee() { let parent_gas_limit = 16_000_000; @@ -666,6 +708,70 @@ mod tests { assert_eq!(result, 5_059_102); } + #[test] + fn test_base_fee_rejects_missing_grandparent_for_non_genesis_parent() { + let parent_header = Header { + number: 10, + timestamp: 100, + gas_limit: 16_000_000, + gas_used: 15_000_000, + base_fee_per_gas: Some(5_000_000), + ..Default::default() + }; + let default_time_base_fee = calc_next_shasta_base_fee( + parent_header.gas_limit, + parent_header.gas_used, + parent_header.base_fee_per_gas.unwrap(), + super::BLOCK_TIME_TARGET, + super::DEFAULT_ELASTICITY_MULTIPLIER, + super::DEFAULT_BASE_FEE_CHANGE_DENOMINATOR, + super::MIN_BASE_FEE_SHASTA, + ); + let input = base_fee_guest_input(parent_header, default_time_base_fee); + + assert!(!super::validate_shasta_block_base_fee( + &[input], + false, + None, + super::MIN_BASE_FEE_SHASTA, + )); + } + + #[test] + fn test_base_fee_rejects_unlinked_grandparent() { + let grandparent = Header { + number: 9, + timestamp: 98, + ..Default::default() + }; + let parent_header = Header { + number: 10, + timestamp: 100, + gas_limit: 16_000_000, + gas_used: 15_000_000, + base_fee_per_gas: Some(5_000_000), + parent_hash: B256::repeat_byte(0x42), + ..Default::default() + }; + let grandparent_time_base_fee = calc_next_shasta_base_fee( + parent_header.gas_limit, + parent_header.gas_used, + parent_header.base_fee_per_gas.unwrap(), + parent_header.timestamp - grandparent.timestamp, + super::DEFAULT_ELASTICITY_MULTIPLIER, + super::DEFAULT_BASE_FEE_CHANGE_DENOMINATOR, + super::MIN_BASE_FEE_SHASTA, + ); + let input = base_fee_guest_input(parent_header, grandparent_time_base_fee); + + assert!(!super::validate_shasta_block_base_fee( + &[input], + false, + Some(&grandparent), + super::MIN_BASE_FEE_SHASTA, + )); + } + #[test] fn test_anchor_range_includes_max_offset() { let l1_header_number = 1000u64; diff --git a/provers/sgx/config/sgx-guest.docker.manifest.template b/provers/sgx/config/sgx-guest.docker.manifest.template index 8aadcda33..bbb42808e 100644 --- a/provers/sgx/config/sgx-guest.docker.manifest.template +++ b/provers/sgx/config/sgx-guest.docker.manifest.template @@ -9,6 +9,9 @@ libos.entrypoint = "/sgx-guest" loader.entrypoint = "file:{{ gramine.libos }}" loader.env.HOME = "/root" loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}" +loader.env.DEV_PACAYA_HEIGHT = "0" +loader.env.DEV_ONTAKE_HEIGHT = "0" +loader.env.DEV_SHASTA_TIMESTAMP = "0" loader.env.RUST_LOG = "info" loader.insecure__use_cmdline_argv = true loader.log_level = "{{ log_level }}" diff --git a/provers/sgx/config/sgx-guest.local.manifest.template b/provers/sgx/config/sgx-guest.local.manifest.template index b1727635d..abe7ec454 100644 --- a/provers/sgx/config/sgx-guest.local.manifest.template +++ b/provers/sgx/config/sgx-guest.local.manifest.template @@ -12,6 +12,7 @@ loader.env.LD_LIBRARY_PATH = "/lib:{{ arch_libdir }}" loader.env.DEV_PACAYA_HEIGHT = "0" loader.env.DEV_ONTAKE_HEIGHT = "0" loader.env.DEV_SHASTA_HEIGHT = "0" +loader.env.DEV_SHASTA_TIMESTAMP = "0" loader.env.RUST_LOG = "debug" loader.insecure__use_cmdline_argv = true loader.log_level = "{{ log_level }}" From 918e1190285839d6cd451643fc383b48bc41bebe Mon Sep 17 00:00:00 2001 From: smtmfft Date: Fri, 26 Jun 2026 17:19:57 +0800 Subject: [PATCH 2/2] chore: bump gaiko submodule --- gaiko | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gaiko b/gaiko index 7fab378d3..47944ca02 160000 --- a/gaiko +++ b/gaiko @@ -1 +1 @@ -Subproject commit 7fab378d395bd11389f4e88a390445f5cb399eb8 +Subproject commit 47944ca0201f806f6ad1da8e30e684eb43b4c745