kona_derive/sources/
ethereum.rs

1//! Contains the [EthereumDataSource], which is a concrete implementation of the
2//! [DataAvailabilityProvider] trait for the Ethereum protocol.
3
4use crate::{
5    BlobProvider, BlobSource, CalldataSource, ChainProvider, DataAvailabilityProvider,
6    PipelineResult,
7};
8use alloc::{boxed::Box, fmt::Debug};
9use alloy_primitives::{Address, Bytes};
10use async_trait::async_trait;
11use kona_genesis::RollupConfig;
12use kona_protocol::BlockInfo;
13
14/// A factory for creating an Ethereum data source provider.
15#[derive(Debug, Clone)]
16pub struct EthereumDataSource<C, B>
17where
18    C: ChainProvider + Send + Clone,
19    B: BlobProvider + Send + Clone,
20{
21    /// The ecotone timestamp.
22    pub ecotone_timestamp: Option<u64>,
23    /// The blob source.
24    pub blob_source: BlobSource<C, B>,
25    /// The calldata source.
26    pub calldata_source: CalldataSource<C>,
27}
28
29impl<C, B> EthereumDataSource<C, B>
30where
31    C: ChainProvider + Send + Clone + Debug,
32    B: BlobProvider + Send + Clone + Debug,
33{
34    /// Instantiates a new [`EthereumDataSource`].
35    pub const fn new(
36        blob_source: BlobSource<C, B>,
37        calldata_source: CalldataSource<C>,
38        cfg: &RollupConfig,
39    ) -> Self {
40        Self { ecotone_timestamp: cfg.hardforks.ecotone_time, blob_source, calldata_source }
41    }
42
43    /// Instantiates a new [`EthereumDataSource`] from parts.
44    pub fn new_from_parts(provider: C, blobs: B, cfg: &RollupConfig) -> Self {
45        Self {
46            ecotone_timestamp: cfg.hardforks.ecotone_time,
47            blob_source: BlobSource::new(provider.clone(), blobs, cfg.batch_inbox_address),
48            calldata_source: CalldataSource::new(provider, cfg.batch_inbox_address),
49        }
50    }
51}
52
53#[async_trait]
54impl<C, B> DataAvailabilityProvider for EthereumDataSource<C, B>
55where
56    C: ChainProvider + Send + Sync + Clone + Debug,
57    B: BlobProvider + Send + Sync + Clone + Debug,
58{
59    type Item = Bytes;
60
61    async fn next(
62        &mut self,
63        block_ref: &BlockInfo,
64        batcher_address: Address,
65    ) -> PipelineResult<Self::Item> {
66        let ecotone_enabled =
67            self.ecotone_timestamp.map(|e| block_ref.timestamp >= e).unwrap_or(false);
68        if ecotone_enabled {
69            self.blob_source.next(block_ref, batcher_address).await
70        } else {
71            self.calldata_source.next(block_ref, batcher_address).await
72        }
73    }
74
75    fn clear(&mut self) {
76        self.blob_source.clear();
77        self.calldata_source.clear();
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::{
85        BlobData,
86        test_utils::{TestBlobProvider, TestChainProvider},
87    };
88    use alloc::vec;
89    use alloy_consensus::TxEnvelope;
90    use alloy_eips::eip2718::Decodable2718;
91    use alloy_primitives::{Address, address};
92    use kona_genesis::{HardForkConfig, RollupConfig, SystemConfig};
93    use kona_protocol::BlockInfo;
94
95    fn default_test_blob_source() -> BlobSource<TestChainProvider, TestBlobProvider> {
96        let chain_provider = TestChainProvider::default();
97        let blob_fetcher = TestBlobProvider::default();
98        let batcher_address = Address::default();
99        BlobSource::new(chain_provider, blob_fetcher, batcher_address)
100    }
101
102    #[tokio::test]
103    async fn test_clear_ethereum_data_source() {
104        let chain = TestChainProvider::default();
105        let blob = TestBlobProvider::default();
106        let cfg = RollupConfig::default();
107        let mut calldata = CalldataSource::new(chain.clone(), Address::ZERO);
108        calldata.calldata.insert(0, Default::default());
109        calldata.open = true;
110        let mut blob = BlobSource::new(chain, blob, Address::ZERO);
111        blob.data = vec![Default::default()];
112        blob.open = true;
113        let mut data_source = EthereumDataSource::new(blob, calldata, &cfg);
114
115        data_source.clear();
116        assert!(data_source.blob_source.data.is_empty());
117        assert!(!data_source.blob_source.open);
118        assert!(data_source.calldata_source.calldata.is_empty());
119        assert!(!data_source.calldata_source.open);
120    }
121
122    #[tokio::test]
123    async fn test_open_blob_source() {
124        let chain = TestChainProvider::default();
125        let mut blob = default_test_blob_source();
126        blob.open = true;
127        blob.data.push(BlobData { data: None, calldata: Some(Bytes::default()) });
128        let calldata = CalldataSource::new(chain.clone(), Address::ZERO);
129        let cfg = RollupConfig {
130            hardforks: HardForkConfig { ecotone_time: Some(0), ..Default::default() },
131            ..Default::default()
132        };
133
134        // Should successfully retrieve a blob batch from the block
135        let mut data_source = EthereumDataSource::new(blob, calldata, &cfg);
136        let data = data_source.next(&BlockInfo::default(), Address::ZERO).await.unwrap();
137        assert_eq!(data, Bytes::default());
138    }
139
140    #[tokio::test]
141    async fn test_open_ethereum_calldata_source_pre_ecotone() {
142        let mut chain = TestChainProvider::default();
143        let blob = TestBlobProvider::default();
144        let batcher_address = address!("6887246668a3b87F54DeB3b94Ba47a6f63F32985");
145        let batch_inbox = address!("FF00000000000000000000000000000000000010");
146        let block_ref = BlockInfo { number: 10, ..Default::default() };
147
148        let mut cfg = RollupConfig::default();
149        cfg.genesis.system_config = Some(SystemConfig { batcher_address, ..Default::default() });
150        cfg.batch_inbox_address = batch_inbox;
151
152        // load a test batcher transaction
153        let raw_batcher_tx = include_bytes!("../../testdata/raw_batcher_tx.hex");
154        let tx = TxEnvelope::decode_2718(&mut raw_batcher_tx.as_ref()).unwrap();
155        chain.insert_block_with_transactions(10, block_ref, vec![tx]);
156
157        // Should successfully retrieve a calldata batch from the block
158        let mut data_source = EthereumDataSource::new_from_parts(chain, blob, &cfg);
159        let calldata_batch = data_source.next(&block_ref, batcher_address).await.unwrap();
160        assert_eq!(calldata_batch.len(), 119823);
161    }
162}