From 56c42f6860cc672e96cc49d70412f67300697053 Mon Sep 17 00:00:00 2001 From: refcell Date: Fri, 4 Oct 2024 16:31:53 -0400 Subject: [PATCH] chore: refactor test providers (#623) --- Cargo.lock | 1 + crates/derive/src/attributes/mod.rs | 6 +- crates/derive/src/batch/span_batch/batch.rs | 6 +- crates/derive/src/sources/ethereum.rs | 6 +- crates/derive/src/stages/batch_queue.rs | 10 +- crates/derive/src/stages/batch_stream.rs | 2 +- crates/derive/src/stages/l1_traversal.rs | 3 +- crates/derive/src/traits/test_utils.rs | 178 +------------------ crates/providers-alloy/Cargo.toml | 4 + crates/providers-alloy/src/pipeline.rs | 42 +++++ crates/providers/Cargo.toml | 7 + crates/providers/src/lib.rs | 3 + crates/providers/src/test_utils.rs | 181 ++++++++++++++++++++ 13 files changed, 253 insertions(+), 196 deletions(-) create mode 100644 crates/providers/src/test_utils.rs diff --git a/Cargo.lock b/Cargo.lock index 342f69965..ab1835bbe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2195,6 +2195,7 @@ version = "0.0.1" dependencies = [ "alloy-consensus", "alloy-primitives", + "anyhow", "async-trait", "op-alloy-consensus", "op-alloy-genesis", diff --git a/crates/derive/src/attributes/mod.rs b/crates/derive/src/attributes/mod.rs index 41818724d..43e5f9334 100644 --- a/crates/derive/src/attributes/mod.rs +++ b/crates/derive/src/attributes/mod.rs @@ -266,13 +266,11 @@ async fn derive_deposits( #[cfg(test)] mod tests { use super::*; - use crate::{ - errors::ResetError, stages::test_utils::MockSystemConfigL2Fetcher, - traits::test_utils::TestChainProvider, - }; + use crate::{errors::ResetError, stages::test_utils::MockSystemConfigL2Fetcher}; use alloc::vec; use alloy_consensus::Header; use alloy_primitives::{Log, LogData, B256, U256, U64}; + use kona_providers::test_utils::TestChainProvider; use op_alloy_genesis::SystemConfig; use op_alloy_protocol::{BlockInfo, DepositError}; diff --git a/crates/derive/src/batch/span_batch/batch.rs b/crates/derive/src/batch/span_batch/batch.rs index a4054ad2c..d470fc68f 100644 --- a/crates/derive/src/batch/span_batch/batch.rs +++ b/crates/derive/src/batch/span_batch/batch.rs @@ -539,14 +539,12 @@ impl SpanBatch { #[cfg(test)] mod tests { use super::*; - use crate::{ - stages::test_utils::{CollectingLayer, TraceStorage}, - traits::test_utils::TestL2ChainProvider, - }; + use crate::stages::test_utils::{CollectingLayer, TraceStorage}; use alloc::vec; use alloy_consensus::Header; use alloy_eips::BlockNumHash; use alloy_primitives::{b256, Bytes}; + use kona_providers::test_utils::TestL2ChainProvider; use op_alloy_consensus::{OpBlock, OpTxType}; use op_alloy_genesis::ChainGenesis; use tracing::Level; diff --git a/crates/derive/src/sources/ethereum.rs b/crates/derive/src/sources/ethereum.rs index b679a6e3d..bf0045c12 100644 --- a/crates/derive/src/sources/ethereum.rs +++ b/crates/derive/src/sources/ethereum.rs @@ -90,15 +90,13 @@ mod tests { use alloy_consensus::TxEnvelope; use alloy_eips::eip2718::Decodable2718; use alloy_primitives::address; + use kona_providers::test_utils::TestChainProvider; use op_alloy_genesis::{RollupConfig, SystemConfig}; use op_alloy_protocol::BlockInfo; use crate::{ sources::{EthereumDataSource, EthereumDataSourceVariant}, - traits::{ - test_utils::{TestBlobProvider, TestChainProvider}, - AsyncIterator, DataAvailabilityProvider, - }, + traits::{test_utils::TestBlobProvider, AsyncIterator, DataAvailabilityProvider}, }; #[tokio::test] diff --git a/crates/derive/src/stages/batch_queue.rs b/crates/derive/src/stages/batch_queue.rs index 64bd75aa3..cc960afc7 100644 --- a/crates/derive/src/stages/batch_queue.rs +++ b/crates/derive/src/stages/batch_queue.rs @@ -490,18 +490,16 @@ where #[cfg(test)] mod tests { use super::*; - use crate::{ - stages::{ - channel_reader::BatchReader, - test_utils::{CollectingLayer, MockBatchQueueProvider, TraceStorage}, - }, - traits::test_utils::TestL2ChainProvider, + use crate::stages::{ + channel_reader::BatchReader, + test_utils::{CollectingLayer, MockBatchQueueProvider, TraceStorage}, }; use alloc::vec; use alloy_consensus::Header; use alloy_eips::{eip2718::Decodable2718, BlockNumHash}; use alloy_primitives::{address, b256, Address, Bytes, TxKind, B256, U256}; use alloy_rlp::{BytesMut, Encodable}; + use kona_providers::test_utils::TestL2ChainProvider; use op_alloy_consensus::{OpBlock, OpTxEnvelope, OpTxType, TxDeposit}; use op_alloy_genesis::ChainGenesis; use op_alloy_protocol::{L1BlockInfoBedrock, L1BlockInfoTx}; diff --git a/crates/derive/src/stages/batch_stream.rs b/crates/derive/src/stages/batch_stream.rs index 4b6cb3509..96c1a786d 100644 --- a/crates/derive/src/stages/batch_stream.rs +++ b/crates/derive/src/stages/batch_stream.rs @@ -227,8 +227,8 @@ mod test { use crate::{ batch::{SingleBatch, SpanBatchElement}, stages::test_utils::{CollectingLayer, MockBatchStreamProvider, TraceStorage}, - traits::test_utils::TestL2ChainProvider, }; + use kona_providers::test_utils::TestL2ChainProvider; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::test] diff --git a/crates/derive/src/stages/l1_traversal.rs b/crates/derive/src/stages/l1_traversal.rs index 538247f8a..02d633301 100644 --- a/crates/derive/src/stages/l1_traversal.rs +++ b/crates/derive/src/stages/l1_traversal.rs @@ -147,10 +147,11 @@ impl ResettableStage for L1Traversal { #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::{errors::PipelineErrorKind, traits::test_utils::TestChainProvider}; + use crate::errors::PipelineErrorKind; use alloc::vec; use alloy_consensus::Receipt; use alloy_primitives::{address, b256, hex, Bytes, Log, LogData, B256}; + use kona_providers::test_utils::TestChainProvider; use op_alloy_genesis::system::{CONFIG_UPDATE_EVENT_VERSION_0, CONFIG_UPDATE_TOPIC}; const L1_SYS_CONFIG_ADDR: Address = address!("1337000000000000000000000000000000000000"); diff --git a/crates/derive/src/traits/test_utils.rs b/crates/derive/src/traits/test_utils.rs index 54b612987..03ff1721d 100644 --- a/crates/derive/src/traits/test_utils.rs +++ b/crates/derive/src/traits/test_utils.rs @@ -4,18 +4,14 @@ use crate::{ errors::{BlobProviderError, PipelineError, PipelineResult}, traits::{AsyncIterator, BlobProvider, DataAvailabilityProvider}, }; -use alloc::{boxed::Box, sync::Arc, vec, vec::Vec}; -use alloy_consensus::{Header, Receipt, TxEnvelope}; +use alloc::{boxed::Box, vec, vec::Vec}; use alloy_eips::eip4844::Blob; use alloy_primitives::{map::HashMap, Address, Bytes, B256}; use anyhow::Result; use async_trait::async_trait; use core::fmt::Debug; use kona_primitives::IndexedBlobHash; -use kona_providers::{ChainProvider, L2ChainProvider}; -use op_alloy_consensus::OpBlock; -use op_alloy_genesis::{RollupConfig, SystemConfig}; -use op_alloy_protocol::{BlockInfo, L2BlockInfo}; +use op_alloy_protocol::BlockInfo; /// Mock data iterator #[derive(Debug, Default, PartialEq)] @@ -60,117 +56,6 @@ impl DataAvailabilityProvider for TestDAP { } } -/// A mock chain provider for testing. -#[derive(Debug, Clone, Default)] -pub struct TestChainProvider { - /// Maps block numbers to block information using a tuple list. - pub blocks: Vec<(u64, BlockInfo)>, - /// Maps block hashes to header information using a tuple list. - pub headers: Vec<(B256, Header)>, - /// Maps block hashes to receipts using a tuple list. - pub receipts: Vec<(B256, Vec)>, - /// Maps block hashes to transactions using a tuple list. - pub transactions: Vec<(B256, Vec)>, -} - -impl TestChainProvider { - /// Insert a block into the mock chain provider. - pub fn insert_block(&mut self, number: u64, block: BlockInfo) { - self.blocks.push((number, block)); - } - - /// Insert a block with transactions into the mock chain provider. - pub fn insert_block_with_transactions( - &mut self, - number: u64, - block: BlockInfo, - txs: Vec, - ) { - self.blocks.push((number, block)); - self.transactions.push((block.hash, txs)); - } - - /// Insert receipts into the mock chain provider. - pub fn insert_receipts(&mut self, hash: B256, receipts: Vec) { - self.receipts.push((hash, receipts)); - } - - /// Insert a header into the mock chain provider. - pub fn insert_header(&mut self, hash: B256, header: Header) { - self.headers.push((hash, header)); - } - - /// Clears headers from the mock chain provider. - pub fn clear_headers(&mut self) { - self.headers.clear(); - } - - /// Clears blocks from the mock chain provider. - pub fn clear_blocks(&mut self) { - self.blocks.clear(); - } - - /// Clears receipts from the mock chain provider. - pub fn clear_receipts(&mut self) { - self.receipts.clear(); - } - - /// Clears all blocks and receipts from the mock chain provider. - pub fn clear(&mut self) { - self.clear_blocks(); - self.clear_receipts(); - self.clear_headers(); - } -} - -#[async_trait] -impl ChainProvider for TestChainProvider { - type Error = anyhow::Error; - - async fn header_by_hash(&mut self, hash: B256) -> Result
{ - if let Some((_, header)) = self.headers.iter().find(|(_, b)| b.hash_slow() == hash) { - Ok(header.clone()) - } else { - Err(anyhow::anyhow!("Header not found")) - } - } - - async fn block_info_by_number(&mut self, _number: u64) -> Result { - if let Some((_, block)) = self.blocks.iter().find(|(n, _)| *n == _number) { - Ok(*block) - } else { - Err(anyhow::anyhow!("Block not found")) - } - } - - async fn receipts_by_hash(&mut self, _hash: B256) -> Result> { - if let Some((_, receipts)) = self.receipts.iter().find(|(h, _)| *h == _hash) { - Ok(receipts.clone()) - } else { - Err(anyhow::anyhow!("Receipts not found")) - } - } - - async fn block_info_and_transactions_by_hash( - &mut self, - hash: B256, - ) -> Result<(BlockInfo, Vec)> { - let block = self - .blocks - .iter() - .find(|(_, b)| b.hash == hash) - .map(|(_, b)| *b) - .ok_or_else(|| anyhow::anyhow!("Block not found"))?; - let txs = self - .transactions - .iter() - .find(|(h, _)| *h == hash) - .map(|(_, txs)| txs.clone()) - .unwrap_or_default(); - Ok((block, txs)) - } -} - /// A mock blob provider for testing. #[derive(Debug, Clone, Default)] pub struct TestBlobProvider { @@ -208,62 +93,3 @@ impl BlobProvider for TestBlobProvider { Ok(blobs) } } - -/// An [L2ChainProvider] implementation for testing. -#[derive(Debug, Default)] -pub struct TestL2ChainProvider { - /// Blocks - pub blocks: Vec, - /// Short circuit the block return to be the first block. - pub short_circuit: bool, - /// Blocks - pub op_blocks: Vec, - /// System configs - pub system_configs: HashMap, -} - -impl TestL2ChainProvider { - /// Creates a new [MockBlockFetcher] with the given origin and batches. - pub const fn new( - blocks: Vec, - op_blocks: Vec, - system_configs: HashMap, - ) -> Self { - Self { blocks, short_circuit: false, op_blocks, system_configs } - } -} - -#[async_trait] -impl L2ChainProvider for TestL2ChainProvider { - type Error = anyhow::Error; - - async fn l2_block_info_by_number(&mut self, number: u64) -> Result { - if self.short_circuit { - return self.blocks.first().copied().ok_or_else(|| anyhow::anyhow!("Block not found")); - } - self.blocks - .iter() - .find(|b| b.block_info.number == number) - .cloned() - .ok_or_else(|| anyhow::anyhow!("Block not found")) - } - - async fn block_by_number(&mut self, number: u64) -> Result { - self.op_blocks - .iter() - .find(|p| p.header.number == number) - .cloned() - .ok_or_else(|| anyhow::anyhow!("L2 Block not found")) - } - - async fn system_config_by_number( - &mut self, - number: u64, - _: Arc, - ) -> Result { - self.system_configs - .get(&number) - .ok_or_else(|| anyhow::anyhow!("System config not found")) - .cloned() - } -} diff --git a/crates/providers-alloy/Cargo.toml b/crates/providers-alloy/Cargo.toml index eb228f5c3..e1818a50c 100644 --- a/crates/providers-alloy/Cargo.toml +++ b/crates/providers-alloy/Cargo.toml @@ -53,6 +53,8 @@ serde_json.workspace = true alloy-rpc-client.workspace = true alloy-node-bindings.workspace = true alloy-transport-http.workspace = true +kona-providers = { workspace = true, features = ["test-utils"] } +kona-derive = { workspace = true, features = ["serde", "test-utils"] } [features] default = [] @@ -62,4 +64,6 @@ test-utils = [ "dep:alloy-rpc-client", "dep:alloy-node-bindings", "dep:alloy-transport-http", + "kona-derive/test-utils", + "kona-providers/test-utils", ] diff --git a/crates/providers-alloy/src/pipeline.rs b/crates/providers-alloy/src/pipeline.rs index 881c24cde..cd04bf097 100644 --- a/crates/providers-alloy/src/pipeline.rs +++ b/crates/providers-alloy/src/pipeline.rs @@ -66,3 +66,45 @@ pub fn new_online_pipeline( .origin(origin) .build() } + +#[cfg(test)] +mod tests { + use super::*; + use crate::OnlineBlobProvider; + use kona_derive::prelude::OriginProvider; + + #[test] + fn test_new_online_pipeline() { + let rollup_config = Arc::new(RollupConfig::default()); + let chain_provider = + AlloyChainProvider::new_http("http://127.0.0.1:8545".try_into().unwrap()); + let l2_chain_provider = AlloyL2ChainProvider::new_http( + "http://127.0.0.1:9545".try_into().unwrap(), + rollup_config.clone(), + ); + let beacon_client = OnlineBeaconClient::new_http("http://127.0.0.1:5555".into()); + let blob_provider: OnlineBlobProvider<_, SimpleSlotDerivation> = + OnlineBlobProvider::new(beacon_client, None, None); + let blob_provider = OnlineBlobProviderWithFallback::new(blob_provider, None); + let dap_source = + EthereumDataSource::new(chain_provider.clone(), blob_provider, &rollup_config); + let builder = StatefulAttributesBuilder::new( + rollup_config.clone(), + l2_chain_provider.clone(), + chain_provider.clone(), + ); + let origin = BlockInfo::default(); + + let pipeline = new_online_pipeline( + rollup_config.clone(), + chain_provider, + dap_source, + l2_chain_provider, + builder, + origin, + ); + + assert_eq!(pipeline.rollup_config, rollup_config); + assert_eq!(pipeline.origin(), Some(origin)); + } +} diff --git a/crates/providers/Cargo.toml b/crates/providers/Cargo.toml index 97c43577c..1321c74e3 100644 --- a/crates/providers/Cargo.toml +++ b/crates/providers/Cargo.toml @@ -23,3 +23,10 @@ op-alloy-consensus = { workspace = true, features = ["k256"] } # Misc async-trait.workspace = true + +# `test-utils` feature +anyhow = { workspace = true, optional = true } + +[features] +default = [] +test-utils = ["dep:anyhow"] diff --git a/crates/providers/src/lib.rs b/crates/providers/src/lib.rs index 3f9a63983..4c3a449a7 100644 --- a/crates/providers/src/lib.rs +++ b/crates/providers/src/lib.rs @@ -19,3 +19,6 @@ pub use l1_chain_provider::ChainProvider; mod l2_chain_provider; pub use l2_chain_provider::L2ChainProvider; + +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; diff --git a/crates/providers/src/test_utils.rs b/crates/providers/src/test_utils.rs new file mode 100644 index 000000000..a736ed7ca --- /dev/null +++ b/crates/providers/src/test_utils.rs @@ -0,0 +1,181 @@ +//! Test Utilities for Provider Traits + +use crate::{ChainProvider, L2ChainProvider}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use alloy_consensus::{Header, Receipt, TxEnvelope}; +use alloy_primitives::{map::HashMap, B256}; +use anyhow::Result; +use async_trait::async_trait; +use op_alloy_consensus::OpBlock; +use op_alloy_genesis::{RollupConfig, SystemConfig}; +use op_alloy_protocol::{BlockInfo, L2BlockInfo}; + +/// A mock chain provider for testing. +#[derive(Debug, Clone, Default)] +pub struct TestChainProvider { + /// Maps block numbers to block information using a tuple list. + pub blocks: Vec<(u64, BlockInfo)>, + /// Maps block hashes to header information using a tuple list. + pub headers: Vec<(B256, Header)>, + /// Maps block hashes to receipts using a tuple list. + pub receipts: Vec<(B256, Vec)>, + /// Maps block hashes to transactions using a tuple list. + pub transactions: Vec<(B256, Vec)>, +} + +impl TestChainProvider { + /// Insert a block into the mock chain provider. + pub fn insert_block(&mut self, number: u64, block: BlockInfo) { + self.blocks.push((number, block)); + } + + /// Insert a block with transactions into the mock chain provider. + pub fn insert_block_with_transactions( + &mut self, + number: u64, + block: BlockInfo, + txs: Vec, + ) { + self.blocks.push((number, block)); + self.transactions.push((block.hash, txs)); + } + + /// Insert receipts into the mock chain provider. + pub fn insert_receipts(&mut self, hash: B256, receipts: Vec) { + self.receipts.push((hash, receipts)); + } + + /// Insert a header into the mock chain provider. + pub fn insert_header(&mut self, hash: B256, header: Header) { + self.headers.push((hash, header)); + } + + /// Clears headers from the mock chain provider. + pub fn clear_headers(&mut self) { + self.headers.clear(); + } + + /// Clears blocks from the mock chain provider. + pub fn clear_blocks(&mut self) { + self.blocks.clear(); + } + + /// Clears receipts from the mock chain provider. + pub fn clear_receipts(&mut self) { + self.receipts.clear(); + } + + /// Clears all blocks and receipts from the mock chain provider. + pub fn clear(&mut self) { + self.clear_blocks(); + self.clear_receipts(); + self.clear_headers(); + } +} + +#[async_trait] +impl ChainProvider for TestChainProvider { + type Error = anyhow::Error; + + async fn header_by_hash(&mut self, hash: B256) -> Result
{ + if let Some((_, header)) = self.headers.iter().find(|(_, b)| b.hash_slow() == hash) { + Ok(header.clone()) + } else { + Err(anyhow::anyhow!("Header not found")) + } + } + + async fn block_info_by_number(&mut self, _number: u64) -> Result { + if let Some((_, block)) = self.blocks.iter().find(|(n, _)| *n == _number) { + Ok(*block) + } else { + Err(anyhow::anyhow!("Block not found")) + } + } + + async fn receipts_by_hash(&mut self, _hash: B256) -> Result> { + if let Some((_, receipts)) = self.receipts.iter().find(|(h, _)| *h == _hash) { + Ok(receipts.clone()) + } else { + Err(anyhow::anyhow!("Receipts not found")) + } + } + + async fn block_info_and_transactions_by_hash( + &mut self, + hash: B256, + ) -> Result<(BlockInfo, Vec)> { + let block = self + .blocks + .iter() + .find(|(_, b)| b.hash == hash) + .map(|(_, b)| *b) + .ok_or_else(|| anyhow::anyhow!("Block not found"))?; + let txs = self + .transactions + .iter() + .find(|(h, _)| *h == hash) + .map(|(_, txs)| txs.clone()) + .unwrap_or_default(); + Ok((block, txs)) + } +} + +/// An [L2ChainProvider] implementation for testing. +#[derive(Debug, Default, Clone)] +pub struct TestL2ChainProvider { + /// Blocks + pub blocks: Vec, + /// Short circuit the block return to be the first block. + pub short_circuit: bool, + /// Blocks + pub op_blocks: Vec, + /// System configs + pub system_configs: HashMap, +} + +impl TestL2ChainProvider { + /// Creates a new [MockBlockFetcher] with the given origin and batches. + pub const fn new( + blocks: Vec, + op_blocks: Vec, + system_configs: HashMap, + ) -> Self { + Self { blocks, short_circuit: false, op_blocks, system_configs } + } +} + +#[async_trait] +impl L2ChainProvider for TestL2ChainProvider { + type Error = anyhow::Error; + + async fn l2_block_info_by_number(&mut self, number: u64) -> Result { + if self.short_circuit { + return self.blocks.first().copied().ok_or_else(|| anyhow::anyhow!("Block not found")); + } + self.blocks + .iter() + .find(|b| b.block_info.number == number) + .cloned() + .ok_or_else(|| anyhow::anyhow!("Block not found")) + } + + async fn block_by_number(&mut self, number: u64) -> Result { + self.op_blocks + .iter() + .find(|p| p.header.number == number) + .cloned() + .ok_or_else(|| anyhow::anyhow!("L2 Block not found")) + } + + async fn system_config_by_number( + &mut self, + number: u64, + _: Arc, + ) -> Result { + self.system_configs + .get(&number) + .ok_or_else(|| anyhow::anyhow!("System config not found")) + .cloned() + } +}