mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 09:08:05 -05:00
chore(state): split_at refactor (#5419)
This commit is contained in:
@@ -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<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
||||
&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<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
||||
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<DB: Database, EF: ExecutorFactory> BlockchainTree<DB, EF> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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<Self> {
|
||||
let last_block = self.last_block();
|
||||
let first_block = self.first_block;
|
||||
if block_number >= last_block {
|
||||
return None
|
||||
}
|
||||
if block_number < first_block {
|
||||
return Some(Self::default())
|
||||
/// If the target block number is not included in the state block range.
|
||||
pub fn split_at(self, at: BlockNumber) -> (Option<Self>, Self) {
|
||||
if at == self.first_block {
|
||||
return (None, self)
|
||||
}
|
||||
|
||||
// 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 lower_state, mut higher_state) = (self.clone(), self);
|
||||
|
||||
let mut detached_bundle_state: BundleStateWithReceipts = self.clone();
|
||||
detached_bundle_state.revert_to(block_number);
|
||||
// Revert lower state to [..at].
|
||||
lower_state.revert_to(at.checked_sub(1).unwrap());
|
||||
|
||||
// split is done as [0, num) and [num, len]
|
||||
let (_, this) = self.receipts.split_at(num_of_detached_block as usize);
|
||||
// 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;
|
||||
|
||||
self.receipts = Receipts::from_vec(this.to_vec().clone());
|
||||
self.bundle.take_n_reverts(num_of_detached_block as usize);
|
||||
|
||||
self.first_block = block_number + 1;
|
||||
|
||||
Some(detached_bundle_state)
|
||||
(Some(lower_state), higher_state)
|
||||
}
|
||||
|
||||
/// Extend one state from another
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user