diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index 565d6801a..1d850933a 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -546,8 +546,9 @@ impl BlockchainTree { let Some(chain) = self.state.chains.get(&chain_id) else { return hashes }; hashes.extend(chain.blocks().values().map(|b| (b.number, b.hash()))); - let fork_block = chain.fork_block_hash(); - if let Some(next_chain_id) = self.block_indices().get_blocks_chain_id(&fork_block) { + let fork_block = chain.fork_block(); + if let Some(next_chain_id) = self.block_indices().get_blocks_chain_id(&fork_block.hash) + { chain_id = next_chain_id; } else { // if there is no fork block that point to other chains, break the loop. @@ -794,7 +795,7 @@ impl BlockchainTree { // check unconnected block buffer for childs of the chains let mut all_chain_blocks = Vec::new(); for (_, chain) in self.state.chains.iter() { - for (&number, blocks) in chain.blocks.iter() { + for (&number, blocks) in chain.blocks().iter() { all_chain_blocks.push(BlockNumHash { number, hash: blocks.hash }) } } @@ -946,18 +947,16 @@ impl BlockchainTree { 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(); - let mut block_fork_number = canonical.fork_block_number(); + let mut fork_block = canonical.fork_block(); let mut chains_to_promote = vec![canonical]; // loop while fork blocks are found in Tree. - while let Some(chain_id) = self.block_indices().get_blocks_chain_id(&block_fork.hash) { - let chain = self.state.chains.remove(&chain_id).expect("To fork to be present"); - block_fork = chain.fork_block(); + while let Some(chain_id) = self.block_indices().get_blocks_chain_id(&fork_block.hash) { + let chain = self.state.chains.remove(&chain_id).expect("fork is present"); // canonical chain is lower part of the chain. let canonical = - self.split_chain(chain_id, chain, ChainSplitTarget::Number(block_fork_number)); - block_fork_number = canonical.fork_block_number(); + self.split_chain(chain_id, chain, ChainSplitTarget::Number(fork_block.number)); + fork_block = canonical.fork_block(); chains_to_promote.push(canonical); } durations_recorder.record_relative(MakeCanonicalAction::SplitChainForks); @@ -989,7 +988,7 @@ impl BlockchainTree { ); // if joins to the tip; - if new_canon_chain.fork_block_hash() == old_tip.hash { + if new_canon_chain.fork_block().hash == old_tip.hash { chain_notification = CanonStateNotification::Commit { new: Arc::new(new_canon_chain.clone()) }; // append to database diff --git a/crates/blockchain-tree/src/chain.rs b/crates/blockchain-tree/src/chain.rs index 36227f003..252b93606 100644 --- a/crates/blockchain-tree/src/chain.rs +++ b/crates/blockchain-tree/src/chain.rs @@ -152,7 +152,7 @@ impl AppendableChain { ) })?; - let mut state = self.state.clone(); + let mut state = self.state().clone(); // Revert state to the state after execution of the parent block state.revert_to(parent.number); @@ -169,11 +169,8 @@ impl AppendableChain { .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; state.extend(block_state); - let chain = - Self { chain: Chain { state, blocks: BTreeMap::from([(block.number, block)]) } }; - // If all is okay, return new chain back. Present chain is not modified. - Ok(chain) + Ok(Self { chain: Chain::from_block(block, state) }) } /// Validate and execute the given block that _extends the canonical chain_, validating its @@ -280,10 +277,10 @@ impl AppendableChain { DB: Database, EF: ExecutorFactory, { - let (_, parent_block) = self.blocks.last_key_value().expect("Chain has at least one block"); + let parent_block = self.chain.tip(); let post_state_data = BundleStateDataRef { - state: &self.state, + state: self.state(), sidechain_block_hashes: &side_chain_block_hashes, canonical_block_hashes, canonical_fork, @@ -299,8 +296,7 @@ impl AppendableChain { ) .map_err(|err| InsertBlockError::new(block.block.clone(), err.into()))?; // extend the state. - self.state.extend(block_state); - self.blocks.insert(block.number, block); + self.chain.append_block(block, block_state); Ok(()) } } diff --git a/crates/storage/provider/src/chain.rs b/crates/storage/provider/src/chain.rs index 53bc31a94..ea240dc6d 100644 --- a/crates/storage/provider/src/chain.rs +++ b/crates/storage/provider/src/chain.rs @@ -16,16 +16,29 @@ use std::{borrow::Cow, collections::BTreeMap, fmt}; /// Used inside the BlockchainTree. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Chain { + /// All blocks in this chain. + blocks: BTreeMap, /// The state of all accounts after execution of the _all_ blocks in this chain's range from /// [Chain::first] to [Chain::tip], inclusive. /// /// This state also contains the individual changes that lead to the current state. - pub state: BundleStateWithReceipts, - /// All blocks in this chain. - pub blocks: BTreeMap, + state: BundleStateWithReceipts, } impl Chain { + /// Create new Chain from blocks and state. + pub fn new( + blocks: impl IntoIterator, + state: BundleStateWithReceipts, + ) -> Self { + Self { blocks: BTreeMap::from_iter(blocks.into_iter().map(|b| (b.number, b))), state } + } + + /// Create new Chain from a single block and its state. + pub fn from_block(block: SealedBlockWithSenders, state: BundleStateWithReceipts) -> Self { + Self::new([block], state) + } + /// Get the blocks in this chain. pub fn blocks(&self) -> &BTreeMap { &self.blocks @@ -96,18 +109,6 @@ impl Chain { ForkBlock { number: first.number.saturating_sub(1), hash: first.parent_hash } } - /// Get the block number at which this chain forked. - #[track_caller] - pub fn fork_block_number(&self) -> BlockNumber { - self.first().number.saturating_sub(1) - } - - /// Get the block hash at which this chain forked. - #[track_caller] - pub fn fork_block_hash(&self) -> BlockHash { - self.first().parent_hash - } - /// Get the first block in this chain. #[track_caller] pub fn first(&self) -> &SealedBlockWithSenders { @@ -124,11 +125,6 @@ impl Chain { self.blocks.last_key_value().expect("Chain should have at least one block").1 } - /// Create new chain with given blocks and post state. - pub fn new(blocks: Vec, state: BundleStateWithReceipts) -> Self { - Self { state, blocks: blocks.into_iter().map(|b| (b.number, b)).collect() } - } - /// Returns length of the chain. pub fn len(&self) -> usize { self.blocks.len() @@ -160,22 +156,30 @@ impl Chain { receipt_attch } + /// Append a single block with state to the chain. + /// This method assumes that blocks attachment to the chain has already been validated. + pub fn append_block(&mut self, block: SealedBlockWithSenders, state: BundleStateWithReceipts) { + self.blocks.insert(block.number, block); + self.state.extend(state); + } + /// Merge two chains by appending the given chain into the current one. /// /// The state of accounts for this chain is set to the state of the newest chain. - pub fn append_chain(&mut self, chain: Chain) -> RethResult<()> { + pub fn append_chain(&mut self, other: Chain) -> RethResult<()> { let chain_tip = self.tip(); - if chain_tip.hash != chain.fork_block_hash() { + let other_fork_block = other.fork_block(); + if chain_tip.hash != other_fork_block.hash { return Err(BlockExecutionError::AppendChainDoesntConnect { chain_tip: Box::new(chain_tip.num_hash()), - other_chain_fork: Box::new(chain.fork_block()), + other_chain_fork: Box::new(other_fork_block), } .into()) } // Insert blocks from other chain - self.blocks.extend(chain.blocks); - self.state.extend(chain.state); + self.blocks.extend(other.blocks); + self.state.extend(other.state); Ok(()) }