diff --git a/Cargo.lock b/Cargo.lock index 4b3305491..1dc869a93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2912,6 +2912,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "exex-in-memory-state" +version = "0.0.0" +dependencies = [ + "eyre", + "reth", + "reth-exex", + "reth-node-api", + "reth-node-ethereum", + "reth-tracing", +] + [[package]] name = "exex-minimal" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 55ff51720..7eed06b7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,9 +87,7 @@ members = [ "examples/txpool-tracing/", "examples/polygon-p2p/", "examples/custom-inspector/", - "examples/exex/minimal/", - "examples/exex/op-bridge/", - "examples/exex/rollup/", + "examples/exex/*", "examples/db-access", "testing/ef-tests/", "testing/testing-utils", diff --git a/examples/README.md b/examples/README.md index 0885aa294..4c135f880 100644 --- a/examples/README.md +++ b/examples/README.md @@ -23,11 +23,12 @@ to make a PR! ## ExEx -| Example | Description | -| ---------------------------------- | --------------------------------------------------------------------------------- | -| [Minimal ExEx](./exex/minimal) | Illustrates how to build a simple ExEx | -| [OP Bridge ExEx](./exex/op-bridge) | Illustrates an ExEx that decodes Optimism deposit and withdrawal receipts from L1 | -| [Rollup](./exex/rollup) | Illustrates a rollup ExEx that derives the state from L1 | +| Example | Description | +|-------------------------------------------|-----------------------------------------------------------------------------------| +| [Minimal ExEx](./exex/minimal) | Illustrates how to build a simple ExEx | +| [OP Bridge ExEx](./exex/op-bridge) | Illustrates an ExEx that decodes Optimism deposit and withdrawal receipts from L1 | +| [Rollup](./exex/rollup) | Illustrates a rollup ExEx that derives the state from L1 | +| [In Memory State](./exex/in-memory-state) | Illustrates an ExEx that tracks the plain state in memory | ## RPC diff --git a/examples/exex/in-memory-state/Cargo.toml b/examples/exex/in-memory-state/Cargo.toml new file mode 100644 index 000000000..c7fd34ea5 --- /dev/null +++ b/examples/exex/in-memory-state/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "exex-in-memory-state" +version = "0.0.0" +publish = false +edition.workspace = true +license.workspace = true + +[dependencies] +reth.workspace = true +reth-exex.workspace = true +reth-node-api.workspace = true +reth-node-ethereum.workspace = true +reth-tracing.workspace = true + +eyre.workspace = true diff --git a/examples/exex/in-memory-state/src/main.rs b/examples/exex/in-memory-state/src/main.rs new file mode 100644 index 000000000..451bb9c42 --- /dev/null +++ b/examples/exex/in-memory-state/src/main.rs @@ -0,0 +1,49 @@ +#![warn(unused_crate_dependencies)] + +use reth::providers::BundleStateWithReceipts; +use reth_exex::{ExExContext, ExExEvent, ExExNotification}; +use reth_node_api::FullNodeComponents; +use reth_node_ethereum::EthereumNode; +use reth_tracing::tracing::info; + +/// An ExEx that keeps track of the entire state in memory +async fn track_state(mut ctx: ExExContext) -> eyre::Result<()> { + // keeps the entire plain state of the chain in memory + let mut state = BundleStateWithReceipts::default(); + + while let Some(notification) = ctx.notifications.recv().await { + match ¬ification { + ExExNotification::ChainCommitted { new } => { + info!(committed_chain = ?new.range(), "Received commit"); + } + ExExNotification::ChainReorged { old, new } => { + // revert to block before the reorg + state.revert_to(new.first().number - 1); + info!(from_chain = ?old.range(), to_chain = ?new.range(), "Received reorg"); + } + ExExNotification::ChainReverted { old } => { + state.revert_to(old.first().number - 1); + info!(reverted_chain = ?old.range(), "Received revert"); + } + }; + + if let Some(committed_chain) = notification.committed_chain() { + // extend the state with the new chain + state.extend(committed_chain.state().clone()); + ctx.events.send(ExExEvent::FinishedHeight(committed_chain.tip().number))?; + } + } + Ok(()) +} + +fn main() -> eyre::Result<()> { + reth::cli::Cli::parse_args().run(|builder, _| async move { + let handle = builder + .node(EthereumNode::default()) + .install_exex("in-memory-state", |ctx| async move { Ok(track_state(ctx)) }) + .launch() + .await?; + + handle.wait_for_node_exit().await + }) +}