From 10d9ed55bfcf5b11656dcec55c30484675bfa696 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 28 Oct 2025 14:25:18 -0500 Subject: [PATCH 01/26] Include state root calculation time in metering Add state root calculation after transaction execution and measure its time independently. The state root calculation time is included in the total execution time and also tracked separately. --- crates/rpc/src/base/meter.rs | 62 +++++++++++++++++++++++++------- crates/rpc/src/base/meter_rpc.rs | 49 +++++++++++++------------ crates/rpc/tests/meter.rs | 45 ++++++++++++----------- 3 files changed, 101 insertions(+), 55 deletions(-) diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 577b8ee3..6ff4e02e 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -7,30 +7,44 @@ use reth::revm::db::State; use reth_evm::{ConfigureEvm, execute::BlockBuilder}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; +use reth_optimism_primitives::OpPrimitives; use reth_primitives_traits::SealedHeader; +use reth_provider::ExecutionOutcome; use tips_core::types::{BundleExtensions, BundleTxs, ParsedBundle}; use crate::TransactionResult; const BLOCK_TIME: u64 = 2; // 2 seconds per block +/// Output from metering a bundle of transactions +#[derive(Debug)] +pub struct MeterBundleOutput { + /// Transaction results with individual metrics + pub results: Vec, + /// Total gas used by all transactions + pub total_gas_used: u64, + /// Total gas fees paid by all transactions + pub total_gas_fees: U256, + /// Bundle hash + pub bundle_hash: B256, + /// Total execution time in microseconds (includes state root calculation) + pub total_execution_time_us: u128, + /// State root calculation time in microseconds + pub state_root_time_us: u128, +} + /// Simulates and meters a bundle of transactions /// -/// Takes a state provider, chain spec, decoded transactions, block header, and bundle metadata, -/// and executes transactions in sequence to measure gas usage and execution time. +/// Takes a state provider, chain spec, parsed bundle, and block header, then executes transactions +/// in sequence to measure gas usage and execution time. /// -/// Returns a tuple of: -/// - Vector of transaction results -/// - Total gas used -/// - Total gas fees paid -/// - Bundle hash -/// - Total execution time in microseconds +/// Returns [`MeterBundleOutput`] containing transaction results and aggregated metrics. pub fn meter_bundle( state_provider: SP, chain_spec: Arc, bundle: ParsedBundle, header: &SealedHeader, -) -> EyreResult<(Vec, u64, U256, B256, u128)> +) -> EyreResult where SP: reth_provider::StateProvider, { @@ -85,7 +99,7 @@ where results.push(TransactionResult { coinbase_diff: gas_fees, - eth_sent_to_coinbase: U256::from(0), + eth_sent_to_coinbase: U256::ZERO, from_address: from, gas_fees, gas_price: U256::from(gas_price), @@ -97,7 +111,31 @@ where }); } } - let total_execution_time = execution_start.elapsed().as_micros(); - Ok((results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time)) + // Calculate state root and measure its calculation time + let block_number = header.number() + 1; + let bundle_update = db.take_bundle(); + let execution_outcome: ExecutionOutcome = ExecutionOutcome::new( + bundle_update, + Vec::new().into(), + block_number, + Vec::new(), + ); + + let state_provider = db.database.as_ref(); + let state_root_start = Instant::now(); + let hashed_state = state_provider.hashed_post_state(execution_outcome.state()); + let _ = state_provider.state_root_with_updates(hashed_state); + let state_root_time_us = state_root_start.elapsed().as_micros(); + + let total_execution_time_us = execution_start.elapsed().as_micros(); + + Ok(MeterBundleOutput { + results, + total_gas_used, + total_gas_fees, + bundle_hash, + total_execution_time_us, + state_root_time_us, + }) } diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 9a99aa32..21862d13 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -85,43 +85,48 @@ where })?; // Meter bundle using utility function - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = - meter_bundle(state_provider, self.provider.chain_spec(), parsed_bundle, &header) - .map_err(|e| { - error!(error = %e, "Bundle metering failed"); - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - format!("Bundle metering failed: {}", e), - None::<()>, - ) - })?; + let result = meter_bundle( + state_provider, + self.provider.chain_spec(), + parsed_bundle, + &header, + ) + .map_err(|e| { + error!(error = %e, "Bundle metering failed"); + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Bundle metering failed: {}", e), + None::<()>, + ) + })?; // Calculate average gas price - let bundle_gas_price = if total_gas_used > 0 { - total_gas_fees / U256::from(total_gas_used) + let bundle_gas_price = if result.total_gas_used > 0 { + result.total_gas_fees / U256::from(result.total_gas_used) } else { U256::from(0) }; info!( - bundle_hash = %bundle_hash, - num_transactions = results.len(), - total_gas_used = total_gas_used, - total_execution_time_us = total_execution_time, + bundle_hash = %result.bundle_hash, + num_transactions = result.results.len(), + total_gas_used = result.total_gas_used, + total_execution_time_us = result.total_execution_time_us, + state_root_time_us = result.state_root_time_us, "Bundle metering completed successfully" ); Ok(MeterBundleResponse { bundle_gas_price, - bundle_hash, - coinbase_diff: total_gas_fees, + bundle_hash: result.bundle_hash, + coinbase_diff: result.total_gas_fees, eth_sent_to_coinbase: U256::from(0), - gas_fees: total_gas_fees, - results, + gas_fees: result.total_gas_fees, + results: result.results, state_block_number: header.number, state_flashblock_index: None, - total_gas_used, - total_execution_time_us: total_execution_time, + total_gas_used: result.total_gas_used, + total_execution_time_us: result.total_execution_time_us, }) } } diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 23235233..86f861c4 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -136,15 +136,16 @@ fn meter_bundle_empty_transactions() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(Vec::new())?; - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = + let output = meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; - assert!(results.is_empty()); - assert_eq!(total_gas_used, 0); - assert_eq!(total_gas_fees, U256::ZERO); + assert!(output.results.is_empty()); + assert_eq!(output.total_gas_used, 0); + assert_eq!(output.total_gas_fees, U256::ZERO); // Even empty bundles have some EVM setup overhead - assert!(total_execution_time > 0); - assert_eq!(bundle_hash, keccak256([])); + assert!(output.total_execution_time_us > 0); + assert!(output.state_root_time_us > 0); + assert_eq!(output.bundle_hash, keccak256([])); Ok(()) } @@ -178,12 +179,13 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(vec![envelope.clone()])?; - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = + let output = meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; - assert_eq!(results.len(), 1); - let result = &results[0]; - assert!(total_execution_time > 0); + assert_eq!(output.results.len(), 1); + let result = &output.results[0]; + assert!(output.total_execution_time_us > 0); + assert!(output.state_root_time_us > 0); assert_eq!(result.from_address, harness.address(User::Alice)); assert_eq!(result.to_address, Some(to)); @@ -192,12 +194,12 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { assert_eq!(result.gas_used, 21_000); assert_eq!(result.coinbase_diff, (U256::from(21_000) * U256::from(10)),); - assert_eq!(total_gas_used, 21_000); - assert_eq!(total_gas_fees, U256::from(21_000) * U256::from(10)); + assert_eq!(output.total_gas_used, 21_000); + assert_eq!(output.total_gas_fees, U256::from(21_000) * U256::from(10)); let mut concatenated = Vec::with_capacity(32); concatenated.extend_from_slice(tx_hash.as_slice()); - assert_eq!(bundle_hash, keccak256(concatenated)); + assert_eq!(output.bundle_hash, keccak256(concatenated)); assert!(result.execution_time_us > 0, "execution_time_us should be greater than zero"); @@ -255,14 +257,15 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(vec![envelope_1.clone(), envelope_2.clone()])?; - let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) = + let output = meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; - assert_eq!(results.len(), 2); - assert!(total_execution_time > 0); + assert_eq!(output.results.len(), 2); + assert!(output.total_execution_time_us > 0); + assert!(output.state_root_time_us > 0); // Check first transaction - let result_1 = &results[0]; + let result_1 = &output.results[0]; assert_eq!(result_1.from_address, harness.address(User::Alice)); assert_eq!(result_1.to_address, Some(to_1)); assert_eq!(result_1.tx_hash, tx_hash_1); @@ -271,7 +274,7 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { assert_eq!(result_1.coinbase_diff, (U256::from(21_000) * U256::from(10)),); // Check second transaction - let result_2 = &results[1]; + let result_2 = &output.results[1]; assert_eq!(result_2.from_address, harness.address(User::Bob)); assert_eq!(result_2.to_address, Some(to_2)); assert_eq!(result_2.tx_hash, tx_hash_2); @@ -280,16 +283,16 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { assert_eq!(result_2.coinbase_diff, U256::from(21_000) * U256::from(15),); // Check aggregated values - assert_eq!(total_gas_used, 42_000); + assert_eq!(output.total_gas_used, 42_000); let expected_total_fees = U256::from(21_000) * U256::from(10) + U256::from(21_000) * U256::from(15); - assert_eq!(total_gas_fees, expected_total_fees); + assert_eq!(output.total_gas_fees, expected_total_fees); // Check bundle hash includes both transactions let mut concatenated = Vec::with_capacity(64); concatenated.extend_from_slice(tx_hash_1.as_slice()); concatenated.extend_from_slice(tx_hash_2.as_slice()); - assert_eq!(bundle_hash, keccak256(concatenated)); + assert_eq!(output.bundle_hash, keccak256(concatenated)); assert!(result_1.execution_time_us > 0, "execution_time_us should be greater than zero"); assert!(result_2.execution_time_us > 0, "execution_time_us should be greater than zero"); From 1bedee4fc88895a912a3634862d7d81d4f0a57f4 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 28 Oct 2025 14:45:40 -0500 Subject: [PATCH 02/26] Update tips-core and expose state root time in RPC response Update tips-core dependency to include state_root_time_us field in MeterBundleResponse. This exposes the state root calculation time as an independent metric in the metering RPC response. --- Cargo.lock | 1909 +++++++++++++++++------------- Cargo.toml | 3 +- crates/rpc/src/base/meter_rpc.rs | 1 + 3 files changed, 1101 insertions(+), 812 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aebb5828..30be95d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.21" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ebac8ff9c2f07667e1803dc777304337e160ce5153335beb45e8ec0751808" +checksum = "4bc32535569185cbcb6ad5fa64d989a47bccb9a08e27284b1f2a3ccf16e6d010" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e318e25fb719e747a7e8db1654170fc185024f3ed5b10f86c08d448a912f6e2" +checksum = "ad704069c12f68d0c742d0cad7e0a03882b42767350584627fbf8a47b1bf1846" dependencies = [ "alloy-eips", "alloy-primitives", @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364380a845193a317bcb7a5398fc86cdb66c47ebe010771dde05f6869bf9e64a" +checksum = "bc374f640a5062224d7708402728e3d6879a514ba10f377da62e7dfb14c673e6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -151,28 +151,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-contract" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d39c80ffc806f27a76ed42f3351a455f3dc4f81d6ff92c8aad2cf36b7d3a34" -dependencies = [ - "alloy-consensus", - "alloy-dyn-abi", - "alloy-json-abi", - "alloy-network", - "alloy-network-primitives", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types-eth", - "alloy-sol-types", - "alloy-transport", - "futures", - "futures-util", - "serde_json", - "thiserror 2.0.17", -] - [[package]] name = "alloy-dyn-abi" version = "1.4.1" @@ -238,9 +216,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4d7c5839d9f3a467900c625416b24328450c65702eb3d8caff8813e4d1d33" +checksum = "7e867b5fd52ed0372a95016f3a37cbff95a9d5409230fbaef2d8ea00e8618098" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -287,9 +265,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba4b1be0988c11f0095a2380aa596e35533276b8fa6c9e06961bbfe0aebcac5" +checksum = "b90be17e9760a6ba6d13cebdb049cea405ebc8bf57d90664ed708cc5bc348342" dependencies = [ "alloy-eips", "alloy-primitives", @@ -302,9 +280,9 @@ dependencies = [ [[package]] name = "alloy-hardforks" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9a33550fc21fd77a3f8b63e99969d17660eec8dcc50a95a80f7c9964f7680b" +checksum = "1e29d7eacf42f89c21d7f089916d0bdb4f36139a31698790e8837d2dbbd4b2c3" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -328,9 +306,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72cf87cda808e593381fb9f005ffa4d2475552b7a6c5ac33d087bf77d82abd0" +checksum = "dcab4c51fb1273e3b0f59078e0cdf8aa99f697925b09f0d2055c18be46b4d48c" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -343,9 +321,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12aeb37b6f2e61b93b1c3d34d01ee720207c76fe447e2a2c217e433ac75b17f5" +checksum = "196d7fd3f5d414f7bbd5886a628b7c42bd98d1b126f9a7cff69dbfd72007b39c" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -369,9 +347,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd29ace62872083e30929cd9b282d82723196d196db589f3ceda67edcc05552" +checksum = "0d3ae2777e900a7a47ad9e3b8ab58eff3d93628265e73bbdee09acf90bf68f75" dependencies = [ "alloy-consensus", "alloy-eips", @@ -400,9 +378,9 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" -version = "0.4.5" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f96fb2fce4024ada5b2c11d4076acf778a0d3e4f011c6dfd2ffce6d0fcf84ee9" +checksum = "95ac97adaba4c26e17192d81f49186ac20c1e844e35a00e169c8d3d58bc84e6b" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -424,8 +402,8 @@ dependencies = [ "derive_more", "foldhash 0.2.0", "getrandom 0.3.4", - "hashbrown 0.16.1", - "indexmap 2.12.1", + "hashbrown 0.16.0", + "indexmap 2.12.0", "itoa", "k256", "keccak-asm", @@ -434,7 +412,7 @@ dependencies = [ "proptest-derive 0.6.0", "rand 0.9.2", "ruint", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "sha3", "tiny-keccak", @@ -442,9 +420,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b710636d7126e08003b8217e24c09f0cca0b46d62f650a841736891b1ed1fc1" +checksum = "9f9bf40c9b2a90c7677f9c39bccd9f06af457f35362439c0497a706f16557703" dependencies = [ "alloy-chains", "alloy-consensus", @@ -484,9 +462,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd4c64eb250a18101d22ae622357c6b505e158e9165d4c7974d59082a600c5e" +checksum = "acfdbe41e2ef1a7e79b5ea115baa750f9381ac9088fb600f4cedc731cf04a151" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -499,7 +477,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "wasmtimer", ] @@ -523,14 +501,14 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "alloy-rpc-client" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0882e72d2c1c0c79dcf4ab60a67472d3f009a949f774d4c17d0bdb669cfde05" +checksum = "e7c2630fde9ff6033a780635e1af6ef40e92d74a9cacb8af3defc1b15cfebca5" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -546,7 +524,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "url", "wasmtimer", @@ -554,9 +532,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cf1398cb33aacb139a960fa3d8cf8b1202079f320e77e952a0b95967bf7a9f" +checksum = "ad098153a12382c22a597e865530033f5e644473742d6c733562d448125e02a2" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -567,9 +545,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-admin" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bcf50ccb65d29b8599f8f5e23dcac685f1d79459654c830cba381345760e901" +checksum = "b7604c415f725bd776d46dae44912c276cc3d8af37f37811e5675389791aa0c6" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -579,9 +557,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e176c26fdd87893b6afeb5d92099d8f7e7a1fe11d6f4fe0883d6e33ac5f31ba" +checksum = "214d9d1033c173ab8fa32edd8a4655cd784447c820b0b66cd0d5167e049567d6" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -591,9 +569,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a63fb40ed24e4c92505f488f9dd256e2afaed17faa1b7a221086ebba74f4122" +checksum = "50b8429b5b62d21bf3691eb1ae12aaae9bb496894d5a114e3cc73e27e6800ec8" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -602,9 +580,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1786681640d4c60f22b6b8376b0f3fa200360bf1c3c2cb913e6c97f51928eb1b" +checksum = "f67f8269e8b5193a5328dd3ef4d60f93524071e53a993776e290581a59aa15fa" dependencies = [ "alloy-eips", "alloy-primitives", @@ -622,9 +600,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b2ca3a434a6d49910a7e8e51797eb25db42ef8a5578c52d877fcb26d0afe7bc" +checksum = "01731601ea631bd825c652a225701ab466c09457f446b8d8129368a095389c5d" dependencies = [ "alloy-primitives", "derive_more", @@ -634,9 +612,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c60bdce3be295924122732b7ecd0b2495ce4790bedc5370ca7019c08ad3f26e" +checksum = "9981491bb98e76099983f516ec7de550db0597031f5828c994961eb4bb993cce" dependencies = [ "alloy-consensus", "alloy-eips", @@ -654,9 +632,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eae0c7c40da20684548cbc8577b6b7447f7bf4ddbac363df95e3da220e41e72" +checksum = "29031a6bf46177d65efce661f7ab37829ca09dd341bc40afb5194e97600655cc" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -676,9 +654,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-mev" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a97bfc6d9b411c85bb08e1174ddd3e5d61b10d3bd13f529d6609f733cb2f6f" +checksum = "c5c5c78bdd2c72c47e66ab977af420fb4a10279707d4edbd2575693c47aa54a2" dependencies = [ "alloy-consensus", "alloy-eips", @@ -691,9 +669,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55324323aa634b01bdecb2d47462a8dce05f5505b14a6e5db361eef16eda476" +checksum = "01b842f5aac6676ff4b2e328262d03bdf49807eaec3fe3a4735c45c97388518b" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -705,9 +683,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b1aa28effb6854be356ce92ed64cea3b323acd04c3f8bfb5126e2839698043" +checksum = "7fa12c608873beeb7afa392944dce8829fa8a50c487f266863bb2dd6b743c4a2" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -717,9 +695,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0df1987ed0ff2d0159d76b52e7ddfc4e4fbddacc54d2fbee765e0d14d7c01b5" +checksum = "01e856112bfa0d9adc85bd7c13db03fad0e71d1d6fb4c2010e475b6718108236" dependencies = [ "alloy-primitives", "arbitrary", @@ -729,9 +707,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff69deedee7232d7ce5330259025b868c5e6a52fa8dffda2c861fb3a5889b24" +checksum = "66a4f629da632d5279bbc5731634f0f5c9484ad9c4cad0cd974d9669dc1f46d6" dependencies = [ "alloy-primitives", "async-trait", @@ -744,9 +722,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.1.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930e17cb1e46446a193a593a3bfff8d0ecee4e510b802575ebe300ae2e43ef75" +checksum = "76c8950810dc43660c0f22883659c4218e090a5c75dce33fa4ca787715997b7b" dependencies = [ "alloy-consensus", "alloy-network", @@ -772,7 +750,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -781,15 +759,14 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ - "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.12.1", + "indexmap 2.12.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "syn-solidity", "tiny-keccak", ] @@ -800,15 +777,13 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ - "alloy-json-abi", "const-hex", "dunce", "heck", "macro-string", "proc-macro2", "quote", - "serde_json", - "syn 2.0.111", + "syn 2.0.110", "syn-solidity", ] @@ -836,9 +811,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be98b07210d24acf5b793c99b759e9a696e4a2e67593aec0487ae3b3e1a2478c" +checksum = "fe215a2f9b51d5f1aa5c8cf22c8be8cdb354934de09c9a4e37aefb79b77552fd" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -851,7 +826,7 @@ dependencies = [ "serde_json", "thiserror 2.0.17", "tokio", - "tower", + "tower 0.5.2", "tracing", "url", "wasmtimer", @@ -859,24 +834,24 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4198a1ee82e562cab85e7f3d5921aab725d9bd154b6ad5017f82df1695877c97" +checksum = "dc1b37b1a30d23deb3a8746e882c70b384c574d355bc2bbea9ea918b0c31366e" dependencies = [ "alloy-json-rpc", "alloy-transport", "reqwest", "serde_json", - "tower", + "tower 0.5.2", "tracing", "url", ] [[package]] name = "alloy-transport-ipc" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8db249779ebc20dc265920c7e706ed0d31dbde8627818d1cbde60919b875bb0" +checksum = "52c81a4deeaa0d4b022095db17b286188d731e29ea141d4ec765e166732972e4" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -894,9 +869,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad2344a12398d7105e3722c9b7a7044ea837128e11d453604dec6e3731a86e2" +checksum = "4e9d6f5f304e8943afede2680e5fc7008780d4fc49387eafd53192ad95e20091" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -905,7 +880,7 @@ dependencies = [ "rustls", "serde_json", "tokio", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite", "tracing", "ws_stream_wasm", ] @@ -932,14 +907,14 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.1.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333544408503f42d7d3792bfc0f7218b643d968a03d2c0ed383ae558fb4a76d0" +checksum = "7ccf423f6de62e8ce1d6c7a11fb7508ae3536d02e0d68aaeb05c8669337d0937" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -951,12 +926,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - [[package]] name = "anstream" version = "0.6.21" @@ -1024,7 +993,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1172,7 +1141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1210,7 +1179,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1299,7 +1268,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1353,21 +1322,11 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" -[[package]] -name = "assert-json-diff" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "async-compression" -version = "0.4.34" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" +checksum = "93c1f86859c1af3d514fa19e8323147ff10ea98684e6c7b307912509f50e67b2" dependencies = [ "compression-codecs", "compression-core", @@ -1387,16 +1346,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-object-pool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1ac0219111eb7bb7cb76d4cf2cb50c598e7ae549091d3616f9e95442c18486f" -dependencies = [ - "async-lock", - "event-listener", -] - [[package]] name = "async-stream" version = "0.3.6" @@ -1416,7 +1365,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1427,7 +1376,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1465,7 +1414,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1475,55 +1424,109 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "az" -version = "1.2.1" +name = "aws-lc-rs" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" +dependencies = [ + "aws-lc-sys", + "zeroize", +] [[package]] -name = "backon" -version = "1.6.0" +name = "aws-lc-sys" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" dependencies = [ - "fastrand", - "tokio", + "bindgen 0.69.5", + "cc", + "cmake", + "dunce", + "fs_extra", ] [[package]] -name = "base-flashtypes" -version = "0.2.1" +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ - "alloy-primitives", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", - "brotli", + "async-trait", + "axum-core", "bytes", - "derive_more", - "reth-optimism-primitives", - "rstest", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", "serde", - "serde_json", + "sync_wrapper", + "tower 0.5.2", + "tower-layer", + "tower-service", ] [[package]] -name = "base-reth-cli" -version = "0.2.1" +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ - "reth", + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", ] [[package]] -name = "base-reth-fbal" -version = "0.2.1" +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" [[package]] -name = "base-reth-flashblocks" +name = "backoff" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +dependencies = [ + "getrandom 0.2.16", + "instant", + "rand 0.8.5", +] + +[[package]] +name = "backon" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" +dependencies = [ + "fastrand", + "tokio", +] + +[[package]] +name = "base-reth-flashblocks-rpc" version = "0.2.1" dependencies = [ "alloy-consensus", "alloy-eips", + "alloy-genesis", "alloy-primitives", "alloy-provider", "alloy-rpc-client", @@ -1531,11 +1534,12 @@ dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", "arc-swap", - "base-flashtypes", "base-reth-test-utils", - "criterion", + "brotli", "eyre", "futures-util", + "jsonrpsee 0.26.0", + "jsonrpsee-types 0.26.0", "metrics", "metrics-derive", "once_cell", @@ -1548,8 +1552,11 @@ dependencies = [ "reth-db-common", "reth-e2e-test-utils", "reth-evm", + "reth-exex", "reth-optimism-chainspec", + "reth-optimism-cli", "reth-optimism-evm", + "reth-optimism-node", "reth-optimism-primitives", "reth-optimism-rpc", "reth-primitives", @@ -1558,55 +1565,31 @@ dependencies = [ "reth-rpc-convert", "reth-rpc-eth-api", "reth-testing-utils", + "reth-tracing", + "rollup-boost", + "serde", + "serde_json", "tokio", - "tokio-tungstenite 0.28.0", + "tokio-stream", + "tokio-tungstenite", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", "url", ] [[package]] -name = "base-reth-node" -version = "0.2.1" -dependencies = [ - "base-reth-cli", - "base-reth-runner", - "clap", - "once_cell", - "reth-cli-util", - "reth-optimism-cli", - "reth-optimism-node", -] - -[[package]] -name = "base-reth-rpc" +name = "base-reth-metering" version = "0.2.1" dependencies = [ "alloy-consensus", - "alloy-contract", "alloy-eips", "alloy-genesis", "alloy-primitives", - "alloy-provider", "alloy-rpc-client", - "alloy-rpc-types", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-sol-macro", - "alloy-sol-types", - "base-flashtypes", - "base-reth-flashblocks", "base-reth-test-utils", "eyre", - "futures-util", - "httpmock", - "jsonrpsee", - "jsonrpsee-types", - "metrics", - "metrics-derive", + "jsonrpsee 0.26.0", "op-alloy-consensus", - "op-alloy-network", - "op-alloy-rpc-types", "rand 0.9.2", "reth", "reth-db", @@ -1614,43 +1597,81 @@ dependencies = [ "reth-e2e-test-utils", "reth-evm", "reth-optimism-chainspec", + "reth-optimism-cli", "reth-optimism-evm", "reth-optimism-node", "reth-optimism-primitives", + "reth-primitives", "reth-primitives-traits", "reth-provider", - "reth-rpc", - "reth-rpc-eth-api", "reth-testing-utils", "reth-tracing", "reth-transaction-pool", + "revm", "serde", "serde_json", "tips-core", "tokio", - "tokio-stream", - "tokio-tungstenite 0.28.0", "tracing", ] [[package]] -name = "base-reth-runner" +name = "base-reth-node" version = "0.2.1" dependencies = [ - "base-reth-flashblocks", - "base-reth-rpc", - "base-tracex", - "derive_more", + "alloy-consensus", + "alloy-eips", + "alloy-hardforks", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-trie", + "base-reth-flashblocks-rpc", + "base-reth-metering", + "base-reth-transaction-tracing", + "chrono", + "clap", "eyre", + "futures", "futures-util", + "itertools 0.14.0", + "jsonrpsee 0.26.0", + "metrics", + "metrics-derive", "once_cell", + "op-alloy-consensus", + "op-alloy-network", + "op-alloy-rpc-jsonrpsee", + "op-alloy-rpc-types", + "op-alloy-rpc-types-engine", + "reqwest", "reth", - "reth-db", + "reth-cli-util", "reth-exex", "reth-optimism-chainspec", + "reth-optimism-cli", + "reth-optimism-evm", "reth-optimism-node", + "reth-optimism-primitives", + "reth-optimism-rpc", + "reth-primitives", + "reth-rpc-convert", + "reth-rpc-eth-api", + "revm", + "revm-bytecode", + "rollup-boost", + "rustls", + "serde", + "serde_json", + "time", + "tokio", + "tokio-stream", + "tokio-tungstenite", "tracing", "url", + "uuid", ] [[package]] @@ -1658,7 +1679,6 @@ name = "base-reth-test-utils" version = "0.2.1" dependencies = [ "alloy-consensus", - "alloy-contract", "alloy-eips", "alloy-genesis", "alloy-primitives", @@ -1666,59 +1686,70 @@ dependencies = [ "alloy-rpc-client", "alloy-rpc-types", "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", "alloy-signer", "alloy-signer-local", - "alloy-sol-macro", - "alloy-sol-types", - "base-flashtypes", - "base-reth-flashblocks", - "base-reth-rpc", + "base-reth-flashblocks-rpc", "chrono", - "derive_more", "eyre", + "futures", "futures-util", - "jsonrpsee", + "jsonrpsee 0.26.0", "once_cell", + "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "reth", "reth-db", + "reth-db-common", "reth-e2e-test-utils", "reth-exex", "reth-ipc", "reth-node-core", "reth-optimism-chainspec", + "reth-optimism-cli", "reth-optimism-node", "reth-optimism-primitives", "reth-optimism-rpc", + "reth-primitives", "reth-primitives-traits", "reth-provider", "reth-rpc-layer", + "reth-testing-utils", "reth-tracing", + "rollup-boost", + "serde", "serde_json", "tokio", "tokio-stream", - "tower", - "tracing-subscriber 0.3.22", + "tokio-util", + "tower 0.5.2", + "tracing", + "tracing-subscriber 0.3.20", "url", ] [[package]] -name = "base-tracex" +name = "base-reth-transaction-tracing" version = "0.2.1" dependencies = [ "alloy-primitives", "chrono", - "derive_more", "eyre", "futures", "lru 0.16.2", "metrics", + "metrics-derive", "reth", "reth-exex", + "reth-optimism-cli", + "reth-optimism-node", + "reth-primitives", "reth-tracing", "tokio", + "tokio-stream", ] [[package]] @@ -1782,6 +1813,29 @@ dependencies = [ "serde", ] +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.110", + "which", +] + [[package]] name = "bindgen" version = "0.71.1" @@ -1795,9 +1849,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 2.1.1", "shlex", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1813,9 +1867,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 2.1.1", "shlex", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -1835,15 +1889,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin-io" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin_hashes" -version = "0.14.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ "bitcoin-io", "hex-conservative", @@ -1917,9 +1971,9 @@ dependencies = [ "boa_interner", "boa_macros", "boa_string", - "indexmap 2.12.1", + "indexmap 2.12.0", "num-bigint", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -1947,9 +2001,9 @@ dependencies = [ "futures-channel", "futures-concurrency", "futures-lite", - "hashbrown 0.16.1", + "hashbrown 0.16.0", "icu_normalizer", - "indexmap 2.12.1", + "indexmap 2.12.0", "intrusive-collections", "itertools 0.14.0", "num-bigint", @@ -1960,7 +2014,7 @@ dependencies = [ "portable-atomic", "rand 0.9.2", "regress", - "rustc-hash", + "rustc-hash 2.1.1", "ryu-js", "serde", "serde_json", @@ -1982,7 +2036,7 @@ checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da" dependencies = [ "boa_macros", "boa_string", - "hashbrown 0.16.1", + "hashbrown 0.16.0", "thin-vec", ] @@ -1994,11 +2048,11 @@ checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848" dependencies = [ "boa_gc", "boa_macros", - "hashbrown 0.16.1", - "indexmap 2.12.1", + "hashbrown 0.16.0", + "indexmap 2.12.0", "once_cell", "phf", - "rustc-hash", + "rustc-hash 2.1.1", "static_assertions", ] @@ -2012,7 +2066,7 @@ dependencies = [ "cow-utils", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] @@ -2031,7 +2085,7 @@ dependencies = [ "num-bigint", "num-traits", "regress", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -2043,16 +2097,16 @@ dependencies = [ "fast-float2", "itoa", "paste", - "rustc-hash", + "rustc-hash 2.1.1", "ryu-js", "static_assertions", ] [[package]] name = "borsh" -version = "1.6.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" dependencies = [ "borsh-derive", "cfg_aliases", @@ -2060,22 +2114,22 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "boyer-moore-magiclen" -version = "0.2.22" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7441b4796eb8a7107d4cd99d829810be75f5573e1081c37faa0e8094169ea0d6" +checksum = "95e6233f2d926b5b123caf9d58e3885885255567fbe7776a7fdcae2a4d7241c4" dependencies = [ "debug-helper", ] @@ -2146,7 +2200,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2231,12 +2285,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - [[package]] name = "castaway" version = "0.2.4" @@ -2298,33 +2346,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "ciborium" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" - -[[package]] -name = "ciborium-ll" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" -dependencies = [ - "ciborium-io", - "half", -] - [[package]] name = "cipher" version = "0.4.4" @@ -2377,7 +2398,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2386,6 +2407,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "coins-bip32" version = "0.12.0" @@ -2480,9 +2510,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.33" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" +checksum = "680dc087785c5230f8e8843e2e57ac7c1c90488b6a91b88caa265410568f441b" dependencies = [ "brotli", "compression-core", @@ -2494,9 +2524,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "3a9b614a5787ef0c8802a55766480563cb3a93b435898c422ed2a359cf811582" [[package]] name = "concat-kdf" @@ -2562,9 +2592,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.10.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" dependencies = [ "unicode-segmentation", ] @@ -2631,9 +2661,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.4.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] @@ -2653,44 +2683,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap", - "criterion-plot", - "futures", - "is-terminal", - "itertools 0.10.5", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "tokio", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools 0.10.5", -] - [[package]] name = "critical-section" version = "1.2.0" @@ -2832,7 +2824,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2866,7 +2858,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2881,7 +2873,7 @@ dependencies = [ "quote", "serde", "strsim", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2892,7 +2884,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2903,7 +2895,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -2956,7 +2948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3015,7 +3007,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3026,7 +3018,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3047,7 +3039,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3057,29 +3049,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "derive_more" -version = "2.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.1.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.1", - "syn 2.0.111", + "syn 2.0.110", "unicode-xid", ] @@ -3199,7 +3190,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3217,6 +3208,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "dunce" version = "1.0.5" @@ -3246,7 +3243,7 @@ checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3298,7 +3295,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3330,6 +3327,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enr" version = "0.13.0" @@ -3359,7 +3371,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3379,7 +3391,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3399,7 +3411,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3475,7 +3487,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3661,6 +3673,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -3773,7 +3791,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -3982,24 +4000,13 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.12.1", + "indexmap 2.12.0", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "zerocopy", -] - [[package]] name = "hash-db" version = "0.15.2" @@ -4040,15 +4047,14 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" dependencies = [ "allocator-api2", "equivalent", "foldhash 0.2.0", "serde", - "serde_core", ] [[package]] @@ -4070,30 +4076,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64 0.22.1", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - [[package]] name = "heck" version = "0.5.0" @@ -4114,9 +4096,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.2.2" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" dependencies = [ "arrayvec", ] @@ -4187,13 +4169,23 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "http" -version = "1.4.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", + "fnv", "itoa", ] @@ -4228,49 +4220,15 @@ checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" [[package]] name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "httpmock" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511f510e9b1888d67f10bab4397f8b019d2a9b249a2c10acbce2d705b1b32e26" -dependencies = [ - "assert-json-diff", - "async-object-pool", - "async-trait", - "base64 0.22.1", - "bytes", - "crossbeam-utils", - "form_urlencoded", - "futures-timer", - "futures-util", - "headers", - "http", - "http-body-util", - "hyper", - "hyper-util", - "path-tree", - "regex", - "serde", - "serde_json", - "serde_regex", - "similar", - "stringmetrics", - "tabwriter", - "thiserror 2.0.17", - "tokio", - "tracing", - "url", -] +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "human_bytes" @@ -4367,9 +4325,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" dependencies = [ "base64 0.22.1", "bytes", @@ -4383,10 +4341,12 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -4401,7 +4361,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.57.0", ] [[package]] @@ -4556,7 +4516,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -4597,13 +4557,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.16.0", "serde", "serde_core", ] @@ -4649,15 +4609,24 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.10" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6778b0196eefee7df739db78758e5cf9b37412268bfa5650bfeed028aed20d9c" +checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a" dependencies = [ "darling 0.20.11", "indoc", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", ] [[package]] @@ -4712,17 +4681,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is-terminal" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.61.2", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -4738,6 +4696,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -4796,14 +4763,28 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" dependencies = [ "once_cell", "wasm-bindgen", ] +[[package]] +name = "jsonrpsee" +version = "0.25.1" +source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" +dependencies = [ + "jsonrpsee-core 0.25.1", + "jsonrpsee-http-client 0.25.1", + "jsonrpsee-proc-macros 0.25.1", + "jsonrpsee-server 0.25.1", + "jsonrpsee-types 0.25.1", + "tokio", + "tracing", +] + [[package]] name = "jsonrpsee" version = "0.26.0" @@ -4811,11 +4792,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-http-client", - "jsonrpsee-proc-macros", - "jsonrpsee-server", - "jsonrpsee-types", + "jsonrpsee-core 0.26.0", + "jsonrpsee-http-client 0.26.0", + "jsonrpsee-proc-macros 0.26.0", + "jsonrpsee-server 0.26.0", + "jsonrpsee-types 0.26.0", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tokio", @@ -4833,7 +4814,7 @@ dependencies = [ "futures-util", "gloo-net", "http", - "jsonrpsee-core", + "jsonrpsee-core 0.26.0", "pin-project", "rustls", "rustls-pki-types", @@ -4847,6 +4828,30 @@ dependencies = [ "url", ] +[[package]] +name = "jsonrpsee-core" +version = "0.25.1" +source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "jsonrpsee-types 0.25.1", + "parking_lot", + "pin-project", + "rand 0.9.2", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tower 0.5.2", + "tracing", +] + [[package]] name = "jsonrpsee-core" version = "0.26.0" @@ -4860,21 +4865,43 @@ dependencies = [ "http", "http-body", "http-body-util", - "jsonrpsee-types", + "jsonrpsee-types 0.26.0", "parking_lot", "pin-project", "rand 0.9.2", - "rustc-hash", + "rustc-hash 2.1.1", "serde", "serde_json", "thiserror 2.0.17", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "wasm-bindgen-futures", ] +[[package]] +name = "jsonrpsee-http-client" +version = "0.25.1" +source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" +dependencies = [ + "base64 0.22.1", + "http-body", + "hyper", + "hyper-rustls", + "hyper-util", + "jsonrpsee-core 0.25.1", + "jsonrpsee-types 0.25.1", + "rustls", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tower 0.5.2", + "url", +] + [[package]] name = "jsonrpsee-http-client" version = "0.26.0" @@ -4886,18 +4913,30 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", "rustls", "rustls-platform-verifier", "serde", "serde_json", "thiserror 2.0.17", "tokio", - "tower", + "tower 0.5.2", "url", ] +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.25.1" +source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "jsonrpsee-proc-macros" version = "0.26.0" @@ -4908,7 +4947,33 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.25.1" +source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" +dependencies = [ + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "jsonrpsee-core 0.25.1", + "jsonrpsee-types 0.25.1", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tokio-util", + "tower 0.5.2", + "tracing", ] [[package]] @@ -4923,8 +4988,8 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", "pin-project", "route-recognizer", "serde", @@ -4934,10 +4999,21 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "tower", + "tower 0.5.2", "tracing", ] +[[package]] +name = "jsonrpsee-types" +version = "0.25.1" +source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" +dependencies = [ + "http", + "serde", + "serde_json", + "thiserror 2.0.17", +] + [[package]] name = "jsonrpsee-types" version = "0.26.0" @@ -4957,9 +5033,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", - "tower", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", + "tower 0.5.2", ] [[package]] @@ -4970,9 +5046,9 @@ checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" dependencies = [ "http", "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", - "tower", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", + "tower 0.5.2", "url", ] @@ -5051,11 +5127,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" -version = "0.2.178" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libgit2-sys" @@ -5190,9 +5272,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loom" @@ -5204,7 +5286,7 @@ dependencies = [ "generator", "scoped-tls", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", ] [[package]] @@ -5231,7 +5313,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.16.0", ] [[package]] @@ -5282,7 +5364,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5305,6 +5387,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.6" @@ -5331,9 +5419,9 @@ dependencies = [ [[package]] name = "metrics" -version = "0.24.3" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +checksum = "25dea7ac8057892855ec285c440160265225438c3c45072613c25a4b26e98ef5" dependencies = [ "ahash", "portable-atomic", @@ -5348,7 +5436,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5358,11 +5446,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" dependencies = [ "base64 0.22.1", - "indexmap 2.12.1", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "indexmap 2.12.0", + "ipnet", "metrics", "metrics-util", "quanta", "thiserror 1.0.69", + "tokio", + "tracing", ] [[package]] @@ -5387,11 +5482,15 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8496cc523d1f94c1385dd8f0f0c2c480b2b8aeccb5b7e4485ad6365523ae376" dependencies = [ + "aho-corasick", "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.15.5", + "indexmap 2.12.0", "metrics", + "ordered-float", "quanta", + "radix_trie", "rand 0.9.2", "rand_xoshiro", "sketches-ddsketch", @@ -5446,9 +5545,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" dependencies = [ "libc", "log", @@ -5483,10 +5582,13 @@ version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ + "async-lock", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", "equivalent", + "event-listener", + "futures-util", "parking_lot", "portable-atomic", "rustc_version 0.4.1", @@ -5559,6 +5661,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nom" version = "7.1.3" @@ -5721,7 +5832,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5764,17 +5875,11 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "oorandom" -version = "11.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" - [[package]] name = "op-alloy-consensus" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726da827358a547be9f1e37c2a756b9e3729cb0350f43408164794b370cad8ae" +checksum = "e82f4f768ba39e52a4efe1b8f3425c04ab0d0e6f90c003fe97e5444cd963405e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5798,9 +5903,9 @@ checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc" [[package]] name = "op-alloy-network" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63f27e65be273ec8fcb0b6af0fd850b550979465ab93423705ceb3dfddbd2ab" +checksum = "f2607d0d985f848f98fa79068d11c612f8476dba7deb7498881794bf51b3cfb5" dependencies = [ "alloy-consensus", "alloy-network", @@ -5814,19 +5919,19 @@ dependencies = [ [[package]] name = "op-alloy-rpc-jsonrpsee" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef9114426b16172254555aad34a8ea96c01895e40da92f5d12ea680a1baeaa7" +checksum = "6911db73a4bf59bf8a963dec153ada1057fa426fdc35e0b35fe82657af3501a3" dependencies = [ "alloy-primitives", - "jsonrpsee", + "jsonrpsee 0.26.0", ] [[package]] name = "op-alloy-rpc-types" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562dd4462562c41f9fdc4d860858c40e14a25df7f983ae82047f15f08fce4d19" +checksum = "890b51c3a619c263d52ee5a945dce173a4052d017f93bf5698613b21cbe0d237" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5843,9 +5948,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.22.4" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f24b8cb66e4b33e6c9e508bf46b8ecafc92eadd0b93fedd306c0accb477657" +checksum = "c92f9dd709b3a769b7604d4d2257846b6de3d3f60e5163982cc4e90c0d0b6f95" dependencies = [ "alloy-consensus", "alloy-eips", @@ -5902,7 +6007,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -5923,6 +6028,20 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "236e667b670a5cdf90c258f5a55794ec5ac5027e960c224bff8367a59e1e6426" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.17", + "tracing", +] + [[package]] name = "opentelemetry" version = "0.31.0" @@ -5937,6 +6056,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "opentelemetry-http" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8863faf2910030d139fb48715ad5ff2f35029fc5f244f6d5f689ddcf4d26253" +dependencies = [ + "async-trait", + "bytes", + "http", + "opentelemetry 0.28.0", + "reqwest", + "tracing", +] + [[package]] name = "opentelemetry-http" version = "0.31.0" @@ -5946,8 +6079,30 @@ dependencies = [ "async-trait", "bytes", "http", - "opentelemetry", + "opentelemetry 0.31.0", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bef114c6d41bea83d6dc60eb41720eedd0261a67af57b66dd2b84ac46c01d91" +dependencies = [ + "async-trait", + "futures-core", + "http", + "opentelemetry 0.28.0", + "opentelemetry-http 0.28.0", + "opentelemetry-proto 0.28.0", + "opentelemetry_sdk 0.28.0", + "prost 0.13.5", "reqwest", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tonic 0.12.3", + "tracing", ] [[package]] @@ -5957,28 +6112,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" dependencies = [ "http", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", - "prost", + "opentelemetry 0.31.0", + "opentelemetry-http 0.31.0", + "opentelemetry-proto 0.31.0", + "opentelemetry_sdk 0.31.0", + "prost 0.14.1", "reqwest", "thiserror 2.0.17", "tokio", - "tonic", + "tonic 0.14.2", "tracing", ] +[[package]] +name = "opentelemetry-proto" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f8870d3024727e99212eb3bb1762ec16e255e3e6f58eeb3dc8db1aa226746d" +dependencies = [ + "base64 0.22.1", + "hex", + "opentelemetry 0.28.0", + "opentelemetry_sdk 0.28.0", + "prost 0.13.5", + "serde", + "tonic 0.12.3", +] + [[package]] name = "opentelemetry-proto" version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ - "opentelemetry", - "opentelemetry_sdk", - "prost", - "tonic", + "opentelemetry 0.31.0", + "opentelemetry_sdk 0.31.0", + "prost 0.14.1", + "tonic 0.14.2", "tonic-prost", ] @@ -5988,6 +6158,27 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" +[[package]] +name = "opentelemetry_sdk" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84dfad6042089c7fc1f6118b7040dc2eb4ab520abbf410b79dc481032af39570" +dependencies = [ + "async-trait", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "opentelemetry 0.28.0", + "percent-encoding", + "rand 0.8.5", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "opentelemetry_sdk" version = "0.31.0" @@ -5997,7 +6188,7 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", - "opentelemetry", + "opentelemetry 0.31.0", "percent-encoding", "rand 0.9.2", "thiserror 2.0.17", @@ -6009,6 +6200,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "p256" version = "0.13.2" @@ -6058,7 +6258,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6096,15 +6296,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "path-tree" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a97453bc21a968f722df730bfe11bd08745cb50d1300b0df2bda131dece136" -dependencies = [ - "smallvec", -] - [[package]] name = "pbkdf2" version = "0.12.2" @@ -6133,9 +6324,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.4" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", "ucd-trie", @@ -6182,7 +6373,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6211,7 +6402,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6251,34 +6442,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "plotters" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" - -[[package]] -name = "plotters-svg" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" -dependencies = [ - "plotters-backend", -] - [[package]] name = "polyval" version = "0.6.2" @@ -6331,6 +6494,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.110", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -6379,7 +6552,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6474,7 +6647,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6485,7 +6658,17 @@ checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive 0.13.5", ] [[package]] @@ -6495,7 +6678,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.14.1", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.110", ] [[package]] @@ -6508,7 +6704,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6563,9 +6759,9 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 2.1.1", "rustls", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror 2.0.17", "tokio", "tracing", @@ -6583,7 +6779,7 @@ dependencies = [ "lru-slab", "rand 0.9.2", "ring", - "rustc-hash", + "rustc-hash 2.1.1", "rustls", "rustls-pki-types", "slab", @@ -6602,7 +6798,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] @@ -6628,6 +6824,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -6812,7 +7018,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -6850,16 +7056,10 @@ version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.16.0", "memchr", ] -[[package]] -name = "relative-path" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" - [[package]] name = "reqwest" version = "0.12.24" @@ -6868,9 +7068,11 @@ checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-channel", "futures-core", "futures-util", + "h2", "http", "http-body", "http-body-util", @@ -6880,6 +7082,7 @@ dependencies = [ "hyper-util", "js-sys", "log", + "mime", "native-tls", "percent-encoding", "pin-project-lite", @@ -6895,7 +7098,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "tower-service", "url", @@ -7177,7 +7380,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -7265,7 +7468,7 @@ dependencies = [ "reth-static-file-types", "reth-storage-errors", "reth-tracing", - "rustc-hash", + "rustc-hash 2.1.1", "strum 0.27.2", "sysinfo", "tempfile", @@ -7471,7 +7674,7 @@ dependencies = [ "derive_more", "eyre", "futures-util", - "jsonrpsee", + "jsonrpsee 0.26.0", "reth-chainspec", "reth-cli-commands", "reth-config", @@ -7877,7 +8080,7 @@ dependencies = [ "alloy-primitives", "auto_impl", "once_cell", - "rustc-hash", + "rustc-hash 2.1.1", ] [[package]] @@ -8086,7 +8289,7 @@ dependencies = [ "alloy-rpc-types-debug", "eyre", "futures", - "jsonrpsee", + "jsonrpsee 0.26.0", "pretty_assertions", "reth-engine-primitives", "reth-evm", @@ -8112,14 +8315,14 @@ dependencies = [ "futures", "futures-util", "interprocess", - "jsonrpsee", + "jsonrpsee 0.26.0", "pin-project", "serde_json", "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", - "tower", + "tower 0.5.2", "tracing", ] @@ -8225,7 +8428,7 @@ dependencies = [ "reth-tasks", "reth-tokio-util", "reth-transaction-pool", - "rustc-hash", + "rustc-hash 2.1.1", "schnellru", "secp256k1 0.30.0", "serde", @@ -8370,7 +8573,7 @@ dependencies = [ "eyre", "fdlimit", "futures", - "jsonrpsee", + "jsonrpsee 0.26.0", "rayon", "reth-basic-payload-builder", "reth-chain-state", @@ -8533,7 +8736,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite", "tracing", "url", ] @@ -8569,7 +8772,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "eyre", "http", - "jsonrpsee-server", + "jsonrpsee-server 0.26.0", "metrics", "metrics-exporter-prometheus", "metrics-process", @@ -8580,7 +8783,7 @@ dependencies = [ "reth-tasks", "tikv-jemalloc-ctl", "tokio", - "tower", + "tower 0.5.2", "tracing", ] @@ -8761,7 +8964,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tokio-tungstenite 0.26.2", + "tokio-tungstenite", "tracing", "url", ] @@ -8902,9 +9105,9 @@ dependencies = [ "derive_more", "eyre", "futures", - "jsonrpsee", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee 0.26.0", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", "metrics", "op-alloy-consensus", "op-alloy-network", @@ -8940,7 +9143,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", ] @@ -9175,7 +9378,7 @@ dependencies = [ "reth-prune-types", "reth-static-file-types", "reth-tokio-util", - "rustc-hash", + "rustc-hash 2.1.1", "thiserror 2.0.17", "tokio", "tracing", @@ -9289,8 +9492,8 @@ dependencies = [ "http-body", "hyper", "itertools 0.14.0", - "jsonrpsee", - "jsonrpsee-types", + "jsonrpsee 0.26.0", + "jsonrpsee-types 0.26.0", "jsonwebtoken", "parking_lot", "pin-project", @@ -9329,7 +9532,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "tracing-futures", ] @@ -9354,7 +9557,7 @@ dependencies = [ "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-serde", - "jsonrpsee", + "jsonrpsee 0.26.0", "reth-chain-state", "reth-engine-primitives", "reth-network-peers", @@ -9371,7 +9574,7 @@ dependencies = [ "alloy-provider", "dyn-clone", "http", - "jsonrpsee", + "jsonrpsee 0.26.0", "metrics", "pin-project", "reth-chain-state", @@ -9396,7 +9599,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "tracing", ] @@ -9414,7 +9617,7 @@ dependencies = [ "alloy-signer", "auto_impl", "dyn-clone", - "jsonrpsee-types", + "jsonrpsee-types 0.26.0", "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", @@ -9437,8 +9640,8 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", "async-trait", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", "metrics", "parking_lot", "reth-chainspec", @@ -9478,8 +9681,8 @@ dependencies = [ "auto_impl", "dyn-clone", "futures", - "jsonrpsee", - "jsonrpsee-types", + "jsonrpsee 0.26.0", + "jsonrpsee-types 0.26.0", "parking_lot", "reth-chain-state", "reth-chainspec", @@ -9519,8 +9722,8 @@ dependencies = [ "derive_more", "futures", "itertools 0.14.0", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", "metrics", "rand 0.9.2", "reqwest", @@ -9556,9 +9759,9 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "alloy-rpc-types-engine", "http", - "jsonrpsee-http-client", + "jsonrpsee-http-client 0.26.0", "pin-project", - "tower", + "tower 0.5.2", "tower-http", "tracing", ] @@ -9571,8 +9774,8 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", - "jsonrpsee-core", - "jsonrpsee-types", + "jsonrpsee-core 0.26.0", + "jsonrpsee-types 0.26.0", "reth-errors", "reth-network-api", "serde", @@ -9796,7 +9999,7 @@ dependencies = [ "tracing-appender", "tracing-journald", "tracing-logfmt", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", "url", ] @@ -9807,13 +10010,13 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "clap", "eyre", - "opentelemetry", - "opentelemetry-otlp", + "opentelemetry 0.31.0", + "opentelemetry-otlp 0.31.0", "opentelemetry-semantic-conventions", - "opentelemetry_sdk", + "opentelemetry_sdk 0.31.0", "tracing", - "tracing-opentelemetry", - "tracing-subscriber 0.3.22", + "tracing-opentelemetry 0.32.0", + "tracing-subscriber 0.3.20", "url", ] @@ -9847,7 +10050,7 @@ dependencies = [ "reth-tasks", "revm-interpreter", "revm-primitives", - "rustc-hash", + "rustc-hash 2.1.1", "schnellru", "serde", "serde_json", @@ -10303,39 +10506,64 @@ dependencies = [ ] [[package]] -name = "route-recognizer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" - -[[package]] -name = "rstest" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" +name = "rollup-boost" +version = "0.1.0" +source = "git+http://github.com/flashbots/rollup-boost?rev=v0.7.11#196237bab2a02298de994b439e0455abb1ac512f" dependencies = [ - "futures-timer", - "futures-util", - "rstest_macros", + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", + "backoff", + "bytes", + "clap", + "dashmap 6.1.0", + "dotenvy", + "eyre", + "futures", + "http", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "jsonrpsee 0.25.1", + "lru 0.16.2", + "metrics", + "metrics-derive", + "metrics-exporter-prometheus", + "metrics-util", + "moka", + "op-alloy-rpc-types-engine", + "opentelemetry 0.28.0", + "opentelemetry-otlp 0.28.0", + "opentelemetry_sdk 0.28.0", + "parking_lot", + "paste", + "reth-optimism-payload-builder", + "rustls", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.17", + "tokio", + "tokio-tungstenite", + "tokio-util", + "tower 0.5.2", + "tower-http", + "tracing", + "tracing-opentelemetry 0.29.0", + "tracing-subscriber 0.3.20", + "url", + "uuid", + "vergen", + "vergen-git2", ] [[package]] -name = "rstest_macros" -version = "0.26.1" +name = "route-recognizer" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" -dependencies = [ - "cfg-if", - "glob", - "proc-macro-crate", - "proc-macro2", - "quote", - "regex", - "relative-path", - "rustc_version 0.4.1", - "syn 2.0.111", - "unicode-ident", -] +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" [[package]] name = "rug" @@ -10384,6 +10612,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.1" @@ -10458,6 +10692,7 @@ version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -10481,9 +10716,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "web-time", "zeroize", @@ -10522,6 +10757,7 @@ version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -10796,7 +11032,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -10805,7 +11041,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.12.0", "itoa", "memchr", "ryu", @@ -10813,16 +11049,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serde_regex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" -dependencies = [ - "regex", - "serde", -] - [[package]] name = "serde_spanned" version = "0.6.9" @@ -10846,15 +11072,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.1", + "indexmap 2.12.0", "schemars 0.9.0", "schemars 1.1.0", "serde_core", @@ -10865,14 +11091,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -10896,6 +11122,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha1_smol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" + [[package]] name = "sha2" version = "0.10.9" @@ -10974,9 +11206,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.7" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -10997,12 +11229,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "similar" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" - [[package]] name = "simple_asn1" version = "0.6.3" @@ -11137,12 +11363,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stringmetrics" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3c8667cd96245cbb600b8dec5680a7319edd719c5aa2b5d23c6bff94f39765" - [[package]] name = "strsim" version = "0.11.1" @@ -11177,7 +11397,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11189,7 +11409,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11211,9 +11431,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.111" +version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", @@ -11229,7 +11449,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11249,7 +11469,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11266,12 +11486,24 @@ dependencies = [ ] [[package]] -name = "tabwriter" -version = "1.4.1" +name = "system-configuration" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce91f2f0ec87dff7e6bcbbeb267439aa1188703003c6055193c821487400432" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "unicode-width 0.2.0", + "bitflags 2.10.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -11359,7 +11591,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11370,7 +11602,7 @@ checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11476,16 +11708,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "tinyvec" version = "1.10.0" @@ -11504,18 +11726,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tips-core" version = "0.1.0" -source = "git+https://github.com/base/tips?rev=b1dfde6a5c8f603f19f9309ca6dc015bad260b44#b1dfde6a5c8f603f19f9309ca6dc015bad260b44" +source = "git+https://github.com/base/tips?rev=e59327bec565808e0505d3fb3a64749dfc61a41a#e59327bec565808e0505d3fb3a64749dfc61a41a" dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-provider", - "alloy-rpc-types", "alloy-serde", "op-alloy-consensus", "op-alloy-flz", "serde", "tracing", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", "uuid", ] @@ -11544,7 +11765,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -11587,27 +11808,17 @@ checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", + "native-tls", "rustls", "rustls-native-certs", "rustls-pki-types", "tokio", + "tokio-native-tls", "tokio-rustls", - "tungstenite 0.26.2", + "tungstenite", "webpki-roots 0.26.11", ] -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite 0.28.0", -] - [[package]] name = "tokio-util" version = "0.7.17" @@ -11659,7 +11870,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.12.0", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -11673,7 +11884,7 @@ version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.12.1", + "indexmap 2.12.0", "toml_datetime 0.7.3", "toml_parser", "winnow", @@ -11694,6 +11905,36 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost 0.13.5", + "socket2 0.5.10", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tonic" version = "0.14.2" @@ -11714,7 +11955,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -11727,8 +11968,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", - "prost", - "tonic", + "prost 0.14.1", + "tonic 0.14.2", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand 0.8.5", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", ] [[package]] @@ -11740,7 +12001,7 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.12.1", + "indexmap 2.12.0", "pin-project-lite", "slab", "sync_wrapper", @@ -11753,9 +12014,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.7" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "async-compression", "base64 0.22.1", @@ -11775,7 +12036,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -11796,9 +12057,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -11808,32 +12069,32 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.4" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror 2.0.17", + "thiserror 1.0.69", "time", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", ] [[package]] name = "tracing-attributes" -version = "0.1.31" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -11851,13 +12112,13 @@ dependencies = [ [[package]] name = "tracing-journald" -version = "0.3.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0" +checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657" dependencies = [ "libc", "tracing-core", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", ] [[package]] @@ -11880,7 +12141,25 @@ dependencies = [ "time", "tracing", "tracing-core", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", +] + +[[package]] +name = "tracing-opentelemetry" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721f2d2569dce9f3dfbbddee5906941e953bfcdf736a62da3377f5751650cc36" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry 0.28.0", + "opentelemetry_sdk 0.28.0", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber 0.3.20", + "web-time", ] [[package]] @@ -11890,15 +12169,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6e5658463dd88089aba75c7791e1d3120633b1bfde22478b28f625a9bb1b8e" dependencies = [ "js-sys", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.31.0", + "opentelemetry_sdk 0.31.0", "rustversion", "smallvec", "thiserror 2.0.17", "tracing", "tracing-core", "tracing-log", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.20", "web-time", ] @@ -11923,9 +12202,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", @@ -11964,7 +12243,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12000,6 +12279,7 @@ dependencies = [ "http", "httparse", "log", + "native-tls", "rand 0.9.2", "rustls", "rustls-pki-types", @@ -12008,23 +12288,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.2", - "sha1", - "thiserror 2.0.17", - "utf-8", -] - [[package]] name = "typenum" version = "1.19.0" @@ -12174,13 +12437,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.19.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.4", "js-sys", - "serde_core", + "serde", + "sha1_smol", "wasm-bindgen", ] @@ -12251,7 +12515,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12299,9 +12563,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" dependencies = [ "cfg-if", "once_cell", @@ -12312,9 +12576,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" dependencies = [ "cfg-if", "js-sys", @@ -12325,9 +12589,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12335,22 +12599,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" dependencies = [ "unicode-ident", ] @@ -12384,9 +12648,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -12438,6 +12702,18 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "widestring" version = "1.2.1" @@ -12466,7 +12742,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] @@ -12596,7 +12872,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12607,7 +12883,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12618,7 +12894,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12629,7 +12905,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -12664,6 +12940,17 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -13026,9 +13313,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -13130,28 +13417,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.31" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.31" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -13171,7 +13458,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", "synstructure", ] @@ -13192,7 +13479,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] @@ -13226,7 +13513,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.111", + "syn 2.0.110", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6a860dca..ad04878e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,7 +61,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 = "e59327bec565808e0505d3fb3a64749dfc61a41a", default-features = false } # reth reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 21862d13..3a63e920 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -127,6 +127,7 @@ where state_flashblock_index: None, total_gas_used: result.total_gas_used, total_execution_time_us: result.total_execution_time_us, + state_root_time_us: result.state_root_time_us, }) } } From b209cf4f5de2c7a12f8057f6d4708359396135ec Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 28 Oct 2025 14:48:35 -0500 Subject: [PATCH 03/26] Add tests for state root time metrics Add unit test to verify total_execution_time_us >= state_root_time_us invariant and RPC integration test to verify state_root_time_us is properly exposed in the response. --- crates/rpc/tests/meter.rs | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 86f861c4..74ce77d9 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -299,3 +299,56 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { Ok(()) } + +#[test] +fn meter_bundle_state_root_time_invariant() -> eyre::Result<()> { + let harness = setup_harness()?; + + let to = Address::random(); + let signed_tx = TransactionBuilder::default() + .signer(harness.signer(User::Alice)) + .chain_id(harness.chain_spec.chain_id()) + .nonce(0) + .to(to) + .value(1_000) + .gas_limit(21_000) + .max_fee_per_gas(10) + .max_priority_fee_per_gas(1) + .into_eip1559(); + + let tx = + OpTransactionSigned::Eip1559(signed_tx.as_eip1559().expect("eip1559 transaction").clone()); + + let envelope = envelope_from_signed(&tx)?; + + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let bundle_with_metadata = create_bundle_with_metadata(vec![envelope.clone()])?; + + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + vec![envelope], + &harness.header, + &bundle_with_metadata, + )?; + + // Verify invariant: total execution time must include state root time + assert!( + output.total_execution_time_us >= output.state_root_time_us, + "total_execution_time_us ({}) should be >= state_root_time_us ({})", + output.total_execution_time_us, + output.state_root_time_us + ); + + // State root time should be non-zero + assert!( + output.state_root_time_us > 0, + "state_root_time_us should be greater than zero" + ); + + Ok(()) +} From d661038c34fabe391d195a44f1e63373817060a5 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 28 Oct 2025 15:01:08 -0500 Subject: [PATCH 04/26] Simplify state root calculation by removing unnecessary ExecutionOutcome wrapper --- crates/rpc/src/base/meter.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 6ff4e02e..8bebb9a5 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -7,9 +7,7 @@ use reth::revm::db::State; use reth_evm::{ConfigureEvm, execute::BlockBuilder}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; -use reth_optimism_primitives::OpPrimitives; use reth_primitives_traits::SealedHeader; -use reth_provider::ExecutionOutcome; use tips_core::types::{BundleExtensions, BundleTxs, ParsedBundle}; use crate::TransactionResult; @@ -113,18 +111,10 @@ where } // Calculate state root and measure its calculation time - let block_number = header.number() + 1; let bundle_update = db.take_bundle(); - let execution_outcome: ExecutionOutcome = ExecutionOutcome::new( - bundle_update, - Vec::new().into(), - block_number, - Vec::new(), - ); - let state_provider = db.database.as_ref(); let state_root_start = Instant::now(); - let hashed_state = state_provider.hashed_post_state(execution_outcome.state()); + let hashed_state = state_provider.hashed_post_state(&bundle_update); let _ = state_provider.state_root_with_updates(hashed_state); let state_root_time_us = state_root_start.elapsed().as_micros(); From ff175a00b704c8090ebe15248f150a635b6405fc Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Wed, 29 Oct 2025 12:04:14 -0500 Subject: [PATCH 05/26] Use pending flashblocks state for bundle metering Integrate flashblocks state into metering to execute bundles on top of pending flashblock state rather than canonical block state. This ensures metered gas usage and execution time accurately reflect the effects of pending transactions (nonces, balances, storage, code changes). Implementation: - Add flashblocks-rpc dependency to metering crate - Update meter_bundle() to accept optional db_cache parameter - Implement three-layer state architecture: 1. StateProviderDatabase (canonical block base state) 2. CacheDB (applies flashblock pending changes via cache) 3. State wrapper (for EVM builder compatibility) - Update MeteringApiImpl to accept FlashblocksState - Get pending blocks and db_cache from flashblocks when available - Fall back to canonical block state when no flashblocks available - Update response to include flashblock_index in logs - Require flashblocks to be enabled for metering RPC - Update all tests to pass FlashblocksState parameter The metering RPC now uses the same state as flashblocks eth_call, ensuring consistent simulation results. --- Cargo.lock | 69 ++++++++----- Cargo.toml | 3 +- crates/flashblocks/Cargo.toml | 3 + crates/flashblocks/src/pending_blocks.rs | 17 +++- crates/flashblocks/src/processor.rs | 38 ++++++- crates/rpc/Cargo.toml | 4 + crates/rpc/src/base/meter.rs | 47 +++++++-- crates/rpc/src/base/meter_rpc.rs | 124 ++++++++++++++++------- crates/rpc/tests/meter.rs | 33 ++++-- 9 files changed, 257 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30be95d2..47803e34 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,7 +256,7 @@ dependencies = [ "alloy-sol-types", "auto_impl", "derive_more", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-rpc-types-engine", "op-revm", "revm", @@ -370,7 +370,7 @@ dependencies = [ "alloy-op-hardforks", "alloy-primitives", "auto_impl", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-revm", "revm", "thiserror 2.0.17", @@ -1543,7 +1543,7 @@ dependencies = [ "metrics", "metrics-derive", "once_cell", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-network", "op-alloy-rpc-types", "rand 0.9.2", @@ -1566,6 +1566,7 @@ dependencies = [ "reth-rpc-eth-api", "reth-testing-utils", "reth-tracing", + "revm-database", "rollup-boost", "serde", "serde_json", @@ -1586,10 +1587,11 @@ dependencies = [ "alloy-genesis", "alloy-primitives", "alloy-rpc-client", + "base-reth-flashblocks-rpc", "base-reth-test-utils", "eyre", "jsonrpsee 0.26.0", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "rand 0.9.2", "reth", "reth-db", @@ -1608,6 +1610,7 @@ dependencies = [ "reth-tracing", "reth-transaction-pool", "revm", + "revm-database", "serde", "serde_json", "tips-core", @@ -1641,7 +1644,7 @@ dependencies = [ "metrics", "metrics-derive", "once_cell", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-network", "op-alloy-rpc-jsonrpsee", "op-alloy-rpc-types", @@ -1697,7 +1700,7 @@ dependencies = [ "futures-util", "jsonrpsee 0.26.0", "once_cell", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-network", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", @@ -4341,7 +4344,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.1", "system-configuration", "tokio", "tower-service", @@ -4361,7 +4364,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.57.0", + "windows-core 0.61.2", ] [[package]] @@ -5875,6 +5878,20 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "op-alloy-consensus" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a501241474c3118833d6195312ae7eb7cc90bbb0d5f524cbb0b06619e49ff67" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more", + "thiserror 2.0.17", +] + [[package]] name = "op-alloy-consensus" version = "0.22.3" @@ -5913,7 +5930,7 @@ dependencies = [ "alloy-provider", "alloy-rpc-types-eth", "alloy-signer", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-rpc-types", ] @@ -5940,7 +5957,7 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-serde", "derive_more", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "serde", "serde_json", "thiserror 2.0.17", @@ -5961,7 +5978,7 @@ dependencies = [ "derive_more", "ethereum_ssz", "ethereum_ssz_derive", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "serde", "snap", "thiserror 2.0.17", @@ -6761,7 +6778,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2 0.5.10", + "socket2 0.6.1", "thiserror 2.0.17", "tokio", "tracing", @@ -6798,7 +6815,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.1", "tracing", "windows-sys 0.60.2", ] @@ -7366,7 +7383,7 @@ dependencies = [ "arbitrary", "bytes", "modular-bitfield", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "reth-codecs-derive", "reth-zstd-compressors", "serde", @@ -8812,7 +8829,7 @@ dependencies = [ "alloy-primitives", "derive_more", "miniz_oxide", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-rpc-types", "paste", "reth-chainspec", @@ -8840,7 +8857,7 @@ dependencies = [ "derive_more", "eyre", "futures-util", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "reth-chainspec", "reth-cli", "reth-cli-commands", @@ -8912,7 +8929,7 @@ dependencies = [ "alloy-evm", "alloy-op-evm", "alloy-primitives", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-rpc-types-engine", "op-revm", "reth-chainspec", @@ -8991,7 +9008,7 @@ dependencies = [ "alloy-rpc-types-eth", "clap", "eyre", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-rpc-types-engine", "op-revm", "reth-chainspec", @@ -9039,7 +9056,7 @@ dependencies = [ "alloy-rpc-types-debug", "alloy-rpc-types-engine", "derive_more", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-rpc-types-engine", "reth-basic-payload-builder", "reth-chain-state", @@ -9078,7 +9095,7 @@ dependencies = [ "arbitrary", "bytes", "modular-bitfield", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "reth-codecs", "reth-primitives-traits", "reth-zstd-compressors", @@ -9109,7 +9126,7 @@ dependencies = [ "jsonrpsee-core 0.26.0", "jsonrpsee-types 0.26.0", "metrics", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-network", "op-alloy-rpc-jsonrpsee", "op-alloy-rpc-types", @@ -9173,7 +9190,7 @@ dependencies = [ "derive_more", "futures-util", "metrics", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-flz", "op-alloy-rpc-types", "op-revm", @@ -9299,7 +9316,7 @@ dependencies = [ "derive_more", "modular-bitfield", "once_cell", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "proptest", "proptest-arbitrary-interop", "rayon", @@ -9618,7 +9635,7 @@ dependencies = [ "auto_impl", "dyn-clone", "jsonrpsee-types 0.26.0", - "op-alloy-consensus", + "op-alloy-consensus 0.22.3", "op-alloy-network", "op-alloy-rpc-types", "op-revm", @@ -11726,13 +11743,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tips-core" version = "0.1.0" -source = "git+https://github.com/base/tips?rev=e59327bec565808e0505d3fb3a64749dfc61a41a#e59327bec565808e0505d3fb3a64749dfc61a41a" +source = "git+https://github.com/base/tips?rev=86b275c0fd63226c3fb85ac5512033f99b67d0f5#86b275c0fd63226c3fb85ac5512033f99b67d0f5" dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-provider", "alloy-serde", - "op-alloy-consensus", + "op-alloy-consensus 0.20.0", "op-alloy-flz", "serde", "tracing", diff --git a/Cargo.toml b/Cargo.toml index ad04878e..c54a4b24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ base-reth-flashblocks = { path = "crates/flashblocks" } # 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 = "e59327bec565808e0505d3fb3a64749dfc61a41a", default-features = false } +tips-core = { git = "https://github.com/base/tips", rev = "86b275c0fd63226c3fb85ac5512033f99b67d0f5", default-features = false } # reth reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } @@ -96,6 +96,7 @@ reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", feature # 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" diff --git a/crates/flashblocks/Cargo.toml b/crates/flashblocks/Cargo.toml index b649312b..571c2350 100644 --- a/crates/flashblocks/Cargo.toml +++ b/crates/flashblocks/Cargo.toml @@ -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 diff --git a/crates/flashblocks/src/pending_blocks.rs b/crates/flashblocks/src/pending_blocks.rs index eaf31ed5..da284e68 100644 --- a/crates/flashblocks/src/pending_blocks.rs +++ b/crates/flashblocks/src/pending_blocks.rs @@ -14,7 +14,7 @@ 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}; @@ -36,6 +36,7 @@ pub struct PendingBlocksBuilder { state_overrides: Option, db_cache: Cache, + bundle_state: BundleState, } impl PendingBlocksBuilder { @@ -52,6 +53,7 @@ impl PendingBlocksBuilder { transaction_senders: HashMap::new(), state_overrides: None, db_cache: Cache::default(), + bundle_state: BundleState::default(), } } @@ -122,6 +124,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 { if self.headers.is_empty() { return Err(eyre!("missing headers")); @@ -143,6 +151,7 @@ impl PendingBlocksBuilder { transaction_senders: self.transaction_senders, state_overrides: self.state_overrides, db_cache: self.db_cache, + bundle_state: self.bundle_state, }) } } @@ -163,6 +172,7 @@ pub struct PendingBlocks { state_overrides: Option, db_cache: Cache, + bundle_state: BundleState, } impl PendingBlocks { @@ -211,6 +221,11 @@ impl PendingBlocks { self.db_cache.clone() } + /// Returns the bundle state. + pub fn get_bundle_state(&self) -> BundleState { + self.bundle_state.clone() + } + /// Returns all transactions for a specific block number. pub fn get_transactions_for_block(&self, block_number: BlockNumber) -> Vec { self.transactions diff --git a/crates/flashblocks/src/processor.rs b/crates/flashblocks/src/processor.rs index 2db3bf0b..f24b23c4 100644 --- a/crates/flashblocks/src/processor.rs +++ b/crates/flashblocks/src/processor.rs @@ -21,14 +21,19 @@ use eyre::eyre; use op_alloy_consensus::OpTxEnvelope; use op_alloy_network::TransactionResponse; use op_alloy_rpc_types::Transaction; +<<<<<<< HEAD use reth::{ chainspec::{ChainSpecProvider, EthChainSpec}, providers::{BlockReaderIdExt, StateProviderFactory}, revm::{ - DatabaseCommit, State, context::result::ResultAndState, database::StateProviderDatabase, + context::result::ResultAndState, + database::StateProviderDatabase, db::CacheDB, + DatabaseCommit, + State, }, }; +use revm_database::states::bundle_state::BundleRetention; use reth_evm::{ConfigureEvm, Evm}; use reth_optimism_chainspec::OpHardforks; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; @@ -293,12 +298,39 @@ where let state_provider = self.client.state_by_block_number_or_tag(BlockNumberOrTag::Number(canonical_block))?; let state_provider_db = StateProviderDatabase::new(state_provider); +<<<<<<< HEAD let state = State::builder().with_database(state_provider_db).with_bundle_update().build(); let mut pending_blocks_builder = PendingBlocksBuilder::new(); let mut db = match &prev_pending_blocks { Some(pending_blocks) => CacheDB { cache: pending_blocks.get_db_cache(), db: state }, None => CacheDB::new(state), +======= + 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) => 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(), +>>>>>>> 1c80299 (Use pending flashblocks state for bundle metering) }; let mut state_overrides = @@ -537,7 +569,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()?))) } diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 390fdf1a..21f7fd54 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -43,6 +43,10 @@ op-alloy-network.workspace = true tokio.workspace = true tokio-stream.workspace = true +# revm +revm.workspace = true +revm-database.workspace = true + # rpc jsonrpsee.workspace = true jsonrpsee-types.workspace = true diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 8bebb9a5..0c37f8b4 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -3,7 +3,8 @@ use std::{sync::Arc, time::Instant}; use alloy_consensus::{BlockHeader, Transaction as _, transaction::SignerRecoverable}; use alloy_primitives::{B256, U256}; use eyre::{Result as EyreResult, eyre}; -use reth::revm::db::State; +use reth::revm::db::{BundleState, Cache, CacheDB, State}; +use revm_database::states::bundle_state::BundleRetention; use reth_evm::{ConfigureEvm, execute::BlockBuilder}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; @@ -12,6 +13,15 @@ use tips_core::types::{BundleExtensions, BundleTxs, ParsedBundle}; use crate::TransactionResult; +/// State from pending flashblocks that is used as a base for metering +#[derive(Debug, Clone)] +pub struct FlashblocksState { + /// The cache of account and storage data + pub cache: Cache, + /// The accumulated bundle of state changes + pub bundle_state: BundleState, +} + const BLOCK_TIME: u64 = 2; // 2 seconds per block /// Output from metering a bundle of transactions @@ -33,8 +43,8 @@ pub struct MeterBundleOutput { /// Simulates and meters a bundle of transactions /// -/// Takes a state provider, chain spec, parsed bundle, and block header, then executes transactions -/// in sequence to measure gas usage and execution time. +/// Takes a state provider, chain spec, parsed bundle, block header, and optional flashblocks state, +/// then executes transactions in sequence to measure gas usage and execution time. /// /// Returns [`MeterBundleOutput`] containing transaction results and aggregated metrics. pub fn meter_bundle( @@ -42,6 +52,7 @@ pub fn meter_bundle( chain_spec: Arc, bundle: ParsedBundle, header: &SealedHeader, + flashblocks_state: Option, ) -> EyreResult where SP: reth_provider::StateProvider, @@ -51,7 +62,29 @@ where // Create state database let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); - let mut db = State::builder().with_database(state_db).with_bundle_update().build(); + // If we have flashblocks state, apply both cache and bundle prestate + let cache_db = if let Some(ref flashblocks) = flashblocks_state { + CacheDB { + cache: flashblocks.cache.clone(), + db: state_db, + } + } else { + CacheDB::new(state_db) + }; + + // Wrap the CacheDB in a State to track bundle changes for state root calculation + let mut db = if let Some(flashblocks) = flashblocks_state.as_ref() { + State::builder() + .with_database(cache_db) + .with_bundle_update() + .with_bundle_prestate(flashblocks.bundle_state.clone()) + .build() + } else { + State::builder() + .with_database(cache_db) + .with_bundle_update() + .build() + }; // Set up next block attributes // Use bundle.min_timestamp if provided, otherwise use header timestamp + BLOCK_TIME @@ -110,9 +143,11 @@ where } } - // Calculate state root and measure its calculation time + // Calculate state root and measure its calculation time. The bundle already includes + // flashblocks state if it was provided via with_bundle_prestate. + db.merge_transitions(BundleRetention::Reverts); let bundle_update = db.take_bundle(); - let state_provider = db.database.as_ref(); + let state_provider = db.database.db.as_ref(); let state_root_start = Instant::now(); let hashed_state = state_provider.hashed_post_state(&bundle_update); let _ = state_provider.state_root_with_updates(hashed_state); diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 3a63e920..aaf35e31 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -1,36 +1,42 @@ -use alloy_consensus::Header; -use alloy_eips::BlockNumberOrTag; +use alloy_consensus::{Header, Sealed}; use alloy_primitives::U256; +use base_reth_flashblocks::FlashblocksAPI; use jsonrpsee::core::{RpcResult, async_trait}; use reth::providers::BlockReaderIdExt; use reth_optimism_chainspec::OpChainSpec; +use reth_primitives_traits::SealedHeader; use reth_provider::{ChainSpecProvider, StateProviderFactory}; +use std::sync::Arc; use tips_core::types::{Bundle, MeterBundleResponse, ParsedBundle}; use tracing::{error, info}; use crate::{MeteringApiServer, meter_bundle}; /// Implementation of the metering RPC API -#[derive(Debug)] -pub struct MeteringApiImpl { +pub struct MeteringApiImpl { provider: Provider, + flashblocks_state: Arc, } -impl MeteringApiImpl +impl MeteringApiImpl where Provider: StateProviderFactory + ChainSpecProvider + BlockReaderIdExt
+ Clone, + FB: FlashblocksAPI, { /// Creates a new instance of MeteringApi - pub const fn new(provider: Provider) -> Self { - Self { provider } + pub fn new(provider: Provider, flashblocks_state: Arc) -> Self { + Self { + provider, + flashblocks_state, + } } } #[async_trait] -impl MeteringApiServer for MeteringApiImpl +impl MeteringApiServer for MeteringApiImpl where Provider: StateProviderFactory + ChainSpecProvider @@ -39,6 +45,7 @@ where + Send + Sync + 'static, + FB: FlashblocksAPI + Send + Sync + 'static, { async fn meter_bundle(&self, bundle: Bundle) -> RpcResult { info!( @@ -47,24 +54,54 @@ where "Starting bundle metering" ); - // Get the latest header - let header = self - .provider - .sealed_header_by_number_or_tag(BlockNumberOrTag::Latest) - .map_err(|e| { - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - format!("Failed to get latest header: {}", e), - None::<()>, - ) - })? - .ok_or_else(|| { - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - "Latest block not found".to_string(), - None::<()>, - ) - })?; + // Get pending flashblocks state + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + + // Get header and flashblock index from pending blocks + // If no pending blocks exist, fall back to latest canonical block + let (header, flashblock_index, canonical_block_number) = if let Some(pb) = pending_blocks.as_ref() { + let latest_header: Sealed
= pb.latest_header(); + let flashblock_index = pb.latest_flashblock_index(); + let canonical_block_number = pb.canonical_block_number(); + + info!( + latest_block = latest_header.number, + canonical_block = %canonical_block_number, + flashblock_index = flashblock_index, + "Using latest flashblock state for metering" + ); + + // Convert Sealed
to SealedHeader + let sealed_header = SealedHeader::new(latest_header.inner().clone(), latest_header.hash()); + (sealed_header, flashblock_index, canonical_block_number) + } else { + // No pending blocks, use latest canonical block + let canonical_block_number = pending_blocks.get_canonical_block_number(); + let header = self + .provider + .sealed_header_by_number_or_tag(canonical_block_number) + .map_err(|e| { + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Failed to get canonical block header: {}", e), + None::<()>, + ) + })? + .ok_or_else(|| { + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + "Canonical block not found".to_string(), + None::<()>, + ) + })?; + + info!( + canonical_block = header.number, + "No flashblocks available, using canonical block state for metering" + ); + + (header, 0, canonical_block_number) + }; let parsed_bundle = ParsedBundle::try_from(bundle).map_err(|e| { jsonrpsee::types::ErrorObjectOwned::owned( @@ -74,15 +111,27 @@ where ) })?; - // Get state provider for the block - let state_provider = self.provider.state_by_block_hash(header.hash()).map_err(|e| { - error!(error = %e, "Failed to get state provider"); - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - format!("Failed to get state provider: {}", e), - None::<()>, - ) - })?; + // Get state provider for the canonical block + let state_provider = self + .provider + .state_by_block_number_or_tag(canonical_block_number) + .map_err(|e| { + error!(error = %e, "Failed to get state provider"); + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Failed to get state provider: {}", e), + None::<()>, + ) + })?; + + // If we have pending flashblocks, get the state to apply pending changes + let flashblocks_state = pending_blocks.as_ref().map(|pb| crate::FlashblocksState { + cache: pb.get_db_cache(), + bundle_state: pb.get_bundle_state(), + }); + + // Get the flashblock index if we have pending flashblocks + let state_flashblock_index = pending_blocks.as_ref().map(|pb| pb.latest_flashblock_index()); // Meter bundle using utility function let result = meter_bundle( @@ -90,6 +139,7 @@ where self.provider.chain_spec(), parsed_bundle, &header, + flashblocks_state, ) .map_err(|e| { error!(error = %e, "Bundle metering failed"); @@ -113,6 +163,8 @@ where total_gas_used = result.total_gas_used, total_execution_time_us = result.total_execution_time_us, state_root_time_us = result.state_root_time_us, + state_block_number = header.number, + flashblock_index = flashblock_index, "Bundle metering completed successfully" ); @@ -124,7 +176,7 @@ where gas_fees: result.total_gas_fees, results: result.results, state_block_number: header.number, - state_flashblock_index: None, + state_flashblock_index, total_gas_used: result.total_gas_used, total_execution_time_us: result.total_execution_time_us, state_root_time_us: result.state_root_time_us, diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 74ce77d9..529b422a 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -136,8 +136,13 @@ fn meter_bundle_empty_transactions() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(Vec::new())?; - let output = - meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + None, + )?; assert!(output.results.is_empty()); assert_eq!(output.total_gas_used, 0); @@ -179,8 +184,13 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(vec![envelope.clone()])?; - let output = - meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + None, + )?; assert_eq!(output.results.len(), 1); let result = &output.results[0]; @@ -257,8 +267,13 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { let parsed_bundle = create_parsed_bundle(vec![envelope_1.clone(), envelope_2.clone()])?; - let output = - meter_bundle(state_provider, harness.chain_spec.clone(), parsed_bundle, &harness.header)?; + let output = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + None, + )?; assert_eq!(output.results.len(), 2); assert!(output.total_execution_time_us > 0); @@ -326,14 +341,14 @@ fn meter_bundle_state_root_time_invariant() -> eyre::Result<()> { .state_by_block_hash(harness.header.hash()) .context("getting state provider")?; - let bundle_with_metadata = create_bundle_with_metadata(vec![envelope.clone()])?; + let parsed_bundle = create_parsed_bundle(vec![envelope.clone()])?; let output = meter_bundle( state_provider, harness.chain_spec.clone(), - vec![envelope], + parsed_bundle, &harness.header, - &bundle_with_metadata, + None, )?; // Verify invariant: total execution time must include state root time From 2f4a2cd9c1def3624d6a8fb307518cc4ba2ba061 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Thu, 30 Oct 2025 02:05:10 -0500 Subject: [PATCH 06/26] Cache flashblock trie nodes to optimize bundle metering Adds a single-entry cache for the latest flashblock's trie nodes, allowing bundle metering operations to reuse the cached trie computation instead of recalculating from scratch each time. Key changes: - Add FlashblockTrieCache: thread-safe single-entry cache for latest flashblock - Add FlashblockTrieData: contains trie updates and hashed state for reuse - Cache keyed by block hash and flashblock index for accuracy - Compute flashblock trie once per flashblock, reuse for all bundle operations - Extract flashblock trie calculation outside timing metrics - Use TrieInput.prepend_cached() to combine flashblock + bundle tries The cache replaces previous entries when a new flashblock is cached, as it's designed only for the current/latest flashblock, not historical ones. --- Cargo.toml | 1 + crates/flashblocks/src/processor.rs | 10 --- crates/rpc/Cargo.toml | 2 + crates/rpc/src/base/flashblock_trie_cache.rs | 88 ++++++++++++++++++++ crates/rpc/src/base/meter.rs | 36 +++++++- crates/rpc/src/base/meter_rpc.rs | 32 ++++++- crates/rpc/tests/meter.rs | 8 ++ 7 files changed, 162 insertions(+), 15 deletions(-) create mode 100644 crates/rpc/src/base/flashblock_trie_cache.rs diff --git a/Cargo.toml b/Cargo.toml index c54a4b24..b3aec2f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,6 +92,7 @@ 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 } diff --git a/crates/flashblocks/src/processor.rs b/crates/flashblocks/src/processor.rs index f24b23c4..5d8cfeff 100644 --- a/crates/flashblocks/src/processor.rs +++ b/crates/flashblocks/src/processor.rs @@ -21,7 +21,6 @@ use eyre::eyre; use op_alloy_consensus::OpTxEnvelope; use op_alloy_network::TransactionResponse; use op_alloy_rpc_types::Transaction; -<<<<<<< HEAD use reth::{ chainspec::{ChainSpecProvider, EthChainSpec}, providers::{BlockReaderIdExt, StateProviderFactory}, @@ -298,14 +297,6 @@ where let state_provider = self.client.state_by_block_number_or_tag(BlockNumberOrTag::Number(canonical_block))?; let state_provider_db = StateProviderDatabase::new(state_provider); -<<<<<<< HEAD - let state = State::builder().with_database(state_provider_db).with_bundle_update().build(); - let mut pending_blocks_builder = PendingBlocksBuilder::new(); - - let mut db = match &prev_pending_blocks { - Some(pending_blocks) => CacheDB { cache: pending_blocks.get_db_cache(), db: state }, - None => CacheDB::new(state), -======= let mut pending_blocks_builder = PendingBlocksBuilder::new(); // Cache reads across flashblocks, accumulating caches from previous @@ -330,7 +321,6 @@ where .with_database(cache_db) .with_bundle_update() .build(), ->>>>>>> 1c80299 (Use pending flashblocks state for bundle metering) }; let mut state_overrides = diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 21f7fd54..af7cf0ce 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -27,6 +27,7 @@ reth-optimism-chainspec.workspace = true reth-transaction-pool = { workspace = true, features = ["test-utils"] } reth-rpc.workspace = true reth-rpc-eth-api.workspace = true +reth-trie-common.workspace = true # alloy alloy-primitives.workspace = true @@ -57,6 +58,7 @@ eyre.workspace = true serde.workspace = true metrics.workspace = true metrics-derive.workspace = true +arc-swap.workspace = true [dev-dependencies] base-flashtypes.workspace = true diff --git a/crates/rpc/src/base/flashblock_trie_cache.rs b/crates/rpc/src/base/flashblock_trie_cache.rs new file mode 100644 index 00000000..21aea516 --- /dev/null +++ b/crates/rpc/src/base/flashblock_trie_cache.rs @@ -0,0 +1,88 @@ +use std::sync::Arc; + +use alloy_primitives::B256; +use arc_swap::ArcSwap; +use eyre::Result as EyreResult; +use reth_provider::StateProvider; +use reth_trie_common::{updates::TrieUpdates, HashedPostState}; + +use crate::FlashblocksState; + +/// Trie nodes and hashed state from computing a flashblock state root. +/// +/// These cached nodes can be reused when computing a bundle's state root +/// to avoid recalculating the flashblock portion of the trie. +#[derive(Debug, Clone)] +pub struct FlashblockTrieData { + pub trie_updates: TrieUpdates, + pub hashed_state: HashedPostState, +} + +/// Internal cache entry for a single flashblock. +#[derive(Debug, Clone)] +struct CachedFlashblockTrie { + block_hash: B256, + flashblock_index: u64, + trie_data: FlashblockTrieData, +} + +/// Thread-safe single-entry cache for the latest flashblock's trie nodes. +/// +/// This cache stores the intermediate trie nodes computed when calculating +/// the latest flashblock's state root. Subsequent bundle metering operations +/// on the same flashblock can reuse these cached nodes instead of recalculating +/// them, significantly improving performance. +/// +/// **Important**: This cache holds only ONE flashblock's trie at a time. +/// When a new flashblock is cached, it replaces any previously cached flashblock. +#[derive(Debug, Clone)] +pub struct FlashblockTrieCache { + cache: Arc>>, +} + +impl FlashblockTrieCache { + /// Creates a new empty flashblock trie cache. + pub fn new() -> Self { + Self { cache: Arc::new(ArcSwap::from_pointee(None)) } + } + + /// Ensures the trie for the given flashblock is cached and returns it. + /// + /// If the cache already contains an entry for the provided `block_hash` and + /// `flashblock_index`, the cached data is returned immediately. Otherwise the trie is + /// recomputed, cached (replacing any previous entry), and returned. + pub fn ensure_cached( + &self, + block_hash: B256, + flashblock_index: u64, + flashblocks_state: &FlashblocksState, + canonical_state_provider: &dyn StateProvider, + ) -> EyreResult { + if let Some(ref cached) = *self.cache.load() { + if cached.block_hash == block_hash && cached.flashblock_index == flashblock_index { + return Ok(cached.trie_data.clone()); + } + } + + let hashed_state = + canonical_state_provider.hashed_post_state(&flashblocks_state.bundle_state); + let (_state_root, trie_updates) = + canonical_state_provider.state_root_with_updates(hashed_state.clone())?; + + let trie_data = FlashblockTrieData { trie_updates, hashed_state }; + + self.cache.store(Arc::new(Some(CachedFlashblockTrie { + block_hash, + flashblock_index, + trie_data: trie_data.clone(), + }))); + + Ok(trie_data) + } +} + +impl Default for FlashblockTrieCache { + fn default() -> Self { + Self::new() + } +} diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 0c37f8b4..1dffaa81 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -4,14 +4,15 @@ use alloy_consensus::{BlockHeader, Transaction as _, transaction::SignerRecovera use alloy_primitives::{B256, U256}; use eyre::{Result as EyreResult, eyre}; use reth::revm::db::{BundleState, Cache, CacheDB, State}; -use revm_database::states::bundle_state::BundleRetention; use reth_evm::{ConfigureEvm, execute::BlockBuilder}; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; use reth_primitives_traits::SealedHeader; +use reth_trie_common::TrieInput; +use revm_database::states::bundle_state::BundleRetention; use tips_core::types::{BundleExtensions, BundleTxs, ParsedBundle}; -use crate::TransactionResult; +use crate::{FlashblockTrieData, TransactionResult}; /// State from pending flashblocks that is used as a base for metering #[derive(Debug, Clone)] @@ -53,6 +54,7 @@ pub fn meter_bundle( bundle: ParsedBundle, header: &SealedHeader, flashblocks_state: Option, + cached_flashblock_trie: Option, ) -> EyreResult where SP: reth_provider::StateProvider, @@ -60,6 +62,17 @@ where // Get bundle hash let bundle_hash = bundle.bundle_hash(); + // If we have flashblocks but no cached trie, compute the flashblock trie first + // (before starting any timers, since we only want to time the bundle's execution and state root) + let flashblock_trie = if cached_flashblock_trie.is_none() && flashblocks_state.is_some() { + let fb_state = flashblocks_state.as_ref().unwrap(); + let fb_hashed_state = state_provider.hashed_post_state(&fb_state.bundle_state); + let (_fb_state_root, fb_trie_updates) = state_provider.state_root_with_updates(fb_hashed_state.clone())?; + Some((fb_trie_updates, fb_hashed_state)) + } else { + None + }; + // Create state database let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); // If we have flashblocks state, apply both cache and bundle prestate @@ -148,11 +161,26 @@ where db.merge_transitions(BundleRetention::Reverts); let bundle_update = db.take_bundle(); let state_provider = db.database.db.as_ref(); + let state_root_start = Instant::now(); let hashed_state = state_provider.hashed_post_state(&bundle_update); - let _ = state_provider.state_root_with_updates(hashed_state); - let state_root_time_us = state_root_start.elapsed().as_micros(); + if let Some(cached) = cached_flashblock_trie { + // We have cached flashblock trie nodes, use them + let mut trie_input = TrieInput::from_state(hashed_state); + trie_input.prepend_cached(cached.trie_updates, cached.hashed_state); + let _ = state_provider.state_root_from_nodes_with_updates(trie_input)?; + } else if let Some((fb_trie_updates, fb_hashed_state)) = flashblock_trie { + // We computed the flashblock trie above, now use it + let mut trie_input = TrieInput::from_state(hashed_state); + trie_input.prepend_cached(fb_trie_updates, fb_hashed_state); + let _ = state_provider.state_root_from_nodes_with_updates(trie_input)?; + } else { + // No flashblocks, just calculate bundle state root + let _ = state_provider.state_root_with_updates(hashed_state)?; + } + + let state_root_time_us = state_root_start.elapsed().as_micros(); let total_execution_time_us = execution_start.elapsed().as_micros(); Ok(MeterBundleOutput { diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index aaf35e31..96323048 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -10,12 +10,14 @@ use std::sync::Arc; use tips_core::types::{Bundle, MeterBundleResponse, ParsedBundle}; use tracing::{error, info}; -use crate::{MeteringApiServer, meter_bundle}; +use crate::{FlashblockTrieCache, MeteringApiServer, meter_bundle}; /// Implementation of the metering RPC API pub struct MeteringApiImpl { provider: Provider, flashblocks_state: Arc, + /// Single-entry cache for the latest flashblock's trie nodes + trie_cache: FlashblockTrieCache, } impl MeteringApiImpl @@ -31,6 +33,7 @@ where Self { provider, flashblocks_state, + trie_cache: FlashblockTrieCache::new(), } } } @@ -133,6 +136,32 @@ where // Get the flashblock index if we have pending flashblocks let state_flashblock_index = pending_blocks.as_ref().map(|pb| pb.latest_flashblock_index()); + // If we have flashblocks, ensure the trie is cached and get it + let cached_trie = if let Some(ref fb_state) = flashblocks_state { + let fb_index = state_flashblock_index.unwrap(); + + // Ensure the flashblock trie is cached and return it + Some( + self.trie_cache + .ensure_cached( + header.hash(), + fb_index, + fb_state, + &*state_provider, + ) + .map_err(|e| { + error!(error = %e, "Failed to cache flashblock trie"); + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Failed to cache flashblock trie: {}", e), + None::<()>, + ) + })?, + ) + } else { + None + }; + // Meter bundle using utility function let result = meter_bundle( state_provider, @@ -140,6 +169,7 @@ where parsed_bundle, &header, flashblocks_state, + cached_trie, ) .map_err(|e| { error!(error = %e, "Bundle metering failed"); diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 529b422a..f706588e 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -142,6 +142,9 @@ fn meter_bundle_empty_transactions() -> eyre::Result<()> { parsed_bundle, &harness.header, None, + None, + None, + None, )?; assert!(output.results.is_empty()); @@ -190,6 +193,9 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { parsed_bundle, &harness.header, None, + None, + None, + None, )?; assert_eq!(output.results.len(), 1); @@ -273,6 +279,7 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { parsed_bundle, &harness.header, None, + None, )?; assert_eq!(output.results.len(), 2); @@ -349,6 +356,7 @@ fn meter_bundle_state_root_time_invariant() -> eyre::Result<()> { parsed_bundle, &harness.header, None, + None, )?; // Verify invariant: total execution time must include state root time From b2554b978d79d80d243d443dea9a08b510a19e4f Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Thu, 30 Oct 2025 02:08:23 -0500 Subject: [PATCH 07/26] linter fixes --- crates/flashblocks/src/pending_blocks.rs | 5 +- crates/rpc/src/base/flashblock_trie_cache.rs | 1 + crates/rpc/src/base/meter.rs | 14 ++- crates/rpc/src/base/meter_rpc.rs | 99 ++++++++++---------- 4 files changed, 63 insertions(+), 56 deletions(-) diff --git a/crates/flashblocks/src/pending_blocks.rs b/crates/flashblocks/src/pending_blocks.rs index da284e68..0577d603 100644 --- a/crates/flashblocks/src/pending_blocks.rs +++ b/crates/flashblocks/src/pending_blocks.rs @@ -14,7 +14,10 @@ use base_flashtypes::Flashblock; use eyre::eyre; use op_alloy_network::Optimism; use op_alloy_rpc_types::{OpTransactionReceipt, Transaction}; -use reth::revm::{db::{BundleState, Cache}, state::EvmState}; +use reth::revm::{ + db::{BundleState, Cache}, + state::EvmState, +}; use reth_rpc_convert::RpcTransaction; use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; diff --git a/crates/rpc/src/base/flashblock_trie_cache.rs b/crates/rpc/src/base/flashblock_trie_cache.rs index 21aea516..5d1d5cd4 100644 --- a/crates/rpc/src/base/flashblock_trie_cache.rs +++ b/crates/rpc/src/base/flashblock_trie_cache.rs @@ -71,6 +71,7 @@ impl FlashblockTrieCache { let trie_data = FlashblockTrieData { trie_updates, hashed_state }; + // Store the new entry, replacing any previous flashblock's cached trie self.cache.store(Arc::new(Some(CachedFlashblockTrie { block_hash, flashblock_index, diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 1dffaa81..e5a94aee 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -64,11 +64,15 @@ where // If we have flashblocks but no cached trie, compute the flashblock trie first // (before starting any timers, since we only want to time the bundle's execution and state root) - let flashblock_trie = if cached_flashblock_trie.is_none() && flashblocks_state.is_some() { - let fb_state = flashblocks_state.as_ref().unwrap(); - let fb_hashed_state = state_provider.hashed_post_state(&fb_state.bundle_state); - let (_fb_state_root, fb_trie_updates) = state_provider.state_root_with_updates(fb_hashed_state.clone())?; - Some((fb_trie_updates, fb_hashed_state)) + let flashblock_trie = if cached_flashblock_trie.is_none() { + if let Some(ref fb_state) = flashblocks_state { + let fb_hashed_state = state_provider.hashed_post_state(&fb_state.bundle_state); + let (_fb_state_root, fb_trie_updates) = + state_provider.state_root_with_updates(fb_hashed_state.clone())?; + Some((fb_trie_updates, fb_hashed_state)) + } else { + None + } } else { None }; diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 96323048..424d9570 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -62,49 +62,51 @@ where // Get header and flashblock index from pending blocks // If no pending blocks exist, fall back to latest canonical block - let (header, flashblock_index, canonical_block_number) = if let Some(pb) = pending_blocks.as_ref() { - let latest_header: Sealed
= pb.latest_header(); - let flashblock_index = pb.latest_flashblock_index(); - let canonical_block_number = pb.canonical_block_number(); - - info!( - latest_block = latest_header.number, - canonical_block = %canonical_block_number, - flashblock_index = flashblock_index, - "Using latest flashblock state for metering" - ); - - // Convert Sealed
to SealedHeader - let sealed_header = SealedHeader::new(latest_header.inner().clone(), latest_header.hash()); - (sealed_header, flashblock_index, canonical_block_number) - } else { - // No pending blocks, use latest canonical block - let canonical_block_number = pending_blocks.get_canonical_block_number(); - let header = self - .provider - .sealed_header_by_number_or_tag(canonical_block_number) - .map_err(|e| { - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - format!("Failed to get canonical block header: {}", e), - None::<()>, - ) - })? - .ok_or_else(|| { - jsonrpsee::types::ErrorObjectOwned::owned( - jsonrpsee::types::ErrorCode::InternalError.code(), - "Canonical block not found".to_string(), - None::<()>, - ) - })?; - - info!( - canonical_block = header.number, - "No flashblocks available, using canonical block state for metering" - ); - - (header, 0, canonical_block_number) - }; + let (header, flashblock_index, canonical_block_number) = + if let Some(pb) = pending_blocks.as_ref() { + let latest_header: Sealed
= pb.latest_header(); + let flashblock_index = pb.latest_flashblock_index(); + let canonical_block_number = pb.canonical_block_number(); + + info!( + latest_block = latest_header.number, + canonical_block = %canonical_block_number, + flashblock_index = flashblock_index, + "Using latest flashblock state for metering" + ); + + // Convert Sealed
to SealedHeader + let sealed_header = + SealedHeader::new(latest_header.inner().clone(), latest_header.hash()); + (sealed_header, flashblock_index, canonical_block_number) + } else { + // No pending blocks, use latest canonical block + let canonical_block_number = pending_blocks.get_canonical_block_number(); + let header = self + .provider + .sealed_header_by_number_or_tag(canonical_block_number) + .map_err(|e| { + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + format!("Failed to get canonical block header: {}", e), + None::<()>, + ) + })? + .ok_or_else(|| { + jsonrpsee::types::ErrorObjectOwned::owned( + jsonrpsee::types::ErrorCode::InternalError.code(), + "Canonical block not found".to_string(), + None::<()>, + ) + })?; + + info!( + canonical_block = header.number, + "No flashblocks available, using canonical block state for metering" + ); + + (header, 0, canonical_block_number) + }; let parsed_bundle = ParsedBundle::try_from(bundle).map_err(|e| { jsonrpsee::types::ErrorObjectOwned::owned( @@ -134,7 +136,9 @@ where }); // Get the flashblock index if we have pending flashblocks - let state_flashblock_index = pending_blocks.as_ref().map(|pb| pb.latest_flashblock_index()); + let state_flashblock_index = pending_blocks + .as_ref() + .map(|pb| pb.latest_flashblock_index()); // If we have flashblocks, ensure the trie is cached and get it let cached_trie = if let Some(ref fb_state) = flashblocks_state { @@ -143,12 +147,7 @@ where // Ensure the flashblock trie is cached and return it Some( self.trie_cache - .ensure_cached( - header.hash(), - fb_index, - fb_state, - &*state_provider, - ) + .ensure_cached(header.hash(), fb_index, fb_state, &*state_provider) .map_err(|e| { error!(error = %e, "Failed to cache flashblock trie"); jsonrpsee::types::ErrorObjectOwned::owned( From 9b0ec974ae6b0ce91f357c702b3d5b458ba22dfd Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Sun, 9 Nov 2025 14:04:16 -0600 Subject: [PATCH 08/26] Rename total_execution_time to total_time for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timer and field names now better reflect that they measure the total elapsed time (including both transaction execution and state root calculation), rather than just execution time. This distinguishes them from the per-transaction execution_time_us metrics. Changes: - Renamed execution_start → total_start timer - Renamed total_execution_time → total_time variable - Renamed total_execution_time_us → total_time_us in MeterBundleOutput - Updated logging and tests to use new naming - Added TODO comment for tips-core field rename --- crates/rpc/src/base/meter.rs | 10 +++++----- crates/rpc/src/base/meter_rpc.rs | 5 +++-- crates/rpc/tests/meter.rs | 14 +++++++------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index e5a94aee..a2055fcb 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -36,8 +36,8 @@ pub struct MeterBundleOutput { pub total_gas_fees: U256, /// Bundle hash pub bundle_hash: B256, - /// Total execution time in microseconds (includes state root calculation) - pub total_execution_time_us: u128, + /// Total time in microseconds (includes transaction execution and state root calculation) + pub total_time_us: u128, /// State root calculation time in microseconds pub state_root_time_us: u128, } @@ -120,7 +120,7 @@ where let mut total_gas_used = 0u64; let mut total_gas_fees = U256::ZERO; - let execution_start = Instant::now(); + let total_start = Instant::now(); { let evm_config = OpEvmConfig::optimism(chain_spec); let mut builder = evm_config.builder_for_next_block(&mut db, header, attributes)?; @@ -185,14 +185,14 @@ where } let state_root_time_us = state_root_start.elapsed().as_micros(); - let total_execution_time_us = execution_start.elapsed().as_micros(); + let total_time_us = total_start.elapsed().as_micros(); Ok(MeterBundleOutput { results, total_gas_used, total_gas_fees, bundle_hash, - total_execution_time_us, + total_time_us, state_root_time_us, }) } diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 424d9570..b62ed234 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -190,7 +190,7 @@ where bundle_hash = %result.bundle_hash, num_transactions = result.results.len(), total_gas_used = result.total_gas_used, - total_execution_time_us = result.total_execution_time_us, + total_time_us = result.total_time_us, state_root_time_us = result.state_root_time_us, state_block_number = header.number, flashblock_index = flashblock_index, @@ -207,7 +207,8 @@ where state_block_number: header.number, state_flashblock_index, total_gas_used: result.total_gas_used, - total_execution_time_us: result.total_execution_time_us, + // TODO: Rename to total_time_us in tips-core. + total_execution_time_us: result.total_time_us, state_root_time_us: result.state_root_time_us, }) } diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index f706588e..7098f9fd 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -151,7 +151,7 @@ fn meter_bundle_empty_transactions() -> eyre::Result<()> { assert_eq!(output.total_gas_used, 0); assert_eq!(output.total_gas_fees, U256::ZERO); // Even empty bundles have some EVM setup overhead - assert!(output.total_execution_time_us > 0); + assert!(output.total_time_us > 0); assert!(output.state_root_time_us > 0); assert_eq!(output.bundle_hash, keccak256([])); @@ -200,7 +200,7 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { assert_eq!(output.results.len(), 1); let result = &output.results[0]; - assert!(output.total_execution_time_us > 0); + assert!(output.total_time_us > 0); assert!(output.state_root_time_us > 0); assert_eq!(result.from_address, harness.address(User::Alice)); @@ -283,7 +283,7 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> { )?; assert_eq!(output.results.len(), 2); - assert!(output.total_execution_time_us > 0); + assert!(output.total_time_us > 0); assert!(output.state_root_time_us > 0); // Check first transaction @@ -359,11 +359,11 @@ fn meter_bundle_state_root_time_invariant() -> eyre::Result<()> { None, )?; - // Verify invariant: total execution time must include state root time + // Verify invariant: total time must include state root time assert!( - output.total_execution_time_us >= output.state_root_time_us, - "total_execution_time_us ({}) should be >= state_root_time_us ({})", - output.total_execution_time_us, + output.total_time_us >= output.state_root_time_us, + "total_time_us ({}) should be >= state_root_time_us ({})", + output.total_time_us, output.state_root_time_us ); From 082e7d51ec1a7959d0a6aad21d22734c151ac4e5 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Mon, 10 Nov 2025 13:16:16 -0600 Subject: [PATCH 09/26] Refactor flashblock trie data handling to reduce code duplication Consolidate the logic for using cached vs computed flashblock trie data by introducing a single flashblock_trie_data variable that handles both cases. This eliminates duplicate code paths in the state root calculation section. --- crates/rpc/src/base/meter.rs | 40 +++++++++++++++++------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index a2055fcb..fcaa1d15 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -62,20 +62,23 @@ where // Get bundle hash let bundle_hash = bundle.bundle_hash(); - // If we have flashblocks but no cached trie, compute the flashblock trie first + // Consolidate flashblock trie data: use cached if available, otherwise compute it // (before starting any timers, since we only want to time the bundle's execution and state root) - let flashblock_trie = if cached_flashblock_trie.is_none() { - if let Some(ref fb_state) = flashblocks_state { - let fb_hashed_state = state_provider.hashed_post_state(&fb_state.bundle_state); - let (_fb_state_root, fb_trie_updates) = - state_provider.state_root_with_updates(fb_hashed_state.clone())?; - Some((fb_trie_updates, fb_hashed_state)) - } else { - None - } - } else { - None - }; + let flashblock_trie_data = cached_flashblock_trie + .map(Ok::<_, eyre::Report>) + .or_else(|| { + flashblocks_state.as_ref().map(|fb_state| { + // Compute the flashblock trie + let fb_hashed_state = state_provider.hashed_post_state(&fb_state.bundle_state); + let (_fb_state_root, fb_trie_updates) = + state_provider.state_root_with_updates(fb_hashed_state.clone())?; + Ok(crate::FlashblockTrieData { + trie_updates: fb_trie_updates, + hashed_state: fb_hashed_state, + }) + }) + }) + .transpose()?; // Create state database let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); @@ -169,15 +172,10 @@ where let state_root_start = Instant::now(); let hashed_state = state_provider.hashed_post_state(&bundle_update); - if let Some(cached) = cached_flashblock_trie { - // We have cached flashblock trie nodes, use them - let mut trie_input = TrieInput::from_state(hashed_state); - trie_input.prepend_cached(cached.trie_updates, cached.hashed_state); - let _ = state_provider.state_root_from_nodes_with_updates(trie_input)?; - } else if let Some((fb_trie_updates, fb_hashed_state)) = flashblock_trie { - // We computed the flashblock trie above, now use it + if let Some(fb_trie_data) = flashblock_trie_data { + // We have flashblock trie data (either cached or computed), use it let mut trie_input = TrieInput::from_state(hashed_state); - trie_input.prepend_cached(fb_trie_updates, fb_hashed_state); + trie_input.prepend_cached(fb_trie_data.trie_updates, fb_trie_data.hashed_state); let _ = state_provider.state_root_from_nodes_with_updates(trie_input)?; } else { // No flashblocks, just calculate bundle state root From 4b92ed6b65b18b703786fffc78bdb004d8be9fcf Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Mon, 10 Nov 2025 18:25:19 -0600 Subject: [PATCH 10/26] Add chain-state metering tests and flashblock harness support --- Cargo.lock | 5 + crates/metering/src/tests/chain_state.rs | 151 ++++++ crates/test-utils/README.md | 124 +++-- crates/test-utils/src/fixtures.rs | 91 +++- crates/test-utils/src/harness.rs | 137 +++--- crates/test-utils/src/node.rs | 557 +++++++---------------- 6 files changed, 514 insertions(+), 551 deletions(-) create mode 100644 crates/metering/src/tests/chain_state.rs diff --git a/Cargo.lock b/Cargo.lock index 47803e34..51a7c457 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1586,7 +1586,10 @@ dependencies = [ "alloy-eips", "alloy-genesis", "alloy-primitives", + "alloy-provider", "alloy-rpc-client", + "alloy-rpc-types-engine", + "arc-swap", "base-reth-flashblocks-rpc", "base-reth-test-utils", "eyre", @@ -1609,8 +1612,10 @@ dependencies = [ "reth-testing-utils", "reth-tracing", "reth-transaction-pool", + "reth-trie-common", "revm", "revm-database", + "rollup-boost", "serde", "serde_json", "tips-core", diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs new file mode 100644 index 00000000..4f378951 --- /dev/null +++ b/crates/metering/src/tests/chain_state.rs @@ -0,0 +1,151 @@ +use alloy_consensus::Receipt; +use alloy_eips::Encodable2718; +use alloy_primitives::{Bytes, B256}; +use alloy_provider::Provider; +use base_reth_flashblocks_rpc::rpc::FlashblocksAPI; +use base_reth_test_utils::harness::TestHarness; +use base_reth_test_utils::node::{default_launcher, BASE_CHAIN_ID}; +use eyre::{eyre, Result}; +use op_alloy_consensus::OpTxEnvelope; +use reth::providers::HeaderProvider; +use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; +use reth_transaction_pool::test_utils::TransactionBuilder; +use tips_core::types::Bundle; + +use super::utils::{build_single_flashblock, secret_from_hex}; +use crate::rpc::{MeteringApiImpl, MeteringApiServer}; + +#[tokio::test] +async fn meters_bundle_after_advancing_blocks() -> Result<()> { + reth_tracing::init_test_tracing(); + let harness = TestHarness::new(default_launcher).await?; + + let provider = harness.provider(); + let bob = &harness.accounts().bob; + let alice_secret = secret_from_hex(harness.accounts().alice.private_key); + + let tx = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(0) + .to(bob.address) + .value(1) + .gas_limit(21_000) + .max_fee_per_gas(1_000_000_000) + .max_priority_fee_per_gas(1_000_000_000) + .into_eip1559(); + + let envelope = OpTxEnvelope::from(OpTransactionSigned::Eip1559( + tx.as_eip1559().unwrap().clone(), + )); + let tx_bytes = Bytes::from(envelope.encoded_2718()); + + harness.advance_chain(1).await?; + + let bundle = Bundle { + txs: vec![tx_bytes.clone()], + block_number: provider.get_block_number().await?, + flashblock_number_min: None, + flashblock_number_max: None, + min_timestamp: None, + max_timestamp: None, + reverting_tx_hashes: vec![], + replacement_uuid: None, + dropping_tx_hashes: vec![], + }; + + let metering_api = + MeteringApiImpl::new(harness.blockchain_provider(), harness.flashblocks_state()); + let response = MeteringApiServer::meter_bundle(&metering_api, bundle) + .await + .map_err(|err| eyre!("meter_bundle rpc failed: {}", err))?; + + assert_eq!(response.results.len(), 1); + assert_eq!(response.total_gas_used, 21_000); + assert!(response.state_flashblock_index.is_none()); + + Ok(()) +} + +#[tokio::test] +async fn pending_flashblock_updates_state() -> Result<()> { + reth_tracing::init_test_tracing(); + let harness = TestHarness::new(default_launcher).await?; + + let provider = harness.provider(); + let bob = &harness.accounts().bob; + let alice_secret = secret_from_hex(harness.accounts().alice.private_key); + + let tx = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(0) + .to(bob.address) + .value(1) + .gas_limit(21_000) + .max_fee_per_gas(1_000_000_000) + .max_priority_fee_per_gas(1_000_000_000) + .into_eip1559(); + + let blockchain_provider = harness.blockchain_provider(); + let latest_number = provider.get_block_number().await?; + let latest_header = blockchain_provider + .sealed_header(latest_number)? + .ok_or_else(|| eyre!("missing header for block {}", latest_number))?; + + let pending_block_number = latest_header.number + 1; + let envelope = OpTxEnvelope::from(OpTransactionSigned::Eip1559( + tx.as_eip1559().unwrap().clone(), + )); + let tx_hash = envelope.tx_hash(); + let tx_bytes = Bytes::from(envelope.encoded_2718()); + let receipt = OpReceipt::Eip1559(Receipt { + status: true.into(), + cumulative_gas_used: 21_000, + logs: vec![], + }); + + // Use a zero parent beacon block root to emulate a flashblock that predates Cancun data, + // which should cause metering to surface the missing-root error while still caching state. + let flashblock = build_single_flashblock( + pending_block_number, + latest_header.hash(), + B256::ZERO, + latest_header.timestamp + 2, + latest_header.gas_limit, + vec![(tx_bytes.clone(), Some((tx_hash, receipt.clone())))], + ); + + harness.send_flashblock(flashblock).await?; + + let bundle = Bundle { + txs: vec![tx_bytes.clone()], + block_number: pending_block_number, + flashblock_number_min: None, + flashblock_number_max: None, + min_timestamp: None, + max_timestamp: None, + reverting_tx_hashes: vec![], + replacement_uuid: None, + dropping_tx_hashes: vec![], + }; + + let metering_api = + MeteringApiImpl::new(blockchain_provider.clone(), harness.flashblocks_state()); + let result = MeteringApiServer::meter_bundle(&metering_api, bundle).await; + + let err = result.expect_err("pending flashblock metering should surface missing beacon root"); + assert!( + err.message().contains("parent beacon block root missing"), + "unexpected error: {err:?}" + ); + + let pending_blocks = harness.flashblocks_state().get_pending_blocks(); + assert!(pending_blocks.is_some(), "expected flashblock to populate pending state"); + assert_eq!( + pending_blocks.as_ref().unwrap().latest_flashblock_index(), + 0 + ); + + Ok(()) +} diff --git a/crates/test-utils/README.md b/crates/test-utils/README.md index 2b735f2c..1f1cc7fd 100644 --- a/crates/test-utils/README.md +++ b/crates/test-utils/README.md @@ -14,7 +14,7 @@ This crate provides reusable testing utilities for integration tests across the ## Quick Start -```rust,ignore +```rust use base_reth_test_utils::TestHarness; #[tokio::test] @@ -38,13 +38,12 @@ async fn test_example() -> eyre::Result<()> { The framework follows a three-layer architecture: -```text +``` ┌─────────────────────────────────────┐ │ TestHarness │ ← Orchestration layer (tests use this) │ - Coordinates node + engine │ │ - Builds blocks from transactions │ │ - Manages test accounts │ -│ - Manages flashblocks │ └─────────────────────────────────────┘ │ │ ┌──────┘ └──────┐ @@ -65,9 +64,9 @@ The framework follows a three-layer architecture: ### 1. TestHarness -The main entry point for integration tests that only need canonical chain control. Combines node, engine, and accounts into a single interface. +The main entry point for integration tests. Combines node, engine, and accounts into a single interface. -```rust,ignore +```rust use base_reth_test_utils::TestHarness; use alloy_primitives::Bytes; @@ -90,18 +89,24 @@ async fn test_harness() -> eyre::Result<()> { let txs: Vec = vec![/* signed transaction bytes */]; harness.build_block_from_transactions(txs).await?; + // Build block from flashblocks + harness.build_block_from_flashblocks(&flashblocks).await?; + + // Send flashblocks for pending state testing + harness.send_flashblock(flashblock).await?; + Ok(()) } ``` -> Need pending-state testing? Use `FlashblocksHarness` (see Flashblocks section below) to gain `send_flashblock` helpers. - **Key Methods:** - `new()` - Create new harness with node, engine, and accounts - `provider()` - Get Alloy RootProvider for RPC calls - `accounts()` - Access test accounts - `advance_chain(n)` - Build N empty blocks -- `build_block_from_transactions(txs)` - Build block with specific transactions (auto-prepends the L1 block info deposit) +- `build_block_from_transactions(txs)` - Build block with specific transactions +- `send_flashblock(fb)` - Send a single flashblock to the node for pending state processing +- `send_flashblocks(iter)` - Convenience helper that sends multiple flashblocks sequentially **Block Building Process:** 1. Fetches latest block header from provider (no local state tracking) @@ -116,36 +121,33 @@ async fn test_harness() -> eyre::Result<()> { In-process Optimism node with Base Sepolia configuration. -```rust,ignore -use base_reth_test_utils::{LocalNode, default_launcher}; +```rust +use base_reth_test_utils::LocalNode; #[tokio::test] async fn test_node() -> eyre::Result<()> { - let node = LocalNode::new(default_launcher).await?; + let node = LocalNode::new().await?; + // Get provider let provider = node.provider()?; + + // Get Engine API let engine = node.engine_api()?; + // Send flashblocks + node.send_flashblock(flashblock).await?; + Ok(()) } ``` -**Features (base):** +**Features:** - Base Sepolia chain configuration - Disabled P2P discovery (isolated testing) - Random unused ports (parallel test safety) - HTTP RPC server at `node.http_api_addr` - Engine API IPC at `node.engine_ipc_path` - -For flashblocks-enabled nodes, use `FlashblocksLocalNode`: - -```rust,ignore -use base_reth_test_utils::FlashblocksLocalNode; - -let node = FlashblocksLocalNode::new().await?; -let pending_state = node.flashblocks_state(); -node.send_flashblock(flashblock).await?; -``` +- Flashblocks-canon ExEx integration **Note:** Most tests should use `TestHarness` instead of `LocalNode` directly. @@ -153,7 +155,7 @@ node.send_flashblock(flashblock).await?; Type-safe Engine API client wrapping raw CL operations. -```rust,ignore +```rust use base_reth_test_utils::EngineApi; use alloy_primitives::B256; use op_alloy_rpc_types_engine::OpPayloadAttributes; @@ -178,8 +180,8 @@ let status = engine.new_payload(payload, vec![], parent_root, requests).await?; Hardcoded test accounts with deterministic addresses (Anvil-compatible). -```rust,ignore -use base_reth_test_utils::{TestAccounts, TestHarness}; +```rust +use base_reth_test_utils::TestAccounts; let accounts = TestAccounts::new(); @@ -207,63 +209,58 @@ Each account includes: ### 5. Flashblocks Support -Use `FlashblocksHarness` when you need `send_flashblock` and access to the in-memory pending state. +Test flashblocks delivery without WebSocket connections. -```rust,ignore -use base_reth_test_utils::FlashblocksHarness; +```rust +use base_reth_test_utils::{FlashblocksContext, FlashblockBuilder}; #[tokio::test] async fn test_flashblocks() -> eyre::Result<()> { - let harness = FlashblocksHarness::new().await?; + let (fb_ctx, receiver) = FlashblocksContext::new(); - harness.send_flashblock(flashblock).await?; + // Create base flashblock + let flashblock = FlashblockBuilder::new(1, 0) + .as_base(B256::ZERO, 1000) + .with_transaction(tx_bytes, tx_hash, 21000) + .with_balance(address, U256::from(1000)) + .build(); - let pending = harness.flashblocks_state(); - // assertions... + fb_ctx.send_flashblock(flashblock).await?; Ok(()) } ``` -`FlashblocksHarness` derefs to the base `TestHarness`, so you can keep using methods like `provider()`, `build_block_from_transactions`, etc. - -Test flashblocks delivery without WebSocket connections by constructing payloads and sending them through `FlashblocksHarness` (or the lower-level `FlashblocksLocalNode`). +**Via TestHarness:** +```rust +let harness = TestHarness::new().await?; +harness.send_flashblock(flashblock).await?; +``` ## Configuration Constants -Key constants are exported from the crate root: - -```rust,ignore -use base_reth_test_utils::{ - BASE_CHAIN_ID, // Chain ID for Base Sepolia (84532) - BLOCK_TIME_SECONDS, // Base L2 block time (2 seconds) - GAS_LIMIT, // Default gas limit (200M) - NODE_STARTUP_DELAY_MS, // IPC endpoint initialization (500ms) - BLOCK_BUILD_DELAY_MS, // Payload construction wait (100ms) - L1_BLOCK_INFO_DEPOSIT_TX, // Pre-captured L1 block info deposit - L1_BLOCK_INFO_DEPOSIT_TX_HASH, - DEFAULT_JWT_SECRET, // All-zeros JWT for local testing -}; +Key constants defined in `harness.rs`: + +```rust +const BLOCK_TIME_SECONDS: u64 = 2; // Base L2 block time +const GAS_LIMIT: u64 = 200_000_000; // Default gas limit +const NODE_STARTUP_DELAY_MS: u64 = 500; // IPC endpoint initialization +const BLOCK_BUILD_DELAY_MS: u64 = 100; // Payload construction wait ``` ## File Structure -```text +``` test-utils/ ├── src/ -│ ├── lib.rs # Public API and re-exports -│ ├── accounts.rs # Test account definitions -│ ├── constants.rs # Shared constants (chain ID, timing, etc.) -│ ├── contracts.rs # Solidity contract bindings -│ ├── engine.rs # EngineApi (CL wrapper) -│ ├── fixtures.rs # Genesis loading, provider factories -│ ├── flashblocks_harness.rs # FlashblocksHarness + helpers -│ ├── harness.rs # TestHarness (orchestration) -│ ├── node.rs # LocalNode (EL wrapper) -│ └── tracing.rs # Tracing initialization helpers +│ ├── lib.rs # Public API and re-exports +│ ├── accounts.rs # Test account definitions +│ ├── node.rs # LocalNode (EL wrapper) +│ ├── engine.rs # EngineApi (CL wrapper) +│ ├── harness.rs # TestHarness (orchestration) +│ └── flashblocks.rs # Flashblocks support ├── assets/ -│ └── genesis.json # Base Sepolia genesis -├── contracts/ # Solidity sources + compiled artifacts +│ └── genesis.json # Base Sepolia genesis └── Cargo.toml ``` @@ -278,11 +275,13 @@ base-reth-test-utils.workspace = true Import in tests: -```rust,ignore +```rust use base_reth_test_utils::TestHarness; #[tokio::test] async fn my_test() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let harness = TestHarness::new().await?; // Your test logic @@ -319,7 +318,6 @@ cargo test -p base-reth-test-utils test_harness_setup - Snapshot/restore functionality - Multi-node network simulation - Performance benchmarking utilities -- Helper builder for Flashblocks ## References diff --git a/crates/test-utils/src/fixtures.rs b/crates/test-utils/src/fixtures.rs index 382a01b5..0f6f8ffd 100644 --- a/crates/test-utils/src/fixtures.rs +++ b/crates/test-utils/src/fixtures.rs @@ -1,22 +1,32 @@ -//! Shared fixtures and test data reused by integration tests across the Base codebase. - use std::sync::Arc; -use alloy_genesis::Genesis; +use alloy_consensus::Receipt; +use alloy_primitives::{b256, bytes, hex::FromHex, map::HashMap, Address, Bytes, B256, U256}; +use alloy_rpc_types_engine::PayloadId; +use base_reth_flashblocks_rpc::subscription::{Flashblock, Metadata}; +use op_alloy_consensus::OpDepositReceipt; use reth::api::{NodeTypes, NodeTypesWithDBAdapter}; use reth_db::{ - ClientVersion, DatabaseEnv, init_db, - mdbx::{DatabaseArguments, KILOBYTE, MEGABYTE, MaxReadTransactionDuration}, - test_utils::{ERROR_DB_CREATION, TempDatabase, create_test_static_files_dir, tempdir_path}, + init_db, + mdbx::{DatabaseArguments, MaxReadTransactionDuration, KILOBYTE, MEGABYTE}, + test_utils::{create_test_static_files_dir, tempdir_path, TempDatabase, ERROR_DB_CREATION}, + ClientVersion, DatabaseEnv, }; -use reth_provider::{ProviderFactory, providers::StaticFileProvider}; +use reth_optimism_primitives::OpReceipt; +use reth_provider::{providers::StaticFileProvider, ProviderFactory}; +use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; + +const FLASHBLOCK_PAYLOAD_ID: [u8; 8] = [0; 8]; +// Pre-captured deposit transaction and hash used by flashblocks tests for the L1 block info deposit. +// Values match `BLOCK_INFO_TXN` and `BLOCK_INFO_TXN_HASH` from `crates/flashblocks-rpc/src/tests/mod.rs`. +const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); +const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = + b256!("0xba56c8b0deb460ff070f8fca8e2ee01e51a3db27841cc862fdd94cc1a47662b6"); -/// Loads the shared test genesis configuration. -pub fn load_genesis() -> Genesis { - serde_json::from_str(include_str!("../assets/genesis.json")).unwrap() +pub fn secret_from_hex(hex_key: &str) -> B256 { + B256::from_hex(hex_key).expect("32-byte private key") } -/// Creates a provider factory for tests with the given chain spec. pub fn create_provider_factory( chain_spec: Arc, ) -> ProviderFactory>>> { @@ -29,7 +39,6 @@ pub fn create_provider_factory( ) } -/// Creates a temporary test database. fn create_test_db() -> Arc> { let path = tempdir_path(); let emsg = format!("{ERROR_DB_CREATION}: {path:?}"); @@ -45,3 +54,61 @@ fn create_test_db() -> Arc> { Arc::new(TempDatabase::new(db, path)) } + +pub fn build_single_flashblock( + block_number: u64, + parent_hash: B256, + parent_beacon_block_root: B256, + timestamp: u64, + gas_limit: u64, + transactions: Vec<(Bytes, Option<(B256, OpReceipt)>)>, +) -> Flashblock { + let base = ExecutionPayloadBaseV1 { + parent_beacon_block_root, + parent_hash, + fee_recipient: Address::ZERO, + prev_randao: B256::ZERO, + block_number, + gas_limit, + timestamp, + extra_data: Bytes::new(), + base_fee_per_gas: U256::from(1), + }; + + let mut flashblock_txs = vec![BLOCK_INFO_DEPOSIT_TX.clone()]; + let mut receipts = HashMap::default(); + receipts.insert( + BLOCK_INFO_DEPOSIT_TX_HASH, + OpReceipt::Deposit(OpDepositReceipt { + inner: Receipt { + status: true.into(), + cumulative_gas_used: 10_000, + logs: vec![], + }, + deposit_nonce: Some(4_012_991u64), + deposit_receipt_version: None, + }), + ); + + for (tx_bytes, maybe_receipt) in transactions { + if let Some((hash, receipt)) = maybe_receipt { + receipts.insert(hash, receipt); + } + flashblock_txs.push(tx_bytes); + } + + Flashblock { + payload_id: PayloadId::new(FLASHBLOCK_PAYLOAD_ID), + index: 0, + base: Some(base), + diff: ExecutionPayloadFlashblockDeltaV1 { + transactions: flashblock_txs, + ..Default::default() + }, + metadata: Metadata { + receipts, + new_account_balances: Default::default(), + block_number, + }, + } +} diff --git a/crates/test-utils/src/harness.rs b/crates/test-utils/src/harness.rs index 7a1a7e3c..aade0c2b 100644 --- a/crates/test-utils/src/harness.rs +++ b/crates/test-utils/src/harness.rs @@ -1,36 +1,30 @@ -//! Unified test harness combining node and engine helpers, plus optional flashblocks adapter. +//! Unified test harness combining node, engine API, and flashblocks functionality -use std::time::Duration; - -use alloy_eips::{BlockHashOrNumber, eip7685::Requests}; -use alloy_primitives::{B64, B256, Bytes}; +use crate::accounts::TestAccounts; +use crate::engine::{EngineApi, IpcEngine}; +use crate::node::{LocalFlashblocksState, LocalNode, LocalNodeProvider, OpAddOns, OpBuilder}; +use alloy_eips::eip7685::Requests; +use alloy_primitives::{Bytes, B256}; use alloy_provider::{Provider, RootProvider}; use alloy_rpc_types::BlockNumberOrTag; use alloy_rpc_types_engine::PayloadAttributes; -use eyre::{Result, eyre}; +use base_reth_flashblocks_rpc::subscription::Flashblock; +use eyre::{eyre, Result}; use futures_util::Future; use op_alloy_network::Optimism; use op_alloy_rpc_types_engine::OpPayloadAttributes; -use reth::{ - builder::NodeHandle, - providers::{BlockNumReader, BlockReader, ChainSpecProvider}, -}; +use reth::builder::NodeHandle; use reth_e2e_test_utils::Adapter; use reth_optimism_node::OpNode; -use reth_optimism_primitives::OpBlock; -use reth_primitives_traits::{Block as BlockT, RecoveredBlock}; +use std::sync::Arc; +use std::time::Duration; use tokio::time::sleep; -use crate::{ - BLOCK_BUILD_DELAY_MS, BLOCK_TIME_SECONDS, GAS_LIMIT, L1_BLOCK_INFO_DEPOSIT_TX, - NODE_STARTUP_DELAY_MS, TestAccounts, - engine::{EngineApi, IpcEngine}, - node::{LocalNode, LocalNodeProvider, OpAddOns, OpBuilder, default_launcher}, - tracing::init_silenced_tracing, -}; +const BLOCK_TIME_SECONDS: u64 = 2; +const GAS_LIMIT: u64 = 200_000_000; +const NODE_STARTUP_DELAY_MS: u64 = 500; +const BLOCK_BUILD_DELAY_MS: u64 = 100; -/// High-level façade that bundles a local node, engine API client, and common helpers. -#[derive(Debug)] pub struct TestHarness { node: LocalNode, engine: EngineApi, @@ -38,64 +32,47 @@ pub struct TestHarness { } impl TestHarness { - /// Launch a new harness using the default launcher configuration. - pub async fn new() -> Result { - Self::with_launcher(default_launcher).await - } - - /// Launch the harness with a custom node launcher (e.g. to tweak components). - pub async fn with_launcher(launcher: L) -> Result + pub async fn new(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, LRet: Future, OpAddOns>>>, { - init_silenced_tracing(); let node = LocalNode::new(launcher).await?; - Self::from_node(node).await - } - - /// Build a harness from an already-running [`LocalNode`]. - pub(crate) async fn from_node(node: LocalNode) -> Result { let engine = node.engine_api()?; let accounts = TestAccounts::new(); sleep(Duration::from_millis(NODE_STARTUP_DELAY_MS)).await; - Ok(Self { node, engine, accounts }) + Ok(Self { + node, + engine, + accounts, + }) } - /// Return an Optimism JSON-RPC provider connected to the harness node. pub fn provider(&self) -> RootProvider { - self.node.provider().expect("provider should always be available after node initialization") + self.node + .provider() + .expect("provider should always be available after node initialization") } - /// Access the deterministic test accounts backing the harness. pub fn accounts(&self) -> &TestAccounts { &self.accounts } - /// Access the low-level blockchain provider for direct database queries. pub fn blockchain_provider(&self) -> LocalNodeProvider { self.node.blockchain_provider() } - /// HTTP URL for sending JSON-RPC requests to the local node. - pub fn rpc_url(&self) -> String { - format!("http://{}", self.node.http_api_addr) + pub fn flashblocks_state(&self) -> Arc { + self.node.flashblocks_state() } - /// Websocket URL for subscribing to JSON-RPC notifications. - pub fn ws_url(&self) -> String { - format!("ws://{}", self.node.ws_api_addr) + pub fn rpc_url(&self) -> String { + format!("http://{}", self.node.http_api_addr) } - /// Build a block using the provided transactions and push it through the engine. - pub async fn build_block_from_transactions(&self, mut transactions: Vec) -> Result<()> { - // Ensure the block always starts with the required L1 block info deposit. - if transactions.first().is_none_or(|tx| tx != &L1_BLOCK_INFO_DEPOSIT_TX) { - transactions.insert(0, L1_BLOCK_INFO_DEPOSIT_TX.clone()); - } - + pub async fn build_block_from_transactions(&self, transactions: Vec) -> Result<()> { let latest_block = self .provider() .get_block_by_number(BlockNumberOrTag::Latest) @@ -103,28 +80,19 @@ impl TestHarness { .ok_or_else(|| eyre!("No genesis block found"))?; let parent_hash = latest_block.header.hash; - let parent_beacon_block_root = - latest_block.header.parent_beacon_block_root.unwrap_or(B256::ZERO); let next_timestamp = latest_block.header.timestamp + BLOCK_TIME_SECONDS; - let min_base_fee = latest_block.header.base_fee_per_gas.unwrap_or_default(); - let chain_spec = self.node.blockchain_provider().chain_spec(); - let base_fee_params = chain_spec.base_fee_params_at_timestamp(next_timestamp); - let eip_1559_params = ((base_fee_params.max_change_denominator as u64) << 32) - | (base_fee_params.elasticity_multiplier as u64); - let payload_attributes = OpPayloadAttributes { payload_attributes: PayloadAttributes { timestamp: next_timestamp, - parent_beacon_block_root: Some(parent_beacon_block_root), + parent_beacon_block_root: Some(B256::ZERO), withdrawals: Some(vec![]), ..Default::default() }, transactions: Some(transactions), gas_limit: Some(GAS_LIMIT), no_tx_pool: Some(true), - min_base_fee: Some(min_base_fee), - eip_1559_params: Some(B64::from(eip_1559_params)), + ..Default::default() }; let forkchoice_result = self @@ -164,49 +132,58 @@ impl TestHarness { .latest_valid_hash .ok_or_else(|| eyre!("Payload status missing latest_valid_hash"))?; - self.engine.update_forkchoice(parent_hash, new_block_hash, None).await?; + self.engine + .update_forkchoice(parent_hash, new_block_hash, None) + .await?; Ok(()) } - /// Advance the canonical chain by `n` empty blocks. + pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { + self.node.send_flashblock(flashblock).await + } + + pub async fn send_flashblocks(&self, flashblocks: I) -> Result<()> + where + I: IntoIterator, + { + for flashblock in flashblocks { + self.send_flashblock(flashblock).await?; + } + Ok(()) + } + pub async fn advance_chain(&self, n: u64) -> Result<()> { for _ in 0..n { self.build_block_from_transactions(vec![]).await?; } Ok(()) } - - /// Return the latest recovered block as seen by the local blockchain provider. - pub fn latest_block(&self) -> RecoveredBlock { - let provider = self.blockchain_provider(); - let best_number = provider.best_block_number().expect("able to read best block number"); - let block = provider - .block(BlockHashOrNumber::Number(best_number)) - .expect("able to load canonical block") - .expect("canonical block exists"); - BlockT::try_into_recovered(block).expect("able to recover canonical block") - } } #[cfg(test)] mod tests { + use crate::node::default_launcher; + + use super::*; use alloy_primitives::U256; use alloy_provider::Provider; - use super::*; #[tokio::test] async fn test_harness_setup() -> Result<()> { - let harness = TestHarness::new().await?; + reth_tracing::init_test_tracing(); + let harness = TestHarness::new(default_launcher).await?; assert_eq!(harness.accounts().alice.name, "Alice"); assert_eq!(harness.accounts().bob.name, "Bob"); let provider = harness.provider(); let chain_id = provider.get_chain_id().await?; - assert_eq!(chain_id, crate::BASE_CHAIN_ID); + assert_eq!(chain_id, crate::node::BASE_CHAIN_ID); - let alice_balance = provider.get_balance(harness.accounts().alice.address).await?; + let alice_balance = provider + .get_balance(harness.accounts().alice.address) + .await?; assert!(alice_balance > U256::ZERO); let block_number = provider.get_block_number().await?; diff --git a/crates/test-utils/src/node.rs b/crates/test-utils/src/node.rs index 700d9732..cd713b8c 100644 --- a/crates/test-utils/src/node.rs +++ b/crates/test-utils/src/node.rs @@ -1,237 +1,58 @@ //! Local node setup with Base Sepolia chainspec -use std::{ - any::Any, - fmt, - net::SocketAddr, - sync::{Arc, Mutex}, -}; - +use crate::engine::EngineApi; use alloy_genesis::Genesis; use alloy_provider::RootProvider; use alloy_rpc_client::RpcClient; -use base_flashtypes::Flashblock; -use base_reth_flashblocks::{FlashblocksReceiver, FlashblocksState}; -use base_reth_rpc::{EthApiExt, EthApiOverrideServer, EthPubSub, EthPubSubApiServer}; +use base_reth_flashblocks_rpc::rpc::{EthApiExt, EthApiOverrideServer}; +use base_reth_flashblocks_rpc::state::FlashblocksState; +use base_reth_flashblocks_rpc::subscription::{Flashblock, FlashblocksReceiver}; use eyre::Result; use futures_util::Future; use once_cell::sync::OnceCell; use op_alloy_network::Optimism; -use reth::{ - api::{FullNodeTypesAdapter, NodeTypesWithDBAdapter}, - args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}, - builder::{ - Node, NodeBuilder, NodeBuilderWithComponents, NodeConfig, NodeHandle, WithLaunchContext, - }, - core::exit::NodeExitFuture, - tasks::TaskManager, -}; -use reth_db::{ - ClientVersion, DatabaseEnv, init_db, - mdbx::DatabaseArguments, - test_utils::{ERROR_DB_CREATION, TempDatabase, tempdir_path}, +use reth::api::{FullNodeTypesAdapter, NodeTypesWithDBAdapter}; +use reth::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}; +use reth::builder::{ + Node, NodeBuilder, NodeBuilderWithComponents, NodeConfig, NodeHandle, WithLaunchContext, }; +use reth::core::exit::NodeExitFuture; +use reth::tasks::TaskManager; use reth_e2e_test_utils::{Adapter, TmpDB}; use reth_exex::ExExEvent; -use reth_node_core::{ - args::DatadirArgs, - dirs::{DataDirPath, MaybePlatformPath}, -}; use reth_optimism_chainspec::OpChainSpec; -use reth_optimism_node::{OpNode, args::RollupArgs}; -use reth_provider::{CanonStateSubscriptions, providers::BlockchainProvider}; +use reth_optimism_node::args::RollupArgs; +use reth_optimism_node::OpNode; +use reth_provider::providers::BlockchainProvider; +use std::any::Any; +use std::net::SocketAddr; +use std::sync::Arc; use tokio::sync::{mpsc, oneshot}; use tokio_stream::StreamExt; -use crate::engine::EngineApi; +pub const BASE_CHAIN_ID: u64 = 84532; -/// Convenience alias for the local blockchain provider type. pub type LocalNodeProvider = BlockchainProvider>; -/// Convenience alias for the Flashblocks state backing the local node. pub type LocalFlashblocksState = FlashblocksState; -/// Handle to a launched local node along with the resources required to keep it alive. pub struct LocalNode { pub(crate) http_api_addr: SocketAddr, engine_ipc_path: String, - pub(crate) ws_api_addr: SocketAddr, + flashblock_sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, + flashblocks_state: Arc, provider: LocalNodeProvider, _node_exit_future: NodeExitFuture, _node: Box, _task_manager: TaskManager, } -impl fmt::Debug for LocalNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("LocalNode") - .field("http_api_addr", &self.http_api_addr) - .field("ws_api_addr", &self.ws_api_addr) - .field("engine_ipc_path", &self.engine_ipc_path) - .finish_non_exhaustive() - } -} - -/// Components that allow tests to interact with the Flashblocks worker tasks. -#[derive(Clone)] -pub struct FlashblocksParts { - sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, - state: Arc, -} - -impl fmt::Debug for FlashblocksParts { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlashblocksParts").finish_non_exhaustive() - } -} - -impl FlashblocksParts { - /// Clone the shared [`FlashblocksState`] handle. - pub fn state(&self) -> Arc { - self.state.clone() - } - - /// Send a flashblock to the background processor and wait until it is handled. - pub async fn send(&self, flashblock: Flashblock) -> Result<()> { - let (tx, rx) = oneshot::channel(); - self.sender.send((flashblock, tx)).await.map_err(|err| eyre::eyre!(err))?; - rx.await.map_err(|err| eyre::eyre!(err))?; - Ok(()) - } -} - -#[derive(Clone)] -struct FlashblocksNodeExtensions { - inner: Arc, -} - -struct FlashblocksNodeExtensionsInner { - sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, - #[allow(clippy::type_complexity)] - receiver: Arc)>>>>, - fb_cell: Arc>>, - process_canonical: bool, -} - -impl FlashblocksNodeExtensions { - fn new(process_canonical: bool) -> Self { - let (sender, receiver) = mpsc::channel::<(Flashblock, oneshot::Sender<()>)>(100); - let inner = FlashblocksNodeExtensionsInner { - sender, - receiver: Arc::new(Mutex::new(Some(receiver))), - fb_cell: Arc::new(OnceCell::new()), - process_canonical, - }; - Self { inner: Arc::new(inner) } - } - - fn apply(&self, builder: OpBuilder) -> OpBuilder { - let fb_cell = self.inner.fb_cell.clone(); - let receiver = self.inner.receiver.clone(); - let process_canonical = self.inner.process_canonical; - - let fb_cell_for_exex = fb_cell.clone(); - - builder - .install_exex("flashblocks-canon", move |mut ctx| { - let fb_cell = fb_cell_for_exex.clone(); - let process_canonical = process_canonical; - async move { - let provider = ctx.provider().clone(); - let fb = init_flashblocks_state(&fb_cell, &provider); - Ok(async move { - while let Some(note) = ctx.notifications.try_next().await? { - if let Some(committed) = note.committed_chain() { - let hash = committed.tip().num_hash(); - if process_canonical { - // Many suites drive canonical updates manually to reproduce race conditions, so - // allowing this to be disabled keeps canonical replay deterministic. - let chain = Arc::unwrap_or_clone(committed); - for (_, block) in chain.into_blocks() { - fb.on_canonical_block_received(block); - } - } - let _ = ctx.events.send(ExExEvent::FinishedHeight(hash)); - } - } - Ok(()) - }) - } - }) - .extend_rpc_modules(move |ctx| { - let fb_cell = fb_cell.clone(); - let provider = ctx.provider().clone(); - let fb = init_flashblocks_state(&fb_cell, &provider); - - let mut canon_stream = tokio_stream::wrappers::BroadcastStream::new( - ctx.provider().subscribe_to_canonical_state(), - ); - tokio::spawn(async move { - use tokio_stream::StreamExt; - while let Some(Ok(notification)) = canon_stream.next().await { - provider.canonical_in_memory_state().notify_canon_state(notification); - } - }); - let api_ext = EthApiExt::new( - ctx.registry.eth_api().clone(), - ctx.registry.eth_handlers().filter.clone(), - fb.clone(), - ); - ctx.modules.replace_configured(api_ext.into_rpc())?; - - // Register eth_subscribe subscription endpoint for flashblocks - // Uses replace_configured since eth_subscribe already exists from reth's standard module - // Pass eth_api to enable proxying standard subscription types to reth's implementation - let eth_pubsub = EthPubSub::new(ctx.registry.eth_api().clone(), fb.clone()); - ctx.modules.replace_configured(eth_pubsub.into_rpc())?; - - let fb_for_task = fb.clone(); - let mut receiver = receiver - .lock() - .expect("flashblock receiver mutex poisoned") - .take() - .expect("flashblock receiver should only be initialized once"); - tokio::spawn(async move { - while let Some((payload, tx)) = receiver.recv().await { - fb_for_task.on_flashblock_received(payload); - let _ = tx.send(()); - } - }); - - Ok(()) - }) - } - - fn wrap_launcher(&self, launcher: L) -> impl FnOnce(OpBuilder) -> LRet - where - L: FnOnce(OpBuilder) -> LRet, - { - let extensions = self.clone(); - move |builder| { - let builder = extensions.apply(builder); - launcher(builder) - } - } - - fn parts(&self) -> Result { - let state = self.inner.fb_cell.get().ok_or_else(|| { - eyre::eyre!("FlashblocksState should be initialized during node launch") - })?; - Ok(FlashblocksParts { sender: self.inner.sender.clone(), state: state.clone() }) - } -} - -/// Optimism node types used for the local harness. pub type OpTypes = FullNodeTypesAdapter>>; -/// Builder that wires up the concrete node components. pub type OpComponentsBuilder = >::ComponentsBuilder; -/// Additional services attached to the node builder. pub type OpAddOns = >::AddOns; -/// Launcher builder used by the harness to customize node startup. pub type OpBuilder = WithLaunchContext>; -/// Default launcher that is reused across the harness and integration tests. pub async fn default_launcher( builder: OpBuilder, ) -> eyre::Result, OpAddOns>> { @@ -240,226 +61,170 @@ pub async fn default_launcher( } impl LocalNode { - /// Launch a new local node using the provided launcher function. pub async fn new(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, LRet: Future, OpAddOns>>>, { - build_node(launcher).await - } + let tasks = TaskManager::current(); + let exec = tasks.executor(); + + let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json"))?; + let chain_spec = Arc::new(OpChainSpec::from_genesis(genesis)); + + let network_config = NetworkArgs { + discovery: DiscoveryArgs { + disable_discovery: true, + ..DiscoveryArgs::default() + }, + ..NetworkArgs::default() + }; + + // Generate unique IPC path for this test instance to avoid conflicts + // Use timestamp + thread ID + process ID for uniqueness + let unique_ipc_path = format!( + "/tmp/reth_engine_api_{}_{}_{:?}.ipc", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos(), + std::process::id(), + std::thread::current().id() + ); + + let mut rpc_args = RpcServerArgs::default() + .with_unused_ports() + .with_http() + .with_auth_ipc(); + rpc_args.auth_ipc_path = unique_ipc_path; + + let node_config = NodeConfig::new(chain_spec.clone()) + .with_network(network_config) + .with_rpc(rpc_args) + .with_unused_ports(); + + let node = OpNode::new(RollupArgs::default()); - /// Creates a test database with a smaller map size to reduce memory usage. - /// - /// Unlike `NodeBuilder::testing_node()` which hardcodes an 8 TB map size, - /// this method configures the database with a 100 MB map size. This prevents - /// `ENOMEM` errors when running parallel tests with `cargo test`, as the - /// default 8 TB size can cause memory exhaustion when multiple test processes - /// run concurrently. - fn create_test_database() -> Result>> { - let default_size = 100 * 1024 * 1024; // 100 MB - Self::create_test_database_with_size(default_size) + let (sender, receiver) = mpsc::channel::<(Flashblock, oneshot::Sender<()>)>(100); + let fb_cell: Arc>>> = Arc::new(OnceCell::new()); + let provider_cell: Arc> = Arc::new(OnceCell::new()); + + let NodeHandle { + node: node_handle, + node_exit_future, + } = NodeBuilder::new(node_config.clone()) + .testing_node(exec.clone()) + .with_types_and_provider::>() + .with_components(node.components_builder()) + .with_add_ons(node.add_ons()) + .install_exex("flashblocks-canon", { + let fb_cell = fb_cell.clone(); + let provider_cell = provider_cell.clone(); + move |mut ctx| async move { + let provider = provider_cell.get_or_init(|| ctx.provider().clone()).clone(); + let fb = fb_cell + .get_or_init(|| Arc::new(FlashblocksState::new(provider.clone()))) + .clone(); + Ok(async move { + while let Some(note) = ctx.notifications.try_next().await? { + if let Some(committed) = note.committed_chain() { + for block in committed.blocks_iter() { + fb.on_canonical_block_received(block); + } + let _ = ctx + .events + .send(ExExEvent::FinishedHeight(committed.tip().num_hash())); + } + } + Ok(()) + }) + } + }) + .extend_rpc_modules({ + let fb_cell = fb_cell.clone(); + let provider_cell = provider_cell.clone(); + let mut receiver = Some(receiver); + move |ctx| { + let provider = provider_cell.get_or_init(|| ctx.provider().clone()).clone(); + let fb = fb_cell + .get_or_init(|| Arc::new(FlashblocksState::new(provider.clone()))) + .clone(); + fb.start(); + let api_ext = EthApiExt::new( + ctx.registry.eth_api().clone(), + ctx.registry.eth_handlers().filter.clone(), + fb.clone(), + ); + ctx.modules.replace_configured(api_ext.into_rpc())?; + // Spawn task to receive flashblocks from the test context + let fb_for_task = fb.clone(); + let mut receiver = receiver + .take() + .expect("flashblock receiver should only be initialized once"); + tokio::spawn(async move { + while let Some((payload, tx)) = receiver.recv().await { + fb_for_task.on_flashblock_received(payload); + let _ = tx.send(()); + } + }); + Ok(()) + } + }) + .launch_with_fn(launcher) + .await?; + + let http_api_addr = node_handle + .rpc_server_handle() + .http_local_addr() + .ok_or_else(|| eyre::eyre!("HTTP RPC server failed to bind to address"))?; + + let engine_ipc_path = node_config.rpc.auth_ipc_path; + let flashblocks_state = fb_cell + .get() + .expect("FlashblocksState should be initialized during node launch") + .clone(); + let provider = provider_cell + .get() + .expect("Provider should be initialized during node launch") + .clone(); + + Ok(Self { + http_api_addr, + engine_ipc_path, + flashblock_sender: sender, + flashblocks_state, + provider, + _node_exit_future: node_exit_future, + _node: Box::new(node_handle), + _task_manager: tasks, + }) } - /// Creates a test database with a configurable map size to reduce memory usage. - /// - /// # Arguments - /// - /// * `max_size` - Maximum map size in bytes. - fn create_test_database_with_size(max_size: usize) -> Result>> { - let path = tempdir_path(); - let emsg = format!("{ERROR_DB_CREATION}: {path:?}"); - let args = - DatabaseArguments::new(ClientVersion::default()).with_geometry_max_size(Some(max_size)); - let db = init_db(&path, args).expect(&emsg); - Ok(Arc::new(TempDatabase::new(db, path))) + pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { + let (tx, rx) = oneshot::channel(); + self.flashblock_sender + .send((flashblock, tx)) + .await + .map_err(|err| eyre::eyre!(err))?; + rx.await.map_err(|err| eyre::eyre!(err))?; + Ok(()) } - /// Create an HTTP provider pointed at the node's public RPC endpoint. pub fn provider(&self) -> Result> { let url = format!("http://{}", self.http_api_addr); let client = RpcClient::builder().http(url.parse()?); Ok(RootProvider::::new(client)) } - /// Build an Engine API client that talks to the node's IPC endpoint. pub fn engine_api(&self) -> Result> { EngineApi::::new(self.engine_ipc_path.clone()) } - /// Clone the underlying blockchain provider so callers can inspect chain state. - pub fn blockchain_provider(&self) -> LocalNodeProvider { - self.provider.clone() - } - - /// Websocket URL for the local node. - pub fn ws_url(&self) -> String { - format!("ws://{}", self.ws_api_addr) - } -} - -async fn build_node(launcher: L) -> Result -where - L: FnOnce(OpBuilder) -> LRet, - LRet: Future, OpAddOns>>>, -{ - let tasks = TaskManager::current(); - let exec = tasks.executor(); - - let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json"))?; - let chain_spec = Arc::new(OpChainSpec::from_genesis(genesis)); - - let network_config = NetworkArgs { - discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() }, - ..NetworkArgs::default() - }; - - let unique_ipc_path = format!( - "/tmp/reth_engine_api_{}_{}_{:?}.ipc", - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos(), - std::process::id(), - std::thread::current().id() - ); - - let mut rpc_args = - RpcServerArgs::default().with_unused_ports().with_http().with_auth_ipc().with_ws(); - rpc_args.auth_ipc_path = unique_ipc_path; - - let node = OpNode::new(RollupArgs::default()); - - let temp_db = LocalNode::create_test_database()?; - let db_path = temp_db.path().to_path_buf(); - - let mut node_config = NodeConfig::new(chain_spec.clone()) - .with_network(network_config) - .with_rpc(rpc_args) - .with_unused_ports(); - - let datadir_path = MaybePlatformPath::::from(db_path.clone()); - node_config = - node_config.with_datadir_args(DatadirArgs { datadir: datadir_path, ..Default::default() }); - - let builder = NodeBuilder::new(node_config.clone()) - .with_database(temp_db) - .with_launch_context(exec.clone()) - .with_types_and_provider::>() - .with_components(node.components_builder()) - .with_add_ons(node.add_ons()); - - let NodeHandle { node: node_handle, node_exit_future } = - builder.launch_with_fn(launcher).await?; - - let http_api_addr = node_handle - .rpc_server_handle() - .http_local_addr() - .ok_or_else(|| eyre::eyre!("HTTP RPC server failed to bind to address"))?; - - let ws_api_addr = node_handle - .rpc_server_handle() - .ws_local_addr() - .ok_or_else(|| eyre::eyre!("Failed to get websocket api address"))?; - - let engine_ipc_path = node_config.rpc.auth_ipc_path; - let provider = node_handle.provider().clone(); - - Ok(LocalNode { - http_api_addr, - ws_api_addr, - engine_ipc_path, - provider, - _node_exit_future: node_exit_future, - _node: Box::new(node_handle), - _task_manager: tasks, - }) -} - -fn init_flashblocks_state( - cell: &Arc>>, - provider: &LocalNodeProvider, -) -> Arc { - cell.get_or_init(|| { - let fb = Arc::new(FlashblocksState::new(provider.clone(), 5)); - fb.start(); - fb - }) - .clone() -} - -/// Local node wrapper that exposes helpers specific to Flashblocks tests. -pub struct FlashblocksLocalNode { - node: LocalNode, - parts: FlashblocksParts, -} - -impl fmt::Debug for FlashblocksLocalNode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FlashblocksLocalNode") - .field("node", &self.node) - .field("parts", &self.parts) - .finish() - } -} - -impl FlashblocksLocalNode { - /// Launch a flashblocks-enabled node using the default launcher. - pub async fn new() -> Result { - Self::with_launcher(default_launcher).await - } - - /// Builds a flashblocks-enabled node with canonical block streaming disabled so tests can call - /// `FlashblocksState::on_canonical_block_received` at precise points. - pub async fn manual_canonical() -> Result { - Self::with_manual_canonical_launcher(default_launcher).await - } - - /// Launch a flashblocks-enabled node with a custom launcher and canonical processing enabled. - pub async fn with_launcher(launcher: L) -> Result - where - L: FnOnce(OpBuilder) -> LRet, - LRet: Future, OpAddOns>>>, - { - Self::with_launcher_inner(launcher, true).await - } - - /// Same as [`Self::with_launcher`] but leaves canonical processing to the caller. - pub async fn with_manual_canonical_launcher(launcher: L) -> Result - where - L: FnOnce(OpBuilder) -> LRet, - LRet: Future, OpAddOns>>>, - { - Self::with_launcher_inner(launcher, false).await - } - - async fn with_launcher_inner(launcher: L, process_canonical: bool) -> Result - where - L: FnOnce(OpBuilder) -> LRet, - LRet: Future, OpAddOns>>>, - { - let extensions = FlashblocksNodeExtensions::new(process_canonical); - let wrapped_launcher = extensions.wrap_launcher(launcher); - let node = LocalNode::new(wrapped_launcher).await?; - - let parts = extensions.parts()?; - Ok(Self { node, parts }) - } - - /// Access the shared Flashblocks state for assertions or manual driving. pub fn flashblocks_state(&self) -> Arc { - self.parts.state() - } - - /// Send a flashblock through the background processor and await completion. - pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { - self.parts.send(flashblock).await + self.flashblocks_state.clone() } - /// Split the wrapper into the underlying node plus flashblocks parts. - pub fn into_parts(self) -> (LocalNode, FlashblocksParts) { - (self.node, self.parts) - } - - /// Borrow the underlying [`LocalNode`]. - pub fn as_node(&self) -> &LocalNode { - &self.node + pub fn blockchain_provider(&self) -> LocalNodeProvider { + self.provider.clone() } } From 345819c734c4cc3e9d680a91cbbc755fee4a7244 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 11 Nov 2025 16:44:25 -0600 Subject: [PATCH 11/26] Fix chain-state metering tests with L1 deposit --- crates/metering/src/tests/chain_state.rs | 298 ++++++++++++++++++++--- crates/rpc/Cargo.toml | 3 + crates/test-utils/README.md | 2 +- crates/test-utils/src/fixtures.rs | 4 +- crates/test-utils/src/harness.rs | 14 +- 5 files changed, 279 insertions(+), 42 deletions(-) diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs index 4f378951..23c5903c 100644 --- a/crates/metering/src/tests/chain_state.rs +++ b/crates/metering/src/tests/chain_state.rs @@ -1,49 +1,93 @@ use alloy_consensus::Receipt; -use alloy_eips::Encodable2718; -use alloy_primitives::{Bytes, B256}; +use alloy_eips::{BlockNumberOrTag, Encodable2718}; +use alloy_primitives::{Bytes, B256, U256}; use alloy_provider::Provider; +use alloy_rpc_types_eth::TransactionInput; +use alloy_sol_types::{sol, SolCall}; use base_reth_flashblocks_rpc::rpc::FlashblocksAPI; use base_reth_test_utils::harness::TestHarness; use base_reth_test_utils::node::{default_launcher, BASE_CHAIN_ID}; use eyre::{eyre, Result}; +use hex_literal::hex; use op_alloy_consensus::OpTxEnvelope; use reth::providers::HeaderProvider; use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; +use reth_primitives::TransactionSigned; use reth_transaction_pool::test_utils::TransactionBuilder; use tips_core::types::Bundle; use super::utils::{build_single_flashblock, secret_from_hex}; use crate::rpc::{MeteringApiImpl, MeteringApiServer}; +use op_alloy_rpc_types::OpTransactionRequest; #[tokio::test] -async fn meters_bundle_after_advancing_blocks() -> Result<()> { +async fn metering_succeeds_after_storage_change() -> Result<()> { reth_tracing::init_test_tracing(); let harness = TestHarness::new(default_launcher).await?; let provider = harness.provider(); - let bob = &harness.accounts().bob; - let alice_secret = secret_from_hex(harness.accounts().alice.private_key); + let alice = &harness.accounts().alice; + let alice_secret = secret_from_hex(alice.private_key); - let tx = TransactionBuilder::default() + // Deploy the Counter contract (nonce 0) + let deploy_signed = TransactionBuilder::default() .signer(alice_secret) .chain_id(BASE_CHAIN_ID) .nonce(0) - .to(bob.address) - .value(1) - .gas_limit(21_000) - .max_fee_per_gas(1_000_000_000) - .max_priority_fee_per_gas(1_000_000_000) + .gas_limit(DEPLOY_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .input(COUNTER_CREATION_BYTECODE.to_vec()) .into_eip1559(); + let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); + harness + .build_block_from_transactions(vec![deploy_bytes]) + .await?; - let envelope = OpTxEnvelope::from(OpTransactionSigned::Eip1559( - tx.as_eip1559().unwrap().clone(), - )); - let tx_bytes = Bytes::from(envelope.encoded_2718()); + let deploy_receipt = provider + .get_transaction_receipt(deploy_envelope.tx_hash()) + .await? + .ok_or_else(|| eyre!("deployment transaction missing receipt"))?; + let contract_address = deploy_receipt + .inner + .contract_address + .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; - harness.advance_chain(1).await?; + // Mutate storage on-chain via setNumber (nonce 1) + let set_call = Counter::setNumberCall { + newNumber: U256::from(42u64), + }; + let set_signed = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(1) + .gas_limit(CALL_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .to(contract_address) + .input(Bytes::from(set_call.abi_encode())) + .into_eip1559(); + let (_set_envelope, set_bytes) = envelope_from_signed(set_signed); + harness + .build_block_from_transactions(vec![set_bytes]) + .await?; + + // Meter an increment call (nonce 2) after the storage change + let increment_call = Counter::incrementCall {}; + let increment_signed = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(2) + .gas_limit(CALL_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .to(contract_address) + .input(Bytes::from(increment_call.abi_encode())) + .into_eip1559(); + let (_increment_envelope, increment_bytes) = envelope_from_signed(increment_signed.clone()); let bundle = Bundle { - txs: vec![tx_bytes.clone()], + txs: vec![increment_bytes.clone()], block_number: provider.get_block_number().await?, flashblock_number_min: None, flashblock_number_max: None, @@ -61,9 +105,41 @@ async fn meters_bundle_after_advancing_blocks() -> Result<()> { .map_err(|err| eyre!("meter_bundle rpc failed: {}", err))?; assert_eq!(response.results.len(), 1); - assert_eq!(response.total_gas_used, 21_000); + let result = &response.results[0]; + assert_eq!(result.to_address, Some(contract_address)); + assert!(result.gas_used > 0); assert!(response.state_flashblock_index.is_none()); + // Confirm canonical storage remains at 42 (increment transaction only simulated) + let number_call = Counter::numberCall {}; + let call_request = OpTransactionRequest::default() + .from(alice.address) + .to(contract_address) + .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))); + let raw_number = provider + .call(call_request) + .block(BlockNumberOrTag::Latest.into()) + .await?; + let decoded: U256 = Counter::numberCall::abi_decode_returns(raw_number.as_ref())?; + assert_eq!(decoded, U256::from(42u64)); + + // Execute the increment on-chain to confirm the transaction is valid when mined + harness + .build_block_from_transactions(vec![increment_bytes]) + .await?; + let number_after_increment = provider + .call( + OpTransactionRequest::default() + .from(alice.address) + .to(contract_address) + .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))), + ) + .block(BlockNumberOrTag::Latest.into()) + .await?; + let decoded_after_increment: U256 = + Counter::numberCall::abi_decode_returns(number_after_increment.as_ref())?; + assert_eq!(decoded_after_increment, U256::from(43u64)); + Ok(()) } @@ -73,19 +149,30 @@ async fn pending_flashblock_updates_state() -> Result<()> { let harness = TestHarness::new(default_launcher).await?; let provider = harness.provider(); - let bob = &harness.accounts().bob; - let alice_secret = secret_from_hex(harness.accounts().alice.private_key); + let alice = &harness.accounts().alice; + let alice_secret = secret_from_hex(alice.private_key); - let tx = TransactionBuilder::default() + // Deploy the contract so the pending flashblock can interact with storage + let deploy_signed = TransactionBuilder::default() .signer(alice_secret) .chain_id(BASE_CHAIN_ID) .nonce(0) - .to(bob.address) - .value(1) - .gas_limit(21_000) - .max_fee_per_gas(1_000_000_000) - .max_priority_fee_per_gas(1_000_000_000) + .gas_limit(DEPLOY_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .input(COUNTER_CREATION_BYTECODE.to_vec()) .into_eip1559(); + let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); + harness + .build_block_from_transactions(vec![deploy_bytes]) + .await?; + let contract_address = provider + .get_transaction_receipt(deploy_envelope.tx_hash()) + .await? + .ok_or_else(|| eyre!("deployment transaction missing receipt"))? + .inner + .contract_address + .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; let blockchain_provider = harness.blockchain_provider(); let latest_number = provider.get_block_number().await?; @@ -94,32 +181,43 @@ async fn pending_flashblock_updates_state() -> Result<()> { .ok_or_else(|| eyre!("missing header for block {}", latest_number))?; let pending_block_number = latest_header.number + 1; - let envelope = OpTxEnvelope::from(OpTransactionSigned::Eip1559( - tx.as_eip1559().unwrap().clone(), - )); - let tx_hash = envelope.tx_hash(); - let tx_bytes = Bytes::from(envelope.encoded_2718()); + let flash_call = Counter::setNumberCall { + newNumber: U256::from(99u64), + }; + let flash_signed = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(1) + .gas_limit(CALL_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .to(contract_address) + .input(Bytes::from(flash_call.abi_encode())) + .into_eip1559(); + let (flash_envelope, flash_bytes) = envelope_from_signed(flash_signed); let receipt = OpReceipt::Eip1559(Receipt { status: true.into(), - cumulative_gas_used: 21_000, + cumulative_gas_used: 80_000, logs: vec![], }); - // Use a zero parent beacon block root to emulate a flashblock that predates Cancun data, - // which should cause metering to surface the missing-root error while still caching state. + // Pending flashblock omits the beacon root, so metering will report the validation error. let flashblock = build_single_flashblock( pending_block_number, latest_header.hash(), B256::ZERO, latest_header.timestamp + 2, latest_header.gas_limit, - vec![(tx_bytes.clone(), Some((tx_hash, receipt.clone())))], + vec![( + flash_bytes.clone(), + Some((flash_envelope.tx_hash(), receipt.clone())), + )], ); harness.send_flashblock(flashblock).await?; let bundle = Bundle { - txs: vec![tx_bytes.clone()], + txs: vec![flash_bytes.clone()], block_number: pending_block_number, flashblock_number_min: None, flashblock_number_max: None, @@ -133,7 +231,6 @@ async fn pending_flashblock_updates_state() -> Result<()> { let metering_api = MeteringApiImpl::new(blockchain_provider.clone(), harness.flashblocks_state()); let result = MeteringApiServer::meter_bundle(&metering_api, bundle).await; - let err = result.expect_err("pending flashblock metering should surface missing beacon root"); assert!( err.message().contains("parent beacon block root missing"), @@ -147,5 +244,132 @@ async fn pending_flashblock_updates_state() -> Result<()> { 0 ); + // Pending state should reflect the storage change even though the simulation failed. + let number_call = Counter::numberCall {}; + let pending_value = provider + .call( + OpTransactionRequest::default() + .from(alice.address) + .to(contract_address) + .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))), + ) + .block(BlockNumberOrTag::Pending.into()) + .await?; + let decoded_pending: U256 = Counter::numberCall::abi_decode_returns(pending_value.as_ref())?; + assert_eq!(decoded_pending, U256::from(99u64)); + + Ok(()) +} + +sol! { + contract Counter { + function setNumber(uint256 newNumber); + function increment(); + function number() view returns (uint256); + } +} + +const COUNTER_CREATION_BYTECODE: &[u8] = &hex!("6080604052348015600e575f5ffd5b506101e18061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80633fb5c1cb146100435780638381f58a1461005f578063d09de08a1461007d575b5f5ffd5b61005d600480360381019061005891906100e4565b610087565b005b610067610090565b604051610074919061011e565b60405180910390f35b610085610095565b005b805f8190555050565b5f5481565b5f5f8154809291906100a690610164565b9190505550565b5f5ffd5b5f819050919050565b6100c3816100b1565b81146100cd575f5ffd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036101a05761019f610137565b5b60018201905091905056fea26469706673582212204b710430bf5e9541dd320fc4eece1bf270f8b7d4835bba28f79ff7bd29904a2964736f6c634300081e0033"); +const GWEI: u128 = 1_000_000_000; +const DEPLOY_GAS_LIMIT: u64 = 1_000_000; +const CALL_GAS_LIMIT: u64 = 150_000; + +fn envelope_from_signed(tx: TransactionSigned) -> (OpTxEnvelope, Bytes) { + let op_signed = OpTransactionSigned::Eip1559( + tx.as_eip1559() + .expect("transaction should be EIP-1559") + .clone(), + ); + let envelope = OpTxEnvelope::from(op_signed); + let bytes = Bytes::from(envelope.encoded_2718()); + (envelope, bytes) +} + +#[tokio::test] +async fn counter_storage_changes_persist_across_blocks() -> Result<()> { + reth_tracing::init_test_tracing(); + let harness = TestHarness::new(default_launcher).await?; + let alice = &harness.accounts().alice; + let alice_secret = secret_from_hex(alice.private_key); + let mut nonce = 0u64; + + let deploy_signed = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(nonce) + .gas_limit(DEPLOY_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .input(COUNTER_CREATION_BYTECODE.to_vec()) + .into_eip1559(); + let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); + + harness + .build_block_from_transactions(vec![deploy_bytes]) + .await?; + nonce += 1; + + let provider = harness.provider(); + let deploy_receipt = provider + .get_transaction_receipt(deploy_envelope.tx_hash()) + .await? + .ok_or_else(|| eyre!("deployment transaction missing receipt"))?; + let contract_address = deploy_receipt + .inner + .contract_address + .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; + + let set_call = Counter::setNumberCall { + newNumber: U256::from(42u64), + }; + let set_signed = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(nonce) + .gas_limit(CALL_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .to(contract_address) + .input(Bytes::from(set_call.abi_encode())) + .into_eip1559(); + let (_set_envelope, set_bytes) = envelope_from_signed(set_signed); + harness + .build_block_from_transactions(vec![set_bytes]) + .await?; + nonce += 1; + + let increment_call = Counter::incrementCall {}; + let increment_signed = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(nonce) + .gas_limit(CALL_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .to(contract_address) + .input(Bytes::from(increment_call.abi_encode())) + .into_eip1559(); + let (_increment_envelope, increment_bytes) = envelope_from_signed(increment_signed); + harness + .build_block_from_transactions(vec![increment_bytes]) + .await?; + + let storage_value = provider + .get_storage_at(contract_address, U256::ZERO) + .await?; + assert_eq!(storage_value, U256::from(43u64)); + + let number_call = Counter::numberCall {}; + let call_request = OpTransactionRequest::default() + .from(alice.address) + .to(contract_address) + .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))); + let raw_number = provider + .call(call_request) + .block(BlockNumberOrTag::Latest.into()) + .await?; + let decoded: U256 = Counter::numberCall::abi_decode_returns(raw_number.as_ref())?; + assert_eq!(decoded, U256::from(43u64)); + Ok(()) } diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index af7cf0ce..6fb49530 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -35,8 +35,10 @@ alloy-consensus.workspace = true alloy-eips.workspace = true alloy-rpc-types.workspace = true alloy-rpc-types-eth.workspace = true +alloy-sol-types.workspace = true # op-alloy +op-alloy-consensus.workspace = true op-alloy-rpc-types.workspace = true op-alloy-network.workspace = true @@ -86,6 +88,7 @@ alloy-provider.workspace = true alloy-sol-macro = { workspace = true, features = ["json"] } alloy-sol-types.workspace = true alloy-contract.workspace = true +hex-literal = "0.4" [package.metadata.cargo-udeps.ignore] normal = ["reth-optimism-cli"] diff --git a/crates/test-utils/README.md b/crates/test-utils/README.md index 1f1cc7fd..6d020e9b 100644 --- a/crates/test-utils/README.md +++ b/crates/test-utils/README.md @@ -104,7 +104,7 @@ async fn test_harness() -> eyre::Result<()> { - `provider()` - Get Alloy RootProvider for RPC calls - `accounts()` - Access test accounts - `advance_chain(n)` - Build N empty blocks -- `build_block_from_transactions(txs)` - Build block with specific transactions +- `build_block_from_transactions(txs)` - Build block with specific transactions (auto-prepends the L1 block info deposit) - `send_flashblock(fb)` - Send a single flashblock to the node for pending state processing - `send_flashblocks(iter)` - Convenience helper that sends multiple flashblocks sequentially diff --git a/crates/test-utils/src/fixtures.rs b/crates/test-utils/src/fixtures.rs index 0f6f8ffd..b7464cf6 100644 --- a/crates/test-utils/src/fixtures.rs +++ b/crates/test-utils/src/fixtures.rs @@ -19,8 +19,8 @@ use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; const FLASHBLOCK_PAYLOAD_ID: [u8; 8] = [0; 8]; // Pre-captured deposit transaction and hash used by flashblocks tests for the L1 block info deposit. // Values match `BLOCK_INFO_TXN` and `BLOCK_INFO_TXN_HASH` from `crates/flashblocks-rpc/src/tests/mod.rs`. -const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); -const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = +pub const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); +pub const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = b256!("0xba56c8b0deb460ff070f8fca8e2ee01e51a3db27841cc862fdd94cc1a47662b6"); pub fn secret_from_hex(hex_key: &str) -> B256 { diff --git a/crates/test-utils/src/harness.rs b/crates/test-utils/src/harness.rs index aade0c2b..8056283f 100644 --- a/crates/test-utils/src/harness.rs +++ b/crates/test-utils/src/harness.rs @@ -4,7 +4,7 @@ use crate::accounts::TestAccounts; use crate::engine::{EngineApi, IpcEngine}; use crate::node::{LocalFlashblocksState, LocalNode, LocalNodeProvider, OpAddOns, OpBuilder}; use alloy_eips::eip7685::Requests; -use alloy_primitives::{Bytes, B256}; +use alloy_primitives::{bytes, Bytes, B256}; use alloy_provider::{Provider, RootProvider}; use alloy_rpc_types::BlockNumberOrTag; use alloy_rpc_types_engine::PayloadAttributes; @@ -24,6 +24,8 @@ const BLOCK_TIME_SECONDS: u64 = 2; const GAS_LIMIT: u64 = 200_000_000; const NODE_STARTUP_DELAY_MS: u64 = 500; const BLOCK_BUILD_DELAY_MS: u64 = 100; +// Pre-captured L1 block info deposit transaction required by the Optimism EVM. +const L1_BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); pub struct TestHarness { node: LocalNode, @@ -72,7 +74,15 @@ impl TestHarness { format!("http://{}", self.node.http_api_addr) } - pub async fn build_block_from_transactions(&self, transactions: Vec) -> Result<()> { + pub async fn build_block_from_transactions(&self, mut transactions: Vec) -> Result<()> { + // Ensure the block always starts with the required L1 block info deposit. + if !transactions + .first() + .is_some_and(|tx| tx == &L1_BLOCK_INFO_DEPOSIT_TX) + { + transactions.insert(0, L1_BLOCK_INFO_DEPOSIT_TX.clone()); + } + let latest_block = self .provider() .get_block_by_number(BlockNumberOrTag::Latest) From b2898d6f648f7668bb19537030c2648f4d244283 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 11 Nov 2025 17:09:43 -0600 Subject: [PATCH 12/26] Restore beacon root for successful metering tests --- crates/test-utils/src/harness.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/test-utils/src/harness.rs b/crates/test-utils/src/harness.rs index 8056283f..d70a7b9a 100644 --- a/crates/test-utils/src/harness.rs +++ b/crates/test-utils/src/harness.rs @@ -90,12 +90,16 @@ impl TestHarness { .ok_or_else(|| eyre!("No genesis block found"))?; let parent_hash = latest_block.header.hash; + let parent_beacon_block_root = latest_block + .header + .parent_beacon_block_root + .unwrap_or(B256::ZERO); let next_timestamp = latest_block.header.timestamp + BLOCK_TIME_SECONDS; let payload_attributes = OpPayloadAttributes { payload_attributes: PayloadAttributes { timestamp: next_timestamp, - parent_beacon_block_root: Some(B256::ZERO), + parent_beacon_block_root: Some(parent_beacon_block_root), withdrawals: Some(vec![]), ..Default::default() }, From aff49c8fac8e294de64c49baddf4e30ddbc12de4 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 11 Nov 2025 17:13:37 -0600 Subject: [PATCH 13/26] Have storage persistence test meter the next block --- crates/metering/src/tests/chain_state.rs | 53 ++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs index 23c5903c..fbd11041 100644 --- a/crates/metering/src/tests/chain_state.rs +++ b/crates/metering/src/tests/chain_state.rs @@ -354,6 +354,8 @@ async fn counter_storage_changes_persist_across_blocks() -> Result<()> { .build_block_from_transactions(vec![increment_bytes]) .await?; + nonce += 1; + let storage_value = provider .get_storage_at(contract_address, U256::ZERO) .await?; @@ -371,5 +373,56 @@ async fn counter_storage_changes_persist_across_blocks() -> Result<()> { let decoded: U256 = Counter::numberCall::abi_decode_returns(raw_number.as_ref())?; assert_eq!(decoded, U256::from(43u64)); + // Meter another increment (nonce 3) to ensure meter_bundle sees the persisted state. + let meter_increment_call = Counter::incrementCall {}; + let meter_increment_signed = TransactionBuilder::default() + .signer(alice_secret) + .chain_id(BASE_CHAIN_ID) + .nonce(nonce) + .gas_limit(CALL_GAS_LIMIT) + .max_fee_per_gas(GWEI) + .max_priority_fee_per_gas(GWEI) + .to(contract_address) + .input(Bytes::from(meter_increment_call.abi_encode())) + .into_eip1559(); + let (_meter_increment_envelope, meter_increment_bytes) = + envelope_from_signed(meter_increment_signed.clone()); + + let bundle = Bundle { + txs: vec![meter_increment_bytes.clone()], + block_number: provider.get_block_number().await?, + flashblock_number_min: None, + flashblock_number_max: None, + min_timestamp: None, + max_timestamp: None, + reverting_tx_hashes: vec![], + replacement_uuid: None, + dropping_tx_hashes: vec![], + }; + let metering_api = + MeteringApiImpl::new(harness.blockchain_provider(), harness.flashblocks_state()); + let response = MeteringApiServer::meter_bundle(&metering_api, bundle) + .await + .map_err(|err| eyre!("meter_bundle rpc failed: {}", err))?; + + assert_eq!(response.results.len(), 1); + let metering_result = &response.results[0]; + assert_eq!(metering_result.to_address, Some(contract_address)); + assert!(metering_result.gas_used > 0); + + // Canonical state remains unchanged by the simulation. + let raw_number_after_sim = provider + .call( + OpTransactionRequest::default() + .from(alice.address) + .to(contract_address) + .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))), + ) + .block(BlockNumberOrTag::Latest.into()) + .await?; + let decoded_after_sim: U256 = + Counter::numberCall::abi_decode_returns(raw_number_after_sim.as_ref())?; + assert_eq!(decoded_after_sim, U256::from(43u64)); + Ok(()) } From 8e7a429392281f67d04ac3d6b9b7ca7c4d4bc1e6 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 11 Nov 2025 17:18:55 -0600 Subject: [PATCH 14/26] Rename metering tests for clarity --- crates/metering/src/tests/chain_state.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs index fbd11041..d4e26486 100644 --- a/crates/metering/src/tests/chain_state.rs +++ b/crates/metering/src/tests/chain_state.rs @@ -21,7 +21,7 @@ use crate::rpc::{MeteringApiImpl, MeteringApiServer}; use op_alloy_rpc_types::OpTransactionRequest; #[tokio::test] -async fn metering_succeeds_after_storage_change() -> Result<()> { +async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { reth_tracing::init_test_tracing(); let harness = TestHarness::new(default_launcher).await?; @@ -144,7 +144,7 @@ async fn metering_succeeds_after_storage_change() -> Result<()> { } #[tokio::test] -async fn pending_flashblock_updates_state() -> Result<()> { +async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { reth_tracing::init_test_tracing(); let harness = TestHarness::new(default_launcher).await?; @@ -286,7 +286,7 @@ fn envelope_from_signed(tx: TransactionSigned) -> (OpTxEnvelope, Bytes) { } #[tokio::test] -async fn counter_storage_changes_persist_across_blocks() -> Result<()> { +async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { reth_tracing::init_test_tracing(); let harness = TestHarness::new(default_launcher).await?; let alice = &harness.accounts().alice; From d0cadbd8ec736d5692e6ceb918b079be431cbd6b Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Mon, 17 Nov 2025 11:35:01 -0600 Subject: [PATCH 15/26] Document why flashblock trie caching isolates bundle I/O Add comments explaining that the trie cache ensures each bundle's state root calculation measures only the bundle's incremental I/O, not the accumulated I/O from previous flashblocks. --- crates/rpc/src/base/flashblock_trie_cache.rs | 6 ++++-- crates/rpc/src/base/meter.rs | 8 ++++---- crates/rpc/src/base/meter_rpc.rs | 7 +++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/rpc/src/base/flashblock_trie_cache.rs b/crates/rpc/src/base/flashblock_trie_cache.rs index 5d1d5cd4..4e11c72a 100644 --- a/crates/rpc/src/base/flashblock_trie_cache.rs +++ b/crates/rpc/src/base/flashblock_trie_cache.rs @@ -10,8 +10,10 @@ use crate::FlashblocksState; /// Trie nodes and hashed state from computing a flashblock state root. /// -/// These cached nodes can be reused when computing a bundle's state root -/// to avoid recalculating the flashblock portion of the trie. +/// When metering bundles, we want each state root calculation to measure only +/// the bundle's incremental I/O, not I/O from previous flashblocks. By caching +/// the flashblock trie once and reusing it for all bundle simulations, we ensure +/// each bundle's state root time reflects only its own I/O cost. #[derive(Debug, Clone)] pub struct FlashblockTrieData { pub trie_updates: TrieUpdates, diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index fcaa1d15..7dc147e9 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -62,13 +62,12 @@ where // Get bundle hash let bundle_hash = bundle.bundle_hash(); - // Consolidate flashblock trie data: use cached if available, otherwise compute it - // (before starting any timers, since we only want to time the bundle's execution and state root) + // Get flashblock trie data before starting timers. This ensures we only measure + // the bundle's incremental I/O cost, not I/O from previous flashblocks. let flashblock_trie_data = cached_flashblock_trie .map(Ok::<_, eyre::Report>) .or_else(|| { flashblocks_state.as_ref().map(|fb_state| { - // Compute the flashblock trie let fb_hashed_state = state_provider.hashed_post_state(&fb_state.bundle_state); let (_fb_state_root, fb_trie_updates) = state_provider.state_root_with_updates(fb_hashed_state.clone())?; @@ -173,7 +172,8 @@ where let hashed_state = state_provider.hashed_post_state(&bundle_update); if let Some(fb_trie_data) = flashblock_trie_data { - // We have flashblock trie data (either cached or computed), use it + // Prepend cached flashblock trie so state root calculation only performs I/O + // for this bundle's changes, not for previous flashblocks. let mut trie_input = TrieInput::from_state(hashed_state); trie_input.prepend_cached(fb_trie_data.trie_updates, fb_trie_data.hashed_state); let _ = state_provider.state_root_from_nodes_with_updates(trie_input)?; diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index b62ed234..02bf741d 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -16,7 +16,8 @@ use crate::{FlashblockTrieCache, MeteringApiServer, meter_bundle}; pub struct MeteringApiImpl { provider: Provider, flashblocks_state: Arc, - /// Single-entry cache for the latest flashblock's trie nodes + /// Cache for the latest flashblock's trie, ensuring each bundle's state root + /// calculation only measures the bundle's incremental I/O. trie_cache: FlashblockTrieCache, } @@ -140,11 +141,9 @@ where .as_ref() .map(|pb| pb.latest_flashblock_index()); - // If we have flashblocks, ensure the trie is cached and get it + // Ensure the flashblock trie is cached for reuse across bundle simulations let cached_trie = if let Some(ref fb_state) = flashblocks_state { let fb_index = state_flashblock_index.unwrap(); - - // Ensure the flashblock trie is cached and return it Some( self.trie_cache .ensure_cached(header.hash(), fb_index, fb_state, &*state_provider) From ec9cfff5cc82950cb9970b8222f224f23119749d Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Mon, 17 Nov 2025 11:59:25 -0600 Subject: [PATCH 16/26] Move build_single_flashblock from metering to test-utils --- Cargo.lock | 14 +++- crates/metering/src/tests/chain_state.rs | 4 +- crates/test-utils/src/fixtures.rs | 73 +------------------ crates/test-utils/src/flashblocks.rs | 93 ++++++++++++++++++++++++ crates/test-utils/src/lib.rs | 2 + 5 files changed, 111 insertions(+), 75 deletions(-) create mode 100644 crates/test-utils/src/flashblocks.rs diff --git a/Cargo.lock b/Cargo.lock index 51a7c457..a59045b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1589,12 +1589,16 @@ dependencies = [ "alloy-provider", "alloy-rpc-client", "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-sol-types", "arc-swap", "base-reth-flashblocks-rpc", "base-reth-test-utils", "eyre", + "hex-literal", "jsonrpsee 0.26.0", "op-alloy-consensus 0.22.3", + "op-alloy-rpc-types", "rand 0.9.2", "reth", "reth-db", @@ -4111,6 +4115,12 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hickory-proto" version = "0.25.2" @@ -4369,7 +4379,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.2", + "windows-core 0.62.2", ] [[package]] @@ -12764,7 +12774,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs index d4e26486..88e5ab7e 100644 --- a/crates/metering/src/tests/chain_state.rs +++ b/crates/metering/src/tests/chain_state.rs @@ -16,7 +16,9 @@ use reth_primitives::TransactionSigned; use reth_transaction_pool::test_utils::TransactionBuilder; use tips_core::types::Bundle; -use super::utils::{build_single_flashblock, secret_from_hex}; +use base_reth_test_utils::flashblocks::build_single_flashblock; + +use super::utils::secret_from_hex; use crate::rpc::{MeteringApiImpl, MeteringApiServer}; use op_alloy_rpc_types::OpTransactionRequest; diff --git a/crates/test-utils/src/fixtures.rs b/crates/test-utils/src/fixtures.rs index b7464cf6..b4fc3a9d 100644 --- a/crates/test-utils/src/fixtures.rs +++ b/crates/test-utils/src/fixtures.rs @@ -1,10 +1,6 @@ use std::sync::Arc; -use alloy_consensus::Receipt; -use alloy_primitives::{b256, bytes, hex::FromHex, map::HashMap, Address, Bytes, B256, U256}; -use alloy_rpc_types_engine::PayloadId; -use base_reth_flashblocks_rpc::subscription::{Flashblock, Metadata}; -use op_alloy_consensus::OpDepositReceipt; +use alloy_primitives::{hex::FromHex, B256}; use reth::api::{NodeTypes, NodeTypesWithDBAdapter}; use reth_db::{ init_db, @@ -12,16 +8,7 @@ use reth_db::{ test_utils::{create_test_static_files_dir, tempdir_path, TempDatabase, ERROR_DB_CREATION}, ClientVersion, DatabaseEnv, }; -use reth_optimism_primitives::OpReceipt; use reth_provider::{providers::StaticFileProvider, ProviderFactory}; -use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; - -const FLASHBLOCK_PAYLOAD_ID: [u8; 8] = [0; 8]; -// Pre-captured deposit transaction and hash used by flashblocks tests for the L1 block info deposit. -// Values match `BLOCK_INFO_TXN` and `BLOCK_INFO_TXN_HASH` from `crates/flashblocks-rpc/src/tests/mod.rs`. -pub const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); -pub const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = - b256!("0xba56c8b0deb460ff070f8fca8e2ee01e51a3db27841cc862fdd94cc1a47662b6"); pub fn secret_from_hex(hex_key: &str) -> B256 { B256::from_hex(hex_key).expect("32-byte private key") @@ -54,61 +41,3 @@ fn create_test_db() -> Arc> { Arc::new(TempDatabase::new(db, path)) } - -pub fn build_single_flashblock( - block_number: u64, - parent_hash: B256, - parent_beacon_block_root: B256, - timestamp: u64, - gas_limit: u64, - transactions: Vec<(Bytes, Option<(B256, OpReceipt)>)>, -) -> Flashblock { - let base = ExecutionPayloadBaseV1 { - parent_beacon_block_root, - parent_hash, - fee_recipient: Address::ZERO, - prev_randao: B256::ZERO, - block_number, - gas_limit, - timestamp, - extra_data: Bytes::new(), - base_fee_per_gas: U256::from(1), - }; - - let mut flashblock_txs = vec![BLOCK_INFO_DEPOSIT_TX.clone()]; - let mut receipts = HashMap::default(); - receipts.insert( - BLOCK_INFO_DEPOSIT_TX_HASH, - OpReceipt::Deposit(OpDepositReceipt { - inner: Receipt { - status: true.into(), - cumulative_gas_used: 10_000, - logs: vec![], - }, - deposit_nonce: Some(4_012_991u64), - deposit_receipt_version: None, - }), - ); - - for (tx_bytes, maybe_receipt) in transactions { - if let Some((hash, receipt)) = maybe_receipt { - receipts.insert(hash, receipt); - } - flashblock_txs.push(tx_bytes); - } - - Flashblock { - payload_id: PayloadId::new(FLASHBLOCK_PAYLOAD_ID), - index: 0, - base: Some(base), - diff: ExecutionPayloadFlashblockDeltaV1 { - transactions: flashblock_txs, - ..Default::default() - }, - metadata: Metadata { - receipts, - new_account_balances: Default::default(), - block_number, - }, - } -} diff --git a/crates/test-utils/src/flashblocks.rs b/crates/test-utils/src/flashblocks.rs new file mode 100644 index 00000000..199a0022 --- /dev/null +++ b/crates/test-utils/src/flashblocks.rs @@ -0,0 +1,93 @@ +//! Flashblock testing utilities + +use alloy_consensus::Receipt; +use alloy_primitives::{b256, bytes, Address, Bytes, B256, U256}; +use alloy_rpc_types_engine::PayloadId; +use base_reth_flashblocks_rpc::subscription::{Flashblock, Metadata}; +use op_alloy_consensus::OpDepositReceipt; +use reth_optimism_primitives::OpReceipt; +use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; +use std::collections::HashMap; + +const FLASHBLOCK_PAYLOAD_ID: [u8; 8] = [0; 8]; + +// Pre-captured deposit transaction and hash used by flashblocks tests for the L1 block info deposit. +// Values match `BLOCK_INFO_TXN` and `BLOCK_INFO_TXN_HASH` from `crates/flashblocks-rpc/src/tests/mod.rs`. +const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); +const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = + b256!("0xba56c8b0deb460ff070f8fca8e2ee01e51a3db27841cc862fdd94cc1a47662b6"); + +/// Builds a single flashblock for testing purposes. +/// +/// This utility creates a base flashblock (index 0) with the required L1 block info deposit +/// transaction and any additional transactions provided. +/// +/// # Arguments +/// +/// * `block_number` - The block number for this flashblock +/// * `parent_hash` - Hash of the parent block +/// * `parent_beacon_block_root` - Parent beacon block root +/// * `timestamp` - Block timestamp +/// * `gas_limit` - Gas limit for the block +/// * `transactions` - Vector of (transaction bytes, optional (hash, receipt)) tuples +/// +/// # Returns +/// +/// A `Flashblock` configured for testing with the provided parameters +pub fn build_single_flashblock( + block_number: u64, + parent_hash: B256, + parent_beacon_block_root: B256, + timestamp: u64, + gas_limit: u64, + transactions: Vec<(Bytes, Option<(B256, OpReceipt)>)>, +) -> Flashblock { + let base = ExecutionPayloadBaseV1 { + parent_beacon_block_root, + parent_hash, + fee_recipient: Address::ZERO, + prev_randao: B256::ZERO, + block_number, + gas_limit, + timestamp, + extra_data: Bytes::new(), + base_fee_per_gas: U256::from(1), + }; + + let mut flashblock_txs = vec![BLOCK_INFO_DEPOSIT_TX.clone()]; + let mut receipts = HashMap::default(); + receipts.insert( + BLOCK_INFO_DEPOSIT_TX_HASH, + OpReceipt::Deposit(OpDepositReceipt { + inner: Receipt { + status: true.into(), + cumulative_gas_used: 10_000, + logs: vec![], + }, + deposit_nonce: Some(4_012_991u64), + deposit_receipt_version: None, + }), + ); + + for (tx_bytes, maybe_receipt) in transactions { + if let Some((hash, receipt)) = maybe_receipt { + receipts.insert(hash, receipt); + } + flashblock_txs.push(tx_bytes); + } + + Flashblock { + payload_id: PayloadId::new(FLASHBLOCK_PAYLOAD_ID), + index: 0, + base: Some(base), + diff: ExecutionPayloadFlashblockDeltaV1 { + transactions: flashblock_txs, + ..Default::default() + }, + metadata: Metadata { + receipts, + new_account_balances: Default::default(), + block_number, + }, + } +} diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index ccc88d90..71a2eb28 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -21,6 +21,8 @@ pub use engine::{EngineAddress, EngineApi, EngineProtocol, HttpEngine, IpcEngine mod fixtures; pub use fixtures::{create_provider_factory, load_genesis}; +pub mod flashblocks; + mod flashblocks_harness; pub use flashblocks_harness::FlashblocksHarness; From 093f062345c2f24b3bee8191cf5c8804a2c9c9dd Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Mon, 17 Nov 2025 12:08:25 -0600 Subject: [PATCH 17/26] tweak comments --- crates/rpc/src/base/meter.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 7dc147e9..624f99bf 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -81,7 +81,8 @@ where // Create state database let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); - // If we have flashblocks state, apply both cache and bundle prestate + + // Apply flashblocks read cache if available let cache_db = if let Some(ref flashblocks) = flashblocks_state { CacheDB { cache: flashblocks.cache.clone(), @@ -91,7 +92,7 @@ where CacheDB::new(state_db) }; - // Wrap the CacheDB in a State to track bundle changes for state root calculation + // Track bundle state changes. If metering using flashblocks state, include its bundle prestate. let mut db = if let Some(flashblocks) = flashblocks_state.as_ref() { State::builder() .with_database(cache_db) From 408c56344be48088b79f04efd1bdafbea53fc45f Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 25 Nov 2025 16:31:16 -0600 Subject: [PATCH 18/26] Update tips-core and fix metering harness/tests --- Cargo.lock | 68 +-- Cargo.toml | 2 +- crates/flashblocks/src/processor.rs | 18 +- crates/metering/src/tests/chain_state.rs | 93 ++-- crates/rpc/src/base/flashblock_trie_cache.rs | 5 +- crates/rpc/src/base/meter.rs | 10 +- crates/rpc/src/base/meter_rpc.rs | 22 +- crates/rpc/tests/meter.rs | 9 +- crates/test-utils/README.md | 84 ++-- crates/test-utils/src/fixtures.rs | 11 +- crates/test-utils/src/flashblocks.rs | 93 ---- crates/test-utils/src/flashblocks_harness.rs | 96 +++- crates/test-utils/src/harness.rs | 119 ++--- crates/test-utils/src/lib.rs | 45 +- crates/test-utils/src/node.rs | 490 +++++++++++++------ 15 files changed, 597 insertions(+), 568 deletions(-) delete mode 100644 crates/test-utils/src/flashblocks.rs diff --git a/Cargo.lock b/Cargo.lock index a59045b2..0c23815a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -256,7 +256,7 @@ dependencies = [ "alloy-sol-types", "auto_impl", "derive_more", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-rpc-types-engine", "op-revm", "revm", @@ -370,7 +370,7 @@ dependencies = [ "alloy-op-hardforks", "alloy-primitives", "auto_impl", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-revm", "revm", "thiserror 2.0.17", @@ -1543,7 +1543,7 @@ dependencies = [ "metrics", "metrics-derive", "once_cell", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", "rand 0.9.2", @@ -1597,7 +1597,7 @@ dependencies = [ "eyre", "hex-literal", "jsonrpsee 0.26.0", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-rpc-types", "rand 0.9.2", "reth", @@ -1653,7 +1653,7 @@ dependencies = [ "metrics", "metrics-derive", "once_cell", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-jsonrpsee", "op-alloy-rpc-types", @@ -1709,7 +1709,7 @@ dependencies = [ "futures-util", "jsonrpsee 0.26.0", "once_cell", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", @@ -4359,7 +4359,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -4379,7 +4379,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core 0.57.0", ] [[package]] @@ -5893,20 +5893,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "op-alloy-consensus" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a501241474c3118833d6195312ae7eb7cc90bbb0d5f524cbb0b06619e49ff67" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "derive_more", - "thiserror 2.0.17", -] - [[package]] name = "op-alloy-consensus" version = "0.22.3" @@ -5945,7 +5931,7 @@ dependencies = [ "alloy-provider", "alloy-rpc-types-eth", "alloy-signer", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-rpc-types", ] @@ -5972,7 +5958,7 @@ dependencies = [ "alloy-rpc-types-eth", "alloy-serde", "derive_more", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "serde", "serde_json", "thiserror 2.0.17", @@ -5993,7 +5979,7 @@ dependencies = [ "derive_more", "ethereum_ssz", "ethereum_ssz_derive", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "serde", "snap", "thiserror 2.0.17", @@ -6793,7 +6779,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.1", "rustls", - "socket2 0.6.1", + "socket2 0.5.10", "thiserror 2.0.17", "tokio", "tracing", @@ -6830,7 +6816,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.5.10", "tracing", "windows-sys 0.60.2", ] @@ -7398,7 +7384,7 @@ dependencies = [ "arbitrary", "bytes", "modular-bitfield", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "reth-codecs-derive", "reth-zstd-compressors", "serde", @@ -8844,7 +8830,7 @@ dependencies = [ "alloy-primitives", "derive_more", "miniz_oxide", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-rpc-types", "paste", "reth-chainspec", @@ -8872,7 +8858,7 @@ dependencies = [ "derive_more", "eyre", "futures-util", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "reth-chainspec", "reth-cli", "reth-cli-commands", @@ -8944,7 +8930,7 @@ dependencies = [ "alloy-evm", "alloy-op-evm", "alloy-primitives", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-rpc-types-engine", "op-revm", "reth-chainspec", @@ -9023,7 +9009,7 @@ dependencies = [ "alloy-rpc-types-eth", "clap", "eyre", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-rpc-types-engine", "op-revm", "reth-chainspec", @@ -9071,7 +9057,7 @@ dependencies = [ "alloy-rpc-types-debug", "alloy-rpc-types-engine", "derive_more", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-rpc-types-engine", "reth-basic-payload-builder", "reth-chain-state", @@ -9110,7 +9096,7 @@ dependencies = [ "arbitrary", "bytes", "modular-bitfield", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "reth-codecs", "reth-primitives-traits", "reth-zstd-compressors", @@ -9141,7 +9127,7 @@ dependencies = [ "jsonrpsee-core 0.26.0", "jsonrpsee-types 0.26.0", "metrics", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-jsonrpsee", "op-alloy-rpc-types", @@ -9205,7 +9191,7 @@ dependencies = [ "derive_more", "futures-util", "metrics", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-flz", "op-alloy-rpc-types", "op-revm", @@ -9331,7 +9317,7 @@ dependencies = [ "derive_more", "modular-bitfield", "once_cell", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "proptest", "proptest-arbitrary-interop", "rayon", @@ -9650,7 +9636,7 @@ dependencies = [ "auto_impl", "dyn-clone", "jsonrpsee-types 0.26.0", - "op-alloy-consensus 0.22.3", + "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", "op-revm", @@ -11758,13 +11744,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tips-core" version = "0.1.0" -source = "git+https://github.com/base/tips?rev=86b275c0fd63226c3fb85ac5512033f99b67d0f5#86b275c0fd63226c3fb85ac5512033f99b67d0f5" +source = "git+https://github.com/base/tips?rev=c08eaa4fe10c26de8911609b41ddab4918698325#c08eaa4fe10c26de8911609b41ddab4918698325" dependencies = [ "alloy-consensus", "alloy-primitives", "alloy-provider", "alloy-serde", - "op-alloy-consensus 0.20.0", + "op-alloy-consensus", "op-alloy-flz", "serde", "tracing", @@ -12774,7 +12760,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b3aec2f8..17a30a48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,7 +62,7 @@ base-reth-flashblocks = { path = "crates/flashblocks" } # 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 = "86b275c0fd63226c3fb85ac5512033f99b67d0f5", 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" } diff --git a/crates/flashblocks/src/processor.rs b/crates/flashblocks/src/processor.rs index 5d8cfeff..cc8f84e7 100644 --- a/crates/flashblocks/src/processor.rs +++ b/crates/flashblocks/src/processor.rs @@ -25,14 +25,10 @@ use reth::{ chainspec::{ChainSpecProvider, EthChainSpec}, providers::{BlockReaderIdExt, StateProviderFactory}, revm::{ - context::result::ResultAndState, - database::StateProviderDatabase, + DatabaseCommit, State, context::result::ResultAndState, database::StateProviderDatabase, db::CacheDB, - DatabaseCommit, - State, }, }; -use revm_database::states::bundle_state::BundleRetention; use reth_evm::{ConfigureEvm, Evm}; use reth_optimism_chainspec::OpHardforks; use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; @@ -302,10 +298,9 @@ where // 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, - }, + Some(pending_blocks) => { + CacheDB { cache: pending_blocks.get_db_cache(), db: state_provider_db } + } None => CacheDB::new(state_provider_db), }; @@ -317,10 +312,7 @@ where .with_bundle_update() .with_bundle_prestate(pending_blocks.get_bundle_state()) .build(), - None => State::builder() - .with_database(cache_db) - .with_bundle_update() - .build(), + None => State::builder().with_database(cache_db).with_bundle_update().build(), }; let mut state_overrides = diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs index 88e5ab7e..baa592d0 100644 --- a/crates/metering/src/tests/chain_state.rs +++ b/crates/metering/src/tests/chain_state.rs @@ -1,31 +1,28 @@ use alloy_consensus::Receipt; use alloy_eips::{BlockNumberOrTag, Encodable2718}; -use alloy_primitives::{Bytes, B256, U256}; +use alloy_primitives::{B256, Bytes, U256}; use alloy_provider::Provider; use alloy_rpc_types_eth::TransactionInput; -use alloy_sol_types::{sol, SolCall}; +use alloy_sol_types::{SolCall, sol}; use base_reth_flashblocks_rpc::rpc::FlashblocksAPI; -use base_reth_test_utils::harness::TestHarness; -use base_reth_test_utils::node::{default_launcher, BASE_CHAIN_ID}; -use eyre::{eyre, Result}; +use base_reth_test_utils::{flashblocks_harness::FlashblocksHarness, node::BASE_CHAIN_ID}; +use eyre::{Result, eyre}; use hex_literal::hex; use op_alloy_consensus::OpTxEnvelope; +use op_alloy_rpc_types::OpTransactionRequest; use reth::providers::HeaderProvider; use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; use reth_primitives::TransactionSigned; use reth_transaction_pool::test_utils::TransactionBuilder; use tips_core::types::Bundle; -use base_reth_test_utils::flashblocks::build_single_flashblock; - use super::utils::secret_from_hex; use crate::rpc::{MeteringApiImpl, MeteringApiServer}; -use op_alloy_rpc_types::OpTransactionRequest; #[tokio::test] async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { reth_tracing::init_test_tracing(); - let harness = TestHarness::new(default_launcher).await?; + let harness = FlashblocksHarness::new().await?; let provider = harness.provider(); let alice = &harness.accounts().alice; @@ -42,9 +39,7 @@ async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { .input(COUNTER_CREATION_BYTECODE.to_vec()) .into_eip1559(); let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); - harness - .build_block_from_transactions(vec![deploy_bytes]) - .await?; + harness.build_block_from_transactions(vec![deploy_bytes]).await?; let deploy_receipt = provider .get_transaction_receipt(deploy_envelope.tx_hash()) @@ -56,9 +51,7 @@ async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; // Mutate storage on-chain via setNumber (nonce 1) - let set_call = Counter::setNumberCall { - newNumber: U256::from(42u64), - }; + let set_call = Counter::setNumberCall { newNumber: U256::from(42u64) }; let set_signed = TransactionBuilder::default() .signer(alice_secret) .chain_id(BASE_CHAIN_ID) @@ -70,9 +63,7 @@ async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { .input(Bytes::from(set_call.abi_encode())) .into_eip1559(); let (_set_envelope, set_bytes) = envelope_from_signed(set_signed); - harness - .build_block_from_transactions(vec![set_bytes]) - .await?; + harness.build_block_from_transactions(vec![set_bytes]).await?; // Meter an increment call (nonce 2) after the storage change let increment_call = Counter::incrementCall {}; @@ -118,17 +109,12 @@ async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { .from(alice.address) .to(contract_address) .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))); - let raw_number = provider - .call(call_request) - .block(BlockNumberOrTag::Latest.into()) - .await?; + let raw_number = provider.call(call_request).block(BlockNumberOrTag::Latest.into()).await?; let decoded: U256 = Counter::numberCall::abi_decode_returns(raw_number.as_ref())?; assert_eq!(decoded, U256::from(42u64)); // Execute the increment on-chain to confirm the transaction is valid when mined - harness - .build_block_from_transactions(vec![increment_bytes]) - .await?; + harness.build_block_from_transactions(vec![increment_bytes]).await?; let number_after_increment = provider .call( OpTransactionRequest::default() @@ -148,7 +134,7 @@ async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { #[tokio::test] async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { reth_tracing::init_test_tracing(); - let harness = TestHarness::new(default_launcher).await?; + let harness = FlashblocksHarness::new().await?; let provider = harness.provider(); let alice = &harness.accounts().alice; @@ -165,9 +151,7 @@ async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { .input(COUNTER_CREATION_BYTECODE.to_vec()) .into_eip1559(); let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); - harness - .build_block_from_transactions(vec![deploy_bytes]) - .await?; + harness.build_block_from_transactions(vec![deploy_bytes]).await?; let contract_address = provider .get_transaction_receipt(deploy_envelope.tx_hash()) .await? @@ -183,9 +167,7 @@ async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { .ok_or_else(|| eyre!("missing header for block {}", latest_number))?; let pending_block_number = latest_header.number + 1; - let flash_call = Counter::setNumberCall { - newNumber: U256::from(99u64), - }; + let flash_call = Counter::setNumberCall { newNumber: U256::from(99u64) }; let flash_signed = TransactionBuilder::default() .signer(alice_secret) .chain_id(BASE_CHAIN_ID) @@ -204,16 +186,13 @@ async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { }); // Pending flashblock omits the beacon root, so metering will report the validation error. - let flashblock = build_single_flashblock( + let flashblock = harness.build_flashblock( pending_block_number, latest_header.hash(), B256::ZERO, latest_header.timestamp + 2, latest_header.gas_limit, - vec![( - flash_bytes.clone(), - Some((flash_envelope.tx_hash(), receipt.clone())), - )], + vec![(flash_bytes.clone(), Some((flash_envelope.tx_hash(), receipt.clone())))], ); harness.send_flashblock(flashblock).await?; @@ -241,10 +220,7 @@ async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { let pending_blocks = harness.flashblocks_state().get_pending_blocks(); assert!(pending_blocks.is_some(), "expected flashblock to populate pending state"); - assert_eq!( - pending_blocks.as_ref().unwrap().latest_flashblock_index(), - 0 - ); + assert_eq!(pending_blocks.as_ref().unwrap().latest_flashblock_index(), 0); // Pending state should reflect the storage change even though the simulation failed. let number_call = Counter::numberCall {}; @@ -271,16 +247,16 @@ sol! { } } -const COUNTER_CREATION_BYTECODE: &[u8] = &hex!("6080604052348015600e575f5ffd5b506101e18061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80633fb5c1cb146100435780638381f58a1461005f578063d09de08a1461007d575b5f5ffd5b61005d600480360381019061005891906100e4565b610087565b005b610067610090565b604051610074919061011e565b60405180910390f35b610085610095565b005b805f8190555050565b5f5481565b5f5f8154809291906100a690610164565b9190505550565b5f5ffd5b5f819050919050565b6100c3816100b1565b81146100cd575f5ffd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036101a05761019f610137565b5b60018201905091905056fea26469706673582212204b710430bf5e9541dd320fc4eece1bf270f8b7d4835bba28f79ff7bd29904a2964736f6c634300081e0033"); +const COUNTER_CREATION_BYTECODE: &[u8] = &hex!( + "6080604052348015600e575f5ffd5b506101e18061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80633fb5c1cb146100435780638381f58a1461005f578063d09de08a1461007d575b5f5ffd5b61005d600480360381019061005891906100e4565b610087565b005b610067610090565b604051610074919061011e565b60405180910390f35b610085610095565b005b805f8190555050565b5f5481565b5f5f8154809291906100a690610164565b9190505550565b5f5ffd5b5f819050919050565b6100c3816100b1565b81146100cd575f5ffd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036101a05761019f610137565b5b60018201905091905056fea26469706673582212204b710430bf5e9541dd320fc4eece1bf270f8b7d4835bba28f79ff7bd29904a2964736f6c634300081e0033" +); const GWEI: u128 = 1_000_000_000; const DEPLOY_GAS_LIMIT: u64 = 1_000_000; const CALL_GAS_LIMIT: u64 = 150_000; fn envelope_from_signed(tx: TransactionSigned) -> (OpTxEnvelope, Bytes) { let op_signed = OpTransactionSigned::Eip1559( - tx.as_eip1559() - .expect("transaction should be EIP-1559") - .clone(), + tx.as_eip1559().expect("transaction should be EIP-1559").clone(), ); let envelope = OpTxEnvelope::from(op_signed); let bytes = Bytes::from(envelope.encoded_2718()); @@ -290,7 +266,7 @@ fn envelope_from_signed(tx: TransactionSigned) -> (OpTxEnvelope, Bytes) { #[tokio::test] async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { reth_tracing::init_test_tracing(); - let harness = TestHarness::new(default_launcher).await?; + let harness = FlashblocksHarness::new().await?; let alice = &harness.accounts().alice; let alice_secret = secret_from_hex(alice.private_key); let mut nonce = 0u64; @@ -306,9 +282,7 @@ async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { .into_eip1559(); let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); - harness - .build_block_from_transactions(vec![deploy_bytes]) - .await?; + harness.build_block_from_transactions(vec![deploy_bytes]).await?; nonce += 1; let provider = harness.provider(); @@ -321,9 +295,7 @@ async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { .contract_address .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; - let set_call = Counter::setNumberCall { - newNumber: U256::from(42u64), - }; + let set_call = Counter::setNumberCall { newNumber: U256::from(42u64) }; let set_signed = TransactionBuilder::default() .signer(alice_secret) .chain_id(BASE_CHAIN_ID) @@ -335,9 +307,7 @@ async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { .input(Bytes::from(set_call.abi_encode())) .into_eip1559(); let (_set_envelope, set_bytes) = envelope_from_signed(set_signed); - harness - .build_block_from_transactions(vec![set_bytes]) - .await?; + harness.build_block_from_transactions(vec![set_bytes]).await?; nonce += 1; let increment_call = Counter::incrementCall {}; @@ -352,15 +322,11 @@ async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { .input(Bytes::from(increment_call.abi_encode())) .into_eip1559(); let (_increment_envelope, increment_bytes) = envelope_from_signed(increment_signed); - harness - .build_block_from_transactions(vec![increment_bytes]) - .await?; + harness.build_block_from_transactions(vec![increment_bytes]).await?; nonce += 1; - let storage_value = provider - .get_storage_at(contract_address, U256::ZERO) - .await?; + let storage_value = provider.get_storage_at(contract_address, U256::ZERO).await?; assert_eq!(storage_value, U256::from(43u64)); let number_call = Counter::numberCall {}; @@ -368,10 +334,7 @@ async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { .from(alice.address) .to(contract_address) .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))); - let raw_number = provider - .call(call_request) - .block(BlockNumberOrTag::Latest.into()) - .await?; + let raw_number = provider.call(call_request).block(BlockNumberOrTag::Latest.into()).await?; let decoded: U256 = Counter::numberCall::abi_decode_returns(raw_number.as_ref())?; assert_eq!(decoded, U256::from(43u64)); diff --git a/crates/rpc/src/base/flashblock_trie_cache.rs b/crates/rpc/src/base/flashblock_trie_cache.rs index 4e11c72a..f1322b40 100644 --- a/crates/rpc/src/base/flashblock_trie_cache.rs +++ b/crates/rpc/src/base/flashblock_trie_cache.rs @@ -4,7 +4,7 @@ use alloy_primitives::B256; use arc_swap::ArcSwap; use eyre::Result as EyreResult; use reth_provider::StateProvider; -use reth_trie_common::{updates::TrieUpdates, HashedPostState}; +use reth_trie_common::{HashedPostState, updates::TrieUpdates}; use crate::FlashblocksState; @@ -60,7 +60,8 @@ impl FlashblockTrieCache { flashblocks_state: &FlashblocksState, canonical_state_provider: &dyn StateProvider, ) -> EyreResult { - if let Some(ref cached) = *self.cache.load() { + let cached_entry = self.cache.load(); + if let Some(cached) = cached_entry.as_ref() { if cached.block_hash == block_hash && cached.flashblock_index == flashblock_index { return Ok(cached.trie_data.clone()); } diff --git a/crates/rpc/src/base/meter.rs b/crates/rpc/src/base/meter.rs index 624f99bf..33c68047 100644 --- a/crates/rpc/src/base/meter.rs +++ b/crates/rpc/src/base/meter.rs @@ -84,10 +84,7 @@ where // Apply flashblocks read cache if available let cache_db = if let Some(ref flashblocks) = flashblocks_state { - CacheDB { - cache: flashblocks.cache.clone(), - db: state_db, - } + CacheDB { cache: flashblocks.cache.clone(), db: state_db } } else { CacheDB::new(state_db) }; @@ -100,10 +97,7 @@ where .with_bundle_prestate(flashblocks.bundle_state.clone()) .build() } else { - State::builder() - .with_database(cache_db) - .with_bundle_update() - .build() + State::builder().with_database(cache_db).with_bundle_update().build() }; // Set up next block attributes diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 02bf741d..5a79cb90 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use alloy_consensus::{Header, Sealed}; use alloy_primitives::U256; use base_reth_flashblocks::FlashblocksAPI; @@ -6,7 +8,6 @@ use reth::providers::BlockReaderIdExt; use reth_optimism_chainspec::OpChainSpec; use reth_primitives_traits::SealedHeader; use reth_provider::{ChainSpecProvider, StateProviderFactory}; -use std::sync::Arc; use tips_core::types::{Bundle, MeterBundleResponse, ParsedBundle}; use tracing::{error, info}; @@ -31,11 +32,7 @@ where { /// Creates a new instance of MeteringApi pub fn new(provider: Provider, flashblocks_state: Arc) -> Self { - Self { - provider, - flashblocks_state, - trie_cache: FlashblockTrieCache::new(), - } + Self { provider, flashblocks_state, trie_cache: FlashblockTrieCache::new() } } } @@ -118,10 +115,8 @@ where })?; // Get state provider for the canonical block - let state_provider = self - .provider - .state_by_block_number_or_tag(canonical_block_number) - .map_err(|e| { + let state_provider = + self.provider.state_by_block_number_or_tag(canonical_block_number).map_err(|e| { error!(error = %e, "Failed to get state provider"); jsonrpsee::types::ErrorObjectOwned::owned( jsonrpsee::types::ErrorCode::InternalError.code(), @@ -137,9 +132,7 @@ where }); // Get the flashblock index if we have pending flashblocks - let state_flashblock_index = pending_blocks - .as_ref() - .map(|pb| pb.latest_flashblock_index()); + let state_flashblock_index = pending_blocks.as_ref().map(|pb| pb.latest_flashblock_index()); // Ensure the flashblock trie is cached for reuse across bundle simulations let cached_trie = if let Some(ref fb_state) = flashblocks_state { @@ -190,7 +183,6 @@ where num_transactions = result.results.len(), total_gas_used = result.total_gas_used, total_time_us = result.total_time_us, - state_root_time_us = result.state_root_time_us, state_block_number = header.number, flashblock_index = flashblock_index, "Bundle metering completed successfully" @@ -206,9 +198,7 @@ where state_block_number: header.number, state_flashblock_index, total_gas_used: result.total_gas_used, - // TODO: Rename to total_time_us in tips-core. total_execution_time_us: result.total_time_us, - state_root_time_us: result.state_root_time_us, }) } } diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 7098f9fd..13ef753c 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -143,8 +143,6 @@ fn meter_bundle_empty_transactions() -> eyre::Result<()> { &harness.header, None, None, - None, - None, )?; assert!(output.results.is_empty()); @@ -194,8 +192,6 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> { &harness.header, None, None, - None, - None, )?; assert_eq!(output.results.len(), 1); @@ -368,10 +364,7 @@ fn meter_bundle_state_root_time_invariant() -> eyre::Result<()> { ); // State root time should be non-zero - assert!( - output.state_root_time_us > 0, - "state_root_time_us should be greater than zero" - ); + assert!(output.state_root_time_us > 0, "state_root_time_us should be greater than zero"); Ok(()) } diff --git a/crates/test-utils/README.md b/crates/test-utils/README.md index 6d020e9b..32842b2c 100644 --- a/crates/test-utils/README.md +++ b/crates/test-utils/README.md @@ -15,7 +15,7 @@ This crate provides reusable testing utilities for integration tests across the ## Quick Start ```rust -use base_reth_test_utils::TestHarness; +use base_reth_test_utils::harness::TestHarness; #[tokio::test] async fn test_example() -> eyre::Result<()> { @@ -44,6 +44,7 @@ The framework follows a three-layer architecture: │ - Coordinates node + engine │ │ - Builds blocks from transactions │ │ - Manages test accounts │ +│ - Manages flashblocks │ └─────────────────────────────────────┘ │ │ ┌──────┘ └──────┐ @@ -64,10 +65,10 @@ The framework follows a three-layer architecture: ### 1. TestHarness -The main entry point for integration tests. Combines node, engine, and accounts into a single interface. +The main entry point for integration tests that only need canonical chain control. Combines node, engine, and accounts into a single interface. ```rust -use base_reth_test_utils::TestHarness; +use base_reth_test_utils::harness::TestHarness; use alloy_primitives::Bytes; #[tokio::test] @@ -89,24 +90,18 @@ async fn test_harness() -> eyre::Result<()> { let txs: Vec = vec![/* signed transaction bytes */]; harness.build_block_from_transactions(txs).await?; - // Build block from flashblocks - harness.build_block_from_flashblocks(&flashblocks).await?; - - // Send flashblocks for pending state testing - harness.send_flashblock(flashblock).await?; - Ok(()) } ``` +> Need pending-state testing? Use `FlashblocksHarness` (see Flashblocks section below) to gain `send_flashblock` helpers. + **Key Methods:** - `new()` - Create new harness with node, engine, and accounts - `provider()` - Get Alloy RootProvider for RPC calls - `accounts()` - Access test accounts - `advance_chain(n)` - Build N empty blocks - `build_block_from_transactions(txs)` - Build block with specific transactions (auto-prepends the L1 block info deposit) -- `send_flashblock(fb)` - Send a single flashblock to the node for pending state processing -- `send_flashblocks(iter)` - Convenience helper that sends multiple flashblocks sequentially **Block Building Process:** 1. Fetches latest block header from provider (no local state tracking) @@ -122,32 +117,35 @@ async fn test_harness() -> eyre::Result<()> { In-process Optimism node with Base Sepolia configuration. ```rust -use base_reth_test_utils::LocalNode; +use base_reth_test_utils::node::LocalNode; #[tokio::test] async fn test_node() -> eyre::Result<()> { - let node = LocalNode::new().await?; + let node = LocalNode::new(default_launcher).await?; - // Get provider let provider = node.provider()?; - - // Get Engine API let engine = node.engine_api()?; - // Send flashblocks - node.send_flashblock(flashblock).await?; - Ok(()) } ``` -**Features:** +**Features (base):** - Base Sepolia chain configuration - Disabled P2P discovery (isolated testing) - Random unused ports (parallel test safety) - HTTP RPC server at `node.http_api_addr` - Engine API IPC at `node.engine_ipc_path` -- Flashblocks-canon ExEx integration + +For flashblocks-enabled nodes, use `FlashblocksLocalNode`: + +```rust +use base_reth_test_utils::node::FlashblocksLocalNode; + +let node = FlashblocksLocalNode::new().await?; +let pending_state = node.flashblocks_state(); +node.send_flashblock(flashblock).await?; +``` **Note:** Most tests should use `TestHarness` instead of `LocalNode` directly. @@ -156,7 +154,7 @@ async fn test_node() -> eyre::Result<()> { Type-safe Engine API client wrapping raw CL operations. ```rust -use base_reth_test_utils::EngineApi; +use base_reth_test_utils::engine::EngineApi; use alloy_primitives::B256; use op_alloy_rpc_types_engine::OpPayloadAttributes; @@ -181,7 +179,8 @@ let status = engine.new_payload(payload, vec![], parent_root, requests).await?; Hardcoded test accounts with deterministic addresses (Anvil-compatible). ```rust -use base_reth_test_utils::TestAccounts; +use base_reth_test_utils::accounts::TestAccounts; +use base_reth_test_utils::harness::TestHarness; let accounts = TestAccounts::new(); @@ -209,34 +208,40 @@ Each account includes: ### 5. Flashblocks Support -Test flashblocks delivery without WebSocket connections. +Use `FlashblocksHarness` when you need `send_flashblock` and access to the in-memory pending state. ```rust -use base_reth_test_utils::{FlashblocksContext, FlashblockBuilder}; +use base_reth_test_utils::flashblocks_harness::FlashblocksHarness; #[tokio::test] async fn test_flashblocks() -> eyre::Result<()> { - let (fb_ctx, receiver) = FlashblocksContext::new(); + let harness = FlashblocksHarness::new().await?; - // Create base flashblock - let flashblock = FlashblockBuilder::new(1, 0) - .as_base(B256::ZERO, 1000) - .with_transaction(tx_bytes, tx_hash, 21000) - .with_balance(address, U256::from(1000)) - .build(); + harness.send_flashblock(flashblock).await?; - fb_ctx.send_flashblock(flashblock).await?; + let pending = harness.flashblocks_state(); + // assertions... Ok(()) } ``` -**Via TestHarness:** +Need to craft a flashblock (including intentionally malformed payloads)? Use `build_flashblock`: + ```rust -let harness = TestHarness::new().await?; +let flashblock = harness.build_flashblock( + next_block_number, + parent_hash, + B256::ZERO, // force missing beacon root to test validation + timestamp, + gas_limit, + vec![(tx_bytes, Some((tx_hash, receipt)))], +); harness.send_flashblock(flashblock).await?; ``` +`FlashblocksHarness` derefs to the base `TestHarness`, so you can keep using methods like `provider()`, `build_block_from_transactions`, etc. Test flashblocks delivery without WebSocket connections by constructing payloads and sending them through `FlashblocksHarness` (or the lower-level `FlashblocksLocalNode`). + ## Configuration Constants Key constants defined in `harness.rs`: @@ -257,8 +262,8 @@ test-utils/ │ ├── accounts.rs # Test account definitions │ ├── node.rs # LocalNode (EL wrapper) │ ├── engine.rs # EngineApi (CL wrapper) -│ ├── harness.rs # TestHarness (orchestration) -│ └── flashblocks.rs # Flashblocks support +│ ├── harness.rs # TestHarness (orchestration) +│ └── flashblocks_harness.rs # FlashblocksHarness + helpers ├── assets/ │ └── genesis.json # Base Sepolia genesis └── Cargo.toml @@ -276,12 +281,10 @@ base-reth-test-utils.workspace = true Import in tests: ```rust -use base_reth_test_utils::TestHarness; +use base_reth_test_utils::harness::TestHarness; #[tokio::test] async fn my_test() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let harness = TestHarness::new().await?; // Your test logic @@ -318,6 +321,7 @@ cargo test -p base-reth-test-utils test_harness_setup - Snapshot/restore functionality - Multi-node network simulation - Performance benchmarking utilities +- Helper builder for Flashblocks ## References diff --git a/crates/test-utils/src/fixtures.rs b/crates/test-utils/src/fixtures.rs index b4fc3a9d..7fd3a775 100644 --- a/crates/test-utils/src/fixtures.rs +++ b/crates/test-utils/src/fixtures.rs @@ -1,14 +1,13 @@ use std::sync::Arc; -use alloy_primitives::{hex::FromHex, B256}; +use alloy_primitives::{B256, hex::FromHex}; use reth::api::{NodeTypes, NodeTypesWithDBAdapter}; use reth_db::{ - init_db, - mdbx::{DatabaseArguments, MaxReadTransactionDuration, KILOBYTE, MEGABYTE}, - test_utils::{create_test_static_files_dir, tempdir_path, TempDatabase, ERROR_DB_CREATION}, - ClientVersion, DatabaseEnv, + ClientVersion, DatabaseEnv, init_db, + mdbx::{DatabaseArguments, KILOBYTE, MEGABYTE, MaxReadTransactionDuration}, + test_utils::{ERROR_DB_CREATION, TempDatabase, create_test_static_files_dir, tempdir_path}, }; -use reth_provider::{providers::StaticFileProvider, ProviderFactory}; +use reth_provider::{ProviderFactory, providers::StaticFileProvider}; pub fn secret_from_hex(hex_key: &str) -> B256 { B256::from_hex(hex_key).expect("32-byte private key") diff --git a/crates/test-utils/src/flashblocks.rs b/crates/test-utils/src/flashblocks.rs deleted file mode 100644 index 199a0022..00000000 --- a/crates/test-utils/src/flashblocks.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! Flashblock testing utilities - -use alloy_consensus::Receipt; -use alloy_primitives::{b256, bytes, Address, Bytes, B256, U256}; -use alloy_rpc_types_engine::PayloadId; -use base_reth_flashblocks_rpc::subscription::{Flashblock, Metadata}; -use op_alloy_consensus::OpDepositReceipt; -use reth_optimism_primitives::OpReceipt; -use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; -use std::collections::HashMap; - -const FLASHBLOCK_PAYLOAD_ID: [u8; 8] = [0; 8]; - -// Pre-captured deposit transaction and hash used by flashblocks tests for the L1 block info deposit. -// Values match `BLOCK_INFO_TXN` and `BLOCK_INFO_TXN_HASH` from `crates/flashblocks-rpc/src/tests/mod.rs`. -const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); -const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = - b256!("0xba56c8b0deb460ff070f8fca8e2ee01e51a3db27841cc862fdd94cc1a47662b6"); - -/// Builds a single flashblock for testing purposes. -/// -/// This utility creates a base flashblock (index 0) with the required L1 block info deposit -/// transaction and any additional transactions provided. -/// -/// # Arguments -/// -/// * `block_number` - The block number for this flashblock -/// * `parent_hash` - Hash of the parent block -/// * `parent_beacon_block_root` - Parent beacon block root -/// * `timestamp` - Block timestamp -/// * `gas_limit` - Gas limit for the block -/// * `transactions` - Vector of (transaction bytes, optional (hash, receipt)) tuples -/// -/// # Returns -/// -/// A `Flashblock` configured for testing with the provided parameters -pub fn build_single_flashblock( - block_number: u64, - parent_hash: B256, - parent_beacon_block_root: B256, - timestamp: u64, - gas_limit: u64, - transactions: Vec<(Bytes, Option<(B256, OpReceipt)>)>, -) -> Flashblock { - let base = ExecutionPayloadBaseV1 { - parent_beacon_block_root, - parent_hash, - fee_recipient: Address::ZERO, - prev_randao: B256::ZERO, - block_number, - gas_limit, - timestamp, - extra_data: Bytes::new(), - base_fee_per_gas: U256::from(1), - }; - - let mut flashblock_txs = vec![BLOCK_INFO_DEPOSIT_TX.clone()]; - let mut receipts = HashMap::default(); - receipts.insert( - BLOCK_INFO_DEPOSIT_TX_HASH, - OpReceipt::Deposit(OpDepositReceipt { - inner: Receipt { - status: true.into(), - cumulative_gas_used: 10_000, - logs: vec![], - }, - deposit_nonce: Some(4_012_991u64), - deposit_receipt_version: None, - }), - ); - - for (tx_bytes, maybe_receipt) in transactions { - if let Some((hash, receipt)) = maybe_receipt { - receipts.insert(hash, receipt); - } - flashblock_txs.push(tx_bytes); - } - - Flashblock { - payload_id: PayloadId::new(FLASHBLOCK_PAYLOAD_ID), - index: 0, - base: Some(base), - diff: ExecutionPayloadFlashblockDeltaV1 { - transactions: flashblock_txs, - ..Default::default() - }, - metadata: Metadata { - receipts, - new_account_balances: Default::default(), - block_number, - }, - } -} diff --git a/crates/test-utils/src/flashblocks_harness.rs b/crates/test-utils/src/flashblocks_harness.rs index a1acaa2c..68fcf7a4 100644 --- a/crates/test-utils/src/flashblocks_harness.rs +++ b/crates/test-utils/src/flashblocks_harness.rs @@ -1,44 +1,48 @@ -//! Flashblocks-aware wrapper around [`TestHarness`] that wires in the custom RPC modules. +use std::{collections::HashMap, ops::Deref, sync::Arc}; -use std::sync::Arc; - -use base_flashtypes::Flashblock; -use derive_more::Deref; +use alloy_consensus::Receipt; +use alloy_primitives::{Address, B256, Bytes, U256, b256, bytes}; +use alloy_rpc_types_engine::PayloadId; +use base_reth_flashblocks_rpc::subscription::{Flashblock, Metadata}; use eyre::Result; use futures_util::Future; +use op_alloy_consensus::OpDepositReceipt; use reth::builder::NodeHandle; use reth_e2e_test_utils::Adapter; use reth_optimism_node::OpNode; +use reth_optimism_primitives::OpReceipt; +use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; use crate::{ harness::TestHarness, - init_silenced_tracing, node::{ FlashblocksLocalNode, FlashblocksParts, LocalFlashblocksState, OpAddOns, OpBuilder, default_launcher, }, + tracing::init_silenced_tracing, }; -/// Helper that exposes [`TestHarness`] conveniences plus Flashblocks helpers. -#[derive(Debug, Deref)] +const FLASHBLOCK_PAYLOAD_ID: [u8; 8] = [0; 8]; +const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!( + "0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000" +); +const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = + b256!("0xba56c8b0deb460ff070f8fca8e2ee01e51a3db27841cc862fdd94cc1a47662b6"); + pub struct FlashblocksHarness { - #[deref] inner: TestHarness, parts: FlashblocksParts, } impl FlashblocksHarness { - /// Launch a flashblocks-enabled harness with the default launcher. pub async fn new() -> Result { Self::with_launcher(default_launcher).await } - /// Launch the harness configured for manual canonical progression. pub async fn manual_canonical() -> Result { Self::manual_canonical_with_launcher(default_launcher).await } - /// Launch the harness using a custom node launcher. pub async fn with_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -49,7 +53,6 @@ impl FlashblocksHarness { Self::from_flashblocks_node(flash_node).await } - /// Launch the harness with a custom launcher while disabling automatic canonical processing. pub async fn manual_canonical_with_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -60,17 +63,14 @@ impl FlashblocksHarness { Self::from_flashblocks_node(flash_node).await } - /// Get a handle to the in-memory Flashblocks state backing the harness. pub fn flashblocks_state(&self) -> Arc { self.parts.state() } - /// Send a single flashblock through the harness. pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { self.parts.send(flashblock).await } - /// Send a batch of flashblocks sequentially, awaiting each confirmation. pub async fn send_flashblocks(&self, flashblocks: I) -> Result<()> where I: IntoIterator, @@ -81,7 +81,61 @@ impl FlashblocksHarness { Ok(()) } - /// Consume the flashblocks harness and return the underlying [`TestHarness`]. + /// Builds a flashblock payload for testing. Callers can intentionally pass invalid + /// values (for example a zeroed beacon root) to assert how downstream components + /// react to malformed flashblocks. + #[allow(clippy::too_many_arguments)] + pub fn build_flashblock( + &self, + block_number: u64, + parent_hash: B256, + parent_beacon_block_root: B256, + timestamp: u64, + gas_limit: u64, + transactions: Vec<(Bytes, Option<(B256, OpReceipt)>)>, + ) -> Flashblock { + let base = ExecutionPayloadBaseV1 { + parent_beacon_block_root, + parent_hash, + fee_recipient: Address::ZERO, + prev_randao: B256::ZERO, + block_number, + gas_limit, + timestamp, + extra_data: Bytes::new(), + base_fee_per_gas: U256::from(1), + }; + + let mut flashblock_txs = vec![BLOCK_INFO_DEPOSIT_TX.clone()]; + let mut receipts = HashMap::default(); + receipts.insert( + BLOCK_INFO_DEPOSIT_TX_HASH, + OpReceipt::Deposit(OpDepositReceipt { + inner: Receipt { status: true.into(), cumulative_gas_used: 10_000, logs: vec![] }, + deposit_nonce: Some(4_012_991u64), + deposit_receipt_version: None, + }), + ); + + for (tx_bytes, maybe_receipt) in transactions { + if let Some((hash, receipt)) = maybe_receipt { + receipts.insert(hash, receipt); + } + flashblock_txs.push(tx_bytes); + } + + Flashblock { + payload_id: PayloadId::new(FLASHBLOCK_PAYLOAD_ID), + index: 0, + base: Some(base), + diff: ExecutionPayloadFlashblockDeltaV1 { + transactions: flashblock_txs, + ..Default::default() + }, + metadata: Metadata { receipts, new_account_balances: Default::default(), block_number }, + } + } + pub fn into_inner(self) -> TestHarness { self.inner } @@ -92,3 +146,11 @@ impl FlashblocksHarness { Ok(Self { inner, parts }) } } + +impl Deref for FlashblocksHarness { + type Target = TestHarness; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} diff --git a/crates/test-utils/src/harness.rs b/crates/test-utils/src/harness.rs index d70a7b9a..be656c82 100644 --- a/crates/test-utils/src/harness.rs +++ b/crates/test-utils/src/harness.rs @@ -1,31 +1,41 @@ -//! Unified test harness combining node, engine API, and flashblocks functionality +//! Unified test harness combining node and engine helpers, plus optional flashblocks adapter. -use crate::accounts::TestAccounts; -use crate::engine::{EngineApi, IpcEngine}; -use crate::node::{LocalFlashblocksState, LocalNode, LocalNodeProvider, OpAddOns, OpBuilder}; -use alloy_eips::eip7685::Requests; -use alloy_primitives::{bytes, Bytes, B256}; +use std::time::Duration; + +use alloy_eips::{BlockHashOrNumber, eip7685::Requests}; +use alloy_primitives::{B64, B256, Bytes, bytes}; use alloy_provider::{Provider, RootProvider}; use alloy_rpc_types::BlockNumberOrTag; use alloy_rpc_types_engine::PayloadAttributes; -use base_reth_flashblocks_rpc::subscription::Flashblock; -use eyre::{eyre, Result}; +use eyre::{Result, eyre}; use futures_util::Future; use op_alloy_network::Optimism; use op_alloy_rpc_types_engine::OpPayloadAttributes; -use reth::builder::NodeHandle; +use reth::{ + builder::NodeHandle, + providers::{BlockNumReader, BlockReader, ChainSpecProvider}, +}; use reth_e2e_test_utils::Adapter; use reth_optimism_node::OpNode; -use std::sync::Arc; -use std::time::Duration; +use reth_optimism_primitives::OpBlock; +use reth_primitives_traits::{Block as BlockT, RecoveredBlock}; use tokio::time::sleep; +use crate::{ + accounts::TestAccounts, + engine::{EngineApi, IpcEngine}, + node::{LocalNode, LocalNodeProvider, OpAddOns, OpBuilder, default_launcher}, + tracing::init_silenced_tracing, +}; + const BLOCK_TIME_SECONDS: u64 = 2; const GAS_LIMIT: u64 = 200_000_000; const NODE_STARTUP_DELAY_MS: u64 = 500; const BLOCK_BUILD_DELAY_MS: u64 = 100; -// Pre-captured L1 block info deposit transaction required by the Optimism EVM. -const L1_BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!("0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000"); +// Pre-captured L1 block info deposit transaction required by OP Stack. +const L1_BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!( + "0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000" +); pub struct TestHarness { node: LocalNode, @@ -34,28 +44,31 @@ pub struct TestHarness { } impl TestHarness { - pub async fn new(launcher: L) -> Result + pub async fn new() -> Result { + Self::with_launcher(default_launcher).await + } + + pub async fn with_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, LRet: Future, OpAddOns>>>, { + init_silenced_tracing(); let node = LocalNode::new(launcher).await?; + Self::from_node(node).await + } + + pub(crate) async fn from_node(node: LocalNode) -> Result { let engine = node.engine_api()?; let accounts = TestAccounts::new(); sleep(Duration::from_millis(NODE_STARTUP_DELAY_MS)).await; - Ok(Self { - node, - engine, - accounts, - }) + Ok(Self { node, engine, accounts }) } pub fn provider(&self) -> RootProvider { - self.node - .provider() - .expect("provider should always be available after node initialization") + self.node.provider().expect("provider should always be available after node initialization") } pub fn accounts(&self) -> &TestAccounts { @@ -66,20 +79,13 @@ impl TestHarness { self.node.blockchain_provider() } - pub fn flashblocks_state(&self) -> Arc { - self.node.flashblocks_state() - } - pub fn rpc_url(&self) -> String { format!("http://{}", self.node.http_api_addr) } pub async fn build_block_from_transactions(&self, mut transactions: Vec) -> Result<()> { // Ensure the block always starts with the required L1 block info deposit. - if !transactions - .first() - .is_some_and(|tx| tx == &L1_BLOCK_INFO_DEPOSIT_TX) - { + if !transactions.first().is_some_and(|tx| tx == &L1_BLOCK_INFO_DEPOSIT_TX) { transactions.insert(0, L1_BLOCK_INFO_DEPOSIT_TX.clone()); } @@ -90,12 +96,16 @@ impl TestHarness { .ok_or_else(|| eyre!("No genesis block found"))?; let parent_hash = latest_block.header.hash; - let parent_beacon_block_root = latest_block - .header - .parent_beacon_block_root - .unwrap_or(B256::ZERO); + let parent_beacon_block_root = + latest_block.header.parent_beacon_block_root.unwrap_or(B256::ZERO); let next_timestamp = latest_block.header.timestamp + BLOCK_TIME_SECONDS; + let min_base_fee = latest_block.header.base_fee_per_gas.unwrap_or_default(); + let chain_spec = self.node.blockchain_provider().chain_spec(); + let base_fee_params = chain_spec.base_fee_params_at_timestamp(next_timestamp); + let eip_1559_params = ((base_fee_params.max_change_denominator as u64) << 32) + | (base_fee_params.elasticity_multiplier as u64); + let payload_attributes = OpPayloadAttributes { payload_attributes: PayloadAttributes { timestamp: next_timestamp, @@ -106,6 +116,8 @@ impl TestHarness { transactions: Some(transactions), gas_limit: Some(GAS_LIMIT), no_tx_pool: Some(true), + min_base_fee: Some(min_base_fee), + eip_1559_params: Some(B64::from(eip_1559_params)), ..Default::default() }; @@ -146,47 +158,38 @@ impl TestHarness { .latest_valid_hash .ok_or_else(|| eyre!("Payload status missing latest_valid_hash"))?; - self.engine - .update_forkchoice(parent_hash, new_block_hash, None) - .await?; + self.engine.update_forkchoice(parent_hash, new_block_hash, None).await?; Ok(()) } - pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { - self.node.send_flashblock(flashblock).await - } - - pub async fn send_flashblocks(&self, flashblocks: I) -> Result<()> - where - I: IntoIterator, - { - for flashblock in flashblocks { - self.send_flashblock(flashblock).await?; - } - Ok(()) - } - pub async fn advance_chain(&self, n: u64) -> Result<()> { for _ in 0..n { self.build_block_from_transactions(vec![]).await?; } Ok(()) } + + pub fn latest_block(&self) -> RecoveredBlock { + let provider = self.blockchain_provider(); + let best_number = provider.best_block_number().expect("able to read best block number"); + let block = provider + .block(BlockHashOrNumber::Number(best_number)) + .expect("able to load canonical block") + .expect("canonical block exists"); + BlockT::try_into_recovered(block).expect("able to recover canonical block") + } } #[cfg(test)] mod tests { - use crate::node::default_launcher; - - use super::*; use alloy_primitives::U256; use alloy_provider::Provider; + use super::*; #[tokio::test] async fn test_harness_setup() -> Result<()> { - reth_tracing::init_test_tracing(); - let harness = TestHarness::new(default_launcher).await?; + let harness = TestHarness::new().await?; assert_eq!(harness.accounts().alice.name, "Alice"); assert_eq!(harness.accounts().bob.name, "Bob"); @@ -195,9 +198,7 @@ mod tests { let chain_id = provider.get_chain_id().await?; assert_eq!(chain_id, crate::node::BASE_CHAIN_ID); - let alice_balance = provider - .get_balance(harness.accounts().alice.address) - .await?; + let alice_balance = provider.get_balance(harness.accounts().alice.address).await?; assert!(alice_balance > U256::ZERO); let block_number = provider.get_block_number().await?; diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 71a2eb28..2c6b290d 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -1,39 +1,6 @@ -#![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 accounts; -pub use accounts::{ALICE, Account, BOB, CHARLIE, DEPLOYER, TestAccount, TestAccounts}; - -mod constants; -pub use constants::{ - BASE_CHAIN_ID, BLOCK_BUILD_DELAY_MS, BLOCK_TIME_SECONDS, DEFAULT_JWT_SECRET, GAS_LIMIT, - L1_BLOCK_INFO_DEPOSIT_TX, L1_BLOCK_INFO_DEPOSIT_TX_HASH, NODE_STARTUP_DELAY_MS, NamedChain, -}; - -mod contracts; -pub use contracts::{DoubleCounter, MockERC20, TransparentUpgradeableProxy}; - -mod engine; -pub use engine::{EngineAddress, EngineApi, EngineProtocol, HttpEngine, IpcEngine}; - -mod fixtures; -pub use fixtures::{create_provider_factory, load_genesis}; - -pub mod flashblocks; - -mod flashblocks_harness; -pub use flashblocks_harness::FlashblocksHarness; - -mod harness; -pub use harness::TestHarness; - -mod node; -pub use node::{ - FlashblocksLocalNode, FlashblocksParts, LocalFlashblocksState, LocalNode, LocalNodeProvider, - OpAddOns, OpBuilder, OpComponentsBuilder, OpTypes, default_launcher, -}; - -mod tracing; -pub use tracing::init_silenced_tracing; +pub mod accounts; +pub mod engine; +pub mod flashblocks_harness; +pub mod harness; +pub mod node; +pub mod tracing; diff --git a/crates/test-utils/src/node.rs b/crates/test-utils/src/node.rs index cd713b8c..5a731bfd 100644 --- a/crates/test-utils/src/node.rs +++ b/crates/test-utils/src/node.rs @@ -1,35 +1,51 @@ //! Local node setup with Base Sepolia chainspec -use crate::engine::EngineApi; +use std::{ + any::Any, + net::SocketAddr, + sync::{Arc, Mutex}, +}; + use alloy_genesis::Genesis; use alloy_provider::RootProvider; use alloy_rpc_client::RpcClient; -use base_reth_flashblocks_rpc::rpc::{EthApiExt, EthApiOverrideServer}; -use base_reth_flashblocks_rpc::state::FlashblocksState; -use base_reth_flashblocks_rpc::subscription::{Flashblock, FlashblocksReceiver}; +use base_reth_flashblocks_rpc::{ + rpc::{EthApiExt, EthApiOverrideServer}, + state::FlashblocksState, + subscription::{Flashblock, FlashblocksReceiver}, +}; use eyre::Result; use futures_util::Future; use once_cell::sync::OnceCell; use op_alloy_network::Optimism; -use reth::api::{FullNodeTypesAdapter, NodeTypesWithDBAdapter}; -use reth::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}; -use reth::builder::{ - Node, NodeBuilder, NodeBuilderWithComponents, NodeConfig, NodeHandle, WithLaunchContext, +use reth::{ + api::{FullNodeTypesAdapter, NodeTypesWithDBAdapter}, + args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}, + builder::{ + Node, NodeBuilder, NodeBuilderWithComponents, NodeConfig, NodeHandle, WithLaunchContext, + }, + core::exit::NodeExitFuture, + tasks::TaskManager, +}; +use reth_db::{ + ClientVersion, DatabaseEnv, init_db, + mdbx::DatabaseArguments, + test_utils::{ERROR_DB_CREATION, TempDatabase, tempdir_path}, }; -use reth::core::exit::NodeExitFuture; -use reth::tasks::TaskManager; use reth_e2e_test_utils::{Adapter, TmpDB}; use reth_exex::ExExEvent; +use reth_node_core::{ + args::DatadirArgs, + dirs::{DataDirPath, MaybePlatformPath}, +}; use reth_optimism_chainspec::OpChainSpec; -use reth_optimism_node::args::RollupArgs; -use reth_optimism_node::OpNode; -use reth_provider::providers::BlockchainProvider; -use std::any::Any; -use std::net::SocketAddr; -use std::sync::Arc; +use reth_optimism_node::{OpNode, args::RollupArgs}; +use reth_provider::{CanonStateSubscriptions, providers::BlockchainProvider}; use tokio::sync::{mpsc, oneshot}; use tokio_stream::StreamExt; +use crate::engine::EngineApi; + pub const BASE_CHAIN_ID: u64 = 84532; pub type LocalNodeProvider = BlockchainProvider>; @@ -38,98 +54,78 @@ pub type LocalFlashblocksState = FlashblocksState; pub struct LocalNode { pub(crate) http_api_addr: SocketAddr, engine_ipc_path: String, - flashblock_sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, - flashblocks_state: Arc, provider: LocalNodeProvider, _node_exit_future: NodeExitFuture, _node: Box, _task_manager: TaskManager, } -pub type OpTypes = - FullNodeTypesAdapter>>; -pub type OpComponentsBuilder = >::ComponentsBuilder; -pub type OpAddOns = >::AddOns; -pub type OpBuilder = - WithLaunchContext>; +#[derive(Clone)] +pub struct FlashblocksParts { + sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, + state: Arc, +} -pub async fn default_launcher( - builder: OpBuilder, -) -> eyre::Result, OpAddOns>> { - let launcher = builder.engine_api_launcher(); - builder.launch_with(launcher).await +impl FlashblocksParts { + pub fn state(&self) -> Arc { + self.state.clone() + } + + pub async fn send(&self, flashblock: Flashblock) -> Result<()> { + let (tx, rx) = oneshot::channel(); + self.sender.send((flashblock, tx)).await.map_err(|err| eyre::eyre!(err))?; + rx.await.map_err(|err| eyre::eyre!(err))?; + Ok(()) + } } -impl LocalNode { - pub async fn new(launcher: L) -> Result - where - L: FnOnce(OpBuilder) -> LRet, - LRet: Future, OpAddOns>>>, - { - let tasks = TaskManager::current(); - let exec = tasks.executor(); - - let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json"))?; - let chain_spec = Arc::new(OpChainSpec::from_genesis(genesis)); - - let network_config = NetworkArgs { - discovery: DiscoveryArgs { - disable_discovery: true, - ..DiscoveryArgs::default() - }, - ..NetworkArgs::default() - }; +#[derive(Clone)] +struct FlashblocksNodeExtensions { + inner: Arc, +} - // Generate unique IPC path for this test instance to avoid conflicts - // Use timestamp + thread ID + process ID for uniqueness - let unique_ipc_path = format!( - "/tmp/reth_engine_api_{}_{}_{:?}.ipc", - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_nanos(), - std::process::id(), - std::thread::current().id() - ); - - let mut rpc_args = RpcServerArgs::default() - .with_unused_ports() - .with_http() - .with_auth_ipc(); - rpc_args.auth_ipc_path = unique_ipc_path; - - let node_config = NodeConfig::new(chain_spec.clone()) - .with_network(network_config) - .with_rpc(rpc_args) - .with_unused_ports(); - - let node = OpNode::new(RollupArgs::default()); +struct FlashblocksNodeExtensionsInner { + sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, + receiver: Arc)>>>>, + fb_cell: Arc>>, + process_canonical: bool, +} +impl FlashblocksNodeExtensions { + fn new(process_canonical: bool) -> Self { let (sender, receiver) = mpsc::channel::<(Flashblock, oneshot::Sender<()>)>(100); - let fb_cell: Arc>>> = Arc::new(OnceCell::new()); - let provider_cell: Arc> = Arc::new(OnceCell::new()); - - let NodeHandle { - node: node_handle, - node_exit_future, - } = NodeBuilder::new(node_config.clone()) - .testing_node(exec.clone()) - .with_types_and_provider::>() - .with_components(node.components_builder()) - .with_add_ons(node.add_ons()) - .install_exex("flashblocks-canon", { - let fb_cell = fb_cell.clone(); - let provider_cell = provider_cell.clone(); - move |mut ctx| async move { - let provider = provider_cell.get_or_init(|| ctx.provider().clone()).clone(); - let fb = fb_cell - .get_or_init(|| Arc::new(FlashblocksState::new(provider.clone()))) - .clone(); + let inner = FlashblocksNodeExtensionsInner { + sender, + receiver: Arc::new(Mutex::new(Some(receiver))), + fb_cell: Arc::new(OnceCell::new()), + process_canonical, + }; + Self { inner: Arc::new(inner) } + } + + fn apply(&self, builder: OpBuilder) -> OpBuilder { + let fb_cell = self.inner.fb_cell.clone(); + let receiver = self.inner.receiver.clone(); + let process_canonical = self.inner.process_canonical; + + let fb_cell_for_exex = fb_cell.clone(); + + builder + .install_exex("flashblocks-canon", move |mut ctx| { + let fb_cell = fb_cell_for_exex.clone(); + let process_canonical = process_canonical; + async move { + let provider = ctx.provider().clone(); + let fb = init_flashblocks_state(&fb_cell, &provider); Ok(async move { while let Some(note) = ctx.notifications.try_next().await? { if let Some(committed) = note.committed_chain() { - for block in committed.blocks_iter() { - fb.on_canonical_block_received(block); + if process_canonical { + // Many suites drive canonical updates manually to reproduce race conditions, so + // allowing this to be disabled keeps canonical replay deterministic. + for block in committed.blocks_iter() { + fb.on_canonical_block_received(block); + } } let _ = ctx .events @@ -140,74 +136,110 @@ impl LocalNode { }) } }) - .extend_rpc_modules({ + .extend_rpc_modules(move |ctx| { let fb_cell = fb_cell.clone(); - let provider_cell = provider_cell.clone(); - let mut receiver = Some(receiver); - move |ctx| { - let provider = provider_cell.get_or_init(|| ctx.provider().clone()).clone(); - let fb = fb_cell - .get_or_init(|| Arc::new(FlashblocksState::new(provider.clone()))) - .clone(); - fb.start(); - let api_ext = EthApiExt::new( - ctx.registry.eth_api().clone(), - ctx.registry.eth_handlers().filter.clone(), - fb.clone(), - ); - ctx.modules.replace_configured(api_ext.into_rpc())?; - // Spawn task to receive flashblocks from the test context - let fb_for_task = fb.clone(); - let mut receiver = receiver - .take() - .expect("flashblock receiver should only be initialized once"); - tokio::spawn(async move { - while let Some((payload, tx)) = receiver.recv().await { - fb_for_task.on_flashblock_received(payload); - let _ = tx.send(()); - } - }); - Ok(()) - } + let provider = ctx.provider().clone(); + let fb = init_flashblocks_state(&fb_cell, &provider); + + let mut canon_stream = tokio_stream::wrappers::BroadcastStream::new( + ctx.provider().subscribe_to_canonical_state(), + ); + tokio::spawn(async move { + use tokio_stream::StreamExt; + while let Some(Ok(notification)) = canon_stream.next().await { + provider.canonical_in_memory_state().notify_canon_state(notification); + } + }); + let api_ext = EthApiExt::new( + ctx.registry.eth_api().clone(), + ctx.registry.eth_handlers().filter.clone(), + fb.clone(), + ); + ctx.modules.replace_configured(api_ext.into_rpc())?; + + let fb_for_task = fb.clone(); + let mut receiver = receiver + .lock() + .expect("flashblock receiver mutex poisoned") + .take() + .expect("flashblock receiver should only be initialized once"); + tokio::spawn(async move { + while let Some((payload, tx)) = receiver.recv().await { + fb_for_task.on_flashblock_received(payload); + let _ = tx.send(()); + } + }); + + Ok(()) }) - .launch_with_fn(launcher) - .await?; - - let http_api_addr = node_handle - .rpc_server_handle() - .http_local_addr() - .ok_or_else(|| eyre::eyre!("HTTP RPC server failed to bind to address"))?; - - let engine_ipc_path = node_config.rpc.auth_ipc_path; - let flashblocks_state = fb_cell - .get() - .expect("FlashblocksState should be initialized during node launch") - .clone(); - let provider = provider_cell - .get() - .expect("Provider should be initialized during node launch") - .clone(); - - Ok(Self { - http_api_addr, - engine_ipc_path, - flashblock_sender: sender, - flashblocks_state, - provider, - _node_exit_future: node_exit_future, - _node: Box::new(node_handle), - _task_manager: tasks, - }) } - pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { - let (tx, rx) = oneshot::channel(); - self.flashblock_sender - .send((flashblock, tx)) - .await - .map_err(|err| eyre::eyre!(err))?; - rx.await.map_err(|err| eyre::eyre!(err))?; - Ok(()) + fn wrap_launcher(&self, launcher: L) -> impl FnOnce(OpBuilder) -> LRet + where + L: FnOnce(OpBuilder) -> LRet, + { + let extensions = self.clone(); + move |builder| { + let builder = extensions.apply(builder); + launcher(builder) + } + } + + fn parts(&self) -> Result { + let state = self.inner.fb_cell.get().ok_or_else(|| { + eyre::eyre!("FlashblocksState should be initialized during node launch") + })?; + Ok(FlashblocksParts { sender: self.inner.sender.clone(), state: state.clone() }) + } +} + +pub type OpTypes = + FullNodeTypesAdapter>>; +pub type OpComponentsBuilder = >::ComponentsBuilder; +pub type OpAddOns = >::AddOns; +pub type OpBuilder = + WithLaunchContext>; + +pub async fn default_launcher( + builder: OpBuilder, +) -> eyre::Result, OpAddOns>> { + let launcher = builder.engine_api_launcher(); + builder.launch_with(launcher).await +} + +impl LocalNode { + pub async fn new(launcher: L) -> Result + where + L: FnOnce(OpBuilder) -> LRet, + LRet: Future, OpAddOns>>>, + { + build_node(launcher).await + } + + /// Creates a test database with a smaller map size to reduce memory usage. + /// + /// Unlike `NodeBuilder::testing_node()` which hardcodes an 8 TB map size, + /// this method configures the database with a 100 MB map size. This prevents + /// `ENOMEM` errors when running parallel tests with `cargo test`, as the + /// default 8 TB size can cause memory exhaustion when multiple test processes + /// run concurrently. + fn create_test_database() -> Result>> { + let default_size = 100 * 1024 * 1024; // 100 MB + Self::create_test_database_with_size(default_size) + } + + /// Creates a test database with a configurable map size to reduce memory usage. + /// + /// # Arguments + /// + /// * `max_size` - Maximum map size in bytes. + fn create_test_database_with_size(max_size: usize) -> Result>> { + let path = tempdir_path(); + let emsg = format!("{ERROR_DB_CREATION}: {path:?}"); + let args = + DatabaseArguments::new(ClientVersion::default()).with_geometry_max_size(Some(max_size)); + let db = init_db(&path, args).expect(&emsg); + Ok(Arc::new(TempDatabase::new(db, path))) } pub fn provider(&self) -> Result> { @@ -220,11 +252,149 @@ impl LocalNode { EngineApi::::new(self.engine_ipc_path.clone()) } + pub fn blockchain_provider(&self) -> LocalNodeProvider { + self.provider.clone() + } +} + +async fn build_node(launcher: L) -> Result +where + L: FnOnce(OpBuilder) -> LRet, + LRet: Future, OpAddOns>>>, +{ + let tasks = TaskManager::current(); + let exec = tasks.executor(); + + let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json"))?; + let chain_spec = Arc::new(OpChainSpec::from_genesis(genesis)); + + let network_config = NetworkArgs { + discovery: DiscoveryArgs { disable_discovery: true, ..DiscoveryArgs::default() }, + ..NetworkArgs::default() + }; + + let unique_ipc_path = format!( + "/tmp/reth_engine_api_{}_{}_{:?}.ipc", + std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos(), + std::process::id(), + std::thread::current().id() + ); + + let mut rpc_args = RpcServerArgs::default().with_unused_ports().with_http().with_auth_ipc(); + rpc_args.auth_ipc_path = unique_ipc_path; + + let node = OpNode::new(RollupArgs::default()); + + let temp_db = LocalNode::create_test_database()?; + let db_path = temp_db.path().to_path_buf(); + + let mut node_config = NodeConfig::new(chain_spec.clone()) + .with_network(network_config) + .with_rpc(rpc_args) + .with_unused_ports(); + + let datadir_path = MaybePlatformPath::::from(db_path.clone()); + node_config = + node_config.with_datadir_args(DatadirArgs { datadir: datadir_path, ..Default::default() }); + + let builder = NodeBuilder::new(node_config.clone()) + .with_database(temp_db) + .with_launch_context(exec.clone()) + .with_types_and_provider::>() + .with_components(node.components_builder()) + .with_add_ons(node.add_ons()); + + let NodeHandle { node: node_handle, node_exit_future } = + builder.launch_with_fn(launcher).await?; + + let http_api_addr = node_handle + .rpc_server_handle() + .http_local_addr() + .ok_or_else(|| eyre::eyre!("HTTP RPC server failed to bind to address"))?; + + let engine_ipc_path = node_config.rpc.auth_ipc_path; + let provider = node_handle.provider().clone(); + + Ok(LocalNode { + http_api_addr, + engine_ipc_path, + provider, + _node_exit_future: node_exit_future, + _node: Box::new(node_handle), + _task_manager: tasks, + }) +} + +fn init_flashblocks_state( + cell: &Arc>>, + provider: &LocalNodeProvider, +) -> Arc { + cell.get_or_init(|| { + let fb = Arc::new(FlashblocksState::new(provider.clone(), 5)); + fb.start(); + fb + }) + .clone() +} + +pub struct FlashblocksLocalNode { + node: LocalNode, + parts: FlashblocksParts, +} + +impl FlashblocksLocalNode { + pub async fn new() -> Result { + Self::with_launcher(default_launcher).await + } + + /// Builds a flashblocks-enabled node with canonical block streaming disabled so tests can call + /// `FlashblocksState::on_canonical_block_received` at precise points. + pub async fn manual_canonical() -> Result { + Self::with_manual_canonical_launcher(default_launcher).await + } + + pub async fn with_launcher(launcher: L) -> Result + where + L: FnOnce(OpBuilder) -> LRet, + LRet: Future, OpAddOns>>>, + { + Self::with_launcher_inner(launcher, true).await + } + + pub async fn with_manual_canonical_launcher(launcher: L) -> Result + where + L: FnOnce(OpBuilder) -> LRet, + LRet: Future, OpAddOns>>>, + { + Self::with_launcher_inner(launcher, false).await + } + + async fn with_launcher_inner(launcher: L, process_canonical: bool) -> Result + where + L: FnOnce(OpBuilder) -> LRet, + LRet: Future, OpAddOns>>>, + { + let extensions = FlashblocksNodeExtensions::new(process_canonical); + let wrapped_launcher = extensions.wrap_launcher(launcher); + let node = LocalNode::new(wrapped_launcher).await?; + + let parts = extensions.parts()?; + Ok(Self { node, parts }) + } + pub fn flashblocks_state(&self) -> Arc { - self.flashblocks_state.clone() + self.parts.state() } - pub fn blockchain_provider(&self) -> LocalNodeProvider { - self.provider.clone() + pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { + self.parts.send(flashblock).await + } + + pub fn into_parts(self) -> (LocalNode, FlashblocksParts) { + (self.node, self.parts) + } + + pub fn as_node(&self) -> &LocalNode { + &self.node } } From fb21f0b5ecd04f2d6710899b9c418441f3003592 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 25 Nov 2025 20:16:58 -0600 Subject: [PATCH 19/26] Silence the global executor errors --- crates/metering/src/tests/chain_state.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs index baa592d0..3d383a8b 100644 --- a/crates/metering/src/tests/chain_state.rs +++ b/crates/metering/src/tests/chain_state.rs @@ -5,7 +5,9 @@ use alloy_provider::Provider; use alloy_rpc_types_eth::TransactionInput; use alloy_sol_types::{SolCall, sol}; use base_reth_flashblocks_rpc::rpc::FlashblocksAPI; -use base_reth_test_utils::{flashblocks_harness::FlashblocksHarness, node::BASE_CHAIN_ID}; +use base_reth_test_utils::{ + flashblocks_harness::FlashblocksHarness, node::BASE_CHAIN_ID, tracing::init_silenced_tracing, +}; use eyre::{Result, eyre}; use hex_literal::hex; use op_alloy_consensus::OpTxEnvelope; @@ -21,7 +23,7 @@ use crate::rpc::{MeteringApiImpl, MeteringApiServer}; #[tokio::test] async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { - reth_tracing::init_test_tracing(); + init_silenced_tracing(); let harness = FlashblocksHarness::new().await?; let provider = harness.provider(); @@ -133,7 +135,7 @@ async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { #[tokio::test] async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { - reth_tracing::init_test_tracing(); + init_silenced_tracing(); let harness = FlashblocksHarness::new().await?; let provider = harness.provider(); @@ -265,7 +267,7 @@ fn envelope_from_signed(tx: TransactionSigned) -> (OpTxEnvelope, Bytes) { #[tokio::test] async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { - reth_tracing::init_test_tracing(); + init_silenced_tracing(); let harness = FlashblocksHarness::new().await?; let alice = &harness.accounts().alice; let alice_secret = secret_from_hex(alice.private_key); From d82c61233ea1735bae2bec3cd849042e79f6b145 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Tue, 25 Nov 2025 20:19:52 -0600 Subject: [PATCH 20/26] Add TODO for state_root_time --- crates/rpc/src/base/meter_rpc.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 5a79cb90..f7903aae 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -198,6 +198,8 @@ where state_block_number: header.number, state_flashblock_index, total_gas_used: result.total_gas_used, + // TODO: reintroduce state_root_time_us once tips-core exposes it again. + // TODO: rename total_execution_time_us to total_time_us since it includes state root time total_execution_time_us: result.total_time_us, }) } From 00e05c197217cc52f422798b8b3b82e6ce636473 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Wed, 26 Nov 2025 15:10:35 -0600 Subject: [PATCH 21/26] Add metrics for bundle state clone cost Track duration and size (number of accounts) when cloning BundleState to help identify if this becomes a performance bottleneck. --- crates/flashblocks/src/metrics.rs | 6 ++++++ crates/flashblocks/src/pending_blocks.rs | 19 +++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/crates/flashblocks/src/metrics.rs b/crates/flashblocks/src/metrics.rs index 3cdaf96f..e1a56167 100644 --- a/crates/flashblocks/src/metrics.rs +++ b/crates/flashblocks/src/metrics.rs @@ -56,4 +56,10 @@ pub struct Metrics { /// Total number of WebSocket reconnection attempts. #[metric(describe = "Total number of WebSocket reconnection attempts")] pub reconnect_attempts: Counter, + + #[metric(describe = "Time taken to clone bundle state")] + pub bundle_state_clone_duration: Histogram, + + #[metric(describe = "Size of bundle state being cloned (number of accounts)")] + pub bundle_state_clone_size: Histogram, } diff --git a/crates/flashblocks/src/pending_blocks.rs b/crates/flashblocks/src/pending_blocks.rs index 0577d603..e5b84ad9 100644 --- a/crates/flashblocks/src/pending_blocks.rs +++ b/crates/flashblocks/src/pending_blocks.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::{sync::Arc, time::Instant}; use alloy_consensus::{Header, Sealed}; use alloy_eips::BlockNumberOrTag; @@ -21,7 +21,7 @@ use reth::revm::{ use reth_rpc_convert::RpcTransaction; use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; -use crate::PendingBlocksAPI; +use crate::{Metrics, PendingBlocksAPI}; /// Builder for [`PendingBlocks`]. #[derive(Debug)] @@ -224,9 +224,20 @@ impl PendingBlocks { self.db_cache.clone() } - /// Returns the bundle state. + /// 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 { - self.bundle_state.clone() + 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. From 19045baaa2a7e036ed740aa00c6ace13f504dc18 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Wed, 26 Nov 2025 16:03:01 -0600 Subject: [PATCH 22/26] Add database layering tests to flashblocks-rpc Add tests verifying the correct State> layering order. These tests demonstrate that CacheDB> loses writes because CacheDB intercepts all writes into its internal cache without propagating them to State's bundle tracking. --- Cargo.lock | 1 + crates/flashblocks/Cargo.toml | 1 + crates/flashblocks/tests/layering.rs | 418 +++++++++++++++++++++++++++ crates/rpc/tests/meter.rs | 99 +++++++ 4 files changed, 519 insertions(+) create mode 100644 crates/flashblocks/tests/layering.rs diff --git a/Cargo.lock b/Cargo.lock index 0c23815a..bd983444 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1566,6 +1566,7 @@ dependencies = [ "reth-rpc-eth-api", "reth-testing-utils", "reth-tracing", + "revm", "revm-database", "rollup-boost", "serde", diff --git a/crates/flashblocks/Cargo.toml b/crates/flashblocks/Cargo.toml index 571c2350..c8b66356 100644 --- a/crates/flashblocks/Cargo.toml +++ b/crates/flashblocks/Cargo.toml @@ -60,6 +60,7 @@ metrics-derive.workspace = true [dev-dependencies] rand.workspace = true +revm.workspace = true reth-db.workspace = true once_cell.workspace = true reth-provider.workspace = true diff --git a/crates/flashblocks/tests/layering.rs b/crates/flashblocks/tests/layering.rs new file mode 100644 index 00000000..cf631d9d --- /dev/null +++ b/crates/flashblocks/tests/layering.rs @@ -0,0 +1,418 @@ +// ============================================================================= +// Database Layering Tests +// +// These tests verify that the three-layer state architecture is correct: +// 1. StateProviderDatabase (canonical block base state) +// 2. CacheDB (applies flashblock pending read cache) +// 3. State wrapper (applies bundle_prestate for pending writes + tracks new changes) +// +// The correct layering is State>. Writes go +// through State first (properly tracked for bundle/state root calculation), +// and reads fall through to CacheDB (flashblock read cache) then to +// StateProviderDatabase (canonical state). +// +// The WRONG layering would be CacheDB>. CacheDB intercepts all +// writes into its internal cache and doesn't propagate them to State, so +// State's bundle tracking captures nothing. See the test +// layering_cachedb_wrapping_state_loses_writes for a demonstration. +// ============================================================================= + +use std::sync::Arc; + +use alloy_consensus::crypto::secp256k1::public_key_to_address; +use alloy_genesis::GenesisAccount; +use alloy_primitives::{Address, B256, U256}; +use eyre::Context; +use rand::{SeedableRng, rngs::StdRng}; +use reth::api::NodeTypesWithDBAdapter; +use reth::revm::db::{AccountState, BundleState, Cache, CacheDB, DbAccount, State}; +use reth_db::{ + ClientVersion, DatabaseEnv, init_db, + mdbx::{DatabaseArguments, KILOBYTE, MEGABYTE, MaxReadTransactionDuration}, + test_utils::{ERROR_DB_CREATION, TempDatabase, create_test_static_files_dir, tempdir_path}, +}; +use reth_optimism_chainspec::{BASE_MAINNET, OpChainSpec, OpChainSpecBuilder}; +use reth_optimism_node::OpNode; +use reth_primitives_traits::SealedHeader; +use reth_provider::{ + HeaderProvider, ProviderFactory, StateProviderFactory, providers::{BlockchainProvider, StaticFileProvider}, +}; +use reth_testing_utils::generators::generate_keys; +use revm::Database; +use revm::primitives::KECCAK_EMPTY; + +type NodeTypes = NodeTypesWithDBAdapter>>; + +#[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)] +enum User { + Alice, + Bob, +} + +#[derive(Debug, Clone)] +struct TestHarness { + provider: BlockchainProvider, + header: SealedHeader, + #[allow(dead_code)] + chain_spec: Arc, + user_to_address: std::collections::HashMap, + #[allow(dead_code)] + user_to_private_key: std::collections::HashMap, +} + +impl TestHarness { + fn address(&self, u: User) -> Address { + self.user_to_address[&u] + } +} + +fn create_chain_spec( + seed: u64, +) -> ( + Arc, + std::collections::HashMap, + std::collections::HashMap, +) { + let keys = generate_keys(&mut StdRng::seed_from_u64(seed), 2); + + let mut addresses = std::collections::HashMap::new(); + let mut private_keys = std::collections::HashMap::new(); + + let alice_key = keys[0]; + let alice_address = public_key_to_address(alice_key.public_key()); + let alice_secret = B256::from(alice_key.secret_bytes()); + addresses.insert(User::Alice, alice_address); + private_keys.insert(User::Alice, alice_secret); + + let bob_key = keys[1]; + let bob_address = public_key_to_address(bob_key.public_key()); + let bob_secret = B256::from(bob_key.secret_bytes()); + addresses.insert(User::Bob, bob_address); + private_keys.insert(User::Bob, bob_secret); + + let genesis = BASE_MAINNET + .genesis + .clone() + .extend_accounts(vec![ + (alice_address, GenesisAccount::default().with_balance(U256::from(1_000_000_000_u64))), + (bob_address, GenesisAccount::default().with_balance(U256::from(1_000_000_000_u64))), + ]) + .with_gas_limit(100_000_000); + + let spec = + Arc::new(OpChainSpecBuilder::base_mainnet().genesis(genesis).isthmus_activated().build()); + + (spec, addresses, private_keys) +} + +fn create_provider_factory( + chain_spec: Arc, +) -> ProviderFactory>>> { + let (static_dir, _) = create_test_static_files_dir(); + let db = create_test_db(); + ProviderFactory::new( + db, + chain_spec, + StaticFileProvider::read_write(static_dir.keep()).expect("static file provider"), + ) +} + +fn create_test_db() -> Arc> { + let path = tempdir_path(); + let emsg = format!("{ERROR_DB_CREATION}: {path:?}"); + + let db = init_db( + &path, + DatabaseArguments::new(ClientVersion::default()) + .with_max_read_transaction_duration(Some(MaxReadTransactionDuration::Unbounded)) + .with_geometry_max_size(Some(4 * MEGABYTE)) + .with_growth_step(Some(4 * KILOBYTE)), + ) + .expect(&emsg); + + Arc::new(TempDatabase::new(db, path)) +} + +fn setup_harness() -> eyre::Result { + let (chain_spec, user_to_address, user_to_private_key) = create_chain_spec(1337); + let factory = create_provider_factory(chain_spec.clone()); + + reth_db_common::init::init_genesis(&factory).context("initializing genesis state")?; + + let provider = BlockchainProvider::new(factory.clone()).context("creating provider")?; + let header = provider + .sealed_header(0) + .context("fetching genesis header")? + .expect("genesis header exists"); + + Ok(TestHarness { provider, header, chain_spec, user_to_address, user_to_private_key }) +} + +/// Demonstrates that State alone cannot see pending state. +/// +/// Without CacheDB or bundle_prestate, State can only see canonical block state. +#[test] +fn layering_old_state_only_cannot_see_pending_state() -> eyre::Result<()> { + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + // OLD implementation: just State wrapping StateProviderDatabase directly + // No CacheDB, no bundle_prestate - cannot see any pending flashblock state + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let mut db = State::builder().with_database(state_db).with_bundle_update().build(); + + // Read through State - can only see canonical state (nonce 0) + let account = db.basic(alice_address)?.expect("account should exist"); + + // Old implementation sees canonical nonce (0), not any pending state + assert_eq!( + account.nonce, 0, + "Old State-only layering can only see canonical state" + ); + + Ok(()) +} + +/// Demonstrates that CacheDB> is the WRONG layering order. +/// +/// CacheDB is a read-through cache. When the EVM writes state changes, those +/// writes go into CacheDB's internal cache and are NOT propagated to the +/// underlying State. As a result, State's bundle tracking captures nothing, +/// and take_bundle() returns an empty bundle - breaking state root calculation. +/// +/// The correct layering is State> where State wraps CacheDB, +/// so all writes go through State first and are properly tracked. +#[test] +fn layering_cachedb_wrapping_state_loses_writes() -> eyre::Result<()> { + use revm::DatabaseCommit; + + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + let bob_address = harness.address(User::Bob); + + // ========================================================================= + // WRONG layering: CacheDB> + // ========================================================================= + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let state = State::builder().with_database(state_db).with_bundle_update().build(); + let mut wrong_db = CacheDB::new(state); + + // Simulate a write: modify Alice's account through the CacheDB + let mut alice_account = wrong_db.basic(alice_address)?.expect("alice should exist"); + alice_account.nonce = 999; + alice_account.balance = U256::from(12345u64); + + // Commit the change through CacheDB + let mut state_changes = revm::state::EvmState::default(); + state_changes.insert( + alice_address, + revm::state::Account { + info: alice_account.clone(), + storage: Default::default(), + status: revm::state::AccountStatus::Touched, + transaction_id: 0, + }, + ); + wrong_db.commit(state_changes); + + // The write went into CacheDB's cache - verify we can read it back + let alice_from_cache = wrong_db.basic(alice_address)?.expect("alice should exist"); + assert_eq!(alice_from_cache.nonce, 999, "CacheDB should have the written nonce"); + + // BUT: The underlying State captured NOTHING! + // CacheDB doesn't propagate writes to its underlying database. + wrong_db.db.merge_transitions(revm_database::states::bundle_state::BundleRetention::Reverts); + let wrong_bundle = wrong_db.db.take_bundle(); + + assert!( + wrong_bundle.state.is_empty(), + "WRONG layering: State's bundle should be EMPTY because CacheDB intercepted all writes. \ + Got {} accounts in bundle.", + wrong_bundle.state.len() + ); + + // ========================================================================= + // CORRECT layering: State> + // ========================================================================= + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let state_db2 = reth::revm::database::StateProviderDatabase::new(state_provider2); + let cache_db = CacheDB::new(state_db2); + let mut correct_db = State::builder().with_database(cache_db).with_bundle_update().build(); + + // Simulate the same write through State + let mut bob_account = correct_db.basic(bob_address)?.expect("bob should exist"); + bob_account.nonce = 888; + bob_account.balance = U256::from(54321u64); + + // Commit through State + let mut state_changes2 = revm::state::EvmState::default(); + state_changes2.insert( + bob_address, + revm::state::Account { + info: bob_account.clone(), + storage: Default::default(), + status: revm::state::AccountStatus::Touched, + transaction_id: 0, + }, + ); + correct_db.commit(state_changes2); + + // State properly captures the write + correct_db.merge_transitions(revm_database::states::bundle_state::BundleRetention::Reverts); + let correct_bundle = correct_db.take_bundle(); + + assert!( + !correct_bundle.state.is_empty(), + "CORRECT layering: State's bundle should contain the written account" + ); + assert!( + correct_bundle.state.contains_key(&bob_address), + "Bundle should contain Bob's account changes" + ); + + Ok(()) +} + +/// Verifies that CacheDB layer is required for pending balance visibility. +/// +/// This test demonstrates that without the CacheDB layer, pending balance +/// changes from flashblocks would not be visible to bundle execution. +#[test] +fn layering_cachedb_makes_pending_balance_visible() -> eyre::Result<()> { + let harness = setup_harness()?; + + // Get the canonical balance for Alice from the state provider + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let alice_address = harness.address(User::Alice); + let canonical_balance = state_provider.account_balance(&alice_address)?.unwrap_or(U256::ZERO); + + // Create a cache with a modified balance (simulating a pending flashblock) + let pending_balance = canonical_balance + U256::from(999_999_u64); + let mut cache = Cache::default(); + cache.accounts.insert( + alice_address, + DbAccount { + info: revm::state::AccountInfo { + balance: pending_balance, + nonce: 0, + code_hash: KECCAK_EMPTY, + code: None, + }, + account_state: AccountState::Touched, + storage: Default::default(), + }, + ); + + // Wrap with CacheDB to apply the pending cache + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let mut cache_db = CacheDB { cache, db: state_db }; + + // Read the balance through CacheDB - should see pending value + let account = cache_db.basic(alice_address)?.expect("account should exist"); + assert_eq!( + account.balance, pending_balance, + "CacheDB should return pending balance from cache" + ); + + // Verify the canonical state still has the original balance + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + let canonical_balance2 = + state_provider2.account_balance(&alice_address)?.unwrap_or(U256::ZERO); + assert_eq!( + canonical_balance, canonical_balance2, + "Canonical state should be unchanged" + ); + + Ok(()) +} + +/// Verifies that bundle_prestate is required for pending state changes visibility. +/// +/// This test demonstrates that without with_bundle_prestate(), the State wrapper +/// would start with empty state and not see pending flashblock state changes. +#[test] +fn layering_bundle_prestate_makes_pending_nonce_visible() -> eyre::Result<()> { + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + // Create a BundleState with a modified nonce (simulating pending flashblock writes) + let pending_nonce = 42u64; + let bundle_state = BundleState::new( + [( + alice_address, + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: 0, // original + code_hash: KECCAK_EMPTY, + code: None, + }), + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: pending_nonce, // pending + code_hash: KECCAK_EMPTY, + code: None, + }), + Default::default(), + )], + Vec::>, Vec<(U256, U256)>)>>::new(), + Vec::<(B256, revm::bytecode::Bytecode)>::new(), + ); + + // Create the three-layer stack WITH bundle_prestate + let state_db = reth::revm::database::StateProviderDatabase::new(state_provider); + let cache_db = CacheDB::new(state_db); + let mut db_with_prestate = State::builder() + .with_database(cache_db) + .with_bundle_update() + .with_bundle_prestate(bundle_state.clone()) + .build(); + + // Read through the State - should see pending nonce from bundle_prestate + let account = db_with_prestate.basic(alice_address)?.expect("account should exist"); + assert_eq!(account.nonce, pending_nonce, "State with bundle_prestate should see pending nonce"); + + // Now create WITHOUT bundle_prestate to show the difference + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + let state_db2 = reth::revm::database::StateProviderDatabase::new(state_provider2); + let cache_db2 = CacheDB::new(state_db2); + let mut db_without_prestate = + State::builder().with_database(cache_db2).with_bundle_update().build(); + + // Read through State without prestate - should see canonical nonce (0) + let account2 = db_without_prestate.basic(alice_address)?.expect("account should exist"); + assert_eq!( + account2.nonce, 0, + "State without bundle_prestate should see canonical nonce" + ); + + Ok(()) +} diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 13ef753c..64a6d514 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -11,6 +11,7 @@ use base_reth_test_utils::create_provider_factory; use eyre::Context; use op_alloy_consensus::OpTxEnvelope; use rand::{SeedableRng, rngs::StdRng}; +use reth::revm::db::{BundleState, Cache}; use reth::{api::NodeTypesWithDBAdapter, chainspec::EthChainSpec}; use reth_db::{DatabaseEnv, test_utils::TempDatabase}; use reth_optimism_chainspec::{BASE_MAINNET, OpChainSpec, OpChainSpecBuilder}; @@ -20,6 +21,7 @@ use reth_primitives_traits::SealedHeader; use reth_provider::{HeaderProvider, StateProviderFactory, providers::BlockchainProvider}; use reth_testing_utils::generators::generate_keys; use reth_transaction_pool::test_utils::TransactionBuilder; +use revm::primitives::KECCAK_EMPTY; use tips_core::types::{Bundle, ParsedBundle}; type NodeTypes = NodeTypesWithDBAdapter>>; @@ -368,3 +370,100 @@ fn meter_bundle_state_root_time_invariant() -> eyre::Result<()> { Ok(()) } + +/// Integration test: verifies meter_bundle uses flashblocks state correctly. +/// +/// A transaction using nonce=1 should fail without flashblocks state (since +/// canonical nonce is 0), but succeed when flashblocks state indicates nonce=1. +#[test] +fn meter_bundle_requires_correct_layering_for_pending_nonce() -> eyre::Result<()> { + let harness = setup_harness()?; + let alice_address = harness.address(User::Alice); + + // Create a transaction that requires nonce=1 (assuming canonical nonce is 0) + let to = Address::random(); + let signed_tx = TransactionBuilder::default() + .signer(harness.signer(User::Alice)) + .chain_id(harness.chain_spec.chain_id()) + .nonce(1) // Requires pending state to have nonce=1 + .to(to) + .value(100) + .gas_limit(21_000) + .max_fee_per_gas(10) + .max_priority_fee_per_gas(1) + .into_eip1559(); + + let tx = + OpTransactionSigned::Eip1559(signed_tx.as_eip1559().expect("eip1559 transaction").clone()); + let envelope = envelope_from_signed(&tx)?; + let parsed_bundle = create_parsed_bundle(vec![envelope])?; + + // Without flashblocks state, transaction should fail (nonce mismatch) + let state_provider = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let result_without_flashblocks = meter_bundle( + state_provider, + harness.chain_spec.clone(), + parsed_bundle.clone(), + &harness.header, + None, // No flashblocks state + None, + ); + + assert!( + result_without_flashblocks.is_err(), + "Transaction with nonce=1 should fail without pending state (canonical nonce is 0)" + ); + + // Now create flashblocks state with nonce=1 for Alice + // Use BundleState::new() to properly calculate state_size + let bundle_state = BundleState::new( + [( + alice_address, + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: 0, // original + code_hash: KECCAK_EMPTY, + code: None, + }), + Some(revm::state::AccountInfo { + balance: U256::from(1_000_000_000u64), + nonce: 1, // pending (after first flashblock tx) + code_hash: KECCAK_EMPTY, + code: None, + }), + Default::default(), // no storage changes + )], + Vec::>, Vec<(U256, U256)>)>>::new(), + Vec::<(B256, revm::bytecode::Bytecode)>::new(), + ); + + let flashblocks_state = FlashblocksState { cache: Cache::default(), bundle_state }; + + // With correct flashblocks state, transaction should succeed + let state_provider2 = harness + .provider + .state_by_block_hash(harness.header.hash()) + .context("getting state provider")?; + + let result_with_flashblocks = meter_bundle( + state_provider2, + harness.chain_spec.clone(), + parsed_bundle, + &harness.header, + Some(flashblocks_state), + None, + ); + + assert!( + result_with_flashblocks.is_ok(), + "Transaction with nonce=1 should succeed with pending state showing nonce=1: {:?}", + result_with_flashblocks.err() + ); + + Ok(()) +} + From d83376013501e1373a748cd3e8edb2343cf7bd45 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Wed, 26 Nov 2025 22:17:16 -0600 Subject: [PATCH 23/26] Add tests for flashblock state visibility Add two tests that verify pending state from flashblocks is correctly visible: - test_eth_call_sees_flashblock_state_changes: Verifies eth_call to pending block sees balance changes from executed flashblock transactions - test_sequential_nonces_across_flashblocks: Verifies transactions in flashblock N+1 can see state changes (nonces) from flashblock N --- crates/flashblocks/tests/state.rs | 140 ++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/crates/flashblocks/tests/state.rs b/crates/flashblocks/tests/state.rs index 75ef5d82..225f8e36 100644 --- a/crates/flashblocks/tests/state.rs +++ b/crates/flashblocks/tests/state.rs @@ -837,6 +837,146 @@ async fn test_duplicate_flashblock_ignored() { assert_eq!(block, block_two); } +/// Verifies that eth_call targeting pending block sees flashblock state changes. +/// +/// This test catches database layering bugs where pending state from flashblocks +/// isn't visible to RPC callers. After a flashblock transfers ETH to Bob, an +/// eth_call simulating a transfer FROM Bob should succeed because Bob now has +/// more funds from the flashblock. +#[tokio::test] +async fn test_eth_call_sees_flashblock_state_changes() { + use alloy_eips::BlockNumberOrTag; + use alloy_provider::Provider; + use alloy_rpc_types_eth::TransactionInput; + use op_alloy_rpc_types::OpTransactionRequest; + + let test = TestHarness::new().await; + let provider = test.node.provider(); + + let bob_address = test.address(User::Bob); + let charlie_address = test.address(User::Charlie); + + // Get Bob's canonical balance to calculate a transfer amount that exceeds it + let canonical_balance = provider.get_balance(bob_address).await.unwrap(); + + // Send base flashblock + test.send_flashblock(FlashblockBuilder::new_base(&test).build()).await; + + // Flashblock 1: Alice sends a large amount to Bob + let transfer_to_bob = 1_000_000_000_000_000_000u128; // 1 ETH + let tx = test.build_transaction_to_send_eth_with_nonce( + User::Alice, + User::Bob, + transfer_to_bob, + 0, + ); + test.send_flashblock( + FlashblockBuilder::new(&test, 1) + .with_transactions(vec![tx]) + .build(), + ) + .await; + + // Verify via state overrides that Bob received the funds + let overrides = test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .expect("state overrides should exist after flashblock execution"); + let bob_override = overrides.get(&bob_address).expect("Bob should have a state override"); + let bob_pending_balance = bob_override.balance.expect("Bob's balance override should be set"); + assert_eq!( + bob_pending_balance, + canonical_balance + U256::from(transfer_to_bob), + "State override should show Bob's increased balance" + ); + + // Now the key test: eth_call from Bob should see this pending balance. + // Try to transfer more than Bob's canonical balance (but less than pending). + // This would fail if eth_call can't see the pending state. + let transfer_amount = canonical_balance + U256::from(100_000u64); + let call_request = OpTransactionRequest::default() + .from(bob_address) + .to(charlie_address) + .value(transfer_amount) + .gas_limit(21_000) + .input(TransactionInput::default()); + + let result = provider.call(call_request).block(BlockNumberOrTag::Pending.into()).await; + assert!( + result.is_ok(), + "eth_call from Bob should succeed because pending state shows increased balance. \ + If this fails, eth_call may not be seeing flashblock state changes. Error: {:?}", + result.err() + ); +} + +/// Verifies that transactions in flashblock N+1 can see state changes from flashblock N. +/// +/// This test catches database layering bugs where writes from earlier flashblocks +/// aren't visible to later flashblock execution. The key is that flashblock 2's +/// transaction uses nonce=1, which only succeeds if the execution layer sees +/// flashblock 1's transaction (which used nonce=0). +#[tokio::test] +async fn test_sequential_nonces_across_flashblocks() { + let test = TestHarness::new().await; + + // Send base flashblock + test.send_flashblock(FlashblockBuilder::new_base(&test).build()).await; + + // Flashblock 1: Alice sends to Bob with nonce 0 + let tx_nonce_0 = test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Bob, 1000, 0); + test.send_flashblock( + FlashblockBuilder::new(&test, 1) + .with_transactions(vec![tx_nonce_0]) + .build(), + ) + .await; + + // Verify flashblock 1 was processed - Alice's pending nonce should now be 1 + let alice_state = test.account_state(User::Alice); + assert_eq!( + alice_state.nonce, 1, + "After flashblock 1, Alice's pending nonce should be 1" + ); + + // Flashblock 2: Alice sends to Charlie with nonce 1 + // This will FAIL if the execution layer can't see flashblock 1's state change + let tx_nonce_1 = + test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Charlie, 2000, 1); + test.send_flashblock( + FlashblockBuilder::new(&test, 2) + .with_transactions(vec![tx_nonce_1]) + .build(), + ) + .await; + + // Verify flashblock 2 was processed - Alice's pending nonce should now be 2 + let alice_state_after = test.account_state(User::Alice); + assert_eq!( + alice_state_after.nonce, 2, + "After flashblock 2, Alice's pending nonce should be 2. \ + If this fails, the database layering may be preventing flashblock 2 \ + from seeing flashblock 1's state changes." + ); + + // Also verify Bob and Charlie received their funds + let overrides = test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .expect("state overrides should exist"); + + assert!( + overrides.get(&test.address(User::Bob)).is_some(), + "Bob should have received funds from flashblock 1" + ); + assert!( + overrides.get(&test.address(User::Charlie)).is_some(), + "Charlie should have received funds from flashblock 2" + ); +} + #[tokio::test] async fn test_progress_canonical_blocks_without_flashblocks() { let mut test = TestHarness::new().await; From 906e492e41aafd4829a9eab8d6423ef98f6085c9 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Wed, 26 Nov 2025 22:18:36 -0600 Subject: [PATCH 24/26] just fix --- crates/flashblocks/tests/layering.rs | 30 +- crates/flashblocks/tests/state.rs | 29 +- crates/metering/src/tests/chain_state.rs | 395 ----------------------- crates/rpc/tests/meter.rs | 8 +- 4 files changed, 23 insertions(+), 439 deletions(-) delete mode 100644 crates/metering/src/tests/chain_state.rs diff --git a/crates/flashblocks/tests/layering.rs b/crates/flashblocks/tests/layering.rs index cf631d9d..049198d3 100644 --- a/crates/flashblocks/tests/layering.rs +++ b/crates/flashblocks/tests/layering.rs @@ -24,8 +24,10 @@ use alloy_genesis::GenesisAccount; use alloy_primitives::{Address, B256, U256}; use eyre::Context; use rand::{SeedableRng, rngs::StdRng}; -use reth::api::NodeTypesWithDBAdapter; -use reth::revm::db::{AccountState, BundleState, Cache, CacheDB, DbAccount, State}; +use reth::{ + api::NodeTypesWithDBAdapter, + revm::db::{AccountState, BundleState, Cache, CacheDB, DbAccount, State}, +}; use reth_db::{ ClientVersion, DatabaseEnv, init_db, mdbx::{DatabaseArguments, KILOBYTE, MEGABYTE, MaxReadTransactionDuration}, @@ -35,11 +37,11 @@ use reth_optimism_chainspec::{BASE_MAINNET, OpChainSpec, OpChainSpecBuilder}; use reth_optimism_node::OpNode; use reth_primitives_traits::SealedHeader; use reth_provider::{ - HeaderProvider, ProviderFactory, StateProviderFactory, providers::{BlockchainProvider, StaticFileProvider}, + HeaderProvider, ProviderFactory, StateProviderFactory, + providers::{BlockchainProvider, StaticFileProvider}, }; use reth_testing_utils::generators::generate_keys; -use revm::Database; -use revm::primitives::KECCAK_EMPTY; +use revm::{Database, primitives::KECCAK_EMPTY}; type NodeTypes = NodeTypesWithDBAdapter>>; @@ -170,10 +172,7 @@ fn layering_old_state_only_cannot_see_pending_state() -> eyre::Result<()> { let account = db.basic(alice_address)?.expect("account should exist"); // Old implementation sees canonical nonce (0), not any pending state - assert_eq!( - account.nonce, 0, - "Old State-only layering can only see canonical state" - ); + assert_eq!(account.nonce, 0, "Old State-only layering can only see canonical state"); Ok(()) } @@ -337,12 +336,8 @@ fn layering_cachedb_makes_pending_balance_visible() -> eyre::Result<()> { .provider .state_by_block_hash(harness.header.hash()) .context("getting state provider")?; - let canonical_balance2 = - state_provider2.account_balance(&alice_address)?.unwrap_or(U256::ZERO); - assert_eq!( - canonical_balance, canonical_balance2, - "Canonical state should be unchanged" - ); + let canonical_balance2 = state_provider2.account_balance(&alice_address)?.unwrap_or(U256::ZERO); + assert_eq!(canonical_balance, canonical_balance2, "Canonical state should be unchanged"); Ok(()) } @@ -409,10 +404,7 @@ fn layering_bundle_prestate_makes_pending_nonce_visible() -> eyre::Result<()> { // Read through State without prestate - should see canonical nonce (0) let account2 = db_without_prestate.basic(alice_address)?.expect("account should exist"); - assert_eq!( - account2.nonce, 0, - "State without bundle_prestate should see canonical nonce" - ); + assert_eq!(account2.nonce, 0, "State without bundle_prestate should see canonical nonce"); Ok(()) } diff --git a/crates/flashblocks/tests/state.rs b/crates/flashblocks/tests/state.rs index 225f8e36..d4063c9d 100644 --- a/crates/flashblocks/tests/state.rs +++ b/crates/flashblocks/tests/state.rs @@ -864,18 +864,10 @@ async fn test_eth_call_sees_flashblock_state_changes() { // Flashblock 1: Alice sends a large amount to Bob let transfer_to_bob = 1_000_000_000_000_000_000u128; // 1 ETH - let tx = test.build_transaction_to_send_eth_with_nonce( - User::Alice, - User::Bob, - transfer_to_bob, - 0, - ); - test.send_flashblock( - FlashblockBuilder::new(&test, 1) - .with_transactions(vec![tx]) - .build(), - ) - .await; + let tx = + test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Bob, transfer_to_bob, 0); + test.send_flashblock(FlashblockBuilder::new(&test, 1).with_transactions(vec![tx]).build()) + .await; // Verify via state overrides that Bob received the funds let overrides = test @@ -927,27 +919,20 @@ async fn test_sequential_nonces_across_flashblocks() { // Flashblock 1: Alice sends to Bob with nonce 0 let tx_nonce_0 = test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Bob, 1000, 0); test.send_flashblock( - FlashblockBuilder::new(&test, 1) - .with_transactions(vec![tx_nonce_0]) - .build(), + FlashblockBuilder::new(&test, 1).with_transactions(vec![tx_nonce_0]).build(), ) .await; // Verify flashblock 1 was processed - Alice's pending nonce should now be 1 let alice_state = test.account_state(User::Alice); - assert_eq!( - alice_state.nonce, 1, - "After flashblock 1, Alice's pending nonce should be 1" - ); + assert_eq!(alice_state.nonce, 1, "After flashblock 1, Alice's pending nonce should be 1"); // Flashblock 2: Alice sends to Charlie with nonce 1 // This will FAIL if the execution layer can't see flashblock 1's state change let tx_nonce_1 = test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Charlie, 2000, 1); test.send_flashblock( - FlashblockBuilder::new(&test, 2) - .with_transactions(vec![tx_nonce_1]) - .build(), + FlashblockBuilder::new(&test, 2).with_transactions(vec![tx_nonce_1]).build(), ) .await; diff --git a/crates/metering/src/tests/chain_state.rs b/crates/metering/src/tests/chain_state.rs deleted file mode 100644 index 3d383a8b..00000000 --- a/crates/metering/src/tests/chain_state.rs +++ /dev/null @@ -1,395 +0,0 @@ -use alloy_consensus::Receipt; -use alloy_eips::{BlockNumberOrTag, Encodable2718}; -use alloy_primitives::{B256, Bytes, U256}; -use alloy_provider::Provider; -use alloy_rpc_types_eth::TransactionInput; -use alloy_sol_types::{SolCall, sol}; -use base_reth_flashblocks_rpc::rpc::FlashblocksAPI; -use base_reth_test_utils::{ - flashblocks_harness::FlashblocksHarness, node::BASE_CHAIN_ID, tracing::init_silenced_tracing, -}; -use eyre::{Result, eyre}; -use hex_literal::hex; -use op_alloy_consensus::OpTxEnvelope; -use op_alloy_rpc_types::OpTransactionRequest; -use reth::providers::HeaderProvider; -use reth_optimism_primitives::{OpReceipt, OpTransactionSigned}; -use reth_primitives::TransactionSigned; -use reth_transaction_pool::test_utils::TransactionBuilder; -use tips_core::types::Bundle; - -use super::utils::secret_from_hex; -use crate::rpc::{MeteringApiImpl, MeteringApiServer}; - -#[tokio::test] -async fn meter_bundle_simulation_reflects_pending_state() -> Result<()> { - init_silenced_tracing(); - let harness = FlashblocksHarness::new().await?; - - let provider = harness.provider(); - let alice = &harness.accounts().alice; - let alice_secret = secret_from_hex(alice.private_key); - - // Deploy the Counter contract (nonce 0) - let deploy_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(0) - .gas_limit(DEPLOY_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .input(COUNTER_CREATION_BYTECODE.to_vec()) - .into_eip1559(); - let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); - harness.build_block_from_transactions(vec![deploy_bytes]).await?; - - let deploy_receipt = provider - .get_transaction_receipt(deploy_envelope.tx_hash()) - .await? - .ok_or_else(|| eyre!("deployment transaction missing receipt"))?; - let contract_address = deploy_receipt - .inner - .contract_address - .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; - - // Mutate storage on-chain via setNumber (nonce 1) - let set_call = Counter::setNumberCall { newNumber: U256::from(42u64) }; - let set_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(1) - .gas_limit(CALL_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .to(contract_address) - .input(Bytes::from(set_call.abi_encode())) - .into_eip1559(); - let (_set_envelope, set_bytes) = envelope_from_signed(set_signed); - harness.build_block_from_transactions(vec![set_bytes]).await?; - - // Meter an increment call (nonce 2) after the storage change - let increment_call = Counter::incrementCall {}; - let increment_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(2) - .gas_limit(CALL_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .to(contract_address) - .input(Bytes::from(increment_call.abi_encode())) - .into_eip1559(); - let (_increment_envelope, increment_bytes) = envelope_from_signed(increment_signed.clone()); - - let bundle = Bundle { - txs: vec![increment_bytes.clone()], - block_number: provider.get_block_number().await?, - flashblock_number_min: None, - flashblock_number_max: None, - min_timestamp: None, - max_timestamp: None, - reverting_tx_hashes: vec![], - replacement_uuid: None, - dropping_tx_hashes: vec![], - }; - - let metering_api = - MeteringApiImpl::new(harness.blockchain_provider(), harness.flashblocks_state()); - let response = MeteringApiServer::meter_bundle(&metering_api, bundle) - .await - .map_err(|err| eyre!("meter_bundle rpc failed: {}", err))?; - - assert_eq!(response.results.len(), 1); - let result = &response.results[0]; - assert_eq!(result.to_address, Some(contract_address)); - assert!(result.gas_used > 0); - assert!(response.state_flashblock_index.is_none()); - - // Confirm canonical storage remains at 42 (increment transaction only simulated) - let number_call = Counter::numberCall {}; - let call_request = OpTransactionRequest::default() - .from(alice.address) - .to(contract_address) - .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))); - let raw_number = provider.call(call_request).block(BlockNumberOrTag::Latest.into()).await?; - let decoded: U256 = Counter::numberCall::abi_decode_returns(raw_number.as_ref())?; - assert_eq!(decoded, U256::from(42u64)); - - // Execute the increment on-chain to confirm the transaction is valid when mined - harness.build_block_from_transactions(vec![increment_bytes]).await?; - let number_after_increment = provider - .call( - OpTransactionRequest::default() - .from(alice.address) - .to(contract_address) - .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))), - ) - .block(BlockNumberOrTag::Latest.into()) - .await?; - let decoded_after_increment: U256 = - Counter::numberCall::abi_decode_returns(number_after_increment.as_ref())?; - assert_eq!(decoded_after_increment, U256::from(43u64)); - - Ok(()) -} - -#[tokio::test] -async fn meter_bundle_errors_when_beacon_root_missing() -> Result<()> { - init_silenced_tracing(); - let harness = FlashblocksHarness::new().await?; - - let provider = harness.provider(); - let alice = &harness.accounts().alice; - let alice_secret = secret_from_hex(alice.private_key); - - // Deploy the contract so the pending flashblock can interact with storage - let deploy_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(0) - .gas_limit(DEPLOY_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .input(COUNTER_CREATION_BYTECODE.to_vec()) - .into_eip1559(); - let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); - harness.build_block_from_transactions(vec![deploy_bytes]).await?; - let contract_address = provider - .get_transaction_receipt(deploy_envelope.tx_hash()) - .await? - .ok_or_else(|| eyre!("deployment transaction missing receipt"))? - .inner - .contract_address - .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; - - let blockchain_provider = harness.blockchain_provider(); - let latest_number = provider.get_block_number().await?; - let latest_header = blockchain_provider - .sealed_header(latest_number)? - .ok_or_else(|| eyre!("missing header for block {}", latest_number))?; - - let pending_block_number = latest_header.number + 1; - let flash_call = Counter::setNumberCall { newNumber: U256::from(99u64) }; - let flash_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(1) - .gas_limit(CALL_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .to(contract_address) - .input(Bytes::from(flash_call.abi_encode())) - .into_eip1559(); - let (flash_envelope, flash_bytes) = envelope_from_signed(flash_signed); - let receipt = OpReceipt::Eip1559(Receipt { - status: true.into(), - cumulative_gas_used: 80_000, - logs: vec![], - }); - - // Pending flashblock omits the beacon root, so metering will report the validation error. - let flashblock = harness.build_flashblock( - pending_block_number, - latest_header.hash(), - B256::ZERO, - latest_header.timestamp + 2, - latest_header.gas_limit, - vec![(flash_bytes.clone(), Some((flash_envelope.tx_hash(), receipt.clone())))], - ); - - harness.send_flashblock(flashblock).await?; - - let bundle = Bundle { - txs: vec![flash_bytes.clone()], - block_number: pending_block_number, - flashblock_number_min: None, - flashblock_number_max: None, - min_timestamp: None, - max_timestamp: None, - reverting_tx_hashes: vec![], - replacement_uuid: None, - dropping_tx_hashes: vec![], - }; - - let metering_api = - MeteringApiImpl::new(blockchain_provider.clone(), harness.flashblocks_state()); - let result = MeteringApiServer::meter_bundle(&metering_api, bundle).await; - let err = result.expect_err("pending flashblock metering should surface missing beacon root"); - assert!( - err.message().contains("parent beacon block root missing"), - "unexpected error: {err:?}" - ); - - let pending_blocks = harness.flashblocks_state().get_pending_blocks(); - assert!(pending_blocks.is_some(), "expected flashblock to populate pending state"); - assert_eq!(pending_blocks.as_ref().unwrap().latest_flashblock_index(), 0); - - // Pending state should reflect the storage change even though the simulation failed. - let number_call = Counter::numberCall {}; - let pending_value = provider - .call( - OpTransactionRequest::default() - .from(alice.address) - .to(contract_address) - .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))), - ) - .block(BlockNumberOrTag::Pending.into()) - .await?; - let decoded_pending: U256 = Counter::numberCall::abi_decode_returns(pending_value.as_ref())?; - assert_eq!(decoded_pending, U256::from(99u64)); - - Ok(()) -} - -sol! { - contract Counter { - function setNumber(uint256 newNumber); - function increment(); - function number() view returns (uint256); - } -} - -const COUNTER_CREATION_BYTECODE: &[u8] = &hex!( - "6080604052348015600e575f5ffd5b506101e18061001c5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c80633fb5c1cb146100435780638381f58a1461005f578063d09de08a1461007d575b5f5ffd5b61005d600480360381019061005891906100e4565b610087565b005b610067610090565b604051610074919061011e565b60405180910390f35b610085610095565b005b805f8190555050565b5f5481565b5f5f8154809291906100a690610164565b9190505550565b5f5ffd5b5f819050919050565b6100c3816100b1565b81146100cd575f5ffd5b50565b5f813590506100de816100ba565b92915050565b5f602082840312156100f9576100f86100ad565b5b5f610106848285016100d0565b91505092915050565b610118816100b1565b82525050565b5f6020820190506101315f83018461010f565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016e826100b1565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036101a05761019f610137565b5b60018201905091905056fea26469706673582212204b710430bf5e9541dd320fc4eece1bf270f8b7d4835bba28f79ff7bd29904a2964736f6c634300081e0033" -); -const GWEI: u128 = 1_000_000_000; -const DEPLOY_GAS_LIMIT: u64 = 1_000_000; -const CALL_GAS_LIMIT: u64 = 150_000; - -fn envelope_from_signed(tx: TransactionSigned) -> (OpTxEnvelope, Bytes) { - let op_signed = OpTransactionSigned::Eip1559( - tx.as_eip1559().expect("transaction should be EIP-1559").clone(), - ); - let envelope = OpTxEnvelope::from(op_signed); - let bytes = Bytes::from(envelope.encoded_2718()); - (envelope, bytes) -} - -#[tokio::test] -async fn meter_bundle_reads_canonical_storage_without_mutation() -> Result<()> { - init_silenced_tracing(); - let harness = FlashblocksHarness::new().await?; - let alice = &harness.accounts().alice; - let alice_secret = secret_from_hex(alice.private_key); - let mut nonce = 0u64; - - let deploy_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(nonce) - .gas_limit(DEPLOY_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .input(COUNTER_CREATION_BYTECODE.to_vec()) - .into_eip1559(); - let (deploy_envelope, deploy_bytes) = envelope_from_signed(deploy_signed); - - harness.build_block_from_transactions(vec![deploy_bytes]).await?; - nonce += 1; - - let provider = harness.provider(); - let deploy_receipt = provider - .get_transaction_receipt(deploy_envelope.tx_hash()) - .await? - .ok_or_else(|| eyre!("deployment transaction missing receipt"))?; - let contract_address = deploy_receipt - .inner - .contract_address - .ok_or_else(|| eyre!("deployment receipt missing contract address"))?; - - let set_call = Counter::setNumberCall { newNumber: U256::from(42u64) }; - let set_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(nonce) - .gas_limit(CALL_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .to(contract_address) - .input(Bytes::from(set_call.abi_encode())) - .into_eip1559(); - let (_set_envelope, set_bytes) = envelope_from_signed(set_signed); - harness.build_block_from_transactions(vec![set_bytes]).await?; - nonce += 1; - - let increment_call = Counter::incrementCall {}; - let increment_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(nonce) - .gas_limit(CALL_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .to(contract_address) - .input(Bytes::from(increment_call.abi_encode())) - .into_eip1559(); - let (_increment_envelope, increment_bytes) = envelope_from_signed(increment_signed); - harness.build_block_from_transactions(vec![increment_bytes]).await?; - - nonce += 1; - - let storage_value = provider.get_storage_at(contract_address, U256::ZERO).await?; - assert_eq!(storage_value, U256::from(43u64)); - - let number_call = Counter::numberCall {}; - let call_request = OpTransactionRequest::default() - .from(alice.address) - .to(contract_address) - .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))); - let raw_number = provider.call(call_request).block(BlockNumberOrTag::Latest.into()).await?; - let decoded: U256 = Counter::numberCall::abi_decode_returns(raw_number.as_ref())?; - assert_eq!(decoded, U256::from(43u64)); - - // Meter another increment (nonce 3) to ensure meter_bundle sees the persisted state. - let meter_increment_call = Counter::incrementCall {}; - let meter_increment_signed = TransactionBuilder::default() - .signer(alice_secret) - .chain_id(BASE_CHAIN_ID) - .nonce(nonce) - .gas_limit(CALL_GAS_LIMIT) - .max_fee_per_gas(GWEI) - .max_priority_fee_per_gas(GWEI) - .to(contract_address) - .input(Bytes::from(meter_increment_call.abi_encode())) - .into_eip1559(); - let (_meter_increment_envelope, meter_increment_bytes) = - envelope_from_signed(meter_increment_signed.clone()); - - let bundle = Bundle { - txs: vec![meter_increment_bytes.clone()], - block_number: provider.get_block_number().await?, - flashblock_number_min: None, - flashblock_number_max: None, - min_timestamp: None, - max_timestamp: None, - reverting_tx_hashes: vec![], - replacement_uuid: None, - dropping_tx_hashes: vec![], - }; - let metering_api = - MeteringApiImpl::new(harness.blockchain_provider(), harness.flashblocks_state()); - let response = MeteringApiServer::meter_bundle(&metering_api, bundle) - .await - .map_err(|err| eyre!("meter_bundle rpc failed: {}", err))?; - - assert_eq!(response.results.len(), 1); - let metering_result = &response.results[0]; - assert_eq!(metering_result.to_address, Some(contract_address)); - assert!(metering_result.gas_used > 0); - - // Canonical state remains unchanged by the simulation. - let raw_number_after_sim = provider - .call( - OpTransactionRequest::default() - .from(alice.address) - .to(contract_address) - .input(TransactionInput::new(Bytes::from(number_call.abi_encode()))), - ) - .block(BlockNumberOrTag::Latest.into()) - .await?; - let decoded_after_sim: U256 = - Counter::numberCall::abi_decode_returns(raw_number_after_sim.as_ref())?; - assert_eq!(decoded_after_sim, U256::from(43u64)); - - Ok(()) -} diff --git a/crates/rpc/tests/meter.rs b/crates/rpc/tests/meter.rs index 64a6d514..ce421868 100644 --- a/crates/rpc/tests/meter.rs +++ b/crates/rpc/tests/meter.rs @@ -11,8 +11,11 @@ use base_reth_test_utils::create_provider_factory; use eyre::Context; use op_alloy_consensus::OpTxEnvelope; use rand::{SeedableRng, rngs::StdRng}; -use reth::revm::db::{BundleState, Cache}; -use reth::{api::NodeTypesWithDBAdapter, chainspec::EthChainSpec}; +use reth::{ + api::NodeTypesWithDBAdapter, + chainspec::EthChainSpec, + revm::db::{BundleState, Cache}, +}; use reth_db::{DatabaseEnv, test_utils::TempDatabase}; use reth_optimism_chainspec::{BASE_MAINNET, OpChainSpec, OpChainSpecBuilder}; use reth_optimism_node::OpNode; @@ -466,4 +469,3 @@ fn meter_bundle_requires_correct_layering_for_pending_nonce() -> eyre::Result<() Ok(()) } - From 8e88f611b8f5388a06daee3bfd5e24a9e2419478 Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Mon, 22 Dec 2025 16:45:09 -0600 Subject: [PATCH 25/26] Fix build issues after rebase - Add missing BundleRetention import in flashblocks processor - Add PendingBlocksAPI import in meter_rpc - Export FlashblockTrieCache, FlashblockTrieData, FlashblocksState from rpc crate - Update runner to require flashblocks for metering API - Restore test-utils files from main --- Cargo.lock | 1394 +++++++----------- crates/flashblocks/src/processor.rs | 1 + crates/rpc/src/base/meter_rpc.rs | 2 +- crates/rpc/src/base/mod.rs | 1 + crates/rpc/src/lib.rs | 3 +- crates/runner/src/extensions/rpc.rs | 17 +- crates/test-utils/README.md | 90 +- crates/test-utils/src/fixtures.rs | 11 +- crates/test-utils/src/flashblocks_harness.rs | 96 +- crates/test-utils/src/harness.rs | 36 +- crates/test-utils/src/lib.rs | 43 +- crates/test-utils/src/node.rs | 89 +- 12 files changed, 771 insertions(+), 1012 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bd983444..1361fc91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -151,6 +151,28 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-contract" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c493b2812943f7b58191063a8d13ea97c76099900869c08231e8eba3bf2f92" +dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-eth", + "alloy-sol-types", + "alloy-transport", + "futures", + "futures-util", + "serde_json", + "thiserror 2.0.17", +] + [[package]] name = "alloy-dyn-abi" version = "1.4.1" @@ -412,7 +434,7 @@ dependencies = [ "proptest-derive 0.6.0", "rand 0.9.2", "ruint", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "sha3", "tiny-keccak", @@ -477,7 +499,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "wasmtimer", ] @@ -524,7 +546,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer", @@ -759,6 +781,7 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" dependencies = [ + "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck", @@ -777,12 +800,14 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" dependencies = [ + "alloy-json-abi", "const-hex", "dunce", "heck", "macro-string", "proc-macro2", "quote", + "serde_json", "syn 2.0.110", "syn-solidity", ] @@ -826,7 +851,7 @@ dependencies = [ "serde_json", "thiserror 2.0.17", "tokio", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer", @@ -842,7 +867,7 @@ dependencies = [ "alloy-transport", "reqwest", "serde_json", - "tower 0.5.2", + "tower", "tracing", "url", ] @@ -880,7 +905,7 @@ dependencies = [ "rustls", "serde_json", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.26.2", "tracing", "ws_stream_wasm", ] @@ -926,6 +951,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.21" @@ -1322,6 +1353,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "async-compression" version = "0.4.33" @@ -1346,6 +1387,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-object-pool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ac0219111eb7bb7cb76d4cf2cb50c598e7ae549091d3616f9e95442c18486f" +dependencies = [ + "async-lock", + "event-listener", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -1424,109 +1475,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "aws-lc-rs" -version = "1.13.3" +name = "az" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" -dependencies = [ - "aws-lc-sys", - "zeroize", -] +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" [[package]] -name = "aws-lc-sys" -version = "0.30.0" +name = "backon" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ - "bindgen 0.69.5", - "cc", - "cmake", - "dunce", - "fs_extra", + "fastrand", + "tokio", ] [[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +name = "base-flashtypes" +version = "0.2.1" dependencies = [ - "async-trait", - "axum-core", + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", + "brotli", "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", + "derive_more", + "reth-optimism-primitives", + "rstest", "serde", - "sync_wrapper", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", + "serde_json", ] [[package]] -name = "az" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" - -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" +name = "base-reth-cli" +version = "0.2.1" dependencies = [ - "getrandom 0.2.16", - "instant", - "rand 0.8.5", + "reth", ] [[package]] -name = "backon" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" -dependencies = [ - "fastrand", - "tokio", -] +name = "base-reth-fbal" +version = "0.2.1" [[package]] -name = "base-reth-flashblocks-rpc" +name = "base-reth-flashblocks" version = "0.2.1" dependencies = [ "alloy-consensus", "alloy-eips", - "alloy-genesis", "alloy-primitives", "alloy-provider", "alloy-rpc-client", @@ -1534,12 +1531,11 @@ dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", "arc-swap", + "base-flashtypes", "base-reth-test-utils", - "brotli", + "criterion", "eyre", "futures-util", - "jsonrpsee 0.26.0", - "jsonrpsee-types 0.26.0", "metrics", "metrics-derive", "once_cell", @@ -1552,11 +1548,8 @@ dependencies = [ "reth-db-common", "reth-e2e-test-utils", "reth-evm", - "reth-exex", "reth-optimism-chainspec", - "reth-optimism-cli", "reth-optimism-evm", - "reth-optimism-node", "reth-optimism-primitives", "reth-optimism-rpc", "reth-primitives", @@ -1565,40 +1558,58 @@ dependencies = [ "reth-rpc-convert", "reth-rpc-eth-api", "reth-testing-utils", - "reth-tracing", "revm", "revm-database", - "rollup-boost", - "serde", - "serde_json", "tokio", - "tokio-stream", - "tokio-tungstenite", + "tokio-tungstenite 0.28.0", "tracing", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", "url", ] [[package]] -name = "base-reth-metering" +name = "base-reth-node" +version = "0.2.1" +dependencies = [ + "base-reth-cli", + "base-reth-runner", + "clap", + "once_cell", + "reth-cli-util", + "reth-optimism-cli", + "reth-optimism-node", +] + +[[package]] +name = "base-reth-rpc" version = "0.2.1" dependencies = [ "alloy-consensus", + "alloy-contract", "alloy-eips", "alloy-genesis", "alloy-primitives", "alloy-provider", "alloy-rpc-client", + "alloy-rpc-types", "alloy-rpc-types-engine", "alloy-rpc-types-eth", + "alloy-sol-macro", "alloy-sol-types", "arc-swap", - "base-reth-flashblocks-rpc", + "base-flashtypes", + "base-reth-flashblocks", "base-reth-test-utils", "eyre", + "futures-util", "hex-literal", - "jsonrpsee 0.26.0", + "httpmock", + "jsonrpsee", + "jsonrpsee-types", + "metrics", + "metrics-derive", "op-alloy-consensus", + "op-alloy-network", "op-alloy-rpc-types", "rand 0.9.2", "reth", @@ -1607,84 +1618,46 @@ dependencies = [ "reth-e2e-test-utils", "reth-evm", "reth-optimism-chainspec", - "reth-optimism-cli", "reth-optimism-evm", "reth-optimism-node", "reth-optimism-primitives", - "reth-primitives", "reth-primitives-traits", "reth-provider", + "reth-rpc", + "reth-rpc-eth-api", "reth-testing-utils", "reth-tracing", "reth-transaction-pool", "reth-trie-common", "revm", "revm-database", - "rollup-boost", "serde", "serde_json", "tips-core", "tokio", + "tokio-stream", + "tokio-tungstenite 0.28.0", "tracing", ] [[package]] -name = "base-reth-node" +name = "base-reth-runner" version = "0.2.1" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-hardforks", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-trie", - "base-reth-flashblocks-rpc", - "base-reth-metering", - "base-reth-transaction-tracing", - "chrono", - "clap", + "base-reth-flashblocks", + "base-reth-rpc", + "base-tracex", + "derive_more", "eyre", - "futures", "futures-util", - "itertools 0.14.0", - "jsonrpsee 0.26.0", - "metrics", - "metrics-derive", "once_cell", - "op-alloy-consensus", - "op-alloy-network", - "op-alloy-rpc-jsonrpsee", - "op-alloy-rpc-types", - "op-alloy-rpc-types-engine", - "reqwest", "reth", - "reth-cli-util", + "reth-db", "reth-exex", "reth-optimism-chainspec", - "reth-optimism-cli", - "reth-optimism-evm", "reth-optimism-node", - "reth-optimism-primitives", - "reth-optimism-rpc", - "reth-primitives", - "reth-rpc-convert", - "reth-rpc-eth-api", - "revm", - "revm-bytecode", - "rollup-boost", - "rustls", - "serde", - "serde_json", - "time", - "tokio", - "tokio-stream", - "tokio-tungstenite", "tracing", "url", - "uuid", ] [[package]] @@ -1692,6 +1665,7 @@ name = "base-reth-test-utils" version = "0.2.1" dependencies = [ "alloy-consensus", + "alloy-contract", "alloy-eips", "alloy-genesis", "alloy-primitives", @@ -1699,70 +1673,59 @@ dependencies = [ "alloy-rpc-client", "alloy-rpc-types", "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", "alloy-signer", "alloy-signer-local", - "base-reth-flashblocks-rpc", + "alloy-sol-macro", + "alloy-sol-types", + "base-flashtypes", + "base-reth-flashblocks", + "base-reth-rpc", "chrono", + "derive_more", "eyre", - "futures", "futures-util", - "jsonrpsee 0.26.0", + "jsonrpsee", "once_cell", - "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "reth", "reth-db", - "reth-db-common", "reth-e2e-test-utils", "reth-exex", "reth-ipc", "reth-node-core", "reth-optimism-chainspec", - "reth-optimism-cli", "reth-optimism-node", "reth-optimism-primitives", "reth-optimism-rpc", - "reth-primitives", "reth-primitives-traits", "reth-provider", "reth-rpc-layer", - "reth-testing-utils", "reth-tracing", - "rollup-boost", - "serde", "serde_json", "tokio", "tokio-stream", - "tokio-util", - "tower 0.5.2", - "tracing", - "tracing-subscriber 0.3.20", + "tower", + "tracing-subscriber 0.3.22", "url", ] [[package]] -name = "base-reth-transaction-tracing" +name = "base-tracex" version = "0.2.1" dependencies = [ "alloy-primitives", "chrono", + "derive_more", "eyre", "futures", "lru 0.16.2", "metrics", - "metrics-derive", "reth", "reth-exex", - "reth-optimism-cli", - "reth-optimism-node", - "reth-primitives", "reth-tracing", "tokio", - "tokio-stream", ] [[package]] @@ -1826,29 +1789,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.69.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" -dependencies = [ - "bitflags 2.10.0", - "cexpr", - "clang-sys", - "itertools 0.12.1", - "lazy_static", - "lazycell", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.110", - "which", -] - [[package]] name = "bindgen" version = "0.71.1" @@ -1862,7 +1802,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.1", + "rustc-hash", "shlex", "syn 2.0.110", ] @@ -1880,7 +1820,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.1", + "rustc-hash", "shlex", "syn 2.0.110", ] @@ -1986,7 +1926,7 @@ dependencies = [ "boa_string", "indexmap 2.12.0", "num-bigint", - "rustc-hash 2.1.1", + "rustc-hash", ] [[package]] @@ -2027,7 +1967,7 @@ dependencies = [ "portable-atomic", "rand 0.9.2", "regress", - "rustc-hash 2.1.1", + "rustc-hash", "ryu-js", "serde", "serde_json", @@ -2065,7 +2005,7 @@ dependencies = [ "indexmap 2.12.0", "once_cell", "phf", - "rustc-hash 2.1.1", + "rustc-hash", "static_assertions", ] @@ -2098,7 +2038,7 @@ dependencies = [ "num-bigint", "num-traits", "regress", - "rustc-hash 2.1.1", + "rustc-hash", ] [[package]] @@ -2110,7 +2050,7 @@ dependencies = [ "fast-float2", "itoa", "paste", - "rustc-hash 2.1.1", + "rustc-hash", "ryu-js", "static_assertions", ] @@ -2298,6 +2238,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "castaway" version = "0.2.4" @@ -2359,6 +2305,33 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.4.4" @@ -2420,15 +2393,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - [[package]] name = "coins-bip32" version = "0.12.0" @@ -2605,9 +2569,9 @@ dependencies = [ [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -2696,6 +2660,44 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "futures", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -3067,22 +3069,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ "convert_case", "proc-macro2", "quote", + "rustc_version 0.4.1", "syn 2.0.110", "unicode-xid", ] @@ -3221,12 +3224,6 @@ dependencies = [ "litrs", ] -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - [[package]] name = "dunce" version = "1.0.5" @@ -3340,21 +3337,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - [[package]] name = "enr" version = "0.13.0" @@ -3686,12 +3668,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -4020,6 +3996,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hash-db" version = "0.15.2" @@ -4089,6 +4076,30 @@ dependencies = [ "num-traits", ] +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64 0.22.1", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + [[package]] name = "heck" version = "0.5.0" @@ -4188,15 +4199,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "http" version = "1.3.1" @@ -4249,6 +4251,40 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "httpmock" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511f510e9b1888d67f10bab4397f8b019d2a9b249a2c10acbce2d705b1b32e26" +dependencies = [ + "assert-json-diff", + "async-object-pool", + "async-trait", + "base64 0.22.1", + "bytes", + "crossbeam-utils", + "form_urlencoded", + "futures-timer", + "futures-util", + "headers", + "http", + "http-body-util", + "hyper", + "hyper-util", + "path-tree", + "regex", + "serde", + "serde_json", + "serde_regex", + "similar", + "stringmetrics", + "tabwriter", + "thiserror 2.0.17", + "tokio", + "tracing", + "url", +] + [[package]] name = "human_bytes" version = "0.4.3" @@ -4360,12 +4396,10 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", - "system-configuration", + "socket2 0.6.1", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -4380,7 +4414,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.57.0", + "windows-core 0.62.2", ] [[package]] @@ -4639,15 +4673,6 @@ dependencies = [ "syn 2.0.110", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "interprocess" version = "2.2.3" @@ -4700,6 +4725,17 @@ dependencies = [ "serde", ] +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -4715,15 +4751,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -4790,20 +4817,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "jsonrpsee" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "jsonrpsee-core 0.25.1", - "jsonrpsee-http-client 0.25.1", - "jsonrpsee-proc-macros 0.25.1", - "jsonrpsee-server 0.25.1", - "jsonrpsee-types 0.25.1", - "tokio", - "tracing", -] - [[package]] name = "jsonrpsee" version = "0.26.0" @@ -4811,11 +4824,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.26.0", - "jsonrpsee-http-client 0.26.0", - "jsonrpsee-proc-macros 0.26.0", - "jsonrpsee-server 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", "tokio", @@ -4833,7 +4846,7 @@ dependencies = [ "futures-util", "gloo-net", "http", - "jsonrpsee-core 0.26.0", + "jsonrpsee-core", "pin-project", "rustls", "rustls-pki-types", @@ -4847,30 +4860,6 @@ dependencies = [ "url", ] -[[package]] -name = "jsonrpsee-core" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "jsonrpsee-types 0.25.1", - "parking_lot", - "pin-project", - "rand 0.9.2", - "rustc-hash 2.1.1", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tower 0.5.2", - "tracing", -] - [[package]] name = "jsonrpsee-core" version = "0.26.0" @@ -4884,43 +4873,21 @@ dependencies = [ "http", "http-body", "http-body-util", - "jsonrpsee-types 0.26.0", + "jsonrpsee-types", "parking_lot", "pin-project", "rand 0.9.2", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "serde_json", "thiserror 2.0.17", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "wasm-bindgen-futures", ] -[[package]] -name = "jsonrpsee-http-client" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "base64 0.22.1", - "http-body", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonrpsee-core 0.25.1", - "jsonrpsee-types 0.25.1", - "rustls", - "rustls-platform-verifier", - "serde", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tower 0.5.2", - "url", -] - [[package]] name = "jsonrpsee-http-client" version = "0.26.0" @@ -4932,30 +4899,18 @@ dependencies = [ "hyper", "hyper-rustls", "hyper-util", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "rustls", "rustls-platform-verifier", "serde", "serde_json", "thiserror 2.0.17", "tokio", - "tower 0.5.2", + "tower", "url", ] -[[package]] -name = "jsonrpsee-proc-macros" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.110", -] - [[package]] name = "jsonrpsee-proc-macros" version = "0.26.0" @@ -4969,32 +4924,6 @@ dependencies = [ "syn 2.0.110", ] -[[package]] -name = "jsonrpsee-server" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "jsonrpsee-core 0.25.1", - "jsonrpsee-types 0.25.1", - "pin-project", - "route-recognizer", - "serde", - "serde_json", - "soketto", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tokio-util", - "tower 0.5.2", - "tracing", -] - [[package]] name = "jsonrpsee-server" version = "0.26.0" @@ -5007,8 +4936,8 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "pin-project", "route-recognizer", "serde", @@ -5018,21 +4947,10 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "tower 0.5.2", + "tower", "tracing", ] -[[package]] -name = "jsonrpsee-types" -version = "0.25.1" -source = "git+https://github.com/paritytech/jsonrpsee?rev=f04afa740e55db60dce20d9839758792f035ffff#f04afa740e55db60dce20d9839758792f035ffff" -dependencies = [ - "http", - "serde", - "serde_json", - "thiserror 2.0.17", -] - [[package]] name = "jsonrpsee-types" version = "0.26.0" @@ -5052,9 +4970,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" dependencies = [ "jsonrpsee-client-transport", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", - "tower 0.5.2", + "jsonrpsee-core", + "jsonrpsee-types", + "tower", ] [[package]] @@ -5065,9 +4983,9 @@ checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" dependencies = [ "http", "jsonrpsee-client-transport", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", - "tower 0.5.2", + "jsonrpsee-core", + "jsonrpsee-types", + "tower", "url", ] @@ -5146,12 +5064,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.177" @@ -5305,7 +5217,7 @@ dependencies = [ "generator", "scoped-tls", "tracing", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", ] [[package]] @@ -5406,12 +5318,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "memchr" version = "2.7.6" @@ -5438,9 +5344,9 @@ dependencies = [ [[package]] name = "metrics" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dea7ac8057892855ec285c440160265225438c3c45072613c25a4b26e98ef5" +checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" dependencies = [ "ahash", "portable-atomic", @@ -5465,18 +5371,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" dependencies = [ "base64 0.22.1", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", "indexmap 2.12.0", - "ipnet", "metrics", "metrics-util", "quanta", "thiserror 1.0.69", - "tokio", - "tracing", ] [[package]] @@ -5501,15 +5400,11 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8496cc523d1f94c1385dd8f0f0c2c480b2b8aeccb5b7e4485ad6365523ae376" dependencies = [ - "aho-corasick", "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.15.5", - "indexmap 2.12.0", "metrics", - "ordered-float", "quanta", - "radix_trie", "rand 0.9.2", "rand_xoshiro", "sketches-ddsketch", @@ -5601,13 +5496,10 @@ version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ - "async-lock", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", "equivalent", - "event-listener", - "futures-util", "parking_lot", "portable-atomic", "rustc_version 0.4.1", @@ -5680,15 +5572,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - [[package]] name = "nom" version = "7.1.3" @@ -5894,6 +5777,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "op-alloy-consensus" version = "0.22.3" @@ -5943,7 +5832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6911db73a4bf59bf8a963dec153ada1057fa426fdc35e0b35fe82657af3501a3" dependencies = [ "alloy-primitives", - "jsonrpsee 0.26.0", + "jsonrpsee", ] [[package]] @@ -6047,20 +5936,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236e667b670a5cdf90c258f5a55794ec5ac5027e960c224bff8367a59e1e6426" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 2.0.17", - "tracing", -] - [[package]] name = "opentelemetry" version = "0.31.0" @@ -6075,20 +5950,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "opentelemetry-http" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8863faf2910030d139fb48715ad5ff2f35029fc5f244f6d5f689ddcf4d26253" -dependencies = [ - "async-trait", - "bytes", - "http", - "opentelemetry 0.28.0", - "reqwest", - "tracing", -] - [[package]] name = "opentelemetry-http" version = "0.31.0" @@ -6098,32 +5959,10 @@ dependencies = [ "async-trait", "bytes", "http", - "opentelemetry 0.31.0", + "opentelemetry", "reqwest", ] -[[package]] -name = "opentelemetry-otlp" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bef114c6d41bea83d6dc60eb41720eedd0261a67af57b66dd2b84ac46c01d91" -dependencies = [ - "async-trait", - "futures-core", - "http", - "opentelemetry 0.28.0", - "opentelemetry-http 0.28.0", - "opentelemetry-proto 0.28.0", - "opentelemetry_sdk 0.28.0", - "prost 0.13.5", - "reqwest", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tonic 0.12.3", - "tracing", -] - [[package]] name = "opentelemetry-otlp" version = "0.31.0" @@ -6131,43 +5970,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" dependencies = [ "http", - "opentelemetry 0.31.0", - "opentelemetry-http 0.31.0", - "opentelemetry-proto 0.31.0", - "opentelemetry_sdk 0.31.0", - "prost 0.14.1", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", "reqwest", "thiserror 2.0.17", "tokio", - "tonic 0.14.2", + "tonic", "tracing", ] -[[package]] -name = "opentelemetry-proto" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8870d3024727e99212eb3bb1762ec16e255e3e6f58eeb3dc8db1aa226746d" -dependencies = [ - "base64 0.22.1", - "hex", - "opentelemetry 0.28.0", - "opentelemetry_sdk 0.28.0", - "prost 0.13.5", - "serde", - "tonic 0.12.3", -] - [[package]] name = "opentelemetry-proto" version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ - "opentelemetry 0.31.0", - "opentelemetry_sdk 0.31.0", - "prost 0.14.1", - "tonic 0.14.2", + "opentelemetry", + "opentelemetry_sdk", + "prost", + "tonic", "tonic-prost", ] @@ -6177,27 +6001,6 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e62e29dfe041afb8ed2a6c9737ab57db4907285d999ef8ad3a59092a36bdc846" -[[package]] -name = "opentelemetry_sdk" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84dfad6042089c7fc1f6118b7040dc2eb4ab520abbf410b79dc481032af39570" -dependencies = [ - "async-trait", - "futures-channel", - "futures-executor", - "futures-util", - "glob", - "opentelemetry 0.28.0", - "percent-encoding", - "rand 0.8.5", - "serde_json", - "thiserror 2.0.17", - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "opentelemetry_sdk" version = "0.31.0" @@ -6207,7 +6010,7 @@ dependencies = [ "futures-channel", "futures-executor", "futures-util", - "opentelemetry 0.31.0", + "opentelemetry", "percent-encoding", "rand 0.9.2", "thiserror 2.0.17", @@ -6219,15 +6022,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-float" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" -dependencies = [ - "num-traits", -] - [[package]] name = "p256" version = "0.13.2" @@ -6315,6 +6109,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "path-tree" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a97453bc21a968f722df730bfe11bd08745cb50d1300b0df2bda131dece136" +dependencies = [ + "smallvec", +] + [[package]] name = "pbkdf2" version = "0.12.2" @@ -6456,9 +6259,37 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" name = "plain_hasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e19e6491bdde87c2c43d70f4c194bc8a758f2eb732df00f61e43f7362e3b4cc" +checksum = "1e19e6491bdde87c2c43d70f4c194bc8a758f2eb732df00f61e43f7362e3b4cc" +dependencies = [ + "crunchy", +] + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ - "crunchy", + "plotters-backend", ] [[package]] @@ -6513,16 +6344,6 @@ dependencies = [ "yansi", ] -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.110", -] - [[package]] name = "primeorder" version = "0.13.6" @@ -6680,16 +6501,6 @@ dependencies = [ "syn 2.0.110", ] -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive 0.13.5", -] - [[package]] name = "prost" version = "0.14.1" @@ -6697,20 +6508,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", - "prost-derive 0.14.1", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.110", + "prost-derive", ] [[package]] @@ -6778,9 +6576,9 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2 0.6.1", "thiserror 2.0.17", "tokio", "tracing", @@ -6798,7 +6596,7 @@ dependencies = [ "lru-slab", "rand 0.9.2", "ring", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "rustls-pki-types", "slab", @@ -6817,7 +6615,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.1", "tracing", "windows-sys 0.60.2", ] @@ -6843,16 +6641,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -7079,6 +6867,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "reqwest" version = "0.12.24" @@ -7087,11 +6881,9 @@ checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", - "encoding_rs", "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "http-body-util", @@ -7101,7 +6893,6 @@ dependencies = [ "hyper-util", "js-sys", "log", - "mime", "native-tls", "percent-encoding", "pin-project-lite", @@ -7117,7 +6908,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", - "tower 0.5.2", + "tower", "tower-http", "tower-service", "url", @@ -7487,7 +7278,7 @@ dependencies = [ "reth-static-file-types", "reth-storage-errors", "reth-tracing", - "rustc-hash 2.1.1", + "rustc-hash", "strum 0.27.2", "sysinfo", "tempfile", @@ -7693,7 +7484,7 @@ dependencies = [ "derive_more", "eyre", "futures-util", - "jsonrpsee 0.26.0", + "jsonrpsee", "reth-chainspec", "reth-cli-commands", "reth-config", @@ -8099,7 +7890,7 @@ dependencies = [ "alloy-primitives", "auto_impl", "once_cell", - "rustc-hash 2.1.1", + "rustc-hash", ] [[package]] @@ -8308,7 +8099,7 @@ dependencies = [ "alloy-rpc-types-debug", "eyre", "futures", - "jsonrpsee 0.26.0", + "jsonrpsee", "pretty_assertions", "reth-engine-primitives", "reth-evm", @@ -8334,14 +8125,14 @@ dependencies = [ "futures", "futures-util", "interprocess", - "jsonrpsee 0.26.0", + "jsonrpsee", "pin-project", "serde_json", "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", - "tower 0.5.2", + "tower", "tracing", ] @@ -8447,7 +8238,7 @@ dependencies = [ "reth-tasks", "reth-tokio-util", "reth-transaction-pool", - "rustc-hash 2.1.1", + "rustc-hash", "schnellru", "secp256k1 0.30.0", "serde", @@ -8592,7 +8383,7 @@ dependencies = [ "eyre", "fdlimit", "futures", - "jsonrpsee 0.26.0", + "jsonrpsee", "rayon", "reth-basic-payload-builder", "reth-chain-state", @@ -8755,7 +8546,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tokio-tungstenite", + "tokio-tungstenite 0.26.2", "tracing", "url", ] @@ -8791,7 +8582,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "eyre", "http", - "jsonrpsee-server 0.26.0", + "jsonrpsee-server", "metrics", "metrics-exporter-prometheus", "metrics-process", @@ -8802,7 +8593,7 @@ dependencies = [ "reth-tasks", "tikv-jemalloc-ctl", "tokio", - "tower 0.5.2", + "tower", "tracing", ] @@ -8983,7 +8774,7 @@ dependencies = [ "serde", "serde_json", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.26.2", "tracing", "url", ] @@ -9124,9 +8915,9 @@ dependencies = [ "derive_more", "eyre", "futures", - "jsonrpsee 0.26.0", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee", + "jsonrpsee-core", + "jsonrpsee-types", "metrics", "op-alloy-consensus", "op-alloy-network", @@ -9162,7 +8953,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", ] @@ -9397,7 +9188,7 @@ dependencies = [ "reth-prune-types", "reth-static-file-types", "reth-tokio-util", - "rustc-hash 2.1.1", + "rustc-hash", "thiserror 2.0.17", "tokio", "tracing", @@ -9511,8 +9302,8 @@ dependencies = [ "http-body", "hyper", "itertools 0.14.0", - "jsonrpsee 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee", + "jsonrpsee-types", "jsonwebtoken", "parking_lot", "pin-project", @@ -9551,7 +9342,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "tracing-futures", ] @@ -9576,7 +9367,7 @@ dependencies = [ "alloy-rpc-types-trace", "alloy-rpc-types-txpool", "alloy-serde", - "jsonrpsee 0.26.0", + "jsonrpsee", "reth-chain-state", "reth-engine-primitives", "reth-network-peers", @@ -9593,7 +9384,7 @@ dependencies = [ "alloy-provider", "dyn-clone", "http", - "jsonrpsee 0.26.0", + "jsonrpsee", "metrics", "pin-project", "reth-chain-state", @@ -9618,7 +9409,7 @@ dependencies = [ "thiserror 2.0.17", "tokio", "tokio-util", - "tower 0.5.2", + "tower", "tower-http", "tracing", ] @@ -9636,7 +9427,7 @@ dependencies = [ "alloy-signer", "auto_impl", "dyn-clone", - "jsonrpsee-types 0.26.0", + "jsonrpsee-types", "op-alloy-consensus", "op-alloy-network", "op-alloy-rpc-types", @@ -9659,8 +9450,8 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", "async-trait", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "metrics", "parking_lot", "reth-chainspec", @@ -9700,8 +9491,8 @@ dependencies = [ "auto_impl", "dyn-clone", "futures", - "jsonrpsee 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee", + "jsonrpsee-types", "parking_lot", "reth-chain-state", "reth-chainspec", @@ -9741,8 +9532,8 @@ dependencies = [ "derive_more", "futures", "itertools 0.14.0", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "metrics", "rand 0.9.2", "reqwest", @@ -9778,9 +9569,9 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "alloy-rpc-types-engine", "http", - "jsonrpsee-http-client 0.26.0", + "jsonrpsee-http-client", "pin-project", - "tower 0.5.2", + "tower", "tower-http", "tracing", ] @@ -9793,8 +9584,8 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", - "jsonrpsee-core 0.26.0", - "jsonrpsee-types 0.26.0", + "jsonrpsee-core", + "jsonrpsee-types", "reth-errors", "reth-network-api", "serde", @@ -10018,7 +9809,7 @@ dependencies = [ "tracing-appender", "tracing-journald", "tracing-logfmt", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", "url", ] @@ -10029,13 +9820,13 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.9.3#27a8c0f5a6dfb27dea8 dependencies = [ "clap", "eyre", - "opentelemetry 0.31.0", - "opentelemetry-otlp 0.31.0", + "opentelemetry", + "opentelemetry-otlp", "opentelemetry-semantic-conventions", - "opentelemetry_sdk 0.31.0", + "opentelemetry_sdk", "tracing", - "tracing-opentelemetry 0.32.0", - "tracing-subscriber 0.3.20", + "tracing-opentelemetry", + "tracing-subscriber 0.3.22", "url", ] @@ -10069,7 +9860,7 @@ dependencies = [ "reth-tasks", "revm-interpreter", "revm-primitives", - "rustc-hash 2.1.1", + "rustc-hash", "schnellru", "serde", "serde_json", @@ -10525,64 +10316,39 @@ dependencies = [ ] [[package]] -name = "rollup-boost" -version = "0.1.0" -source = "git+http://github.com/flashbots/rollup-boost?rev=v0.7.11#196237bab2a02298de994b439e0455abb1ac512f" +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + +[[package]] +name = "rstest" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ - "alloy-primitives", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", - "backoff", - "bytes", - "clap", - "dashmap 6.1.0", - "dotenvy", - "eyre", - "futures", - "http", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "jsonrpsee 0.25.1", - "lru 0.16.2", - "metrics", - "metrics-derive", - "metrics-exporter-prometheus", - "metrics-util", - "moka", - "op-alloy-rpc-types-engine", - "opentelemetry 0.28.0", - "opentelemetry-otlp 0.28.0", - "opentelemetry_sdk 0.28.0", - "parking_lot", - "paste", - "reth-optimism-payload-builder", - "rustls", - "serde", - "serde_json", - "sha2", - "thiserror 2.0.17", - "tokio", - "tokio-tungstenite", - "tokio-util", - "tower 0.5.2", - "tower-http", - "tracing", - "tracing-opentelemetry 0.29.0", - "tracing-subscriber 0.3.20", - "url", - "uuid", - "vergen", - "vergen-git2", + "futures-timer", + "futures-util", + "rstest_macros", ] [[package]] -name = "route-recognizer" -version = "0.3.1" +name = "rstest_macros" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" +dependencies = [ + "cfg-if", + "glob", + "proc-macro-crate", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.1", + "syn 2.0.110", + "unicode-ident", +] [[package]] name = "rug" @@ -10631,12 +10397,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -10711,7 +10471,6 @@ version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ - "aws-lc-rs", "log", "once_cell", "ring", @@ -10776,7 +10535,6 @@ version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -11068,6 +10826,16 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_regex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf" +dependencies = [ + "regex", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -11141,12 +10909,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "sha2" version = "0.10.9" @@ -11248,6 +11010,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "simple_asn1" version = "0.6.3" @@ -11382,6 +11150,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringmetrics" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3c8667cd96245cbb600b8dec5680a7319edd719c5aa2b5d23c6bff94f39765" + [[package]] name = "strsim" version = "0.11.1" @@ -11505,24 +11279,12 @@ dependencies = [ ] [[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" +name = "tabwriter" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +checksum = "fce91f2f0ec87dff7e6bcbbeb267439aa1188703003c6055193c821487400432" dependencies = [ - "core-foundation-sys", - "libc", + "unicode-width 0.2.0", ] [[package]] @@ -11727,6 +11489,16 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.10.0" @@ -11755,7 +11527,7 @@ dependencies = [ "op-alloy-flz", "serde", "tracing", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", "uuid", ] @@ -11827,17 +11599,27 @@ checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" dependencies = [ "futures-util", "log", - "native-tls", "rustls", "rustls-native-certs", "rustls-pki-types", "tokio", - "tokio-native-tls", "tokio-rustls", - "tungstenite", + "tungstenite 0.26.2", "webpki-roots 0.26.11", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.28.0", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -11924,36 +11706,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum", - "base64 0.22.1", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost 0.13.5", - "socket2 0.5.10", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "tonic" version = "0.14.2" @@ -11974,7 +11726,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -11987,28 +11739,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", - "prost 0.14.1", - "tonic 0.14.2", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", + "prost", + "tonic", ] [[package]] @@ -12055,7 +11787,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -12076,9 +11808,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -12095,14 +11827,14 @@ dependencies = [ "crossbeam-channel", "thiserror 1.0.69", "time", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", ] [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -12111,9 +11843,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -12137,7 +11869,7 @@ checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657" dependencies = [ "libc", "tracing-core", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", ] [[package]] @@ -12160,25 +11892,7 @@ dependencies = [ "time", "tracing", "tracing-core", - "tracing-subscriber 0.3.20", -] - -[[package]] -name = "tracing-opentelemetry" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721f2d2569dce9f3dfbbddee5906941e953bfcdf736a62da3377f5751650cc36" -dependencies = [ - "js-sys", - "once_cell", - "opentelemetry 0.28.0", - "opentelemetry_sdk 0.28.0", - "smallvec", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber 0.3.20", - "web-time", + "tracing-subscriber 0.3.22", ] [[package]] @@ -12188,15 +11902,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e6e5658463dd88089aba75c7791e1d3120633b1bfde22478b28f625a9bb1b8e" dependencies = [ "js-sys", - "opentelemetry 0.31.0", - "opentelemetry_sdk 0.31.0", + "opentelemetry", + "opentelemetry_sdk", "rustversion", "smallvec", "thiserror 2.0.17", "tracing", "tracing-core", "tracing-log", - "tracing-subscriber 0.3.20", + "tracing-subscriber 0.3.22", "web-time", ] @@ -12221,9 +11935,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -12298,7 +12012,6 @@ dependencies = [ "http", "httparse", "log", - "native-tls", "rand 0.9.2", "rustls", "rustls-pki-types", @@ -12307,6 +12020,23 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.2", + "sha1", + "thiserror 2.0.17", + "utf-8", +] + [[package]] name = "typenum" version = "1.19.0" @@ -12463,7 +12193,6 @@ dependencies = [ "getrandom 0.3.4", "js-sys", "serde", - "sha1_smol", "wasm-bindgen", ] @@ -12721,18 +12450,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", -] - [[package]] name = "widestring" version = "1.2.1" @@ -12761,7 +12478,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -12959,17 +12676,6 @@ dependencies = [ "windows-link 0.2.1", ] -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-result" version = "0.1.2" diff --git a/crates/flashblocks/src/processor.rs b/crates/flashblocks/src/processor.rs index cc8f84e7..1701c573 100644 --- a/crates/flashblocks/src/processor.rs +++ b/crates/flashblocks/src/processor.rs @@ -36,6 +36,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::Sender, mpsc::UnboundedReceiver}; use tracing::{debug, error, info, warn}; diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index f7903aae..244c9438 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use alloy_consensus::{Header, Sealed}; use alloy_primitives::U256; -use base_reth_flashblocks::FlashblocksAPI; +use base_reth_flashblocks::{FlashblocksAPI, PendingBlocksAPI}; use jsonrpsee::core::{RpcResult, async_trait}; use reth::providers::BlockReaderIdExt; use reth_optimism_chainspec::OpChainSpec; diff --git a/crates/rpc/src/base/mod.rs b/crates/rpc/src/base/mod.rs index b4097c40..f7bee2a8 100644 --- a/crates/rpc/src/base/mod.rs +++ b/crates/rpc/src/base/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod flashblock_trie_cache; pub(crate) mod meter; pub(crate) mod meter_rpc; pub(crate) mod pubsub; diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index 5a5db0a0..9f0a18aa 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -8,7 +8,8 @@ pub use tips_core::types::{Bundle, MeterBundleResponse, TransactionResult}; mod base; pub use base::{ - meter::meter_bundle, + flashblock_trie_cache::{FlashblockTrieCache, FlashblockTrieData}, + meter::{FlashblocksState, meter_bundle}, meter_rpc::MeteringApiImpl, pubsub::{EthPubSub, EthPubSubApiServer}, traits::{MeteringApiServer, TransactionStatusApiServer}, diff --git a/crates/runner/src/extensions/rpc.rs b/crates/runner/src/extensions/rpc.rs index 164a3af5..5607e407 100644 --- a/crates/runner/src/extensions/rpc.rs +++ b/crates/runner/src/extensions/rpc.rs @@ -49,12 +49,6 @@ impl BaseNodeExtension for BaseRpcExtension { let sequencer_rpc = self.sequencer_rpc.clone(); builder.extend_rpc_modules(move |ctx| { - if metering_enabled { - info!(message = "Starting Metering RPC"); - let metering_api = MeteringApiImpl::new(ctx.provider().clone()); - ctx.modules.merge_configured(metering_api.into_rpc())?; - } - let proxy_api = TransactionStatusApiImpl::new(sequencer_rpc.clone(), ctx.pool().clone()) .expect("Failed to create transaction status proxy"); @@ -77,6 +71,14 @@ impl BaseNodeExtension for BaseRpcExtension { let mut flashblocks_client = FlashblocksSubscriber::new(fb.clone(), ws_url); flashblocks_client.start(); + // Metering API requires flashblocks state to access pending blocks for bundle simulation + if metering_enabled { + info!(message = "Starting Metering RPC"); + let metering_api = + MeteringApiImpl::new(ctx.provider().clone(), fb.clone()); + ctx.modules.merge_configured(metering_api.into_rpc())?; + } + let api_ext = EthApiExt::new( ctx.registry.eth_api().clone(), ctx.registry.eth_handlers().filter.clone(), @@ -91,6 +93,9 @@ impl BaseNodeExtension for BaseRpcExtension { ctx.modules.replace_configured(eth_pubsub.into_rpc())?; } else { info!(message = "flashblocks integration is disabled"); + if metering_enabled { + info!(message = "Metering RPC requires flashblocks to be enabled - skipping"); + } } Ok(()) diff --git a/crates/test-utils/README.md b/crates/test-utils/README.md index 32842b2c..2b735f2c 100644 --- a/crates/test-utils/README.md +++ b/crates/test-utils/README.md @@ -14,8 +14,8 @@ This crate provides reusable testing utilities for integration tests across the ## Quick Start -```rust -use base_reth_test_utils::harness::TestHarness; +```rust,ignore +use base_reth_test_utils::TestHarness; #[tokio::test] async fn test_example() -> eyre::Result<()> { @@ -38,7 +38,7 @@ async fn test_example() -> eyre::Result<()> { The framework follows a three-layer architecture: -``` +```text ┌─────────────────────────────────────┐ │ TestHarness │ ← Orchestration layer (tests use this) │ - Coordinates node + engine │ @@ -67,8 +67,8 @@ The framework follows a three-layer architecture: The main entry point for integration tests that only need canonical chain control. Combines node, engine, and accounts into a single interface. -```rust -use base_reth_test_utils::harness::TestHarness; +```rust,ignore +use base_reth_test_utils::TestHarness; use alloy_primitives::Bytes; #[tokio::test] @@ -116,8 +116,8 @@ async fn test_harness() -> eyre::Result<()> { In-process Optimism node with Base Sepolia configuration. -```rust -use base_reth_test_utils::node::LocalNode; +```rust,ignore +use base_reth_test_utils::{LocalNode, default_launcher}; #[tokio::test] async fn test_node() -> eyre::Result<()> { @@ -139,8 +139,8 @@ async fn test_node() -> eyre::Result<()> { For flashblocks-enabled nodes, use `FlashblocksLocalNode`: -```rust -use base_reth_test_utils::node::FlashblocksLocalNode; +```rust,ignore +use base_reth_test_utils::FlashblocksLocalNode; let node = FlashblocksLocalNode::new().await?; let pending_state = node.flashblocks_state(); @@ -153,8 +153,8 @@ node.send_flashblock(flashblock).await?; Type-safe Engine API client wrapping raw CL operations. -```rust -use base_reth_test_utils::engine::EngineApi; +```rust,ignore +use base_reth_test_utils::EngineApi; use alloy_primitives::B256; use op_alloy_rpc_types_engine::OpPayloadAttributes; @@ -178,9 +178,8 @@ let status = engine.new_payload(payload, vec![], parent_root, requests).await?; Hardcoded test accounts with deterministic addresses (Anvil-compatible). -```rust -use base_reth_test_utils::accounts::TestAccounts; -use base_reth_test_utils::harness::TestHarness; +```rust,ignore +use base_reth_test_utils::{TestAccounts, TestHarness}; let accounts = TestAccounts::new(); @@ -210,8 +209,8 @@ Each account includes: Use `FlashblocksHarness` when you need `send_flashblock` and access to the in-memory pending state. -```rust -use base_reth_test_utils::flashblocks_harness::FlashblocksHarness; +```rust,ignore +use base_reth_test_utils::FlashblocksHarness; #[tokio::test] async fn test_flashblocks() -> eyre::Result<()> { @@ -226,46 +225,45 @@ async fn test_flashblocks() -> eyre::Result<()> { } ``` -Need to craft a flashblock (including intentionally malformed payloads)? Use `build_flashblock`: - -```rust -let flashblock = harness.build_flashblock( - next_block_number, - parent_hash, - B256::ZERO, // force missing beacon root to test validation - timestamp, - gas_limit, - vec![(tx_bytes, Some((tx_hash, receipt)))], -); -harness.send_flashblock(flashblock).await?; -``` +`FlashblocksHarness` derefs to the base `TestHarness`, so you can keep using methods like `provider()`, `build_block_from_transactions`, etc. -`FlashblocksHarness` derefs to the base `TestHarness`, so you can keep using methods like `provider()`, `build_block_from_transactions`, etc. Test flashblocks delivery without WebSocket connections by constructing payloads and sending them through `FlashblocksHarness` (or the lower-level `FlashblocksLocalNode`). +Test flashblocks delivery without WebSocket connections by constructing payloads and sending them through `FlashblocksHarness` (or the lower-level `FlashblocksLocalNode`). ## Configuration Constants -Key constants defined in `harness.rs`: - -```rust -const BLOCK_TIME_SECONDS: u64 = 2; // Base L2 block time -const GAS_LIMIT: u64 = 200_000_000; // Default gas limit -const NODE_STARTUP_DELAY_MS: u64 = 500; // IPC endpoint initialization -const BLOCK_BUILD_DELAY_MS: u64 = 100; // Payload construction wait +Key constants are exported from the crate root: + +```rust,ignore +use base_reth_test_utils::{ + BASE_CHAIN_ID, // Chain ID for Base Sepolia (84532) + BLOCK_TIME_SECONDS, // Base L2 block time (2 seconds) + GAS_LIMIT, // Default gas limit (200M) + NODE_STARTUP_DELAY_MS, // IPC endpoint initialization (500ms) + BLOCK_BUILD_DELAY_MS, // Payload construction wait (100ms) + L1_BLOCK_INFO_DEPOSIT_TX, // Pre-captured L1 block info deposit + L1_BLOCK_INFO_DEPOSIT_TX_HASH, + DEFAULT_JWT_SECRET, // All-zeros JWT for local testing +}; ``` ## File Structure -``` +```text test-utils/ ├── src/ -│ ├── lib.rs # Public API and re-exports -│ ├── accounts.rs # Test account definitions -│ ├── node.rs # LocalNode (EL wrapper) -│ ├── engine.rs # EngineApi (CL wrapper) +│ ├── lib.rs # Public API and re-exports +│ ├── accounts.rs # Test account definitions +│ ├── constants.rs # Shared constants (chain ID, timing, etc.) +│ ├── contracts.rs # Solidity contract bindings +│ ├── engine.rs # EngineApi (CL wrapper) +│ ├── fixtures.rs # Genesis loading, provider factories +│ ├── flashblocks_harness.rs # FlashblocksHarness + helpers │ ├── harness.rs # TestHarness (orchestration) -│ └── flashblocks_harness.rs # FlashblocksHarness + helpers +│ ├── node.rs # LocalNode (EL wrapper) +│ └── tracing.rs # Tracing initialization helpers ├── assets/ -│ └── genesis.json # Base Sepolia genesis +│ └── genesis.json # Base Sepolia genesis +├── contracts/ # Solidity sources + compiled artifacts └── Cargo.toml ``` @@ -280,8 +278,8 @@ base-reth-test-utils.workspace = true Import in tests: -```rust -use base_reth_test_utils::harness::TestHarness; +```rust,ignore +use base_reth_test_utils::TestHarness; #[tokio::test] async fn my_test() -> eyre::Result<()> { diff --git a/crates/test-utils/src/fixtures.rs b/crates/test-utils/src/fixtures.rs index 7fd3a775..382a01b5 100644 --- a/crates/test-utils/src/fixtures.rs +++ b/crates/test-utils/src/fixtures.rs @@ -1,6 +1,8 @@ +//! Shared fixtures and test data reused by integration tests across the Base codebase. + use std::sync::Arc; -use alloy_primitives::{B256, hex::FromHex}; +use alloy_genesis::Genesis; use reth::api::{NodeTypes, NodeTypesWithDBAdapter}; use reth_db::{ ClientVersion, DatabaseEnv, init_db, @@ -9,10 +11,12 @@ use reth_db::{ }; use reth_provider::{ProviderFactory, providers::StaticFileProvider}; -pub fn secret_from_hex(hex_key: &str) -> B256 { - B256::from_hex(hex_key).expect("32-byte private key") +/// Loads the shared test genesis configuration. +pub fn load_genesis() -> Genesis { + serde_json::from_str(include_str!("../assets/genesis.json")).unwrap() } +/// Creates a provider factory for tests with the given chain spec. pub fn create_provider_factory( chain_spec: Arc, ) -> ProviderFactory>>> { @@ -25,6 +29,7 @@ pub fn create_provider_factory( ) } +/// Creates a temporary test database. fn create_test_db() -> Arc> { let path = tempdir_path(); let emsg = format!("{ERROR_DB_CREATION}: {path:?}"); diff --git a/crates/test-utils/src/flashblocks_harness.rs b/crates/test-utils/src/flashblocks_harness.rs index 68fcf7a4..a1acaa2c 100644 --- a/crates/test-utils/src/flashblocks_harness.rs +++ b/crates/test-utils/src/flashblocks_harness.rs @@ -1,48 +1,44 @@ -use std::{collections::HashMap, ops::Deref, sync::Arc}; +//! Flashblocks-aware wrapper around [`TestHarness`] that wires in the custom RPC modules. -use alloy_consensus::Receipt; -use alloy_primitives::{Address, B256, Bytes, U256, b256, bytes}; -use alloy_rpc_types_engine::PayloadId; -use base_reth_flashblocks_rpc::subscription::{Flashblock, Metadata}; +use std::sync::Arc; + +use base_flashtypes::Flashblock; +use derive_more::Deref; use eyre::Result; use futures_util::Future; -use op_alloy_consensus::OpDepositReceipt; use reth::builder::NodeHandle; use reth_e2e_test_utils::Adapter; use reth_optimism_node::OpNode; -use reth_optimism_primitives::OpReceipt; -use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; use crate::{ harness::TestHarness, + init_silenced_tracing, node::{ FlashblocksLocalNode, FlashblocksParts, LocalFlashblocksState, OpAddOns, OpBuilder, default_launcher, }, - tracing::init_silenced_tracing, }; -const FLASHBLOCK_PAYLOAD_ID: [u8; 8] = [0; 8]; -const BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!( - "0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000" -); -const BLOCK_INFO_DEPOSIT_TX_HASH: B256 = - b256!("0xba56c8b0deb460ff070f8fca8e2ee01e51a3db27841cc862fdd94cc1a47662b6"); - +/// Helper that exposes [`TestHarness`] conveniences plus Flashblocks helpers. +#[derive(Debug, Deref)] pub struct FlashblocksHarness { + #[deref] inner: TestHarness, parts: FlashblocksParts, } impl FlashblocksHarness { + /// Launch a flashblocks-enabled harness with the default launcher. pub async fn new() -> Result { Self::with_launcher(default_launcher).await } + /// Launch the harness configured for manual canonical progression. pub async fn manual_canonical() -> Result { Self::manual_canonical_with_launcher(default_launcher).await } + /// Launch the harness using a custom node launcher. pub async fn with_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -53,6 +49,7 @@ impl FlashblocksHarness { Self::from_flashblocks_node(flash_node).await } + /// Launch the harness with a custom launcher while disabling automatic canonical processing. pub async fn manual_canonical_with_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -63,14 +60,17 @@ impl FlashblocksHarness { Self::from_flashblocks_node(flash_node).await } + /// Get a handle to the in-memory Flashblocks state backing the harness. pub fn flashblocks_state(&self) -> Arc { self.parts.state() } + /// Send a single flashblock through the harness. pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { self.parts.send(flashblock).await } + /// Send a batch of flashblocks sequentially, awaiting each confirmation. pub async fn send_flashblocks(&self, flashblocks: I) -> Result<()> where I: IntoIterator, @@ -81,61 +81,7 @@ impl FlashblocksHarness { Ok(()) } - /// Builds a flashblock payload for testing. Callers can intentionally pass invalid - /// values (for example a zeroed beacon root) to assert how downstream components - /// react to malformed flashblocks. - #[allow(clippy::too_many_arguments)] - pub fn build_flashblock( - &self, - block_number: u64, - parent_hash: B256, - parent_beacon_block_root: B256, - timestamp: u64, - gas_limit: u64, - transactions: Vec<(Bytes, Option<(B256, OpReceipt)>)>, - ) -> Flashblock { - let base = ExecutionPayloadBaseV1 { - parent_beacon_block_root, - parent_hash, - fee_recipient: Address::ZERO, - prev_randao: B256::ZERO, - block_number, - gas_limit, - timestamp, - extra_data: Bytes::new(), - base_fee_per_gas: U256::from(1), - }; - - let mut flashblock_txs = vec![BLOCK_INFO_DEPOSIT_TX.clone()]; - let mut receipts = HashMap::default(); - receipts.insert( - BLOCK_INFO_DEPOSIT_TX_HASH, - OpReceipt::Deposit(OpDepositReceipt { - inner: Receipt { status: true.into(), cumulative_gas_used: 10_000, logs: vec![] }, - deposit_nonce: Some(4_012_991u64), - deposit_receipt_version: None, - }), - ); - - for (tx_bytes, maybe_receipt) in transactions { - if let Some((hash, receipt)) = maybe_receipt { - receipts.insert(hash, receipt); - } - flashblock_txs.push(tx_bytes); - } - - Flashblock { - payload_id: PayloadId::new(FLASHBLOCK_PAYLOAD_ID), - index: 0, - base: Some(base), - diff: ExecutionPayloadFlashblockDeltaV1 { - transactions: flashblock_txs, - ..Default::default() - }, - metadata: Metadata { receipts, new_account_balances: Default::default(), block_number }, - } - } - + /// Consume the flashblocks harness and return the underlying [`TestHarness`]. pub fn into_inner(self) -> TestHarness { self.inner } @@ -146,11 +92,3 @@ impl FlashblocksHarness { Ok(Self { inner, parts }) } } - -impl Deref for FlashblocksHarness { - type Target = TestHarness; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} diff --git a/crates/test-utils/src/harness.rs b/crates/test-utils/src/harness.rs index be656c82..7a1a7e3c 100644 --- a/crates/test-utils/src/harness.rs +++ b/crates/test-utils/src/harness.rs @@ -3,7 +3,7 @@ use std::time::Duration; use alloy_eips::{BlockHashOrNumber, eip7685::Requests}; -use alloy_primitives::{B64, B256, Bytes, bytes}; +use alloy_primitives::{B64, B256, Bytes}; use alloy_provider::{Provider, RootProvider}; use alloy_rpc_types::BlockNumberOrTag; use alloy_rpc_types_engine::PayloadAttributes; @@ -22,21 +22,15 @@ use reth_primitives_traits::{Block as BlockT, RecoveredBlock}; use tokio::time::sleep; use crate::{ - accounts::TestAccounts, + BLOCK_BUILD_DELAY_MS, BLOCK_TIME_SECONDS, GAS_LIMIT, L1_BLOCK_INFO_DEPOSIT_TX, + NODE_STARTUP_DELAY_MS, TestAccounts, engine::{EngineApi, IpcEngine}, node::{LocalNode, LocalNodeProvider, OpAddOns, OpBuilder, default_launcher}, tracing::init_silenced_tracing, }; -const BLOCK_TIME_SECONDS: u64 = 2; -const GAS_LIMIT: u64 = 200_000_000; -const NODE_STARTUP_DELAY_MS: u64 = 500; -const BLOCK_BUILD_DELAY_MS: u64 = 100; -// Pre-captured L1 block info deposit transaction required by OP Stack. -const L1_BLOCK_INFO_DEPOSIT_TX: Bytes = bytes!( - "0x7ef90104a06c0c775b6b492bab9d7e81abdf27f77cafb698551226455a82f559e0f93fea3794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8b0098999be000008dd00101c1200000000000000020000000068869d6300000000015f277f000000000000000000000000000000000000000000000000000000000d42ac290000000000000000000000000000000000000000000000000000000000000001abf52777e63959936b1bf633a2a643f0da38d63deffe49452fed1bf8a44975d50000000000000000000000005050f69a9786f081509234f1a7f4684b5e5b76c9000000000000000000000000" -); - +/// High-level façade that bundles a local node, engine API client, and common helpers. +#[derive(Debug)] pub struct TestHarness { node: LocalNode, engine: EngineApi, @@ -44,10 +38,12 @@ pub struct TestHarness { } impl TestHarness { + /// Launch a new harness using the default launcher configuration. pub async fn new() -> Result { Self::with_launcher(default_launcher).await } + /// Launch the harness with a custom node launcher (e.g. to tweak components). pub async fn with_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -58,6 +54,7 @@ impl TestHarness { Self::from_node(node).await } + /// Build a harness from an already-running [`LocalNode`]. pub(crate) async fn from_node(node: LocalNode) -> Result { let engine = node.engine_api()?; let accounts = TestAccounts::new(); @@ -67,25 +64,35 @@ impl TestHarness { Ok(Self { node, engine, accounts }) } + /// Return an Optimism JSON-RPC provider connected to the harness node. pub fn provider(&self) -> RootProvider { self.node.provider().expect("provider should always be available after node initialization") } + /// Access the deterministic test accounts backing the harness. pub fn accounts(&self) -> &TestAccounts { &self.accounts } + /// Access the low-level blockchain provider for direct database queries. pub fn blockchain_provider(&self) -> LocalNodeProvider { self.node.blockchain_provider() } + /// HTTP URL for sending JSON-RPC requests to the local node. pub fn rpc_url(&self) -> String { format!("http://{}", self.node.http_api_addr) } + /// Websocket URL for subscribing to JSON-RPC notifications. + pub fn ws_url(&self) -> String { + format!("ws://{}", self.node.ws_api_addr) + } + + /// Build a block using the provided transactions and push it through the engine. pub async fn build_block_from_transactions(&self, mut transactions: Vec) -> Result<()> { // Ensure the block always starts with the required L1 block info deposit. - if !transactions.first().is_some_and(|tx| tx == &L1_BLOCK_INFO_DEPOSIT_TX) { + if transactions.first().is_none_or(|tx| tx != &L1_BLOCK_INFO_DEPOSIT_TX) { transactions.insert(0, L1_BLOCK_INFO_DEPOSIT_TX.clone()); } @@ -118,7 +125,6 @@ impl TestHarness { no_tx_pool: Some(true), min_base_fee: Some(min_base_fee), eip_1559_params: Some(B64::from(eip_1559_params)), - ..Default::default() }; let forkchoice_result = self @@ -163,6 +169,7 @@ impl TestHarness { Ok(()) } + /// Advance the canonical chain by `n` empty blocks. pub async fn advance_chain(&self, n: u64) -> Result<()> { for _ in 0..n { self.build_block_from_transactions(vec![]).await?; @@ -170,6 +177,7 @@ impl TestHarness { Ok(()) } + /// Return the latest recovered block as seen by the local blockchain provider. pub fn latest_block(&self) -> RecoveredBlock { let provider = self.blockchain_provider(); let best_number = provider.best_block_number().expect("able to read best block number"); @@ -196,7 +204,7 @@ mod tests { let provider = harness.provider(); let chain_id = provider.get_chain_id().await?; - assert_eq!(chain_id, crate::node::BASE_CHAIN_ID); + assert_eq!(chain_id, crate::BASE_CHAIN_ID); let alice_balance = provider.get_balance(harness.accounts().alice.address).await?; assert!(alice_balance > U256::ZERO); diff --git a/crates/test-utils/src/lib.rs b/crates/test-utils/src/lib.rs index 2c6b290d..ccc88d90 100644 --- a/crates/test-utils/src/lib.rs +++ b/crates/test-utils/src/lib.rs @@ -1,6 +1,37 @@ -pub mod accounts; -pub mod engine; -pub mod flashblocks_harness; -pub mod harness; -pub mod node; -pub mod tracing; +#![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 accounts; +pub use accounts::{ALICE, Account, BOB, CHARLIE, DEPLOYER, TestAccount, TestAccounts}; + +mod constants; +pub use constants::{ + BASE_CHAIN_ID, BLOCK_BUILD_DELAY_MS, BLOCK_TIME_SECONDS, DEFAULT_JWT_SECRET, GAS_LIMIT, + L1_BLOCK_INFO_DEPOSIT_TX, L1_BLOCK_INFO_DEPOSIT_TX_HASH, NODE_STARTUP_DELAY_MS, NamedChain, +}; + +mod contracts; +pub use contracts::{DoubleCounter, MockERC20, TransparentUpgradeableProxy}; + +mod engine; +pub use engine::{EngineAddress, EngineApi, EngineProtocol, HttpEngine, IpcEngine}; + +mod fixtures; +pub use fixtures::{create_provider_factory, load_genesis}; + +mod flashblocks_harness; +pub use flashblocks_harness::FlashblocksHarness; + +mod harness; +pub use harness::TestHarness; + +mod node; +pub use node::{ + FlashblocksLocalNode, FlashblocksParts, LocalFlashblocksState, LocalNode, LocalNodeProvider, + OpAddOns, OpBuilder, OpComponentsBuilder, OpTypes, default_launcher, +}; + +mod tracing; +pub use tracing::init_silenced_tracing; diff --git a/crates/test-utils/src/node.rs b/crates/test-utils/src/node.rs index 5a731bfd..700d9732 100644 --- a/crates/test-utils/src/node.rs +++ b/crates/test-utils/src/node.rs @@ -2,6 +2,7 @@ use std::{ any::Any, + fmt, net::SocketAddr, sync::{Arc, Mutex}, }; @@ -9,11 +10,9 @@ use std::{ use alloy_genesis::Genesis; use alloy_provider::RootProvider; use alloy_rpc_client::RpcClient; -use base_reth_flashblocks_rpc::{ - rpc::{EthApiExt, EthApiOverrideServer}, - state::FlashblocksState, - subscription::{Flashblock, FlashblocksReceiver}, -}; +use base_flashtypes::Flashblock; +use base_reth_flashblocks::{FlashblocksReceiver, FlashblocksState}; +use base_reth_rpc::{EthApiExt, EthApiOverrideServer, EthPubSub, EthPubSubApiServer}; use eyre::Result; use futures_util::Future; use once_cell::sync::OnceCell; @@ -46,31 +45,52 @@ use tokio_stream::StreamExt; use crate::engine::EngineApi; -pub const BASE_CHAIN_ID: u64 = 84532; - +/// Convenience alias for the local blockchain provider type. pub type LocalNodeProvider = BlockchainProvider>; +/// Convenience alias for the Flashblocks state backing the local node. pub type LocalFlashblocksState = FlashblocksState; +/// Handle to a launched local node along with the resources required to keep it alive. pub struct LocalNode { pub(crate) http_api_addr: SocketAddr, engine_ipc_path: String, + pub(crate) ws_api_addr: SocketAddr, provider: LocalNodeProvider, _node_exit_future: NodeExitFuture, _node: Box, _task_manager: TaskManager, } +impl fmt::Debug for LocalNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("LocalNode") + .field("http_api_addr", &self.http_api_addr) + .field("ws_api_addr", &self.ws_api_addr) + .field("engine_ipc_path", &self.engine_ipc_path) + .finish_non_exhaustive() + } +} + +/// Components that allow tests to interact with the Flashblocks worker tasks. #[derive(Clone)] pub struct FlashblocksParts { sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, state: Arc, } +impl fmt::Debug for FlashblocksParts { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FlashblocksParts").finish_non_exhaustive() + } +} + impl FlashblocksParts { + /// Clone the shared [`FlashblocksState`] handle. pub fn state(&self) -> Arc { self.state.clone() } + /// Send a flashblock to the background processor and wait until it is handled. pub async fn send(&self, flashblock: Flashblock) -> Result<()> { let (tx, rx) = oneshot::channel(); self.sender.send((flashblock, tx)).await.map_err(|err| eyre::eyre!(err))?; @@ -86,6 +106,7 @@ struct FlashblocksNodeExtensions { struct FlashblocksNodeExtensionsInner { sender: mpsc::Sender<(Flashblock, oneshot::Sender<()>)>, + #[allow(clippy::type_complexity)] receiver: Arc)>>>>, fb_cell: Arc>>, process_canonical: bool, @@ -120,16 +141,16 @@ impl FlashblocksNodeExtensions { Ok(async move { while let Some(note) = ctx.notifications.try_next().await? { if let Some(committed) = note.committed_chain() { + let hash = committed.tip().num_hash(); if process_canonical { // Many suites drive canonical updates manually to reproduce race conditions, so // allowing this to be disabled keeps canonical replay deterministic. - for block in committed.blocks_iter() { + let chain = Arc::unwrap_or_clone(committed); + for (_, block) in chain.into_blocks() { fb.on_canonical_block_received(block); } } - let _ = ctx - .events - .send(ExExEvent::FinishedHeight(committed.tip().num_hash())); + let _ = ctx.events.send(ExExEvent::FinishedHeight(hash)); } } Ok(()) @@ -157,6 +178,12 @@ impl FlashblocksNodeExtensions { ); ctx.modules.replace_configured(api_ext.into_rpc())?; + // Register eth_subscribe subscription endpoint for flashblocks + // Uses replace_configured since eth_subscribe already exists from reth's standard module + // Pass eth_api to enable proxying standard subscription types to reth's implementation + let eth_pubsub = EthPubSub::new(ctx.registry.eth_api().clone(), fb.clone()); + ctx.modules.replace_configured(eth_pubsub.into_rpc())?; + let fb_for_task = fb.clone(); let mut receiver = receiver .lock() @@ -193,13 +220,18 @@ impl FlashblocksNodeExtensions { } } +/// Optimism node types used for the local harness. pub type OpTypes = FullNodeTypesAdapter>>; +/// Builder that wires up the concrete node components. pub type OpComponentsBuilder = >::ComponentsBuilder; +/// Additional services attached to the node builder. pub type OpAddOns = >::AddOns; +/// Launcher builder used by the harness to customize node startup. pub type OpBuilder = WithLaunchContext>; +/// Default launcher that is reused across the harness and integration tests. pub async fn default_launcher( builder: OpBuilder, ) -> eyre::Result, OpAddOns>> { @@ -208,6 +240,7 @@ pub async fn default_launcher( } impl LocalNode { + /// Launch a new local node using the provided launcher function. pub async fn new(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -242,19 +275,27 @@ impl LocalNode { Ok(Arc::new(TempDatabase::new(db, path))) } + /// Create an HTTP provider pointed at the node's public RPC endpoint. pub fn provider(&self) -> Result> { let url = format!("http://{}", self.http_api_addr); let client = RpcClient::builder().http(url.parse()?); Ok(RootProvider::::new(client)) } + /// Build an Engine API client that talks to the node's IPC endpoint. pub fn engine_api(&self) -> Result> { EngineApi::::new(self.engine_ipc_path.clone()) } + /// Clone the underlying blockchain provider so callers can inspect chain state. pub fn blockchain_provider(&self) -> LocalNodeProvider { self.provider.clone() } + + /// Websocket URL for the local node. + pub fn ws_url(&self) -> String { + format!("ws://{}", self.ws_api_addr) + } } async fn build_node(launcher: L) -> Result @@ -280,7 +321,8 @@ where std::thread::current().id() ); - let mut rpc_args = RpcServerArgs::default().with_unused_ports().with_http().with_auth_ipc(); + let mut rpc_args = + RpcServerArgs::default().with_unused_ports().with_http().with_auth_ipc().with_ws(); rpc_args.auth_ipc_path = unique_ipc_path; let node = OpNode::new(RollupArgs::default()); @@ -312,11 +354,17 @@ where .http_local_addr() .ok_or_else(|| eyre::eyre!("HTTP RPC server failed to bind to address"))?; + let ws_api_addr = node_handle + .rpc_server_handle() + .ws_local_addr() + .ok_or_else(|| eyre::eyre!("Failed to get websocket api address"))?; + let engine_ipc_path = node_config.rpc.auth_ipc_path; let provider = node_handle.provider().clone(); Ok(LocalNode { http_api_addr, + ws_api_addr, engine_ipc_path, provider, _node_exit_future: node_exit_future, @@ -337,12 +385,23 @@ fn init_flashblocks_state( .clone() } +/// Local node wrapper that exposes helpers specific to Flashblocks tests. pub struct FlashblocksLocalNode { node: LocalNode, parts: FlashblocksParts, } +impl fmt::Debug for FlashblocksLocalNode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FlashblocksLocalNode") + .field("node", &self.node) + .field("parts", &self.parts) + .finish() + } +} + impl FlashblocksLocalNode { + /// Launch a flashblocks-enabled node using the default launcher. pub async fn new() -> Result { Self::with_launcher(default_launcher).await } @@ -353,6 +412,7 @@ impl FlashblocksLocalNode { Self::with_manual_canonical_launcher(default_launcher).await } + /// Launch a flashblocks-enabled node with a custom launcher and canonical processing enabled. pub async fn with_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -361,6 +421,7 @@ impl FlashblocksLocalNode { Self::with_launcher_inner(launcher, true).await } + /// Same as [`Self::with_launcher`] but leaves canonical processing to the caller. pub async fn with_manual_canonical_launcher(launcher: L) -> Result where L: FnOnce(OpBuilder) -> LRet, @@ -382,18 +443,22 @@ impl FlashblocksLocalNode { Ok(Self { node, parts }) } + /// Access the shared Flashblocks state for assertions or manual driving. pub fn flashblocks_state(&self) -> Arc { self.parts.state() } + /// Send a flashblock through the background processor and await completion. pub async fn send_flashblock(&self, flashblock: Flashblock) -> Result<()> { self.parts.send(flashblock).await } + /// Split the wrapper into the underlying node plus flashblocks parts. pub fn into_parts(self) -> (LocalNode, FlashblocksParts) { (self.node, self.parts) } + /// Borrow the underlying [`LocalNode`]. pub fn as_node(&self) -> &LocalNode { &self.node } From fa5ec8c8d18483d6f51df08ebab560f7ecc444dc Mon Sep 17 00:00:00 2001 From: Niran Babalola Date: Mon, 22 Dec 2025 17:58:13 -0600 Subject: [PATCH 26/26] Fix clippy warnings and apply nightly fmt - Add missing doc comments for bundle state clone metrics - Remove unused crate dependencies (alloy-sol-types, op-alloy-consensus, revm) - Fix collapsible_if lint in flashblock_trie_cache.rs - Add Debug derive to MeteringApiImpl - Export MeterBundleOutput from public API --- Cargo.lock | 1 - crates/flashblocks/src/metrics.rs | 2 ++ crates/rpc/Cargo.toml | 3 --- crates/rpc/src/base/flashblock_trie_cache.rs | 11 +++++++---- crates/rpc/src/base/meter_rpc.rs | 3 ++- crates/rpc/src/lib.rs | 2 +- crates/runner/src/extensions/rpc.rs | 3 +-- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1361fc91..a4cded85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1629,7 +1629,6 @@ dependencies = [ "reth-tracing", "reth-transaction-pool", "reth-trie-common", - "revm", "revm-database", "serde", "serde_json", diff --git a/crates/flashblocks/src/metrics.rs b/crates/flashblocks/src/metrics.rs index e1a56167..3c2824ec 100644 --- a/crates/flashblocks/src/metrics.rs +++ b/crates/flashblocks/src/metrics.rs @@ -57,9 +57,11 @@ pub struct Metrics { #[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, } diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index 6fb49530..8a4aa655 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -35,10 +35,8 @@ alloy-consensus.workspace = true alloy-eips.workspace = true alloy-rpc-types.workspace = true alloy-rpc-types-eth.workspace = true -alloy-sol-types.workspace = true # op-alloy -op-alloy-consensus.workspace = true op-alloy-rpc-types.workspace = true op-alloy-network.workspace = true @@ -47,7 +45,6 @@ tokio.workspace = true tokio-stream.workspace = true # revm -revm.workspace = true revm-database.workspace = true # rpc diff --git a/crates/rpc/src/base/flashblock_trie_cache.rs b/crates/rpc/src/base/flashblock_trie_cache.rs index f1322b40..13134f86 100644 --- a/crates/rpc/src/base/flashblock_trie_cache.rs +++ b/crates/rpc/src/base/flashblock_trie_cache.rs @@ -16,7 +16,9 @@ use crate::FlashblocksState; /// each bundle's state root time reflects only its own I/O cost. #[derive(Debug, Clone)] pub struct FlashblockTrieData { + /// The trie updates from computing the state root. pub trie_updates: TrieUpdates, + /// The hashed post state used for state root computation. pub hashed_state: HashedPostState, } @@ -61,10 +63,11 @@ impl FlashblockTrieCache { canonical_state_provider: &dyn StateProvider, ) -> EyreResult { let cached_entry = self.cache.load(); - if let Some(cached) = cached_entry.as_ref() { - if cached.block_hash == block_hash && cached.flashblock_index == flashblock_index { - return Ok(cached.trie_data.clone()); - } + if let Some(cached) = cached_entry.as_ref() + && cached.block_hash == block_hash + && cached.flashblock_index == flashblock_index + { + return Ok(cached.trie_data.clone()); } let hashed_state = diff --git a/crates/rpc/src/base/meter_rpc.rs b/crates/rpc/src/base/meter_rpc.rs index 244c9438..efd7b8e6 100644 --- a/crates/rpc/src/base/meter_rpc.rs +++ b/crates/rpc/src/base/meter_rpc.rs @@ -13,7 +13,8 @@ use tracing::{error, info}; use crate::{FlashblockTrieCache, MeteringApiServer, meter_bundle}; -/// Implementation of the metering RPC API +/// Implementation of the metering RPC API. +#[derive(Debug)] pub struct MeteringApiImpl { provider: Provider, flashblocks_state: Arc, diff --git a/crates/rpc/src/lib.rs b/crates/rpc/src/lib.rs index 9f0a18aa..4f0facc3 100644 --- a/crates/rpc/src/lib.rs +++ b/crates/rpc/src/lib.rs @@ -9,7 +9,7 @@ pub use tips_core::types::{Bundle, MeterBundleResponse, TransactionResult}; mod base; pub use base::{ flashblock_trie_cache::{FlashblockTrieCache, FlashblockTrieData}, - meter::{FlashblocksState, meter_bundle}, + meter::{FlashblocksState, MeterBundleOutput, meter_bundle}, meter_rpc::MeteringApiImpl, pubsub::{EthPubSub, EthPubSubApiServer}, traits::{MeteringApiServer, TransactionStatusApiServer}, diff --git a/crates/runner/src/extensions/rpc.rs b/crates/runner/src/extensions/rpc.rs index 5607e407..06be28e2 100644 --- a/crates/runner/src/extensions/rpc.rs +++ b/crates/runner/src/extensions/rpc.rs @@ -74,8 +74,7 @@ impl BaseNodeExtension for BaseRpcExtension { // Metering API requires flashblocks state to access pending blocks for bundle simulation if metering_enabled { info!(message = "Starting Metering RPC"); - let metering_api = - MeteringApiImpl::new(ctx.provider().clone(), fb.clone()); + let metering_api = MeteringApiImpl::new(ctx.provider().clone(), fb.clone()); ctx.modules.merge_configured(metering_api.into_rpc())?; }