Skip to content
This repository was archived by the owner on Jan 16, 2026. It is now read-only.
Merged
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
21 changes: 20 additions & 1 deletion crates/derive/src/online/alloy_providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

use crate::{
traits::{ChainProvider, L2ChainProvider},
types::{Block, BlockInfo, L2BlockInfo, L2ExecutionPayloadEnvelope, OpBlock, RollupConfig},
types::{
Block, BlockInfo, L2BlockInfo, L2ExecutionPayloadEnvelope, OpBlock, RollupConfig,
SystemConfig,
},
};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloy_consensus::{Header, Receipt, ReceiptWithBloom, TxEnvelope, TxType};
Expand Down Expand Up @@ -165,6 +168,8 @@ pub struct AlloyL2ChainProvider<T: Provider<Http<reqwest::Client>>> {
payload_by_number_cache: LruCache<u64, L2ExecutionPayloadEnvelope>,
/// `l2_block_info_by_number` LRU cache.
l2_block_info_by_number_cache: LruCache<u64, L2BlockInfo>,
/// `system_config_by_l2_hash` LRU cache.
system_config_by_number_cache: LruCache<u64, SystemConfig>,
}

impl<T: Provider<Http<reqwest::Client>>> AlloyL2ChainProvider<T> {
Expand All @@ -175,6 +180,7 @@ impl<T: Provider<Http<reqwest::Client>>> AlloyL2ChainProvider<T> {
rollup_config,
payload_by_number_cache: LruCache::new(NonZeroUsize::new(CACHE_SIZE).unwrap()),
l2_block_info_by_number_cache: LruCache::new(NonZeroUsize::new(CACHE_SIZE).unwrap()),
system_config_by_number_cache: LruCache::new(NonZeroUsize::new(CACHE_SIZE).unwrap()),
}
}
}
Expand Down Expand Up @@ -209,4 +215,17 @@ impl<T: Provider<Http<reqwest::Client>>> L2ChainProvider for AlloyL2ChainProvide
self.payload_by_number_cache.put(number, payload_envelope.clone());
Ok(payload_envelope)
}

async fn system_config_by_number(
&mut self,
number: u64,
rollup_config: Arc<RollupConfig>,
) -> Result<SystemConfig> {
if let Some(system_config) = self.system_config_by_number_cache.get(&number) {
return Ok(*system_config);
}

let envelope = self.payload_by_number(number).await?;
envelope.to_system_config(&rollup_config)
}
}
2 changes: 1 addition & 1 deletion crates/derive/src/sources/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ where
blob_provider: blobs,
ecotone_timestamp: cfg.ecotone_time,
plasma_enabled: cfg.is_plasma_enabled(),
signer: cfg.l1_signer_address(),
signer: cfg.genesis.system_config.batcher_addr,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/derive/src/stages/attributes_queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod deposits;
pub(crate) use deposits::derive_deposits;

mod builder;
pub use builder::{AttributesBuilder, StatefulAttributesBuilder, SystemConfigL2Fetcher};
pub use builder::{AttributesBuilder, StatefulAttributesBuilder};

/// [AttributesProvider] is a trait abstraction that generalizes the [BatchQueue] stage.
///
Expand Down
125 changes: 68 additions & 57 deletions crates/derive/src/stages/attributes_queue/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
use super::derive_deposits;
use crate::{
params::SEQUENCER_FEE_VAULT_ADDRESS,
traits::ChainProvider,
traits::{ChainProvider, L2ChainProvider},
types::{
BlockID, BuilderError, EcotoneTransactionBuilder, L1BlockInfoTx, L2BlockInfo,
L2PayloadAttributes, RawTransaction, RollupConfig, SystemConfig,
L2PayloadAttributes, RawTransaction, RollupConfig,
},
};
use alloc::{boxed::Box, fmt::Debug, sync::Arc, vec, vec::Vec};
use alloy_primitives::B256;
use alloy_rlp::Encodable;
use async_trait::async_trait;

Expand All @@ -32,43 +31,37 @@ pub trait AttributesBuilder {
) -> Result<L2PayloadAttributes, BuilderError>;
}

/// The [SystemConfigL2Fetcher] fetches the system config by L2 hash.
pub trait SystemConfigL2Fetcher {
/// Fetch the system config by L2 hash.
fn system_config_by_l2_hash(&self, hash: B256) -> anyhow::Result<SystemConfig>;
}

/// A stateful implementation of the [AttributesBuilder].
#[derive(Debug, Default)]
pub struct StatefulAttributesBuilder<S, R>
pub struct StatefulAttributesBuilder<L1P, L2P>
where
S: SystemConfigL2Fetcher + Debug,
R: ChainProvider + Debug,
L1P: ChainProvider + Debug,
L2P: L2ChainProvider + Debug,
{
/// The rollup config.
rollup_cfg: Arc<RollupConfig>,
/// The system config fetcher.
config_fetcher: S,
config_fetcher: L2P,
/// The L1 receipts fetcher.
receipts_fetcher: R,
receipts_fetcher: L1P,
}

impl<S, R> StatefulAttributesBuilder<S, R>
impl<L1P, L2P> StatefulAttributesBuilder<L1P, L2P>
where
S: SystemConfigL2Fetcher + Debug,
R: ChainProvider + Debug,
L1P: ChainProvider + Debug,
L2P: L2ChainProvider + Debug,
{
/// Create a new [StatefulAttributesBuilder] with the given epoch.
pub fn new(rcfg: Arc<RollupConfig>, cfg: S, receipts: R) -> Self {
Self { rollup_cfg: rcfg, config_fetcher: cfg, receipts_fetcher: receipts }
pub fn new(rcfg: Arc<RollupConfig>, sys_cfg_fetcher: L2P, receipts: L1P) -> Self {
Self { rollup_cfg: rcfg, config_fetcher: sys_cfg_fetcher, receipts_fetcher: receipts }
}
}

#[async_trait]
impl<S, R> AttributesBuilder for StatefulAttributesBuilder<S, R>
impl<L1P, L2P> AttributesBuilder for StatefulAttributesBuilder<L1P, L2P>
where
S: SystemConfigL2Fetcher + Send + Debug,
R: ChainProvider + Send + Debug,
L1P: ChainProvider + Debug + Send,
L2P: L2ChainProvider + Debug + Send,
{
async fn prepare_payload_attributes(
&mut self,
Expand All @@ -77,8 +70,10 @@ where
) -> Result<L2PayloadAttributes, BuilderError> {
let l1_header;
let deposit_transactions: Vec<RawTransaction>;
let mut sys_config =
self.config_fetcher.system_config_by_l2_hash(l2_parent.block_info.hash)?;
let mut sys_config = self
.config_fetcher
.system_config_by_number(l2_parent.block_info.number, self.rollup_cfg.clone())
.await?;

// If the L1 origin changed in this block, then we are in the first block of the epoch.
// In this case we need to fetch all transaction receipts from the L1 origin block so
Expand Down Expand Up @@ -177,27 +172,28 @@ where
mod tests {
use super::*;
use crate::{
stages::test_utils::MockSystemConfigL2Fetcher, traits::test_utils::TestChainProvider,
types::BlockInfo,
stages::test_utils::MockSystemConfigL2Fetcher,
traits::test_utils::TestChainProvider,
types::{BlockInfo, SystemConfig},
};
use alloy_consensus::Header;
use alloy_primitives::b256;
use alloy_primitives::B256;

#[tokio::test]
async fn test_prepare_payload_block_mismatch_epoch_reset() {
let cfg = Arc::new(RollupConfig::default());
let l2_hash = b256!("0000000000000000000000000000000000000000000000000000000000000002");
let l2_number = 1;
let mut fetcher = MockSystemConfigL2Fetcher::default();
fetcher.insert(l2_hash, SystemConfig::default());
fetcher.insert(l2_number, SystemConfig::default());
let mut provider = TestChainProvider::default();
let header = Header::default();
let hash = header.hash_slow();
provider.insert_header(hash, header);
let mut builder = StatefulAttributesBuilder::new(cfg, fetcher, provider);
let epoch = BlockID { hash, number: 1 };
let epoch = BlockID { hash, number: l2_number };
let l2_parent = L2BlockInfo {
block_info: BlockInfo { hash: l2_hash, number: 1, ..Default::default() },
l1_origin: BlockID { hash: l2_hash, number: 2 },
block_info: BlockInfo { hash: B256::ZERO, number: l2_number, ..Default::default() },
l1_origin: BlockID { hash: B256::left_padding_from(&[0xFF]), number: 2 },
seq_num: 0,
};
// This should error because the l2 parent's l1_origin.hash should equal the epoch header
Expand All @@ -211,18 +207,18 @@ mod tests {
#[tokio::test]
async fn test_prepare_payload_block_mismatch() {
let cfg = Arc::new(RollupConfig::default());
let l2_hash = b256!("0000000000000000000000000000000000000000000000000000000000000002");
let l2_number = 1;
let mut fetcher = MockSystemConfigL2Fetcher::default();
fetcher.insert(l2_hash, SystemConfig::default());
fetcher.insert(l2_number, SystemConfig::default());
let mut provider = TestChainProvider::default();
let header = Header::default();
let hash = header.hash_slow();
provider.insert_header(hash, header);
let mut builder = StatefulAttributesBuilder::new(cfg, fetcher, provider);
let epoch = BlockID { hash, number: 1 };
let epoch = BlockID { hash, number: l2_number };
let l2_parent = L2BlockInfo {
block_info: BlockInfo { hash: l2_hash, number: 1, ..Default::default() },
l1_origin: BlockID { hash: l2_hash, number: 1 },
block_info: BlockInfo { hash: B256::ZERO, number: l2_number, ..Default::default() },
l1_origin: BlockID { hash: B256::ZERO, number: l2_number },
seq_num: 0,
};
// This should error because the l2 parent's l1_origin.hash should equal the epoch hash
Expand All @@ -237,18 +233,18 @@ mod tests {
let block_time = 10;
let timestamp = 100;
let cfg = Arc::new(RollupConfig { block_time, ..Default::default() });
let l2_hash = b256!("0000000000000000000000000000000000000000000000000000000000000002");
let l2_number = 1;
let mut fetcher = MockSystemConfigL2Fetcher::default();
fetcher.insert(l2_hash, SystemConfig::default());
fetcher.insert(l2_number, SystemConfig::default());
let mut provider = TestChainProvider::default();
let header = Header { timestamp, ..Default::default() };
let hash = header.hash_slow();
provider.insert_header(hash, header);
let mut builder = StatefulAttributesBuilder::new(cfg, fetcher, provider);
let epoch = BlockID { hash, number: 1 };
let epoch = BlockID { hash, number: l2_number };
let l2_parent = L2BlockInfo {
block_info: BlockInfo { hash: l2_hash, number: 1, ..Default::default() },
l1_origin: BlockID { hash, number: 1 },
block_info: BlockInfo { hash: B256::ZERO, number: l2_number, ..Default::default() },
l1_origin: BlockID { hash, number: l2_number },
seq_num: 0,
};
let next_l2_time = l2_parent.block_info.timestamp + block_time;
Expand All @@ -268,19 +264,24 @@ mod tests {
let block_time = 10;
let timestamp = 100;
let cfg = Arc::new(RollupConfig { block_time, ..Default::default() });
let l2_hash = b256!("0000000000000000000000000000000000000000000000000000000000000002");
let l2_number = 1;
let mut fetcher = MockSystemConfigL2Fetcher::default();
fetcher.insert(l2_hash, SystemConfig::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 = BlockID { hash, number: 1 };
let epoch = BlockID { hash, number: l2_number };
let l2_parent = L2BlockInfo {
block_info: BlockInfo { hash: l2_hash, number: 1, timestamp, parent_hash: hash },
l1_origin: BlockID { hash, number: 1 },
block_info: BlockInfo {
hash: B256::ZERO,
number: l2_number,
timestamp,
parent_hash: hash,
},
l1_origin: BlockID { hash, number: l2_number },
seq_num: 0,
};
let next_l2_time = l2_parent.block_info.timestamp + block_time;
Expand All @@ -306,19 +307,24 @@ mod tests {
let block_time = 10;
let timestamp = 100;
let cfg = Arc::new(RollupConfig { block_time, canyon_time: Some(0), ..Default::default() });
let l2_hash = b256!("0000000000000000000000000000000000000000000000000000000000000002");
let l2_number = 1;
let mut fetcher = MockSystemConfigL2Fetcher::default();
fetcher.insert(l2_hash, SystemConfig::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 = BlockID { hash, number: 1 };
let epoch = BlockID { hash, number: l2_number };
let l2_parent = L2BlockInfo {
block_info: BlockInfo { hash: l2_hash, number: 1, timestamp, parent_hash: hash },
l1_origin: BlockID { hash, number: 1 },
block_info: BlockInfo {
hash: B256::ZERO,
number: l2_number,
timestamp,
parent_hash: hash,
},
l1_origin: BlockID { hash, number: l2_number },
seq_num: 0,
};
let next_l2_time = l2_parent.block_info.timestamp + block_time;
Expand All @@ -345,20 +351,25 @@ mod tests {
let timestamp = 100;
let cfg =
Arc::new(RollupConfig { block_time, ecotone_time: Some(0), ..Default::default() });
let l2_hash = b256!("0000000000000000000000000000000000000000000000000000000000000002");
let l2_number = 1;
let mut fetcher = MockSystemConfigL2Fetcher::default();
fetcher.insert(l2_hash, SystemConfig::default());
fetcher.insert(l2_number, SystemConfig::default());
let mut provider = TestChainProvider::default();
let header = Header { timestamp, ..Default::default() };
let parent_beacon_block_root = Some(header.parent_beacon_block_root.unwrap_or_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 = BlockID { hash, number: 1 };
let epoch = BlockID { hash, number: l2_number };
let l2_parent = L2BlockInfo {
block_info: BlockInfo { hash: l2_hash, number: 1, timestamp, parent_hash: hash },
l1_origin: BlockID { hash, number: 1 },
block_info: BlockInfo {
hash: B256::ZERO,
number: l2_number,
timestamp,
parent_hash: hash,
},
l1_origin: BlockID { hash, number: l2_number },
seq_num: 0,
};
let next_l2_time = l2_parent.block_info.timestamp + block_time;
Expand Down
1 change: 0 additions & 1 deletion crates/derive/src/stages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ pub use batch_queue::{BatchQueue, BatchQueueProvider};
mod attributes_queue;
pub use attributes_queue::{
AttributesBuilder, AttributesProvider, AttributesQueue, StatefulAttributesBuilder,
SystemConfigL2Fetcher,
};

#[cfg(test)]
Expand Down
40 changes: 29 additions & 11 deletions crates/derive/src/stages/test_utils/sys_config_fetcher.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
//! Implements a mock [L2SystemConfigFetcher] for testing.

use crate::{stages::attributes_queue::SystemConfigL2Fetcher, types::SystemConfig};
use alloy_primitives::B256;
use crate::{
traits::L2ChainProvider,
types::{L2BlockInfo, L2ExecutionPayloadEnvelope, RollupConfig, SystemConfig},
};
use alloc::{boxed::Box, sync::Arc};
use anyhow::Result;
use async_trait::async_trait;
use hashbrown::HashMap;

/// A mock implementation of the [`SystemConfigL2Fetcher`] for testing.
#[derive(Debug, Default)]
pub struct MockSystemConfigL2Fetcher {
/// A map from [B256] block hash to a [SystemConfig].
pub system_configs: HashMap<B256, SystemConfig>,
/// A map from [u64] block number to a [SystemConfig].
pub system_configs: HashMap<u64, SystemConfig>,
}

impl MockSystemConfigL2Fetcher {
/// Inserts a new system config into the mock fetcher with the given hash.
pub fn insert(&mut self, hash: B256, config: SystemConfig) {
self.system_configs.insert(hash, config);
/// Inserts a new system config into the mock fetcher with the given block number.
pub fn insert(&mut self, number: u64, config: SystemConfig) {
self.system_configs.insert(number, config);
}

/// Clears all system configs from the mock fetcher.
Expand All @@ -23,11 +28,24 @@ impl MockSystemConfigL2Fetcher {
}
}

impl SystemConfigL2Fetcher for MockSystemConfigL2Fetcher {
fn system_config_by_l2_hash(&self, hash: B256) -> anyhow::Result<SystemConfig> {
#[async_trait]
impl L2ChainProvider for MockSystemConfigL2Fetcher {
async fn system_config_by_number(
&mut self,
number: u64,
_: Arc<RollupConfig>,
) -> Result<SystemConfig> {
self.system_configs
.get(&hash)
.get(&number)
.cloned()
.ok_or_else(|| anyhow::anyhow!("system config not found"))
.ok_or_else(|| anyhow::anyhow!("system config not found: {number}"))
}

async fn l2_block_info_by_number(&mut self, _: u64) -> Result<L2BlockInfo> {
unimplemented!()
}

async fn payload_by_number(&mut self, _: u64) -> Result<L2ExecutionPayloadEnvelope> {
unimplemented!()
}
}
Loading