Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f2db3e1
Include state root calculation time in metering
niran Oct 28, 2025
8ff1966
Update tips-core and expose state root time in RPC response
niran Oct 28, 2025
72e03bc
Add tests for state root time metrics
niran Oct 28, 2025
69572d1
Simplify state root calculation by removing unnecessary ExecutionOutc…
niran Oct 28, 2025
ccda29a
Use pending flashblocks state for bundle metering
niran Oct 29, 2025
a967eb4
Cache flashblock trie nodes to optimize bundle metering
niran Oct 30, 2025
5224e6d
linter fixes
niran Oct 30, 2025
4bfbdf4
Rename total_execution_time to total_time for clarity
niran Nov 9, 2025
070479d
Refactor flashblock trie data handling to reduce code duplication
niran Nov 10, 2025
c9ef133
Add chain-state metering tests and flashblock harness support
niran Nov 11, 2025
ca8890c
Fix chain-state metering tests with L1 deposit
niran Nov 11, 2025
f47208b
Restore beacon root for successful metering tests
niran Nov 11, 2025
ae41884
Have storage persistence test meter the next block
niran Nov 11, 2025
057ebd4
Rename metering tests for clarity
niran Nov 11, 2025
c41e018
Document why flashblock trie caching isolates bundle I/O
niran Nov 17, 2025
484dc26
Move build_single_flashblock from metering to test-utils
niran Nov 17, 2025
aa71dd3
tweak comments
niran Nov 17, 2025
1cc4b40
Update tips-core and fix metering harness/tests
niran Nov 25, 2025
1be16e2
Silence the global executor errors
niran Nov 26, 2025
a529566
Add TODO for state_root_time
niran Nov 26, 2025
91e71b7
Add metrics for bundle state clone cost
niran Nov 26, 2025
36eda0f
Add database layering tests to flashblocks-rpc
niran Nov 26, 2025
40e1e73
Add tests for flashblock state visibility
niran Nov 27, 2025
fb622d5
just fix
niran Nov 27, 2025
f2f9240
Fix build issues after rebase
niran Dec 22, 2025
8e42c76
Fix clippy warnings and apply nightly fmt
niran Dec 22, 2025
519d506
Fix build issues after rebase
niran Jan 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
527 changes: 270 additions & 257 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ base-reth-test-utils = { path = "crates/test-utils" }
base-reth-flashblocks = { path = "crates/flashblocks" }

# base/tips
tips-core = { git = "https://github.com/base/tips", rev = "b1dfde6a5c8f603f19f9309ca6dc015bad260b44" }
# Note: default-features = false avoids version conflicts with reth's alloy/op-alloy dependencies
tips-core = { git = "https://github.com/base/tips", rev = "c08eaa4fe10c26de8911609b41ddab4918698325", default-features = false }

# reth
reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
Expand Down Expand Up @@ -94,10 +95,12 @@ reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag =
reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", features = [
"op",
] }
reth-trie-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }

# revm
revm = { version = "31.0.2", default-features = false }
revm-bytecode = { version = "7.1.1", default-features = false }
revm-database = { version = "9.0.6", default-features = false }

# alloy
alloy-trie = "0.9.1"
Expand Down
6 changes: 6 additions & 0 deletions crates/flashblocks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ reth-optimism-evm.workspace = true
reth-optimism-chainspec.workspace = true
reth-optimism-primitives.workspace = true

# revm
revm-database.workspace = true

# alloy
alloy-eips.workspace = true
alloy-provider.workspace = true
Expand Down Expand Up @@ -58,11 +61,14 @@ metrics-derive.workspace = true
rayon.workspace = true

[dev-dependencies]
alloy-genesis.workspace = true
rand.workspace = true
revm.workspace = true
reth-db.workspace = true
once_cell.workspace = true
reth-provider.workspace = true
reth-db-common.workspace = true
reth-optimism-node.workspace = true
alloy-rpc-client.workspace = true
op-alloy-consensus.workspace = true
reth-testing-utils.workspace = true
Expand Down
8 changes: 8 additions & 0 deletions crates/flashblocks/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,12 @@ pub struct Metrics {
/// Total number of WebSocket reconnection attempts.
#[metric(describe = "Total number of WebSocket reconnection attempts")]
pub reconnect_attempts: Counter,

/// Time taken to clone bundle state.
#[metric(describe = "Time taken to clone bundle state")]
pub bundle_state_clone_duration: Histogram,

/// Size of bundle state being cloned (number of accounts).
#[metric(describe = "Size of bundle state being cloned (number of accounts)")]
pub bundle_state_clone_size: Histogram,
}
35 changes: 32 additions & 3 deletions crates/flashblocks/src/pending_blocks.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{sync::Arc, time::Instant};

use alloy_consensus::{Header, Sealed};
use alloy_eips::BlockNumberOrTag;
Expand All @@ -14,11 +14,14 @@ use base_flashtypes::Flashblock;
use eyre::eyre;
use op_alloy_network::Optimism;
use op_alloy_rpc_types::{OpTransactionReceipt, Transaction};
use reth::revm::{db::Cache, state::EvmState};
use reth::revm::{
db::{BundleState, Cache},
state::EvmState,
};
use reth_rpc_convert::RpcTransaction;
use reth_rpc_eth_api::{RpcBlock, RpcReceipt};

use crate::PendingBlocksAPI;
use crate::{Metrics, PendingBlocksAPI};

/// Builder for [`PendingBlocks`].
#[derive(Debug)]
Expand All @@ -36,6 +39,7 @@ pub struct PendingBlocksBuilder {
state_overrides: Option<StateOverride>,

db_cache: Cache,
bundle_state: BundleState,
}

impl PendingBlocksBuilder {
Expand All @@ -52,6 +56,7 @@ impl PendingBlocksBuilder {
transaction_senders: HashMap::new(),
state_overrides: None,
db_cache: Cache::default(),
bundle_state: BundleState::default(),
}
}

Expand Down Expand Up @@ -122,6 +127,12 @@ impl PendingBlocksBuilder {
self
}

#[inline]
pub(crate) fn with_bundle_state(&mut self, bundle_state: BundleState) -> &Self {
self.bundle_state = bundle_state;
self
}

pub(crate) fn build(self) -> eyre::Result<PendingBlocks> {
if self.headers.is_empty() {
return Err(eyre!("missing headers"));
Expand All @@ -143,6 +154,7 @@ impl PendingBlocksBuilder {
transaction_senders: self.transaction_senders,
state_overrides: self.state_overrides,
db_cache: self.db_cache,
bundle_state: self.bundle_state,
})
}
}
Expand All @@ -163,6 +175,7 @@ pub struct PendingBlocks {
state_overrides: Option<StateOverride>,

db_cache: Cache,
bundle_state: BundleState,
}

impl PendingBlocks {
Expand Down Expand Up @@ -211,6 +224,22 @@ impl PendingBlocks {
self.db_cache.clone()
}

/// Returns a clone of the bundle state.
///
/// NOTE: This clones the entire BundleState, which contains a HashMap of all touched
/// accounts and their storage slots. The cost scales with the number of accounts and
/// storage slots modified in the flashblock. Monitor `bundle_state_clone_duration` and
/// `bundle_state_clone_size` metrics to track if this becomes a bottleneck.
pub fn get_bundle_state(&self) -> BundleState {
let metrics = Metrics::default();
let size = self.bundle_state.state.len();
let start = Instant::now();
let cloned = self.bundle_state.clone();
metrics.bundle_state_clone_duration.record(start.elapsed());
metrics.bundle_state_clone_size.record(size as f64);
cloned
}

/// Returns all transactions for a specific block number.
pub fn get_transactions_for_block(&self, block_number: BlockNumber) -> Vec<Transaction> {
self.transactions
Expand Down
33 changes: 27 additions & 6 deletions crates/flashblocks/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use reth_optimism_primitives::{OpBlock, OpPrimitives};
use reth_optimism_rpc::OpReceiptBuilder as OpRpcReceiptBuilder;
use reth_primitives::RecoveredBlock;
use reth_rpc_convert::transaction::ConvertReceiptInput;
use revm_database::states::bundle_state::BundleRetention;
use tokio::sync::{Mutex, broadcast::Sender, mpsc::UnboundedReceiver};
use tracing::{debug, error, info, warn};

Expand Down Expand Up @@ -295,12 +296,26 @@ where
let state_provider =
self.client.state_by_block_number_or_tag(BlockNumberOrTag::Number(canonical_block))?;
let state_provider_db = StateProviderDatabase::new(state_provider);
let state = State::builder().with_database(state_provider_db).with_bundle_update().build();
let mut pending_blocks_builder = PendingBlocksBuilder::new();

// Cache reads across flashblocks, accumulating caches from previous
// pending blocks if available
let cache_db = match &prev_pending_blocks {
Some(pending_blocks) => {
CacheDB { cache: pending_blocks.get_db_cache(), db: state_provider_db }
}
None => CacheDB::new(state_provider_db),
};

// Track state changes across flashblocks, accumulating bundle state
// from previous pending blocks if available
let mut db = match &prev_pending_blocks {
Some(pending_blocks) => CacheDB { cache: pending_blocks.get_db_cache(), db: state },
None => CacheDB::new(state),
Some(pending_blocks) => State::builder()
.with_database(cache_db)
.with_bundle_update()
.with_bundle_prestate(pending_blocks.get_bundle_state())
.build(),
None => State::builder().with_database(cache_db).with_bundle_update().build(),
};

let mut state_overrides =
Expand Down Expand Up @@ -535,8 +550,12 @@ where
&& transaction.is_deposit())
.then(|| {
evm.db_mut()
.load_account(recovered_transaction.signer())
.map(|acc| acc.info.nonce)
.load_cache_account(recovered_transaction.signer())
.map(|acc| {
acc.account_info()
.map(|info| info.nonce)
.unwrap_or(0)
})
})
.transpose()
.map_err(|_| {
Expand Down Expand Up @@ -627,7 +646,9 @@ where
last_block_header = block.header.clone();
}

pending_blocks_builder.with_db_cache(db.cache);
db.merge_transitions(BundleRetention::Reverts);
pending_blocks_builder.with_bundle_state(db.take_bundle());
pending_blocks_builder.with_db_cache(db.database.cache);
pending_blocks_builder.with_state_overrides(state_overrides);
Ok(Some(Arc::new(pending_blocks_builder.build()?)))
}
Expand Down
Loading
Loading