From 222bfbf93172b0dbfc57d54a7f2b33bb4326a03e Mon Sep 17 00:00:00 2001 From: Haardik H Date: Fri, 26 Sep 2025 12:43:34 -0400 Subject: [PATCH 1/3] use canon block instead of latest in rpcs --- crates/flashblocks-rpc/src/pending_blocks.rs | 5 ++ crates/flashblocks-rpc/src/rpc.rs | 31 ++++--- crates/flashblocks-rpc/src/state.rs | 8 ++ crates/flashblocks-rpc/src/tests/state.rs | 91 +++++++++++++++++++- 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/crates/flashblocks-rpc/src/pending_blocks.rs b/crates/flashblocks-rpc/src/pending_blocks.rs index c6c1422..dba4a1b 100644 --- a/crates/flashblocks-rpc/src/pending_blocks.rs +++ b/crates/flashblocks-rpc/src/pending_blocks.rs @@ -1,4 +1,5 @@ use alloy_consensus::{Header, Sealed}; +use alloy_eips::BlockNumberOrTag; use alloy_primitives::{ map::foldhash::{HashMap, HashMapExt}, Address, BlockNumber, TxHash, B256, U256, @@ -151,6 +152,10 @@ impl PendingBlocks { self.headers.last().unwrap().number } + pub fn canonical_block_number(&self) -> BlockNumberOrTag { + BlockNumberOrTag::Number(self.headers.first().unwrap().number - 1) + } + pub fn latest_flashblock_index(&self) -> u64 { self.flashblocks.last().unwrap().index } diff --git a/crates/flashblocks-rpc/src/rpc.rs b/crates/flashblocks-rpc/src/rpc.rs index 1586f14..203a1ea 100644 --- a/crates/flashblocks-rpc/src/rpc.rs +++ b/crates/flashblocks-rpc/src/rpc.rs @@ -35,6 +35,9 @@ pub const MAX_TIMEOUT_SEND_RAW_TX_SYNC_MS: u64 = 6_000; /// Core API for accessing flashblock state and data. pub trait FlashblocksAPI { + /// Retrieves the canonical block number. + fn get_canonical_block_number(&self) -> BlockNumberOrTag; + /// Retrieves the current block. If `full` is true, includes full transaction details. fn get_block(&self, full: bool) -> Option>; @@ -219,16 +222,15 @@ where let block_id = block_number.unwrap_or_default(); if block_id.is_pending() { self.metrics.get_transaction_count.increment(1); - let latest_count = EthState::transaction_count( - &self.eth_api, - address, - Some(BlockId::Number(BlockNumberOrTag::Latest)), - ) - .await - .map_err(Into::into)?; - + let canon_block = self.flashblocks_state.get_canonical_block_number(); let fb_count = self.flashblocks_state.get_transaction_count(address); - return Ok(latest_count + fb_count); + + let canon_count = + EthState::transaction_count(&self.eth_api, address, Some(canon_block.into())) + .await + .map_err(Into::into)?; + + return Ok(canon_count + fb_count); } EthState::transaction_count(&self.eth_api, address, block_number) @@ -329,11 +331,12 @@ where block_overrides = ?block_overrides, ); - let block_id = block_number.unwrap_or_default(); + let mut block_id = block_number.unwrap_or_default(); let mut pending_overrides = EvmOverrides::default(); // If the call is to pending block use cached override (if they exist) if block_id.is_pending() { self.metrics.call.increment(1); + block_id = self.flashblocks_state.get_canonical_block_number().into(); pending_overrides.state = self.flashblocks_state.get_state_overrides(); } @@ -348,7 +351,7 @@ where EthCall::call( &self.eth_api, transaction, - block_number, + Some(block_id), EvmOverrides::new(Some(final_overrides), block_overrides), ) .await @@ -368,11 +371,12 @@ where overrides = ?overrides, ); - let block_id = block_number.unwrap_or_default(); + let mut block_id = block_number.unwrap_or_default(); let mut pending_overrides = EvmOverrides::default(); // If the call is to pending block use cached override (if they exist) if block_id.is_pending() { self.metrics.estimate_gas.increment(1); + block_id = self.flashblocks_state.get_canonical_block_number().into(); pending_overrides.state = self.flashblocks_state.get_state_overrides(); } @@ -396,12 +400,13 @@ where block_number = ?block_number, ); - let block_id = block_number.unwrap_or_default(); + let mut block_id = block_number.unwrap_or_default(); let mut pending_overrides = EvmOverrides::default(); // If the call is to pending block use cached override (if they exist) if block_id.is_pending() { self.metrics.simulate_v1.increment(1); + block_id = self.flashblocks_state.get_canonical_block_number().into(); pending_overrides.state = self.flashblocks_state.get_state_overrides(); } diff --git a/crates/flashblocks-rpc/src/state.rs b/crates/flashblocks-rpc/src/state.rs index 6c1bf5c..52a3393 100644 --- a/crates/flashblocks-rpc/src/state.rs +++ b/crates/flashblocks-rpc/src/state.rs @@ -120,6 +120,14 @@ impl FlashblocksReceiver for FlashblocksState { } impl FlashblocksAPI for FlashblocksState { + fn get_canonical_block_number(&self) -> BlockNumberOrTag { + self.pending_blocks + .load() + .as_ref() + .map(|pb| pb.canonical_block_number()) + .unwrap_or(BlockNumberOrTag::Latest) + } + fn get_block(&self, full: bool) -> Option> { self.pending_blocks .load() diff --git a/crates/flashblocks-rpc/src/tests/state.rs b/crates/flashblocks-rpc/src/tests/state.rs index 99f6954..e20320f 100644 --- a/crates/flashblocks-rpc/src/tests/state.rs +++ b/crates/flashblocks-rpc/src/tests/state.rs @@ -31,12 +31,12 @@ mod tests { use reth_provider::providers::BlockchainProvider; use reth_provider::{ BlockWriter, ChainSpecProvider, ExecutionOutcome, LatestStateProviderRef, ProviderFactory, + StateProviderFactory, }; use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; use std::sync::Arc; use std::time::Duration; use tokio::time::sleep; - // The amount of time to wait (in milliseconds) after sending a new flashblock or canonical block // so it can be processed by the state processor const SLEEP_TIME: u64 = 10; @@ -157,7 +157,10 @@ mod tests { sleep(Duration::from_millis(SLEEP_TIME)).await; } - async fn new_canonical_block(&mut self, mut user_transactions: Vec) { + async fn new_canonical_block_without_processing( + &mut self, + mut user_transactions: Vec, + ) -> RecoveredBlock { let current_tip = self.current_canonical_block(); let deposit_transaction = @@ -214,6 +217,13 @@ mod tests { .unwrap(); provider_rw.commit().unwrap(); + block + } + + async fn new_canonical_block(&mut self, user_transactions: Vec) { + let block = self + .new_canonical_block_without_processing(user_transactions) + .await; self.flashblocks.on_canonical_block_received(&block); sleep(Duration::from_millis(SLEEP_TIME)).await; } @@ -718,6 +728,83 @@ mod tests { ); } + #[tokio::test] + async fn test_nonce_uses_pending_canon_block_instead_of_latest() { + // Test for race condition when a canon block comes in but user + // requests their nonce prior to the StateProcessor processing the canon block + // causing it to return an n+1 nonce instead of n + // because underlying reth node `latest` block is already updated, but + // relevant pending state has not been cleared yet + reth_tracing::init_test_tracing(); + let mut test = TestHarness::new(); + + test.send_flashblock(FlashblockBuilder::new_base(&test).build()) + .await; + test.send_flashblock( + FlashblockBuilder::new(&test, 1) + .with_transactions(vec![test.build_transaction_to_send_eth( + User::Alice, + User::Bob, + 100, + )]) + .build(), + ) + .await; + + let pending_nonce = test + .provider + .basic_account(&test.address(User::Alice)) + .unwrap() + .unwrap() + .nonce + + test + .flashblocks + .get_transaction_count(test.address(User::Alice)) + .to::(); + assert_eq!(pending_nonce, 1); + + test.new_canonical_block_without_processing(vec![ + test.build_transaction_to_send_eth_with_nonce(User::Alice, User::Bob, 100, 0) + ]) + .await; + + let pending_nonce = test + .provider + .basic_account(&test.address(User::Alice)) + .unwrap() + .unwrap() + .nonce + + test + .flashblocks + .get_transaction_count(test.address(User::Alice)) + .to::(); + + // This is 2, because canon block has reached the underlying chain + // but the StateProcessor hasn't processed it + // so pending nonce is effectively double-counting the same transaction, leading to a nonce of 2 + assert_eq!(pending_nonce, 2); + + // On the RPC level, we correctly return 1 because we + // use the pending canon block instead of the latest block when fetching + // onchain nonce count to compute + // pending_nonce = onchain_nonce + pending_txn_count + let canon_block = test.flashblocks.get_canonical_block_number(); + let canon_state_provider = test + .provider + .state_by_block_number_or_tag(canon_block) + .unwrap(); + let canon_nonce = canon_state_provider + .account_nonce(&test.address(User::Alice)) + .unwrap() + .unwrap(); + let pending_nonce = canon_nonce + + test + .flashblocks + .get_transaction_count(test.address(User::Alice)) + .to::(); + assert_eq!(pending_nonce, 1); + } + #[tokio::test] async fn test_missing_receipts_will_not_process() { reth_tracing::init_test_tracing(); From a310bc77a88e6ded9ce4272e294d9d6a711008a7 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Mon, 29 Sep 2025 10:55:38 -0400 Subject: [PATCH 2/3] fix another minor race condition --- crates/flashblocks-rpc/src/rpc.rs | 96 ++++++++++------ crates/flashblocks-rpc/src/state.rs | 58 +--------- crates/flashblocks-rpc/src/tests/state.rs | 130 ++++++++++++---------- 3 files changed, 139 insertions(+), 145 deletions(-) diff --git a/crates/flashblocks-rpc/src/rpc.rs b/crates/flashblocks-rpc/src/rpc.rs index 203a1ea..83e4d98 100644 --- a/crates/flashblocks-rpc/src/rpc.rs +++ b/crates/flashblocks-rpc/src/rpc.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use std::time::Duration; use crate::metrics::Metrics; +use crate::pending_blocks::PendingBlocks; use crate::subscription::Flashblock; use alloy_eips::{BlockId, BlockNumberOrTag}; use alloy_primitives::{Address, TxHash, U256}; @@ -35,28 +36,11 @@ pub const MAX_TIMEOUT_SEND_RAW_TX_SYNC_MS: u64 = 6_000; /// Core API for accessing flashblock state and data. pub trait FlashblocksAPI { - /// Retrieves the canonical block number. - fn get_canonical_block_number(&self) -> BlockNumberOrTag; - - /// Retrieves the current block. If `full` is true, includes full transaction details. - fn get_block(&self, full: bool) -> Option>; - - /// Gets transaction receipt by hash. - fn get_transaction_receipt(&self, tx_hash: TxHash) -> Option>; - - /// Gets transaction count (nonce) for an address. - fn get_transaction_count(&self, address: Address) -> U256; - - /// Gets transaction details by hash. - fn get_transaction_by_hash(&self, tx_hash: TxHash) -> Option>; - - /// Gets balance for an address. Returns None if address not updated in flashblocks. - fn get_balance(&self, address: Address) -> Option; + /// Retrieves the pending blocks. + fn get_pending_blocks(&self) -> Option>; /// Creates a subscription to receive flashblock updates. fn subscribe_to_flashblocks(&self) -> broadcast::Receiver; - - fn get_state_overrides(&self) -> Option; } #[cfg_attr(not(test), rpc(server, namespace = "eth"))] @@ -160,7 +144,8 @@ where if number.is_pending() { self.metrics.get_block_by_number.increment(1); - Ok(self.flashblocks_state.get_block(full)) + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + Ok(pending_blocks.as_ref().map(|pb| pb.get_latest_block(full))) } else { EthBlocks::rpc_block(&self.eth_api, number.into(), full) .await @@ -177,7 +162,11 @@ where tx_hash = %tx_hash ); - if let Some(fb_receipt) = self.flashblocks_state.get_transaction_receipt(tx_hash) { + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + if let Some(fb_receipt) = pending_blocks + .as_ref() + .and_then(|pb| pb.get_receipt(tx_hash)) + { self.metrics.get_transaction_receipt.increment(1); return Ok(Some(fb_receipt)); } @@ -199,7 +188,11 @@ where let block_id = block_number.unwrap_or_default(); if block_id.is_pending() { self.metrics.get_balance.increment(1); - if let Some(balance) = self.flashblocks_state.get_balance(address) { + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + if let Some(balance) = pending_blocks + .as_ref() + .and_then(|pb| pb.get_balance(address)) + { return Ok(balance); } } @@ -222,8 +215,15 @@ where let block_id = block_number.unwrap_or_default(); if block_id.is_pending() { self.metrics.get_transaction_count.increment(1); - let canon_block = self.flashblocks_state.get_canonical_block_number(); - let fb_count = self.flashblocks_state.get_transaction_count(address); + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + let canon_block = pending_blocks + .as_ref() + .map(|pb| pb.canonical_block_number()) + .unwrap_or(BlockNumberOrTag::Latest); + let fb_count = pending_blocks + .as_ref() + .map(|pb| pb.get_transaction_count(address)) + .unwrap_or_else(|| U256::from(0)); let canon_count = EthState::transaction_count(&self.eth_api, address, Some(canon_block.into())) @@ -247,7 +247,12 @@ where tx_hash = %tx_hash ); - if let Some(fb_transaction) = self.flashblocks_state.get_transaction_by_hash(tx_hash) { + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + + if let Some(fb_transaction) = pending_blocks + .as_ref() + .and_then(|pb| pb.get_transaction_by_hash(tx_hash)) + { self.metrics.get_transaction_receipt.increment(1); return Ok(Some(fb_transaction)); } @@ -336,8 +341,16 @@ where // If the call is to pending block use cached override (if they exist) if block_id.is_pending() { self.metrics.call.increment(1); - block_id = self.flashblocks_state.get_canonical_block_number().into(); - pending_overrides.state = self.flashblocks_state.get_state_overrides(); + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + block_id = pending_blocks + .as_ref() + .map(|pb| pb.canonical_block_number()) + .unwrap_or(BlockNumberOrTag::Latest) + .into(); + pending_overrides.state = pending_blocks + .as_ref() + .map(|pb| pb.get_state_overrides()) + .unwrap_or_default(); } // Apply user's overrides on top @@ -376,8 +389,16 @@ where // If the call is to pending block use cached override (if they exist) if block_id.is_pending() { self.metrics.estimate_gas.increment(1); - block_id = self.flashblocks_state.get_canonical_block_number().into(); - pending_overrides.state = self.flashblocks_state.get_state_overrides(); + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + block_id = pending_blocks + .as_ref() + .map(|pb| pb.canonical_block_number()) + .unwrap_or(BlockNumberOrTag::Latest) + .into(); + pending_overrides.state = pending_blocks + .as_ref() + .map(|pb| pb.get_state_overrides()) + .unwrap_or_default(); } let mut state_overrides_builder = @@ -406,8 +427,16 @@ where // If the call is to pending block use cached override (if they exist) if block_id.is_pending() { self.metrics.simulate_v1.increment(1); - block_id = self.flashblocks_state.get_canonical_block_number().into(); - pending_overrides.state = self.flashblocks_state.get_state_overrides(); + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + block_id = pending_blocks + .as_ref() + .map(|pb| pb.canonical_block_number()) + .unwrap_or(BlockNumberOrTag::Latest) + .into(); + pending_overrides.state = pending_blocks + .as_ref() + .map(|pb| pb.get_state_overrides()) + .unwrap_or_default(); } // Prepend flashblocks pending overrides to the block state calls @@ -449,7 +478,10 @@ where match receiver.recv().await { Ok(flashblock) if flashblock.metadata.receipts.contains_key(&tx_hash) => { debug!(message = "found receipt in flashblock", tx_hash = %tx_hash); - return self.flashblocks_state.get_transaction_receipt(tx_hash); + let pending_blocks = self.flashblocks_state.get_pending_blocks(); + return pending_blocks + .as_ref() + .and_then(|pb| pb.get_receipt(tx_hash)); } Ok(_) => { trace!(message = "flashblock does not contain receipt", tx_hash = %tx_hash); diff --git a/crates/flashblocks-rpc/src/state.rs b/crates/flashblocks-rpc/src/state.rs index 52a3393..36ff299 100644 --- a/crates/flashblocks-rpc/src/state.rs +++ b/crates/flashblocks-rpc/src/state.rs @@ -7,14 +7,13 @@ use alloy_consensus::{Header, TxReceipt}; use alloy_eips::BlockNumberOrTag; use alloy_primitives::map::foldhash::HashMap; use alloy_primitives::map::B256HashMap; -use alloy_primitives::{Address, BlockNumber, Bytes, Sealable, TxHash, B256, U256}; +use alloy_primitives::{BlockNumber, Bytes, Sealable, B256}; use alloy_rpc_types::{TransactionTrait, Withdrawal}; use alloy_rpc_types_engine::{ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3}; -use alloy_rpc_types_eth::state::{AccountOverride, StateOverride, StateOverridesBuilder}; +use alloy_rpc_types_eth::state::{AccountOverride, StateOverridesBuilder}; use arc_swap::ArcSwapOption; use eyre::eyre; use op_alloy_consensus::OpTxEnvelope; -use op_alloy_network::Optimism; use op_alloy_network::TransactionResponse; use op_alloy_rpc_types::Transaction; use reth::chainspec::{ChainSpecProvider, EthChainSpec}; @@ -30,8 +29,6 @@ use reth_optimism_primitives::{DepositReceipt, OpBlock, OpPrimitives}; use reth_optimism_rpc::OpReceiptBuilder; use reth_primitives::RecoveredBlock; use reth_rpc_convert::transaction::ConvertReceiptInput; -use reth_rpc_convert::RpcTransaction; -use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; use std::collections::{BTreeMap, HashSet}; use std::sync::Arc; use std::time::Instant; @@ -120,60 +117,13 @@ impl FlashblocksReceiver for FlashblocksState { } impl FlashblocksAPI for FlashblocksState { - fn get_canonical_block_number(&self) -> BlockNumberOrTag { - self.pending_blocks - .load() - .as_ref() - .map(|pb| pb.canonical_block_number()) - .unwrap_or(BlockNumberOrTag::Latest) - } - - fn get_block(&self, full: bool) -> Option> { - self.pending_blocks - .load() - .as_ref() - .map(|pb| pb.get_latest_block(full)) - } - - fn get_transaction_receipt(&self, tx_hash: TxHash) -> Option> { - self.pending_blocks - .load() - .as_ref() - .and_then(|pb| pb.get_receipt(tx_hash)) - } - - fn get_transaction_count(&self, address: Address) -> U256 { - self.pending_blocks - .load() - .as_ref() - .map(|pb| pb.get_transaction_count(address)) - .unwrap_or_else(|| U256::from(0)) - } - - fn get_transaction_by_hash(&self, tx_hash: TxHash) -> Option> { - self.pending_blocks - .load() - .as_ref() - .and_then(|pb| pb.get_transaction_by_hash(tx_hash)) - } - - fn get_balance(&self, address: Address) -> Option { - self.pending_blocks - .load() - .as_ref() - .and_then(|pb| pb.get_balance(address)) + fn get_pending_blocks(&self) -> Option> { + self.pending_blocks.load_full() } fn subscribe_to_flashblocks(&self) -> tokio::sync::broadcast::Receiver { self.flashblock_sender.subscribe() } - - fn get_state_overrides(&self) -> Option { - self.pending_blocks - .load() - .as_ref() - .and_then(|pb| pb.get_state_overrides()) - } } #[derive(Debug, Clone)] diff --git a/crates/flashblocks-rpc/src/tests/state.rs b/crates/flashblocks-rpc/src/tests/state.rs index e20320f..180b428 100644 --- a/crates/flashblocks-rpc/src/tests/state.rs +++ b/crates/flashblocks-rpc/src/tests/state.rs @@ -8,13 +8,15 @@ mod tests { use alloy_consensus::crypto::secp256k1::public_key_to_address; use alloy_consensus::{BlockHeader, Receipt}; use alloy_consensus::{Header, Transaction}; - use alloy_eips::{BlockHashOrNumber, Decodable2718, Encodable2718}; + use alloy_eips::{BlockHashOrNumber, BlockNumberOrTag, Decodable2718, Encodable2718}; use alloy_genesis::GenesisAccount; use alloy_primitives::map::foldhash::HashMap; use alloy_primitives::{Address, BlockNumber, Bytes, B256, U256}; use alloy_provider::network::BlockResponse; + use alloy_rpc_types::state::StateOverride; use alloy_rpc_types_engine::PayloadId; use op_alloy_consensus::OpDepositReceipt; + use op_alloy_network::Optimism; use reth::builder::NodeTypesWithDBAdapter; use reth::chainspec::EthChainSpec; use reth::providers::{AccountReader, BlockNumReader, BlockReader}; @@ -33,6 +35,7 @@ mod tests { BlockWriter, ChainSpecProvider, ExecutionOutcome, LatestStateProviderRef, ProviderFactory, StateProviderFactory, }; + use reth_rpc_eth_api::RpcBlock; use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; use std::sync::Arc; use std::time::Duration; @@ -70,6 +73,43 @@ mod tests { self.user_to_private_key[&u] } + fn get_transaction_count(&self, address: Address) -> U256 { + self.flashblocks + .get_pending_blocks() + .as_ref() + .map(|pb| pb.get_transaction_count(address)) + .unwrap_or_else(|| U256::from(0)) + } + + fn get_balance(&self, address: Address) -> Option { + self.flashblocks + .get_pending_blocks() + .as_ref() + .and_then(|pb| pb.get_balance(address)) + } + + fn get_state_overrides(&self) -> Option { + self.flashblocks + .get_pending_blocks() + .as_ref() + .and_then(|pb| pb.get_state_overrides()) + } + + fn get_block(&self, full: bool) -> Option> { + self.flashblocks + .get_pending_blocks() + .as_ref() + .map(|pb| pb.get_latest_block(full)) + } + + fn get_canonical_flashblock_number(&self) -> BlockNumberOrTag { + self.flashblocks + .get_pending_blocks() + .as_ref() + .map(|pb| pb.canonical_block_number()) + .unwrap_or_else(|| BlockNumberOrTag::Latest) + } + fn current_canonical_block(&self) -> RecoveredBlock { let latest_block_num = self .provider @@ -91,12 +131,8 @@ mod tests { .expect("can lookup account state") .expect("should be existing account state"); - let nonce = self - .flashblocks - .get_transaction_count(self.address(u)) - .to::(); + let nonce = self.get_transaction_count(self.address(u)).to::(); let balance = self - .flashblocks .get_balance(self.address(u)) .unwrap_or(basic_account.balance); @@ -431,17 +467,15 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; assert_eq!( - test.flashblocks - .get_block(true) + test.get_block(true) .expect("block is built") .transactions .len(), 1 ); - assert!(test.flashblocks.get_state_overrides().is_some()); + assert!(test.get_state_overrides().is_some()); assert!(!test - .flashblocks .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -457,13 +491,12 @@ mod tests { ) .await; - let pending = test.flashblocks.get_block(true); + let pending = test.get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test - .flashblocks .get_state_overrides() .expect("should be set from txn execution"); @@ -481,7 +514,6 @@ mod tests { .await; let overrides = test - .flashblocks .get_state_overrides() .expect("should be set from txn execution in flashblock index 1"); @@ -505,17 +537,15 @@ mod tests { let initial_block_number = initial_base.metadata.block_number; test.send_flashblock(initial_base).await; assert_eq!( - test.flashblocks - .get_block(true) + test.get_block(true) .expect("block is built") .transactions .len(), 1 ); - assert!(test.flashblocks.get_state_overrides().is_some()); + assert!(test.get_state_overrides().is_some()); assert!(!test - .flashblocks .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -531,13 +561,12 @@ mod tests { ) .await; - let pending = test.flashblocks.get_block(true); + let pending = test.get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test - .flashblocks .get_state_overrides() .expect("should be set from txn execution"); @@ -559,25 +588,19 @@ mod tests { .await; assert_eq!( - test.flashblocks - .get_block(true) + test.get_block(true) .expect("block is built") .transactions .len(), 1 ); assert_eq!( - test.flashblocks - .get_block(true) - .expect("block is built") - .header - .number, + test.get_block(true).expect("block is built").header.number, initial_block_number + 1 ); - assert!(test.flashblocks.get_state_overrides().is_some()); + assert!(test.get_state_overrides().is_some()); assert!(test - .flashblocks .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -595,7 +618,6 @@ mod tests { .await; let overrides = test - .flashblocks .get_state_overrides() .expect("should be set from txn execution"); @@ -618,16 +640,14 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; assert_eq!( - test.flashblocks - .get_block(true) + test.get_block(true) .expect("block is built") .transactions .len(), 1 ); - assert!(test.flashblocks.get_state_overrides().is_some()); + assert!(test.get_state_overrides().is_some()); assert!(!test - .flashblocks .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -642,13 +662,12 @@ mod tests { .build(), ) .await; - let pending = test.flashblocks.get_block(true); + let pending = test.get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test - .flashblocks .get_state_overrides() .expect("should be set from txn execution"); @@ -679,13 +698,12 @@ mod tests { .build(), ) .await; - let pending = test.flashblocks.get_block(true); + let pending = test.get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test - .flashblocks .get_state_overrides() .expect("should be set from txn execution"); @@ -707,13 +725,12 @@ mod tests { )]) .await; - let pending = test.flashblocks.get_block(true); + let pending = test.get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test - .flashblocks .get_state_overrides() .expect("should be set from txn execution"); @@ -758,7 +775,6 @@ mod tests { .unwrap() .nonce + test - .flashblocks .get_transaction_count(test.address(User::Alice)) .to::(); assert_eq!(pending_nonce, 1); @@ -775,7 +791,6 @@ mod tests { .unwrap() .nonce + test - .flashblocks .get_transaction_count(test.address(User::Alice)) .to::(); @@ -788,7 +803,7 @@ mod tests { // use the pending canon block instead of the latest block when fetching // onchain nonce count to compute // pending_nonce = onchain_nonce + pending_txn_count - let canon_block = test.flashblocks.get_canonical_block_number(); + let canon_block = test.get_canonical_flashblock_number(); let canon_state_provider = test .provider .state_by_block_number_or_tag(canon_block) @@ -799,7 +814,6 @@ mod tests { .unwrap(); let pending_nonce = canon_nonce + test - .flashblocks .get_transaction_count(test.address(User::Alice)) .to::(); assert_eq!(pending_nonce, 1); @@ -813,7 +827,7 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; - let current_block = test.flashblocks.get_block(true); + let current_block = test.get_block(true); test.send_flashblock( FlashblockBuilder::new(&test, 1) @@ -827,7 +841,7 @@ mod tests { ) .await; - let pending_block = test.flashblocks.get_block(true); + let pending_block = test.get_block(true); // When the flashblock is invalid, the chain doesn't progress assert_eq!(pending_block.unwrap().hash(), current_block.unwrap().hash()); @@ -841,7 +855,7 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; - let current_block = test.flashblocks.get_block(true).expect("should be a block"); + let current_block = test.get_block(true).expect("should be a block"); assert_eq!(current_block.header().number, 1); assert_eq!(current_block.transactions.len(), 1); @@ -853,7 +867,7 @@ mod tests { ) .await; - let current_block = test.flashblocks.get_block(true); + let current_block = test.get_block(true); assert!(current_block.is_none()); } @@ -865,7 +879,7 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; - let current_block = test.flashblocks.get_block(true).expect("should be a block"); + let current_block = test.get_block(true).expect("should be a block"); assert_eq!(current_block.header().number, 1); assert_eq!(current_block.transactions.len(), 1); @@ -877,7 +891,7 @@ mod tests { ) .await; - let current_block = test.flashblocks.get_block(true).expect("should be a block"); + let current_block = test.get_block(true).expect("should be a block"); assert_eq!(current_block.header().number, 2); assert_eq!(current_block.transactions.len(), 1); @@ -888,15 +902,14 @@ mod tests { reth_tracing::init_test_tracing(); let test = TestHarness::new(); - assert!(test.flashblocks.get_block(true).is_none()); + assert!(test.get_block(true).is_none()); test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; // Just the block info transaction assert_eq!( - test.flashblocks - .get_block(true) + test.get_block(true) .expect("should be set") .transactions .len(), @@ -917,8 +930,7 @@ mod tests { // Still the block info transaction, the txns in the third payload are ignored as it's // missing a Flashblock assert_eq!( - test.flashblocks - .get_block(true) + test.get_block(true) .expect("should be set") .transactions .len(), @@ -943,10 +955,10 @@ mod tests { .build(); test.send_flashblock(fb.clone()).await; - let block = test.flashblocks.get_block(true); + let block = test.get_block(true); test.send_flashblock(fb.clone()).await; - let block_two = test.flashblocks.get_block(true); + let block_two = test.get_block(true); assert_eq!(block, block_two); } @@ -959,7 +971,7 @@ mod tests { let genesis_block = test.current_canonical_block(); assert_eq!(genesis_block.number, 0); assert_eq!(genesis_block.transaction_count(), 0); - assert!(test.flashblocks.get_block(true).is_none()); + assert!(test.get_block(true).is_none()); test.new_canonical_block(vec![test.build_transaction_to_send_eth( User::Alice, @@ -971,7 +983,7 @@ mod tests { let block_one = test.current_canonical_block(); assert_eq!(block_one.number, 1); assert_eq!(block_one.transaction_count(), 2); - assert!(test.flashblocks.get_block(true).is_none()); + assert!(test.get_block(true).is_none()); test.new_canonical_block(vec![ test.build_transaction_to_send_eth(User::Bob, User::Charlie, 100), @@ -982,6 +994,6 @@ mod tests { let block_two = test.current_canonical_block(); assert_eq!(block_two.number, 2); assert_eq!(block_two.transaction_count(), 3); - assert!(test.flashblocks.get_block(true).is_none()); + assert!(test.get_block(true).is_none()); } } From d9d1e9603f4861099dd65ed66812a153954e3c51 Mon Sep 17 00:00:00 2001 From: Haardik H Date: Mon, 29 Sep 2025 11:47:33 -0400 Subject: [PATCH 3/3] cleanup --- crates/flashblocks-rpc/src/rpc.rs | 91 ++++------ crates/flashblocks-rpc/src/state.rs | 60 ++++++- crates/flashblocks-rpc/src/tests/state.rs | 204 ++++++++++++++-------- 3 files changed, 221 insertions(+), 134 deletions(-) diff --git a/crates/flashblocks-rpc/src/rpc.rs b/crates/flashblocks-rpc/src/rpc.rs index 83e4d98..85be61a 100644 --- a/crates/flashblocks-rpc/src/rpc.rs +++ b/crates/flashblocks-rpc/src/rpc.rs @@ -9,6 +9,7 @@ use alloy_primitives::{Address, TxHash, U256}; use alloy_rpc_types::simulate::{SimBlock, SimulatePayload, SimulatedBlock}; use alloy_rpc_types::state::{EvmOverrides, StateOverride, StateOverridesBuilder}; use alloy_rpc_types::BlockOverrides; +use arc_swap::Guard; use jsonrpsee::{ core::{async_trait, RpcResult}, proc_macros::rpc, @@ -37,12 +38,34 @@ pub const MAX_TIMEOUT_SEND_RAW_TX_SYNC_MS: u64 = 6_000; /// Core API for accessing flashblock state and data. pub trait FlashblocksAPI { /// Retrieves the pending blocks. - fn get_pending_blocks(&self) -> Option>; + fn get_pending_blocks(&self) -> Guard>>; - /// Creates a subscription to receive flashblock updates. fn subscribe_to_flashblocks(&self) -> broadcast::Receiver; } +pub trait PendingBlocksAPI { + /// Get the canonical block number on top of which all pending state is built + fn get_canonical_block_number(&self) -> BlockNumberOrTag; + + /// Get the pending transactions count for an address + fn get_transaction_count(&self, address: Address) -> U256; + + /// Retrieves the current block. If `full` is true, includes full transaction details. + fn get_block(&self, full: bool) -> Option>; + + /// Gets transaction receipt by hash. + fn get_transaction_receipt(&self, tx_hash: TxHash) -> Option>; + + /// Gets transaction details by hash. + fn get_transaction_by_hash(&self, tx_hash: TxHash) -> Option>; + + /// Gets balance for an address. Returns None if address not updated in flashblocks. + fn get_balance(&self, address: Address) -> Option; + + /// Gets the state overrides for the pending blocks + fn get_state_overrides(&self) -> Option; +} + #[cfg_attr(not(test), rpc(server, namespace = "eth"))] #[cfg_attr(test, rpc(server, client, namespace = "eth"))] pub trait EthApiOverride { @@ -145,7 +168,7 @@ where if number.is_pending() { self.metrics.get_block_by_number.increment(1); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - Ok(pending_blocks.as_ref().map(|pb| pb.get_latest_block(full))) + Ok(pending_blocks.get_block(full)) } else { EthBlocks::rpc_block(&self.eth_api, number.into(), full) .await @@ -163,10 +186,7 @@ where ); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - if let Some(fb_receipt) = pending_blocks - .as_ref() - .and_then(|pb| pb.get_receipt(tx_hash)) - { + if let Some(fb_receipt) = pending_blocks.get_transaction_receipt(tx_hash) { self.metrics.get_transaction_receipt.increment(1); return Ok(Some(fb_receipt)); } @@ -189,10 +209,7 @@ where if block_id.is_pending() { self.metrics.get_balance.increment(1); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - if let Some(balance) = pending_blocks - .as_ref() - .and_then(|pb| pb.get_balance(address)) - { + if let Some(balance) = pending_blocks.get_balance(address) { return Ok(balance); } } @@ -216,14 +233,8 @@ where if block_id.is_pending() { self.metrics.get_transaction_count.increment(1); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - let canon_block = pending_blocks - .as_ref() - .map(|pb| pb.canonical_block_number()) - .unwrap_or(BlockNumberOrTag::Latest); - let fb_count = pending_blocks - .as_ref() - .map(|pb| pb.get_transaction_count(address)) - .unwrap_or_else(|| U256::from(0)); + let canon_block = pending_blocks.get_canonical_block_number(); + let fb_count = pending_blocks.get_transaction_count(address); let canon_count = EthState::transaction_count(&self.eth_api, address, Some(canon_block.into())) @@ -249,10 +260,7 @@ where let pending_blocks = self.flashblocks_state.get_pending_blocks(); - if let Some(fb_transaction) = pending_blocks - .as_ref() - .and_then(|pb| pb.get_transaction_by_hash(tx_hash)) - { + if let Some(fb_transaction) = pending_blocks.get_transaction_by_hash(tx_hash) { self.metrics.get_transaction_receipt.increment(1); return Ok(Some(fb_transaction)); } @@ -342,15 +350,8 @@ where if block_id.is_pending() { self.metrics.call.increment(1); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - block_id = pending_blocks - .as_ref() - .map(|pb| pb.canonical_block_number()) - .unwrap_or(BlockNumberOrTag::Latest) - .into(); - pending_overrides.state = pending_blocks - .as_ref() - .map(|pb| pb.get_state_overrides()) - .unwrap_or_default(); + block_id = pending_blocks.get_canonical_block_number().into(); + pending_overrides.state = pending_blocks.get_state_overrides(); } // Apply user's overrides on top @@ -390,15 +391,8 @@ where if block_id.is_pending() { self.metrics.estimate_gas.increment(1); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - block_id = pending_blocks - .as_ref() - .map(|pb| pb.canonical_block_number()) - .unwrap_or(BlockNumberOrTag::Latest) - .into(); - pending_overrides.state = pending_blocks - .as_ref() - .map(|pb| pb.get_state_overrides()) - .unwrap_or_default(); + block_id = pending_blocks.get_canonical_block_number().into(); + pending_overrides.state = pending_blocks.get_state_overrides(); } let mut state_overrides_builder = @@ -428,15 +422,8 @@ where if block_id.is_pending() { self.metrics.simulate_v1.increment(1); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - block_id = pending_blocks - .as_ref() - .map(|pb| pb.canonical_block_number()) - .unwrap_or(BlockNumberOrTag::Latest) - .into(); - pending_overrides.state = pending_blocks - .as_ref() - .map(|pb| pb.get_state_overrides()) - .unwrap_or_default(); + block_id = pending_blocks.get_canonical_block_number().into(); + pending_overrides.state = pending_blocks.get_state_overrides(); } // Prepend flashblocks pending overrides to the block state calls @@ -479,9 +466,7 @@ where Ok(flashblock) if flashblock.metadata.receipts.contains_key(&tx_hash) => { debug!(message = "found receipt in flashblock", tx_hash = %tx_hash); let pending_blocks = self.flashblocks_state.get_pending_blocks(); - return pending_blocks - .as_ref() - .and_then(|pb| pb.get_receipt(tx_hash)); + return pending_blocks.get_transaction_receipt(tx_hash); } Ok(_) => { trace!(message = "flashblock does not contain receipt", tx_hash = %tx_hash); diff --git a/crates/flashblocks-rpc/src/state.rs b/crates/flashblocks-rpc/src/state.rs index 36ff299..b00d5e0 100644 --- a/crates/flashblocks-rpc/src/state.rs +++ b/crates/flashblocks-rpc/src/state.rs @@ -1,20 +1,20 @@ use crate::metrics::Metrics; use crate::pending_blocks::{PendingBlocks, PendingBlocksBuilder}; -use crate::rpc::FlashblocksAPI; +use crate::rpc::{FlashblocksAPI, PendingBlocksAPI}; use crate::subscription::{Flashblock, FlashblocksReceiver}; use alloy_consensus::transaction::{Recovered, SignerRecoverable, TransactionMeta}; use alloy_consensus::{Header, TxReceipt}; use alloy_eips::BlockNumberOrTag; use alloy_primitives::map::foldhash::HashMap; use alloy_primitives::map::B256HashMap; -use alloy_primitives::{BlockNumber, Bytes, Sealable, B256}; -use alloy_rpc_types::{TransactionTrait, Withdrawal}; +use alloy_primitives::{Address, BlockNumber, Bytes, Sealable, B256, U256}; +use alloy_rpc_types::{state::StateOverride, TransactionTrait, Withdrawal}; use alloy_rpc_types_engine::{ExecutionPayloadV1, ExecutionPayloadV2, ExecutionPayloadV3}; use alloy_rpc_types_eth::state::{AccountOverride, StateOverridesBuilder}; -use arc_swap::ArcSwapOption; +use arc_swap::{ArcSwapOption, Guard}; use eyre::eyre; use op_alloy_consensus::OpTxEnvelope; -use op_alloy_network::TransactionResponse; +use op_alloy_network::{Optimism, TransactionResponse}; use op_alloy_rpc_types::Transaction; use reth::chainspec::{ChainSpecProvider, EthChainSpec}; use reth::providers::{BlockReaderIdExt, StateProviderFactory}; @@ -28,7 +28,8 @@ use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes}; use reth_optimism_primitives::{DepositReceipt, OpBlock, OpPrimitives}; use reth_optimism_rpc::OpReceiptBuilder; use reth_primitives::RecoveredBlock; -use reth_rpc_convert::transaction::ConvertReceiptInput; +use reth_rpc_convert::{transaction::ConvertReceiptInput, RpcTransaction}; +use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; use std::collections::{BTreeMap, HashSet}; use std::sync::Arc; use std::time::Instant; @@ -117,8 +118,8 @@ impl FlashblocksReceiver for FlashblocksState { } impl FlashblocksAPI for FlashblocksState { - fn get_pending_blocks(&self) -> Option> { - self.pending_blocks.load_full() + fn get_pending_blocks(&self) -> Guard>> { + self.pending_blocks.load() } fn subscribe_to_flashblocks(&self) -> tokio::sync::broadcast::Receiver { @@ -126,6 +127,49 @@ impl FlashblocksAPI for FlashblocksState { } } +impl PendingBlocksAPI for Guard>> { + fn get_canonical_block_number(&self) -> BlockNumberOrTag { + self.as_ref() + .map(|pb| pb.canonical_block_number()) + .unwrap_or(BlockNumberOrTag::Latest) + } + + fn get_transaction_count(&self, address: Address) -> U256 { + self.as_ref() + .map(|pb| pb.get_transaction_count(address)) + .unwrap_or_else(|| U256::from(0)) + } + + fn get_block(&self, full: bool) -> Option> { + self.as_ref().map(|pb| pb.get_latest_block(full)) + } + + fn get_transaction_receipt( + &self, + tx_hash: alloy_primitives::TxHash, + ) -> Option> { + self.as_ref().and_then(|pb| pb.get_receipt(tx_hash)) + } + + fn get_transaction_by_hash( + &self, + tx_hash: alloy_primitives::TxHash, + ) -> Option> { + self.as_ref() + .and_then(|pb| pb.get_transaction_by_hash(tx_hash)) + } + + fn get_balance(&self, address: Address) -> Option { + self.as_ref().and_then(|pb| pb.get_balance(address)) + } + + fn get_state_overrides(&self) -> Option { + self.as_ref() + .map(|pb| pb.get_state_overrides()) + .unwrap_or_default() + } +} + #[derive(Debug, Clone)] struct StateProcessor { rx: Arc>>, diff --git a/crates/flashblocks-rpc/src/tests/state.rs b/crates/flashblocks-rpc/src/tests/state.rs index 180b428..cb1ae12 100644 --- a/crates/flashblocks-rpc/src/tests/state.rs +++ b/crates/flashblocks-rpc/src/tests/state.rs @@ -1,6 +1,6 @@ #[cfg(test)] mod tests { - use crate::rpc::FlashblocksAPI; + use crate::rpc::{FlashblocksAPI, PendingBlocksAPI}; use crate::state::FlashblocksState; use crate::subscription::{Flashblock, FlashblocksReceiver, Metadata}; use crate::tests::utils::create_test_provider_factory; @@ -8,15 +8,13 @@ mod tests { use alloy_consensus::crypto::secp256k1::public_key_to_address; use alloy_consensus::{BlockHeader, Receipt}; use alloy_consensus::{Header, Transaction}; - use alloy_eips::{BlockHashOrNumber, BlockNumberOrTag, Decodable2718, Encodable2718}; + use alloy_eips::{BlockHashOrNumber, Decodable2718, Encodable2718}; use alloy_genesis::GenesisAccount; use alloy_primitives::map::foldhash::HashMap; use alloy_primitives::{Address, BlockNumber, Bytes, B256, U256}; use alloy_provider::network::BlockResponse; - use alloy_rpc_types::state::StateOverride; use alloy_rpc_types_engine::PayloadId; use op_alloy_consensus::OpDepositReceipt; - use op_alloy_network::Optimism; use reth::builder::NodeTypesWithDBAdapter; use reth::chainspec::EthChainSpec; use reth::providers::{AccountReader, BlockNumReader, BlockReader}; @@ -35,7 +33,6 @@ mod tests { BlockWriter, ChainSpecProvider, ExecutionOutcome, LatestStateProviderRef, ProviderFactory, StateProviderFactory, }; - use reth_rpc_eth_api::RpcBlock; use rollup_boost::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; use std::sync::Arc; use std::time::Duration; @@ -73,43 +70,6 @@ mod tests { self.user_to_private_key[&u] } - fn get_transaction_count(&self, address: Address) -> U256 { - self.flashblocks - .get_pending_blocks() - .as_ref() - .map(|pb| pb.get_transaction_count(address)) - .unwrap_or_else(|| U256::from(0)) - } - - fn get_balance(&self, address: Address) -> Option { - self.flashblocks - .get_pending_blocks() - .as_ref() - .and_then(|pb| pb.get_balance(address)) - } - - fn get_state_overrides(&self) -> Option { - self.flashblocks - .get_pending_blocks() - .as_ref() - .and_then(|pb| pb.get_state_overrides()) - } - - fn get_block(&self, full: bool) -> Option> { - self.flashblocks - .get_pending_blocks() - .as_ref() - .map(|pb| pb.get_latest_block(full)) - } - - fn get_canonical_flashblock_number(&self) -> BlockNumberOrTag { - self.flashblocks - .get_pending_blocks() - .as_ref() - .map(|pb| pb.canonical_block_number()) - .unwrap_or_else(|| BlockNumberOrTag::Latest) - } - fn current_canonical_block(&self) -> RecoveredBlock { let latest_block_num = self .provider @@ -131,13 +91,19 @@ mod tests { .expect("can lookup account state") .expect("should be existing account state"); - let nonce = self.get_transaction_count(self.address(u)).to::(); + let nonce = self + .flashblocks + .get_pending_blocks() + .get_transaction_count(self.address(u)) + .to::(); let balance = self + .flashblocks + .get_pending_blocks() .get_balance(self.address(u)) .unwrap_or(basic_account.balance); Account { - nonce, + nonce: nonce + basic_account.nonce, balance, bytecode_hash: basic_account.bytecode_hash, } @@ -467,15 +433,23 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; assert_eq!( - test.get_block(true) + test.flashblocks + .get_pending_blocks() + .get_block(true) .expect("block is built") .transactions .len(), 1 ); - assert!(test.get_state_overrides().is_some()); + assert!(test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .is_some()); assert!(!test + .flashblocks + .get_pending_blocks() .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -491,12 +465,14 @@ mod tests { ) .await; - let pending = test.get_block(true); + let pending = test.flashblocks.get_pending_blocks().get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test + .flashblocks + .get_pending_blocks() .get_state_overrides() .expect("should be set from txn execution"); @@ -514,6 +490,8 @@ mod tests { .await; let overrides = test + .flashblocks + .get_pending_blocks() .get_state_overrides() .expect("should be set from txn execution in flashblock index 1"); @@ -537,15 +515,23 @@ mod tests { let initial_block_number = initial_base.metadata.block_number; test.send_flashblock(initial_base).await; assert_eq!( - test.get_block(true) + test.flashblocks + .get_pending_blocks() + .get_block(true) .expect("block is built") .transactions .len(), 1 ); - assert!(test.get_state_overrides().is_some()); + assert!(test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .is_some()); assert!(!test + .flashblocks + .get_pending_blocks() .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -561,12 +547,14 @@ mod tests { ) .await; - let pending = test.get_block(true); + let pending = test.flashblocks.get_pending_blocks().get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test + .flashblocks + .get_pending_blocks() .get_state_overrides() .expect("should be set from txn execution"); @@ -588,19 +576,32 @@ mod tests { .await; assert_eq!( - test.get_block(true) + test.flashblocks + .get_pending_blocks() + .get_block(true) .expect("block is built") .transactions .len(), 1 ); assert_eq!( - test.get_block(true).expect("block is built").header.number, + test.flashblocks + .get_pending_blocks() + .get_block(true) + .expect("block is built") + .header + .number, initial_block_number + 1 ); - assert!(test.get_state_overrides().is_some()); assert!(test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .is_some()); + assert!(test + .flashblocks + .get_pending_blocks() .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -618,6 +619,8 @@ mod tests { .await; let overrides = test + .flashblocks + .get_pending_blocks() .get_state_overrides() .expect("should be set from txn execution"); @@ -640,14 +643,22 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; assert_eq!( - test.get_block(true) + test.flashblocks + .get_pending_blocks() + .get_block(true) .expect("block is built") .transactions .len(), 1 ); - assert!(test.get_state_overrides().is_some()); + assert!(test + .flashblocks + .get_pending_blocks() + .get_state_overrides() + .is_some()); assert!(!test + .flashblocks + .get_pending_blocks() .get_state_overrides() .unwrap() .contains_key(&test.address(User::Alice))); @@ -662,12 +673,14 @@ mod tests { .build(), ) .await; - let pending = test.get_block(true); + let pending = test.flashblocks.get_pending_blocks().get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test + .flashblocks + .get_pending_blocks() .get_state_overrides() .expect("should be set from txn execution"); @@ -698,12 +711,14 @@ mod tests { .build(), ) .await; - let pending = test.get_block(true); + let pending = test.flashblocks.get_pending_blocks().get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test + .flashblocks + .get_pending_blocks() .get_state_overrides() .expect("should be set from txn execution"); @@ -725,12 +740,14 @@ mod tests { )]) .await; - let pending = test.get_block(true); + let pending = test.flashblocks.get_pending_blocks().get_block(true); assert!(pending.is_some()); let pending = pending.unwrap(); assert_eq!(pending.transactions.len(), 2); let overrides = test + .flashblocks + .get_pending_blocks() .get_state_overrides() .expect("should be set from txn execution"); @@ -775,6 +792,8 @@ mod tests { .unwrap() .nonce + test + .flashblocks + .get_pending_blocks() .get_transaction_count(test.address(User::Alice)) .to::(); assert_eq!(pending_nonce, 1); @@ -791,6 +810,8 @@ mod tests { .unwrap() .nonce + test + .flashblocks + .get_pending_blocks() .get_transaction_count(test.address(User::Alice)) .to::(); @@ -803,7 +824,10 @@ mod tests { // use the pending canon block instead of the latest block when fetching // onchain nonce count to compute // pending_nonce = onchain_nonce + pending_txn_count - let canon_block = test.get_canonical_flashblock_number(); + let canon_block = test + .flashblocks + .get_pending_blocks() + .get_canonical_block_number(); let canon_state_provider = test .provider .state_by_block_number_or_tag(canon_block) @@ -814,6 +838,8 @@ mod tests { .unwrap(); let pending_nonce = canon_nonce + test + .flashblocks + .get_pending_blocks() .get_transaction_count(test.address(User::Alice)) .to::(); assert_eq!(pending_nonce, 1); @@ -827,7 +853,7 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; - let current_block = test.get_block(true); + let current_block = test.flashblocks.get_pending_blocks().get_block(true); test.send_flashblock( FlashblockBuilder::new(&test, 1) @@ -841,7 +867,7 @@ mod tests { ) .await; - let pending_block = test.get_block(true); + let pending_block = test.flashblocks.get_pending_blocks().get_block(true); // When the flashblock is invalid, the chain doesn't progress assert_eq!(pending_block.unwrap().hash(), current_block.unwrap().hash()); @@ -855,7 +881,11 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; - let current_block = test.get_block(true).expect("should be a block"); + let current_block = test + .flashblocks + .get_pending_blocks() + .get_block(true) + .expect("should be a block"); assert_eq!(current_block.header().number, 1); assert_eq!(current_block.transactions.len(), 1); @@ -867,7 +897,7 @@ mod tests { ) .await; - let current_block = test.get_block(true); + let current_block = test.flashblocks.get_pending_blocks().get_block(true); assert!(current_block.is_none()); } @@ -879,7 +909,11 @@ mod tests { test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; - let current_block = test.get_block(true).expect("should be a block"); + let current_block = test + .flashblocks + .get_pending_blocks() + .get_block(true) + .expect("should be a block"); assert_eq!(current_block.header().number, 1); assert_eq!(current_block.transactions.len(), 1); @@ -891,7 +925,11 @@ mod tests { ) .await; - let current_block = test.get_block(true).expect("should be a block"); + let current_block = test + .flashblocks + .get_pending_blocks() + .get_block(true) + .expect("should be a block"); assert_eq!(current_block.header().number, 2); assert_eq!(current_block.transactions.len(), 1); @@ -902,14 +940,20 @@ mod tests { reth_tracing::init_test_tracing(); let test = TestHarness::new(); - assert!(test.get_block(true).is_none()); + assert!(test + .flashblocks + .get_pending_blocks() + .get_block(true) + .is_none()); test.send_flashblock(FlashblockBuilder::new_base(&test).build()) .await; // Just the block info transaction assert_eq!( - test.get_block(true) + test.flashblocks + .get_pending_blocks() + .get_block(true) .expect("should be set") .transactions .len(), @@ -930,7 +974,9 @@ mod tests { // Still the block info transaction, the txns in the third payload are ignored as it's // missing a Flashblock assert_eq!( - test.get_block(true) + test.flashblocks + .get_pending_blocks() + .get_block(true) .expect("should be set") .transactions .len(), @@ -955,10 +1001,10 @@ mod tests { .build(); test.send_flashblock(fb.clone()).await; - let block = test.get_block(true); + let block = test.flashblocks.get_pending_blocks().get_block(true); test.send_flashblock(fb.clone()).await; - let block_two = test.get_block(true); + let block_two = test.flashblocks.get_pending_blocks().get_block(true); assert_eq!(block, block_two); } @@ -971,7 +1017,11 @@ mod tests { let genesis_block = test.current_canonical_block(); assert_eq!(genesis_block.number, 0); assert_eq!(genesis_block.transaction_count(), 0); - assert!(test.get_block(true).is_none()); + assert!(test + .flashblocks + .get_pending_blocks() + .get_block(true) + .is_none()); test.new_canonical_block(vec![test.build_transaction_to_send_eth( User::Alice, @@ -983,7 +1033,11 @@ mod tests { let block_one = test.current_canonical_block(); assert_eq!(block_one.number, 1); assert_eq!(block_one.transaction_count(), 2); - assert!(test.get_block(true).is_none()); + assert!(test + .flashblocks + .get_pending_blocks() + .get_block(true) + .is_none()); test.new_canonical_block(vec![ test.build_transaction_to_send_eth(User::Bob, User::Charlie, 100), @@ -994,6 +1048,10 @@ mod tests { let block_two = test.current_canonical_block(); assert_eq!(block_two.number, 2); assert_eq!(block_two.transaction_count(), 3); - assert!(test.get_block(true).is_none()); + assert!(test + .flashblocks + .get_pending_blocks() + .get_block(true) + .is_none()); } }