diff --git a/Cargo.lock b/Cargo.lock index 404731a766..f936d09512 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -196,16 +196,17 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260d3ff3bff0bb84599f032a2f2c6828180b0ea0cd41fdaf44f39cef3ba41861" +checksum = "a289ffd7448036f2f436b377f981c79ce0b2090877bad938d43387dc09931877" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more", - "hashbrown 0.14.5", + "foldhash", + "hashbrown 0.15.0", "hex-literal", "indexmap", "itoa", @@ -373,9 +374,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e7f6e8fe5b443f82b3f1e15abfa191128f71569148428e49449d01f6f49e8b" +checksum = "0409e3ba5d1de409997a7db8b8e9d679d52088c1dee042a85033affd3cadeab4" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -387,9 +388,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b96ce28d2fde09abb6135f410c41fad670a3a770b6776869bd852f1df102e6f" +checksum = "a18372ef450d59f74c7a64a738f546ba82c92f816597fed1802ef559304c81f1" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -405,9 +406,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "906746396a8296537745711630d9185746c0b50c033d5e9d18b0a6eba3d53f90" +checksum = "f7bad89dd0d5f109e8feeaf787a9ed7a05a91a9a0efc6687d147a70ebca8eff7" dependencies = [ "const-hex", "dunce", @@ -420,9 +421,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86a533ce22525969661b25dfe296c112d35eb6861f188fd284f8bd4bb3842ae" +checksum = "4aa666f1036341b46625e72bd36878bf45ad0185f1b88601223e1ec6ed4b72b1" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -956,9 +957,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.24" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" dependencies = [ "jobserver", "libc", @@ -1447,6 +1448,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1479,9 +1486,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1494,9 +1501,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1504,15 +1511,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1521,15 +1528,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1538,21 +1545,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1651,17 +1658,18 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", - "serde", -] [[package]] name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", + "serde", +] [[package]] name = "heck" @@ -2327,11 +2335,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -2596,12 +2604,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.1" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" -dependencies = [ - "portable-atomic", -] +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -2816,18 +2821,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "baf123a161dde1e524adf36f90bc5d8d3462824a9c43553ad07a8183161189ec" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "a4502d8515ca9f32f1fb543d987f63d95a14934883db45bdb48060b6b69257f8" dependencies = [ "proc-macro2", "quote", @@ -2890,12 +2895,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "portable-atomic" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" - [[package]] name = "pprof" version = "0.13.0" @@ -2980,9 +2979,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" dependencies = [ "unicode-ident", ] @@ -3579,9 +3578,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -3964,9 +3963,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab661c8148c2261222a4d641ad5477fd4bea79406a99056096a0b41b35617a5" +checksum = "f3a850d65181df41b83c6be01a7d91f5e9377c43d48faa5af7d95816f437f5a3" dependencies = [ "paste", "proc-macro2", diff --git a/crates/derive/Cargo.toml b/crates/derive/Cargo.toml index f7b552f35f..c4d714c416 100644 --- a/crates/derive/Cargo.toml +++ b/crates/derive/Cargo.toml @@ -61,6 +61,7 @@ proptest.workspace = true tracing-subscriber.workspace = true alloy-node-bindings.workspace = true serde_json.workspace = true +kona-providers = { workspace = true, features = ["test-utils"] } [features] default = ["serde"] @@ -85,5 +86,6 @@ test-utils = [ "dep:alloy-node-bindings", "dep:tracing-subscriber", "dep:alloy-rpc-client", + "kona-providers/test-utils", "alloy-transport-http/reqwest" ] diff --git a/crates/derive/src/lib.rs b/crates/derive/src/lib.rs index a2d388444a..4a65a6259d 100644 --- a/crates/derive/src/lib.rs +++ b/crates/derive/src/lib.rs @@ -30,7 +30,10 @@ pub mod sources; pub mod stages; pub mod traits; +mod macros; + #[cfg(feature = "metrics")] pub mod metrics; -mod macros; +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; diff --git a/crates/derive/src/pipeline/core.rs b/crates/derive/src/pipeline/core.rs index 7ec1ac29eb..7619cdc33f 100644 --- a/crates/derive/src/pipeline/core.rs +++ b/crates/derive/src/pipeline/core.rs @@ -195,3 +195,121 @@ where } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::*; + use alloy_rpc_types_engine::PayloadAttributes; + use op_alloy_genesis::SystemConfig; + use op_alloy_rpc_types_engine::OptimismPayloadAttributes; + + fn default_test_payload_attributes() -> OptimismAttributesWithParent { + OptimismAttributesWithParent { + attributes: OptimismPayloadAttributes { + payload_attributes: PayloadAttributes { + timestamp: 0, + prev_randao: Default::default(), + suggested_fee_recipient: Default::default(), + withdrawals: None, + parent_beacon_block_root: None, + }, + transactions: None, + no_tx_pool: None, + gas_limit: None, + eip_1559_params: None, + }, + parent: Default::default(), + is_last_in_span: false, + } + } + + #[test] + fn test_pipeline_next_attributes_empty() { + let mut pipeline = new_test_pipeline(); + let result = pipeline.next(); + assert_eq!(result, None); + } + + #[test] + fn test_pipeline_next_attributes_with_peek() { + let mut pipeline = new_test_pipeline(); + let expected = default_test_payload_attributes(); + pipeline.prepared.push_back(expected.clone()); + + let result = pipeline.peek(); + assert_eq!(result, Some(&expected)); + + let result = pipeline.next(); + assert_eq!(result, Some(expected)); + } + + #[tokio::test] + async fn test_derivation_pipeline_missing_block() { + let mut pipeline = new_test_pipeline(); + let cursor = L2BlockInfo::default(); + let result = pipeline.step(cursor).await; + assert_eq!( + result, + StepResult::OriginAdvanceErr( + PipelineError::Provider("Block not found".to_string()).temp() + ) + ); + } + + #[tokio::test] + async fn test_derivation_pipeline_prepared_attributes() { + let rollup_config = Arc::new(RollupConfig::default()); + let l2_chain_provider = TestL2ChainProvider::default(); + let expected = default_test_payload_attributes(); + let attributes = TestNextAttributes { next_attributes: Some(expected) }; + let mut pipeline = DerivationPipeline::new(attributes, rollup_config, l2_chain_provider); + + // Step on the pipeline and expect the result. + let cursor = L2BlockInfo::default(); + let result = pipeline.step(cursor).await; + assert_eq!(result, StepResult::PreparedAttributes); + } + + #[tokio::test] + async fn test_derivation_pipeline_advance_origin() { + let rollup_config = Arc::new(RollupConfig::default()); + let l2_chain_provider = TestL2ChainProvider::default(); + let attributes = TestNextAttributes::default(); + let mut pipeline = DerivationPipeline::new(attributes, rollup_config, l2_chain_provider); + + // Step on the pipeline and expect the result. + let cursor = L2BlockInfo::default(); + let result = pipeline.step(cursor).await; + assert_eq!(result, StepResult::AdvancedOrigin); + } + + #[tokio::test] + async fn test_derivation_pipeline_signal_reset_missing_sys_config() { + let rollup_config = Arc::new(RollupConfig::default()); + let l2_chain_provider = TestL2ChainProvider::default(); + let attributes = TestNextAttributes::default(); + let mut pipeline = DerivationPipeline::new(attributes, rollup_config, l2_chain_provider); + + // Signal the pipeline to reset. + let l2_safe_head = L2BlockInfo::default(); + let l1_origin = BlockInfo::default(); + let result = pipeline.signal(Signal::Reset { l2_safe_head, l1_origin }).await.unwrap_err(); + assert_eq!(result, PipelineError::Provider("System config not found".to_string()).temp()); + } + + #[tokio::test] + async fn test_derivation_pipeline_signal_reset_ok() { + let rollup_config = Arc::new(RollupConfig::default()); + let mut l2_chain_provider = TestL2ChainProvider::default(); + l2_chain_provider.system_configs.insert(0, SystemConfig::default()); + let attributes = TestNextAttributes::default(); + let mut pipeline = DerivationPipeline::new(attributes, rollup_config, l2_chain_provider); + + // Signal the pipeline to reset. + let l2_safe_head = L2BlockInfo::default(); + let l1_origin = BlockInfo::default(); + let result = pipeline.signal(Signal::Reset { l2_safe_head, l1_origin }).await; + assert!(result.is_ok()); + } +} diff --git a/crates/derive/src/test_utils/mod.rs b/crates/derive/src/test_utils/mod.rs new file mode 100644 index 0000000000..85de9c2862 --- /dev/null +++ b/crates/derive/src/test_utils/mod.rs @@ -0,0 +1,114 @@ +//! Test Utilities for `kona-derive`. +//! +//! This includes top-level [crate::pipeline::DerivationPipeline] +//! test utilities as well as individual stage test utilities. + +use alloc::{boxed::Box, sync::Arc}; +use op_alloy_genesis::{RollupConfig, SystemConfig}; +use op_alloy_protocol::{BlockInfo, L2BlockInfo}; +use op_alloy_rpc_types_engine::OptimismAttributesWithParent; + +// Re-export these types used internally to the test pipeline. +pub use crate::{ + batch::SingleBatch, + errors::PipelineError, + pipeline::{DerivationPipeline, PipelineBuilder, PipelineResult}, + stages::{ + test_utils::MockAttributesBuilder, AttributesProvider, AttributesQueue, BatchQueue, + BatchStream, ChannelBank, ChannelReader, FrameQueue, L1Retrieval, L1Traversal, + }, + traits::{ + test_utils::TestDAP, FlushableStage, NextAttributes, OriginAdvancer, OriginProvider, + ResettableStage, + }, +}; +pub use kona_providers::test_utils::{TestChainProvider, TestL2ChainProvider}; + +/// A fully custom [NextAttributes]. +#[derive(Default, Debug, Clone)] +pub struct TestNextAttributes { + /// The next [OptimismAttributesWithParent] to return. + pub next_attributes: Option, +} + +#[async_trait::async_trait] +impl FlushableStage for TestNextAttributes { + /// Flushes the stage. + async fn flush_channel(&mut self) -> PipelineResult<()> { + Ok(()) + } +} + +#[async_trait::async_trait] +impl ResettableStage for TestNextAttributes { + /// Resets the derivation stage to its initial state. + async fn reset(&mut self, _: BlockInfo, _: &SystemConfig) -> PipelineResult<()> { + Ok(()) + } +} + +#[async_trait::async_trait] +impl OriginProvider for TestNextAttributes { + /// Returns the current origin. + fn origin(&self) -> Option { + Some(BlockInfo::default()) + } +} + +#[async_trait::async_trait] +impl OriginAdvancer for TestNextAttributes { + /// Advances the origin to the given block. + async fn advance_origin(&mut self) -> PipelineResult<()> { + Ok(()) + } +} + +#[async_trait::async_trait] +impl NextAttributes for TestNextAttributes { + /// Returns the next valid attributes. + async fn next_attributes( + &mut self, + _: L2BlockInfo, + ) -> PipelineResult { + self.next_attributes.take().ok_or(PipelineError::Eof.temp()) + } +} + +/// An [L1Traversal] using test providers and sources. +pub type TestL1Traversal = L1Traversal; + +/// An [L1Retrieval] stage using test providers and sources. +pub type TestL1Retrieval = L1Retrieval; + +/// A [FrameQueue] using test providers and sources. +pub type TestFrameQueue = FrameQueue; + +/// A [ChannelBank] using test providers and sources. +pub type TestChannelBank = ChannelBank; + +/// A [ChannelReader] using test providers and sources. +pub type TestChannelReader = ChannelReader; + +/// A [BatchStream] using test providers and sources. +pub type TestBatchStream = BatchStream; + +/// A [BatchQueue] using test providers and sources. +pub type TestBatchQueue = BatchQueue; + +/// An [AttributesQueue] using test providers and sources. +pub type TestAttributesQueue = AttributesQueue; + +/// A [DerivationPipeline] using test providers and sources. +pub type TestPipeline = DerivationPipeline; + +/// Constructs a [DerivationPipeline] using test providers and sources. +pub fn new_test_pipeline() -> TestPipeline { + PipelineBuilder::new() + .rollup_config(Arc::new(RollupConfig::default())) + .origin(BlockInfo::default()) + .dap_source(TestDAP::default()) + .builder(MockAttributesBuilder::default()) + .chain_provider(TestChainProvider::default()) + .l2_chain_provider(TestL2ChainProvider::default()) + .build() +} diff --git a/crates/derive/src/traits/pipeline.rs b/crates/derive/src/traits/pipeline.rs index 7c50a9ce8a..26795e359e 100644 --- a/crates/derive/src/traits/pipeline.rs +++ b/crates/derive/src/traits/pipeline.rs @@ -9,7 +9,7 @@ use op_alloy_protocol::{BlockInfo, L2BlockInfo}; use op_alloy_rpc_types_engine::OptimismAttributesWithParent; /// A pipeline error. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum StepResult { /// Attributes were successfully prepared. PreparedAttributes,