Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fda04a5
Include state root calculation time in metering
niran Oct 28, 2025
fe163b4
Update tips-core and expose state root time in RPC response
niran Oct 28, 2025
305a2ce
Add tests for state root time metrics
niran Oct 28, 2025
ad9586d
Simplify state root calculation by removing unnecessary ExecutionOutc…
niran Oct 28, 2025
d741e03
Use pending flashblocks state for bundle metering
niran Oct 29, 2025
8929514
Cache flashblock trie nodes to optimize bundle metering
niran Oct 30, 2025
2c8699f
linter fixes
niran Oct 30, 2025
afe211a
Rename total_execution_time to total_time for clarity
niran Nov 9, 2025
76c6c0e
Refactor flashblock trie data handling to reduce code duplication
niran Nov 10, 2025
62bd1c3
Add chain-state metering tests and flashblock harness support
niran Nov 11, 2025
8749989
Fix chain-state metering tests with L1 deposit
niran Nov 11, 2025
5aacb8a
Restore beacon root for successful metering tests
niran Nov 11, 2025
e55c43a
Have storage persistence test meter the next block
niran Nov 11, 2025
a41a001
Rename metering tests for clarity
niran Nov 11, 2025
3572284
Drop redundant beacon-root RPC test
niran Nov 11, 2025
eb37396
Document why flashblock trie caching isolates bundle I/O
niran Nov 17, 2025
82b03cb
Move build_single_flashblock from metering to test-utils
niran Nov 17, 2025
4211c10
tweak comments
niran Nov 17, 2025
5b05bb2
Update tips-core and fix metering harness/tests
niran Nov 25, 2025
9d07837
Silence the global executor errors
niran Nov 26, 2025
d8c7184
Add TODO for state_root_time
niran Nov 26, 2025
9752d0a
Add metrics for bundle state clone cost
niran Nov 26, 2025
1942db8
Add database layering tests to flashblocks-rpc
niran Nov 26, 2025
5fbc033
Add tests for flashblock state visibility
niran Nov 27, 2025
4d324f5
just fix
niran Nov 27, 2025
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
30 changes: 24 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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

# base/tips
# Note: default-features = false avoids version conflicts with reth's alloy/op-alloy dependencies
tips-core = { git = "https://github.com/base/tips", rev = "a21ee492dede17f31eea108c12c669a8190f31aa", default-features = false }
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 All @@ -74,10 +74,12 @@ reth-db-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-ipc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" }
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-primitives = { version = "1.4.1", default-features = false, features = [
Expand All @@ -89,6 +91,7 @@ alloy-rpc-types = { version = "1.0.41", default-features = false }
alloy-rpc-types-engine = { version = "1.0.41", default-features = false }
alloy-rpc-types-eth = { version = "1.0.41" }
alloy-consensus = { version = "1.0.41" }
alloy-sol-types = { version = "1.4.1" }
alloy-trie = { version = "0.9.1", default-features = false }
alloy-provider = { version = "1.0.41" }
alloy-hardforks = "0.4.4"
Expand Down
3 changes: 3 additions & 0 deletions crates/flashblocks-rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ reth-primitives.workspace = true
reth-primitives-traits.workspace = true
reth-exex.workspace = true

# revm
revm-database.workspace = true

# alloy
alloy-primitives.workspace = true
alloy-eips.workspace = true
Expand Down
19 changes: 18 additions & 1 deletion crates/flashblocks-rpc/src/pending_blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ use arc_swap::Guard;
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};

Expand All @@ -33,6 +36,7 @@ pub struct PendingBlocksBuilder {
state_overrides: Option<StateOverride>,

db_cache: Cache,
bundle_state: BundleState,
}

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

Expand Down Expand Up @@ -116,6 +121,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 @@ -137,6 +148,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 @@ -156,6 +168,7 @@ pub struct PendingBlocks {
state_overrides: Option<StateOverride>,

db_cache: Cache,
bundle_state: BundleState,
}

impl PendingBlocks {
Expand Down Expand Up @@ -195,6 +208,10 @@ impl PendingBlocks {
self.db_cache.clone()
}

pub fn get_bundle_state(&self) -> BundleState {
self.bundle_state.clone()
}
Comment on lines 217 to 225
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any intuition for how expensive this clone can get? Slightly concerned about introducing a perf regression here. A not too older version here had a significant perf regression because of some extremely large clones that started happening. Im unsure how fast bundlestate can grow.

This may be worth testing on mainnet dev node before merging to main

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added metrics for this in 9752d0a.


pub fn get_transactions_for_block(&self, block_number: BlockNumber) -> Vec<Transaction> {
self.transactions
.iter()
Expand Down
25 changes: 21 additions & 4 deletions crates/flashblocks-rpc/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use reth_optimism_primitives::{DepositReceipt, OpBlock, OpPrimitives};
use reth_optimism_rpc::OpReceiptBuilder;
use reth_primitives::RecoveredBlock;
use reth_rpc_convert::transaction::ConvertReceiptInput;
use revm_database::states::bundle_state::BundleRetention;
use tokio::sync::{
Mutex,
broadcast::{self, Sender},
Expand Down Expand Up @@ -375,12 +376,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 = match &prev_pending_blocks {
Expand Down Expand Up @@ -620,7 +635,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
16 changes: 14 additions & 2 deletions crates/metering/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,25 @@ reth-optimism-chainspec.workspace = true
reth-optimism-primitives.workspace = true
reth-transaction-pool.workspace = true
reth-optimism-cli.workspace = true # Enables serde & codec traits for OpReceipt/OpTxEnvelope
reth-trie-common.workspace = true

# alloy
alloy-primitives.workspace = true
alloy-consensus.workspace = true
alloy-eips.workspace = true
alloy-sol-types.workspace = true
alloy-rpc-types-eth.workspace = true

# op-alloy
op-alloy-consensus.workspace = true
op-alloy-rpc-types.workspace = true

# base
base-reth-flashblocks-rpc = { path = "../flashblocks-rpc" }

# revm
revm.workspace = true
revm-database.workspace = true

# rpc
jsonrpsee.workspace = true
Expand All @@ -45,6 +53,7 @@ jsonrpsee.workspace = true
tracing.workspace = true
serde.workspace = true
eyre.workspace = true
arc-swap.workspace = true

[dev-dependencies]
alloy-genesis.workspace = true
Expand All @@ -59,5 +68,8 @@ reth-tracing.workspace = true
reth-transaction-pool = { workspace = true, features = ["test-utils"] }
serde_json.workspace = true
tokio.workspace = true
base-reth-test-utils.workspace = true

base-reth-test-utils = { path = "../test-utils" }
alloy-rpc-types-engine.workspace = true
rollup-boost.workspace = true
alloy-provider.workspace = true
hex-literal = "0.4"
4 changes: 2 additions & 2 deletions crates/metering/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Simulates a bundle of transactions, providing gas usage and execution time metri
The method accepts a Bundle object with the following fields:

- `txs`: Array of signed, RLP-encoded transactions (hex strings with 0x prefix)
- `block_number`: Target block number for bundle validity (note: simulation always uses the latest available block state)
- `block_number`: Target block number for bundle validity (note: simulation uses pending flashblocks state when available, otherwise latest canonical block)
- `min_timestamp` (optional): Minimum timestamp for bundle validity (also used as simulation timestamp if provided)
- `max_timestamp` (optional): Maximum timestamp for bundle validity
- `reverting_tx_hashes` (optional): Array of transaction hashes allowed to revert
Expand All @@ -26,7 +26,7 @@ The method accepts a Bundle object with the following fields:
- `coinbaseDiff`: Total gas fees paid
- `ethSentToCoinbase`: ETH sent directly to coinbase
- `gasFees`: Total gas fees
- `stateBlockNumber`: Block number used for state (always the latest available block)
- `stateBlockNumber`: Block number used for state (latest flashblock if pending flashblocks exist, otherwise latest canonical block)
- `totalGasUsed`: Total gas consumed
- `totalExecutionTimeUs`: Total execution time (μs)
- `results`: Array of per-transaction results:
Expand Down
Loading
Loading