chore(state): split_at refactor (#5419)

This commit is contained in:
Roman Krasiuk
2023-11-14 03:59:17 -08:00
committed by GitHub
parent fbf4873ea8
commit 2bd8c7e7e6
3 changed files with 51 additions and 58 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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)
);
}
}