Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 58 additions & 5 deletions crates/protocol/derive/src/attributes/stateful.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use alloy_rlp::Encodable;
use alloy_rpc_types_engine::PayloadAttributes;
use async_trait::async_trait;
use kona_genesis::RollupConfig;
use kona_hardforks::{Hardfork, Hardforks};
use kona_hardforks::{Hardfork, Hardforks, Isthmus};
use kona_protocol::{
DEPOSIT_EVENT_ABI_HASH, L1BlockInfoTx, L2BlockInfo, closing_deposit_context_tx, decode_deposit,
};
Expand Down Expand Up @@ -130,6 +130,9 @@ where
));
}

let mut upgrade_gas =
u64::from_be_bytes(alloy_primitives::U64::from(sys_config.gas_limit).to_be_bytes());

let mut upgrade_transactions: Vec<Bytes> = vec![];
if self.rollup_cfg.is_ecotone_active(next_l2_time) &&
!self.rollup_cfg.is_ecotone_active(l2_parent.block_info.timestamp)
Expand All @@ -144,6 +147,7 @@ where
if self.rollup_cfg.is_isthmus_active(next_l2_time) &&
!self.rollup_cfg.is_isthmus_active(l2_parent.block_info.timestamp)
{
upgrade_gas += Isthmus::deposits().map(|tx| tx.gas_limit).sum::<u64>();
upgrade_transactions.append(&mut Hardforks::ISTHMUS.txs().collect());
}

Expand Down Expand Up @@ -197,9 +201,7 @@ where
},
transactions: Some(txs),
no_tx_pool: Some(true),
gas_limit: Some(u64::from_be_bytes(
alloy_primitives::U64::from(sys_config.gas_limit).to_be_bytes(),
)),
gas_limit: Some(upgrade_gas),
eip_1559_params: sys_config.eip_1559_params(
&self.rollup_cfg,
l2_parent.block_info.timestamp,
Expand Down Expand Up @@ -250,7 +252,7 @@ mod tests {
};
use alloc::vec;
use alloy_consensus::Header;
use alloy_primitives::{B256, Log, LogData, U64, U256};
use alloy_primitives::{B64, B256, Log, LogData, U64, U256};
use kona_genesis::{HardForkConfig, SystemConfig};
use kona_protocol::{BlockInfo, DepositError};

Expand Down Expand Up @@ -628,4 +630,55 @@ mod tests {
assert_eq!(payload.transactions.as_ref().unwrap().len(), 10);
assert_eq!(payload, expected);
}

#[tokio::test]
async fn test_prepare_payload_with_isthmus() {
let block_time = 2;
let timestamp = 100;
let cfg = Arc::new(RollupConfig {
block_time,
hardforks: HardForkConfig { isthmus_time: Some(102), ..Default::default() },
..Default::default()
});
let l2_number = 1;
let mut fetcher = TestSystemConfigL2Fetcher::default();
fetcher.insert(l2_number, SystemConfig::default());
let mut provider = TestChainProvider::default();
let header = Header { timestamp, ..Default::default() };
let prev_randao = header.mix_hash;
let hash = header.hash_slow();
provider.insert_header(hash, header);
let mut builder = StatefulAttributesBuilder::new(cfg, fetcher, provider);
let epoch = BlockNumHash { hash, number: l2_number };
let l2_parent = L2BlockInfo {
block_info: BlockInfo {
hash: B256::ZERO,
number: l2_number,
timestamp,
parent_hash: hash,
},
l1_origin: BlockNumHash { hash, number: l2_number },
seq_num: 0,
};
let next_l2_time = l2_parent.block_info.timestamp + block_time;
let payload = builder.prepare_payload_attributes(l2_parent, epoch).await.unwrap();
let gas_limit = u64::from_be_bytes(
alloy_primitives::U64::from(SystemConfig::default().gas_limit).to_be_bytes(),
) + 3040000;
let expected = OpPayloadAttributes {
payload_attributes: PayloadAttributes {
timestamp: next_l2_time,
prev_randao,
suggested_fee_recipient: SEQUENCER_FEE_VAULT_ADDRESS,
parent_beacon_block_root: Some(B256::ZERO),
withdrawals: Some(vec![]),
},
transactions: payload.transactions.clone(),
no_tx_pool: Some(true),
gas_limit: Some(gas_limit),
eip_1559_params: Some(B64::ZERO),
};
assert_eq!(payload.transactions.as_ref().unwrap().len(), 18);
assert_eq!(payload, expected);
}
}
14 changes: 14 additions & 0 deletions crates/protocol/genesis/src/rollup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,20 @@
self.da_challenge_address.is_some_and(|addr| !addr.is_zero())
}

/// Returns true if the specified block is the first block subject to the Isthmus upgrade.
pub fn is_isthmus_activation_block(&self, l2_block_time: u64) -> bool {
self.is_isthmus_active(l2_block_time) &&
l2_block_time >= self.block_time &&
!self.is_isthmus_active(l2_block_time - self.block_time)
}

/// Returns true if the specified block is the first block subject to the Interop upgrade.
pub fn is_interop_activation_block(&self, l2_block_time: u64) -> bool {
self.is_interop_active(l2_block_time) &&
l2_block_time >= self.block_time &&
!self.is_interop_active(l2_block_time - self.block_time)

Check warning on line 265 in crates/protocol/genesis/src/rollup.rs

View check run for this annotation

Codecov / codecov/patch

crates/protocol/genesis/src/rollup.rs#L264-L265

Added lines #L264 - L265 were not covered by tests
}

/// Returns the max sequencer drift for the given timestamp.
pub fn max_sequencer_drift(&self, timestamp: u64) -> u64 {
if self.is_fjord_active(timestamp) {
Expand Down
84 changes: 84 additions & 0 deletions crates/protocol/protocol/src/batch/single.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@
return BatchValidity::Drop;
}

// Future forks that contain upgrade transactions must be added here.
let contains_txs = self.transactions.iter().any(|tx| !tx.is_empty());
let isthmus_act_block = cfg.is_isthmus_activation_block(self.timestamp);
let interop_act_block = cfg.is_interop_activation_block(self.timestamp);
if contains_txs && (isthmus_act_block || interop_act_block) {
return BatchValidity::Drop;

Check warning on line 130 in crates/protocol/protocol/src/batch/single.rs

View check run for this annotation

Codecov / codecov/patch

crates/protocol/protocol/src/batch/single.rs#L130

Added line #L130 was not covered by tests
}

// Check if we ran out of sequencer time drift
let max_drift = cfg.max_sequencer_drift(batch_origin.timestamp);
let max = if let Some(max) = batch_origin.timestamp.checked_add(max_drift) {
Expand Down Expand Up @@ -466,6 +474,82 @@
);
}

#[test]
fn test_check_batch_activation_block_dropped_isthmus() {
// Use the example transaction
let mut transactions = example_transactions();

// Extend the transactions with the 7702 transaction
let eip_7702_tx = eip_7702_tx();
let sig = PrimitiveSignature::test_signature();
let tx_signed = eip_7702_tx.into_signed(sig);
let envelope: TxEnvelope = tx_signed.into();
let encoded = envelope.encoded_2718();
transactions.push(encoded.into());

// Construct a basic `SingleBatch`
let parent_hash = BlockHash::ZERO;
let epoch_num = 1;
let epoch_hash = BlockHash::ZERO;
let timestamp = 2;

let single_batch =
SingleBatch { parent_hash, epoch_num, epoch_hash, timestamp, transactions };

// Notice: Isthmus is active.
let cfg = RollupConfig {
max_sequencer_drift: 1,
block_time: 1,
hardforks: HardForkConfig { isthmus_time: Some(1), ..Default::default() },
..Default::default()
};
let l1_blocks = vec![BlockInfo::default(), BlockInfo::default()];
let l2_safe_head = L2BlockInfo {
block_info: BlockInfo { timestamp: 1, ..Default::default() },
..Default::default()
};
let inclusion_block = BlockInfo::default();
assert_eq!(
single_batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block),
BatchValidity::Drop
);
}

#[test]
fn test_check_batch_valid_activation_block_isthmus() {
// Construct a basic `SingleBatch`
let parent_hash = BlockHash::ZERO;
let epoch_num = 1;
let epoch_hash = BlockHash::ZERO;
let timestamp = 2;

let single_batch = SingleBatch {
parent_hash,
epoch_num,
epoch_hash,
timestamp,
transactions: Default::default(),
};

// Notice: Isthmus is active.
let cfg = RollupConfig {
max_sequencer_drift: 2,
block_time: 1,
hardforks: HardForkConfig { isthmus_time: Some(1), ..Default::default() },
..Default::default()
};
let l1_blocks = vec![BlockInfo::default(), BlockInfo::default()];
let l2_safe_head = L2BlockInfo {
block_info: BlockInfo { timestamp: 1, ..Default::default() },
..Default::default()
};
let inclusion_block = BlockInfo::default();
assert_eq!(
single_batch.check_batch(&cfg, &l1_blocks, l2_safe_head, &inclusion_block),
BatchValidity::Accept
);
}

#[test]
fn test_check_batch_accept_7702_post_isthmus() {
// Use the example transaction
Expand Down
Loading