diff --git a/Cargo.lock b/Cargo.lock index 2d6cfb0a..9c79afc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1976,6 +1976,18 @@ dependencies = [ "url", ] +[[package]] +name = "base-flashblocks-node" +version = "0.0.0" +dependencies = [ + "base-client-node", + "base-flashblocks", + "reth-chain-state", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "base-flashtypes" version = "0.0.0" @@ -2072,7 +2084,7 @@ version = "0.0.0" dependencies = [ "base-cli-utils", "base-client-node", - "base-flashblocks", + "base-flashblocks-node", "base-metering", "base-txpool", "clap", diff --git a/Cargo.toml b/Cargo.toml index 7dd484f7..7f7eaff2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ base-primitives = { path = "crates/shared/primitives" } base-access-lists = { path = "crates/shared/access-lists" } base-reth-rpc-types = { path = "crates/shared/reth-rpc-types" } base-jwt = { path = "crates/shared/jwt" } +base-flashblocks-node = { path = "crates/client/flashblocks-node" } # Client base-client-cli = { path = "crates/client/cli" } diff --git a/bin/node/Cargo.toml b/bin/node/Cargo.toml index 43f2ce23..ff58f2d4 100644 --- a/bin/node/Cargo.toml +++ b/bin/node/Cargo.toml @@ -16,7 +16,7 @@ workspace = true # internal base-cli-utils.workspace = true base-client-node.workspace = true -base-flashblocks.workspace = true +base-flashblocks-node.workspace = true base-metering.workspace = true base-txpool.workspace = true diff --git a/bin/node/src/cli.rs b/bin/node/src/cli.rs index ea0d1b0d..03177da5 100644 --- a/bin/node/src/cli.rs +++ b/bin/node/src/cli.rs @@ -1,6 +1,6 @@ //! Contains the CLI arguments -use base_flashblocks::FlashblocksConfig; +use base_flashblocks_node::FlashblocksConfig; use base_txpool::TxpoolConfig; use reth_optimism_node::args::RollupArgs; diff --git a/bin/node/src/main.rs b/bin/node/src/main.rs index 3af028d5..21338592 100644 --- a/bin/node/src/main.rs +++ b/bin/node/src/main.rs @@ -6,7 +6,7 @@ pub mod cli; use base_client_node::BaseNodeRunner; -use base_flashblocks::{FlashblocksConfig, FlashblocksExtension}; +use base_flashblocks_node::{FlashblocksConfig, FlashblocksExtension}; use base_metering::{MeteringConfig, MeteringExtension}; use base_txpool::TxPoolExtension; diff --git a/crates/client/flashblocks-node/Cargo.toml b/crates/client/flashblocks-node/Cargo.toml new file mode 100644 index 00000000..41f9c628 --- /dev/null +++ b/crates/client/flashblocks-node/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "base-flashblocks-node" +description = "Flashblocks Node Extension" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +# workspace +base-flashblocks.workspace = true +base-client-node.workspace = true + +# reth +reth-chain-state.workspace = true + +tokio-stream.workspace = true +tracing.workspace = true +tokio.workspace = true diff --git a/crates/client/flashblocks-node/README.md b/crates/client/flashblocks-node/README.md new file mode 100644 index 00000000..7daaabb1 --- /dev/null +++ b/crates/client/flashblocks-node/README.md @@ -0,0 +1,58 @@ +# `base-flashblocks-node` + +CI +MIT License + +## Extension + +This crate provides the Flashblocks extension for the Base node, which enables real-time streaming of pending block state and extended RPC capabilities. + +## Usage + +### Programmatic Integration + +To integrate the Flashblocks extension into your node, use the `install_ext` method on your `BaseNodeRunner`: + +```rust +use base_client_node::BaseNodeRunner; +use base_flashblocks_node::{FlashblocksConfig, FlashblocksExtension}; + +let mut runner = BaseNodeRunner::new(rollup_args); + +// Create flashblocks configuration +let flashblocks_config: Option = args.into(); + +// Install the flashblocks extension (should be installed last as it uses replace_configured) +runner.install_ext::(flashblocks_config); + +let handle = runner.run(builder); +``` + +### CLI Arguments + +When running the node binary, Flashblocks can be configured with the following CLI arguments: + +- `--websocket-url `: The WebSocket URL to stream flashblock updates from (required to enable Flashblocks) +- `--max-pending-blocks-depth `: Maximum number of pending flashblocks to retain in memory (default: 3) + +### Example + +```bash +# Run the Base node with Flashblocks enabled +base-node \ + --websocket-url ws://flashblock-service:8080 \ + --max-pending-blocks-depth 5 +``` + +## What It Does + +The `FlashblocksExtension` wires up: + +1. **State Processor**: Maintains pending block state by processing incoming flashblocks +2. **Canonical Subscription**: Reconciles pending state with canonical blocks +3. **RPC Extensions**: Provides extended Ethereum RPC methods with flashblock awareness +4. **WebSocket Subscriber**: Connects to and streams updates from the flashblock service + +## License + +Licensed under the [MIT License](https://github.com/base/node-reth/blob/main/LICENSE). diff --git a/crates/client/flashblocks/src/extension.rs b/crates/client/flashblocks-node/src/extension.rs similarity index 80% rename from crates/client/flashblocks/src/extension.rs rename to crates/client/flashblocks-node/src/extension.rs index c1b14e6a..7e6996d7 100644 --- a/crates/client/flashblocks/src/extension.rs +++ b/crates/client/flashblocks-node/src/extension.rs @@ -1,38 +1,14 @@ //! Contains the [`FlashblocksExtension`] which wires up the flashblocks feature //! (canonical block subscription and RPC surface) on the Base node builder. -use std::sync::Arc; - use base_client_node::{BaseBuilder, BaseNodeExtension, FromExtensionConfig}; +use base_flashblocks::{ + EthApiExt, EthApiOverrideServer, EthPubSub, EthPubSubApiServer, FlashblocksConfig, + FlashblocksSubscriber, +}; use reth_chain_state::CanonStateSubscriptions; use tokio_stream::{StreamExt, wrappers::BroadcastStream}; use tracing::info; -use url::Url; - -use crate::{ - EthApiExt, EthApiOverrideServer, EthPubSub, EthPubSubApiServer, FlashblocksState, - FlashblocksSubscriber, -}; - -/// Flashblocks-specific configuration knobs. -#[derive(Debug, Clone)] -pub struct FlashblocksConfig { - /// The websocket endpoint that streams flashblock updates. - pub websocket_url: Url, - /// Maximum number of pending flashblocks to retain in memory. - pub max_pending_blocks_depth: u64, - /// Shared Flashblocks state. - pub state: Arc, -} - -impl FlashblocksConfig { - /// Create a new Flashblocks configuration. - pub fn new(websocket_url: String, max_pending_blocks_depth: u64) -> Self { - let state = Arc::new(FlashblocksState::new(max_pending_blocks_depth)); - let ws_url = Url::parse(&websocket_url).expect("valid websocket URL"); - Self { websocket_url: ws_url, max_pending_blocks_depth, state } - } -} /// Helper struct that wires the Flashblocks feature (canonical subscription and RPC) into the node builder. #[derive(Debug)] diff --git a/crates/client/flashblocks-node/src/lib.rs b/crates/client/flashblocks-node/src/lib.rs new file mode 100644 index 00000000..dd52f943 --- /dev/null +++ b/crates/client/flashblocks-node/src/lib.rs @@ -0,0 +1,8 @@ +#![doc = include_str!("../README.md")] +#![doc(issue_tracker_base_url = "https://github.com/base/node-reth/issues/")] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] + +mod extension; +pub use base_flashblocks::FlashblocksConfig; +pub use extension::FlashblocksExtension; diff --git a/crates/client/flashblocks/Cargo.toml b/crates/client/flashblocks/Cargo.toml index a5fd1204..67ba4340 100644 --- a/crates/client/flashblocks/Cargo.toml +++ b/crates/client/flashblocks/Cargo.toml @@ -13,9 +13,10 @@ workspace = true [features] test-utils = [ - "base-client-node/test-utils", + "dep:base-client-node", "dep:derive_more", "dep:eyre", + "dep:reth-chain-state", "reth-chain-state/test-utils", "reth-chainspec/test-utils", "reth-evm/test-utils", @@ -27,10 +28,10 @@ test-utils = [ [dependencies] # workspace base-flashtypes.workspace = true -base-client-node.workspace = true +base-client-node = { workspace = true, optional = true } # reth -reth-chain-state.workspace = true +reth-chain-state = { workspace = true, optional = true } reth-evm.workspace = true reth-primitives.workspace = true reth-rpc.workspace = true @@ -92,6 +93,7 @@ derive_more = { workspace = true, features = ["deref"], optional = true } eyre = { workspace = true, optional = true } [dev-dependencies] +base-client-node = { workspace = true, features = ["test-utils"] } base-flashblocks = { path = ".", features = ["test-utils"] } rstest.workspace = true rand.workspace = true diff --git a/crates/client/flashblocks/src/config.rs b/crates/client/flashblocks/src/config.rs new file mode 100644 index 00000000..62b793df --- /dev/null +++ b/crates/client/flashblocks/src/config.rs @@ -0,0 +1,25 @@ +use std::sync::Arc; + +use url::Url; + +use crate::FlashblocksState; + +/// Flashblocks-specific configuration knobs. +#[derive(Debug, Clone)] +pub struct FlashblocksConfig { + /// The websocket endpoint that streams flashblock updates. + pub websocket_url: Url, + /// Maximum number of pending flashblocks to retain in memory. + pub max_pending_blocks_depth: u64, + /// Shared Flashblocks state. + pub state: Arc, +} + +impl FlashblocksConfig { + /// Create a new Flashblocks configuration. + pub fn new(websocket_url: String, max_pending_blocks_depth: u64) -> Self { + let state = Arc::new(FlashblocksState::new(max_pending_blocks_depth)); + let ws_url = Url::parse(&websocket_url).expect("valid websocket URL"); + Self { websocket_url: ws_url, max_pending_blocks_depth, state } + } +} diff --git a/crates/client/flashblocks/src/lib.rs b/crates/client/flashblocks/src/lib.rs index 30931992..647d479b 100644 --- a/crates/client/flashblocks/src/lib.rs +++ b/crates/client/flashblocks/src/lib.rs @@ -41,14 +41,14 @@ pub use validation::{ ReorgDetectionResult, ReorgDetector, SequenceValidationResult, }; +mod config; +pub use config::FlashblocksConfig; + mod rpc; pub use rpc::{ BaseSubscriptionKind, EthApiExt, EthApiOverrideServer, EthPubSub, EthPubSubApiServer, ExtendedSubscriptionKind, }; -mod extension; -pub use extension::{FlashblocksConfig, FlashblocksExtension}; - #[cfg(any(test, feature = "test-utils"))] pub mod test_harness;