diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index cb6765219466..345a9fe34916 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -21,7 +21,7 @@ use reth_primitives::{ SealedBlockWithSenders, SealedHeader, U256, }; use reth_provider::{ - chain::{ChainSplit, SplitAt}, + chain::{ChainSplit, ChainSplitTarget}, BlockExecutionWriter, BlockNumReader, BlockWriter, BundleStateWithReceipts, CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications, Chain, DatabaseProvider, DisplayBlocksChain, ExecutorFactory, HeaderProvider, @@ -844,7 +844,7 @@ impl BlockchainTree { &mut self, chain_id: BlockChainId, chain: AppendableChain, - split_at: SplitAt, + split_at: ChainSplitTarget, ) -> Chain { let chain = chain.into_inner(); match chain.split(split_at) { @@ -949,7 +949,7 @@ impl BlockchainTree { trace!(target: "blockchain_tree", ?chain, "Found chain to make canonical"); // we are splitting chain at the block hash that we want to make canonical - let canonical = self.split_chain(chain_id, chain, SplitAt::Hash(*block_hash)); + let canonical = self.split_chain(chain_id, chain, ChainSplitTarget::Hash(*block_hash)); durations_recorder.record_relative(MakeCanonicalAction::SplitChain); let mut block_fork = canonical.fork_block(); @@ -961,7 +961,8 @@ impl BlockchainTree { let chain = self.state.chains.remove(&chain_id).expect("To fork to be present"); block_fork = chain.fork_block(); // canonical chain is lower part of the chain. - let canonical = self.split_chain(chain_id, chain, SplitAt::Number(block_fork_number)); + let canonical = + self.split_chain(chain_id, chain, ChainSplitTarget::Number(block_fork_number)); block_fork_number = canonical.fork_block_number(); chains_to_promote.push(canonical); } diff --git a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs index ed2556bc8c38..dba187adae33 100644 --- a/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs +++ b/crates/storage/provider/src/bundle_state/bundle_state_with_receipts.rs @@ -261,16 +261,11 @@ impl BundleStateWithReceipts { self.first_block } - /// Return last block of the bundle. - pub fn last_block(&self) -> BlockNumber { - self.first_block + self.len() as BlockNumber - } - /// Revert to given block number. /// /// If number is in future, or in the past return false /// - /// Note: Given Block number will stay inside the bundle state. + /// NOTE: Provided block number will stay inside the bundle state. pub fn revert_to(&mut self, block_number: BlockNumber) -> bool { let Some(index) = self.block_number_to_index(block_number) else { return false }; @@ -286,40 +281,31 @@ impl BundleStateWithReceipts { true } - /// This will detach lower part of the chain and return it back. - /// Specified block number will be included in detachment + /// Splits the block range state at a given block number. + /// Returns two split states ([..at], [at..]). + /// The plain state of the 2nd bundle state will contain extra changes + /// that were made in state transitions belonging to the lower state. /// - /// This plain state will contains some additional information that - /// are is a artifacts of the lower part state. + /// # Panics /// - /// If block number is in future, return None. - pub fn split_at(&mut self, block_number: BlockNumber) -> Option { - let last_block = self.last_block(); - let first_block = self.first_block; - if block_number >= last_block { - return None + /// If the target block number is not included in the state block range. + pub fn split_at(self, at: BlockNumber) -> (Option, Self) { + if at == self.first_block { + return (None, self) } - if block_number < first_block { - return Some(Self::default()) - } - - // detached number should be included so we are adding +1 to it. - // for example if block number is same as first_block then - // number of detached block shoud be 1. - let num_of_detached_block = (block_number - first_block) + 1; - - let mut detached_bundle_state: BundleStateWithReceipts = self.clone(); - detached_bundle_state.revert_to(block_number); - // split is done as [0, num) and [num, len] - let (_, this) = self.receipts.split_at(num_of_detached_block as usize); + let (mut lower_state, mut higher_state) = (self.clone(), self); - self.receipts = Receipts::from_vec(this.to_vec().clone()); - self.bundle.take_n_reverts(num_of_detached_block as usize); + // Revert lower state to [..at]. + lower_state.revert_to(at.checked_sub(1).unwrap()); - self.first_block = block_number + 1; + // Truncate higher state to [at..]. + let at_idx = higher_state.block_number_to_index(at).unwrap(); + higher_state.receipts = Receipts::from_vec(higher_state.receipts.split_off(at_idx)); + higher_state.bundle.take_n_reverts(at_idx); + higher_state.first_block = at; - Some(detached_bundle_state) + (Some(lower_state), higher_state) } /// Extend one state from another diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index 3c9d40d81a81..95bf68b3b24e 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -197,10 +197,10 @@ impl Chain { /// it retains the up to date state as if the chains were one, i.e. the second chain is an /// extension of the first. #[track_caller] - pub fn split(mut self, split_at: SplitAt) -> ChainSplit { + pub fn split(mut self, split_at: ChainSplitTarget) -> ChainSplit { let chain_tip = *self.blocks.last_entry().expect("chain is never empty").key(); let block_number = match split_at { - SplitAt::Hash(block_hash) => { + ChainSplitTarget::Hash(block_hash) => { let Some(block_number) = self.block_number(block_hash) else { return ChainSplit::NoSplitPending(self) }; @@ -210,7 +210,7 @@ impl Chain { } block_number } - SplitAt::Number(block_number) => { + ChainSplitTarget::Number(block_number) => { if block_number >= chain_tip { return ChainSplit::NoSplitCanonical(self) } @@ -221,15 +221,18 @@ impl Chain { } }; - let higher_number_blocks = self.blocks.split_off(&(block_number + 1)); + let split_at = block_number + 1; + let higher_number_blocks = self.blocks.split_off(&split_at); - let mut state = std::mem::take(&mut self.state); - let canonical_state = - state.split_at(block_number).expect("Detach block number to be in range"); + let state = std::mem::take(&mut self.state); + let (canonical_state, pending_state) = state.split_at(split_at); ChainSplit::Split { - canonical: Chain { state: canonical_state, blocks: self.blocks }, - pending: Chain { state, blocks: higher_number_blocks }, + canonical: Chain { + state: canonical_state.expect("split in range"), + blocks: self.blocks, + }, + pending: Chain { state: pending_state, blocks: higher_number_blocks }, } } } @@ -325,9 +328,9 @@ pub struct BlockReceipts { pub tx_receipts: Vec<(TxHash, Receipt)>, } -/// Used in spliting the chain. +/// The target block of the chain split. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum SplitAt { +pub enum ChainSplitTarget { /// Split at block number. Number(BlockNumber), /// Split at block hash. @@ -347,10 +350,11 @@ pub enum ChainSplit { /// Chain is split into two. /// Given block split is contained in first chain. Split { - /// Left contains lower block numbers that get are considered canonicalized. It ends with - /// the [SplitAt] block. The substate of this chain is now empty and not usable. + /// Left contains lower block numbers that are considered canonicalized. It ends with + /// the [ChainSplitTarget] block. The substate of this chain is now empty and not usable. canonical: Chain, - /// Right contains all subsequent blocks after the [SplitAt], that are still pending. + /// Right contains all subsequent blocks after the [ChainSplitTarget] that are still + /// pending. /// /// The substate of the original chain is moved here. pending: Chain, @@ -447,11 +451,10 @@ mod tests { let chain = Chain::new(vec![block1.clone(), block2.clone()], block_state_extended); - let mut split2_state = chain.state.clone(); - let split1_state = split2_state.split_at(1).unwrap(); + let (split1_state, split2_state) = chain.state.clone().split_at(2); let chain_split1 = - Chain { state: split1_state, blocks: BTreeMap::from([(1, block1.clone())]) }; + Chain { state: split1_state.unwrap(), blocks: BTreeMap::from([(1, block1.clone())]) }; let chain_split2 = Chain { state: split2_state, blocks: BTreeMap::from([(2, block2.clone())]) }; @@ -464,23 +467,26 @@ mod tests { // split in two assert_eq!( - chain.clone().split(SplitAt::Hash(block1_hash)), + chain.clone().split(ChainSplitTarget::Hash(block1_hash)), ChainSplit::Split { canonical: chain_split1, pending: chain_split2 } ); // split at unknown block hash assert_eq!( - chain.clone().split(SplitAt::Hash(B256::new([100; 32]))), + chain.clone().split(ChainSplitTarget::Hash(B256::new([100; 32]))), ChainSplit::NoSplitPending(chain.clone()) ); // split at higher number assert_eq!( - chain.clone().split(SplitAt::Number(10)), + chain.clone().split(ChainSplitTarget::Number(10)), ChainSplit::NoSplitCanonical(chain.clone()) ); // split at lower number - assert_eq!(chain.clone().split(SplitAt::Number(0)), ChainSplit::NoSplitPending(chain)); + assert_eq!( + chain.clone().split(ChainSplitTarget::Number(0)), + ChainSplit::NoSplitPending(chain) + ); } }