Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(state): split_at refactor #5419

Merged
merged 2 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions crates/blockchain-tree/src/blockchain_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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();
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand All @@ -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 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)
}
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
Expand Down
41 changes: 23 additions & 18 deletions crates/storage/provider/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
};
Expand All @@ -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)
}
Expand All @@ -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 },
}
}
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -447,11 +450,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())]) };
Expand All @@ -464,23 +466,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)
);
}
}
Loading