chore(BlockchainTree): unwind function (#1775)

Co-authored-by: Georgios Konstantopoulos <me@gakonst.com>
This commit is contained in:
rakita
2023-03-16 01:52:38 +01:00
committed by GitHub
parent 1cba25e651
commit 3b8c876524
2 changed files with 70 additions and 7 deletions

View File

@@ -9,6 +9,7 @@ use std::collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet};
///
/// It contains list of canonical block hashes, forks to childs blocks
/// and block hash to chain id.
#[derive(Debug)]
pub struct BlockIndices {
/// Last finalized block.
last_finalized_block: BlockNumber,
@@ -241,6 +242,16 @@ impl BlockIndices {
self.canonical_chain.extend(blocks.iter().map(|(number, block)| (*number, block.hash())))
}
/// this is function that is going to remove N number of last canonical hashes.
///
/// NOTE: This is not safe standalone, as it will not disconnect
/// blocks that deppends on unwinded canonical chain. And should be
/// used when canonical chain is reinserted inside Tree.
fn unwind_canonical_chain(&mut self, unwind_to: BlockNumber) {
// this will remove all blocks numbers that are going to be replaced.
self.canonical_chain.retain(|num, _| *num <= unwind_to);
}
/// Used for finalization of block.
/// Return list of chains for removal that depend on finalized canonical chain.
pub fn finalize_canonical_blocks(

View File

@@ -462,15 +462,10 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
unreachable!("all chains should point to canonical chain.");
}
// revert `N` blocks from current canonical chain and put them inside BlockchanTree
// This is main reorgs on tables.
let old_canon_chain = self.revert_canonical(canon_fork.number)?;
// commit new canonical chain.
self.commit_canonical(new_canon_chain)?;
// TODO we can potentially merge now reverted canonical chain with
// one of the chain from the tree. Low priority.
// insert old canonical chain to BlockchainTree.
// insert old canon chain
self.insert_chain(old_canon_chain);
}
@@ -497,6 +492,26 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
Ok(())
}
/// Unwind tables and put it inside state
pub fn unwind(&mut self, unwind_to: BlockNumber) -> Result<(), Error> {
// nothing to be done if unwind_to is higher then the tip
if self.block_indices.canonical_tip().number <= unwind_to {
return Ok(())
}
// revert `N` blocks from current canonical chain and put them inside BlockchanTree
let old_canon_chain = self.revert_canonical(unwind_to)?;
// check if there is block in chain
if old_canon_chain.blocks().is_empty() {
return Ok(())
}
self.block_indices.unwind_canonical_chain(unwind_to);
// insert old canonical chain to BlockchainTree.
self.insert_chain(old_canon_chain);
Ok(())
}
/// Revert canonical blocks from database and insert them to pending table
/// Revert should be non inclusive, and revert_until should stay in db.
/// Return the chain that represent reverted canonical blocks.
@@ -874,6 +889,43 @@ mod tests {
.with_fork_to_child(HashMap::from([(block1.hash(), HashSet::from([block2a_hash]))]))
.assert(&tree);
// unwind canonical
assert_eq!(tree.unwind(block1.number), Ok(()));
// Trie state:
// b2 b2a (pending block)
// / /
// / /
// / /
// b1 (canonical block)
// |
// |
// g1 (canonical blocks)
// |
TreeTester::default()
.with_chain_num(2)
.with_block_to_chain(HashMap::from([(block2a_hash, 4), (block2.hash, 6)]))
.with_fork_to_child(HashMap::from([(
block1.hash(),
HashSet::from([block2a_hash, block2.hash]),
)]))
.assert(&tree);
// commit b2a
assert_eq!(tree.make_canonical(&block2.hash), Ok(()));
// Trie state:
// b2 b2a (side chain)
// | /
// | /
// b1 (canon)
// |
// g1 (10)
// |
TreeTester::default()
.with_chain_num(1)
.with_block_to_chain(HashMap::from([(block2a_hash, 4)]))
.with_fork_to_child(HashMap::from([(block1.hash(), HashSet::from([block2a_hash]))]))
.assert(&tree);
// update canonical block to b2, this would make b2a be removed
assert_eq!(tree.restore_canonical_hashes(12), Ok(()));
// Trie state: