// The handle_reorg function is triggered by the adding of orphaned blocks. Reorg chains are constructed by
// finding the orphan chain tip with the highest accumulated difficulty that can be linked to the newly added
// orphan block and then building a chain from the strongest orphan tip back to the main chain. The newly added
// orphan block is considered to be a orphan tip if no better tips can be found that link to it. When a valid
// reorg chain is constructed with a higher accumulated difficulty, then the main chain is rewound and updated
// with the newly un-orphaned blocks from the reorg chain.
The handle_reorg function is triggered by the adding of orphaned blocks. Reorg chains are constructed by
finding the orphan chain tip with the highest accumulated difficulty that can be linked to the newly added
orphan block and then building a chain from the strongest orphan tip back to the main chain. The newly added
orphan block is considered to be a orphan tip if no better tips can be found that link to it. When a valid
reorg chain is constructed with a higher accumulated difficulty, then the main chain is rewound and updated
with the newly un-orphaned blocks from the reorg chain.
The handle_reorg function is triggered by the adding of orphaned blocks . Reorg chains are constructed by finding the orphan chain tip with the highest accumulated difficulty that can be linked to the newly added orphan block and then building a chain from the strongest orphan tip back to the main chain . The newly added orphan block is considered to be a orphan tip if no better tips can be found that link to it . When a valid reorg chain is constructed with a higher accumulated difficulty then the main chain is rewound and updated with the newly un - orphaned blocks from the reorg chain .
fn handle_reorg<T: BlockchainBackend>(
    db: &mut T,
    block_validator: &StatefulValidator<Block, T>,
    chain_strength_comparer: &dyn ChainStrengthComparer,
    new_block: Arc<Block>,
) -> Result<BlockAddResult, ChainStorageError>
{
    // We can assume that the new block is part of the reorg chain if it exists, otherwise the reorg would have
    // happened on the previous call to this function.
    // Try and construct a path from `new_block` to the main chain:
    let mut reorg_chain = try_construct_fork(db, new_block.clone())?;
    if reorg_chain.is_empty() {
        debug!(
            target: LOG_TARGET,
            "No reorg required, could not construct complete chain using block #{} ({}).",
            new_block.header.height,
            new_block.hash().to_hex()
        );
        return Ok(BlockAddResult::OrphanBlock);
    }
    // Try and find all orphaned chain tips that can be linked to the new orphan block, if no better orphan chain
    // tips can be found then the new_block is a tip.
    let new_block_hash = new_block.hash();
    // TODO: Improve efficiency of `find_orphan_chain_tips -> for_each_orphan -> lmdb_for_each` as this wastes
    // TODO:   multiple seconds for each block to be added during a large sync
    let orphan_chain_tips = find_orphan_chain_tips(db, new_block.header.height, new_block_hash.clone());
    trace!(
        target: LOG_TARGET,
        "Search for orphan tips linked to block #{} complete.",
        new_block.header.height
    );
    // Check the accumulated difficulty of the best fork chain compared to the main chain.
    let fork_header = find_strongest_orphan_tip(db, orphan_chain_tips, chain_strength_comparer)?;
    if fork_header.is_none() {
        // This should never happen because a block is always added to the orphan pool before
        // checking, but just in case
        return Err(ChainStorageError::InvalidOperation(
            "No chain tips found in orphan pool".to_string(),
        ));
    }
    let fork_header = fork_header.unwrap();
    let fork_tip_hash = fork_header.hash();
    let tip_header = db
        .fetch_last_header()?
        .ok_or_else(|| ChainStorageError::InvalidQuery("Cannot retrieve header. Blockchain DB is empty".into()))?;
    if fork_tip_hash == new_block_hash {
        debug!(
            target: LOG_TARGET,
            "Comparing candidate block #{} (accum_diff:{}, hash:{}) to main chain #{} (accum_diff: {}, hash: ({})).",
            new_block.header.height,
            fork_header.total_accumulated_difficulty_inclusive_squared()?,
            fork_tip_hash.to_hex(),
            tip_header.height,
            tip_header.total_accumulated_difficulty_inclusive_squared()?,
            tip_header.hash().to_hex()
        );
    } else {
        debug!(
            target: LOG_TARGET,
            "Comparing fork (accum_diff:{}, hash:{}) with block #{} ({}) to main chain #{} (accum_diff: {}, hash: \
             ({})).",
            fork_header.total_accumulated_difficulty_inclusive_squared()?,
            fork_tip_hash.to_hex(),
            new_block.header.height,
            new_block_hash.to_hex(),
            tip_header.height,
            tip_header.total_accumulated_difficulty_inclusive_squared()?,
            tip_header.hash().to_hex()
        );
    }
    match chain_strength_comparer.compare(&fork_header, &tip_header) {
        Ordering::Greater => {
            debug!(
                target: LOG_TARGET,
                "Accumulated difficulty validation PASSED for block #{} ({})",
                new_block.header.height,
                new_block_hash.to_hex()
            );
        },
        Ordering::Less | Ordering::Equal => {
            debug!(
                target: LOG_TARGET,
                "Fork chain (accum_diff:{}, hash:{}) with block {} ({}) has a weaker difficulty.",
                fork_header.total_accumulated_difficulty_inclusive_squared()?,
                fork_tip_hash.to_hex(),
                new_block.header.height,
                new_block_hash.to_hex(),
            );
            debug!(
                target: LOG_TARGET,
                "Orphan block received: #{}", new_block.header.height
            );
            return Ok(BlockAddResult::OrphanBlock);
        },
    }
    // We've built the strongest orphan chain we can by going backwards and forwards from the new orphan block
    // that is linked with the main chain.
    let fork_tip_block = fetch_orphan(db, fork_tip_hash.clone()).map(Arc::new)?;
    if fork_tip_hash != new_block_hash {
        // New block is not the tip, find complete chain from tip to main chain.
        reorg_chain = try_construct_fork(db, fork_tip_block)?;
    }
    let added_blocks = reorg_chain.iter().cloned().collect::<Vec<_>>();
    let fork_height = reorg_chain
        .front()
        .expect("The new orphan block should be in the queue")
        .header
        .height -
        1;
    let removed_blocks = reorganize_chain(db, block_validator, fork_height, reorg_chain)?;
    let num_removed_blocks = removed_blocks.len();
    let num_added_blocks = added_blocks.len();
    // reorg is required when any blocks are removed or more than one are added
    // see https://github.com/tari-project/tari/issues/2101
    if num_removed_blocks > 0 || num_added_blocks > 1 {
        info!(
            target: LOG_TARGET,
            "Chain reorg required from {} to {} (accum_diff:{}, hash:{}) to (accum_diff:{}, hash:{}). Number of \
             blocks to remove: {}, to add: {}.
            ",
            tip_header,
            fork_header,
            tip_header.pow,
            tip_header.hash().to_hex(),
            fork_header.pow,
            fork_tip_hash.to_hex(),
            num_removed_blocks,
            num_added_blocks,
        );
        Ok(BlockAddResult::ChainReorg(removed_blocks, added_blocks))
    } else {
        trace!(
            target: LOG_TARGET,
            "No reorg required. Number of blocks to remove: {}, to add: {}.",
            num_removed_blocks,
            num_added_blocks,
        );
        Ok(BlockAddResult::Ok)
    }
}
fn handle_reorg < T : BlockchainBackend > ( db : & mut T , block_validator : & StatefulValidator < Block , T > , chain_strength_comparer : & dyn ChainStrengthComparer , new_block : Arc < Block > , ) -> Result < BlockAddResult , ChainStorageError > { // We can assume that the new block is part of the reorg chain if it exists, otherwise the reorg would have // happened on the previous call to this function. // Try and construct a path from `new_block` to the main chain: let mut reorg_chain = try_construct_fork ( db , new_block . clone ( ) ) ? ; if reorg_chain . is_empty ( ) { debug ! ( target LOG_TARGET " " new_block header height new_block hash ( ) to_hex ( ) ) ; return Ok ( BlockAddResult :: OrphanBlock ) ; } // Try and find all orphaned chain tips that can be linked to the new orphan block, if no better orphan chain // tips can be found then the new_block is a tip. let new_block_hash = new_block . hash ( ) ; // TODO: Improve efficiency of `find_orphan_chain_tips -> for_each_orphan -> lmdb_for_each` as this wastes // TODO:   multiple seconds for each block to be added during a large sync let orphan_chain_tips = find_orphan_chain_tips ( db , new_block . header . height , new_block_hash . clone ( ) ) ; trace ! ( target LOG_TARGET " " new_block header height ) ; // Check the accumulated difficulty of the best fork chain compared to the main chain. let fork_header = find_strongest_orphan_tip ( db , orphan_chain_tips , chain_strength_comparer ) ? ; if fork_header . is_none ( ) { // This should never happen because a block is always added to the orphan pool before // checking, but just in case return Err ( ChainStorageError :: InvalidOperation ( " " . to_string ( ) , ) ) ; } let fork_header = fork_header . unwrap ( ) ; let fork_tip_hash = fork_header . hash ( ) ; let tip_header = db . fetch_last_header ( ) ? . ok_or_else ( | | ChainStorageError :: InvalidQuery ( " " . into ( ) ) ) ? ; if fork_tip_hash == new_block_hash { debug ! ( target LOG_TARGET " " new_block header height fork_header total_accumulated_difficulty_inclusive_squared ( ) fork_tip_hash to_hex ( ) tip_header height tip_header total_accumulated_difficulty_inclusive_squared ( ) tip_header hash ( ) to_hex ( ) ) ; } else { debug ! ( target LOG_TARGET " \
 " fork_header total_accumulated_difficulty_inclusive_squared ( ) fork_tip_hash to_hex ( ) new_block header height new_block_hash to_hex ( ) tip_header height tip_header total_accumulated_difficulty_inclusive_squared ( ) tip_header hash ( ) to_hex ( ) ) ; } match chain_strength_comparer . compare ( & fork_header , & tip_header ) { Ordering :: Greater => { debug ! ( target LOG_TARGET " " new_block header height new_block_hash to_hex ( ) ) ; } , Ordering :: Less | Ordering :: Equal => { debug ! ( target LOG_TARGET " " fork_header total_accumulated_difficulty_inclusive_squared ( ) fork_tip_hash to_hex ( ) new_block header height new_block_hash to_hex ( ) ) ; debug ! ( target LOG_TARGET " " new_block header height ) ; return Ok ( BlockAddResult :: OrphanBlock ) ; } , } // We've built the strongest orphan chain we can by going backwards and forwards from the new orphan block // that is linked with the main chain. let fork_tip_block = fetch_orphan ( db , fork_tip_hash . clone ( ) ) . map ( Arc :: new ) ? ; if fork_tip_hash != new_block_hash { // New block is not the tip, find complete chain from tip to main chain. reorg_chain = try_construct_fork ( db , fork_tip_block ) ? ; } let added_blocks = reorg_chain . iter ( ) . cloned ( ) . collect :: < Vec < _ > > ( ) ; let fork_height = reorg_chain . front ( ) . expect ( " " ) . header . height - 1 ; let removed_blocks = reorganize_chain ( db , block_validator , fork_height , reorg_chain ) ? ; let num_removed_blocks = removed_blocks . len ( ) ; let num_added_blocks = added_blocks . len ( ) ; // reorg is required when any blocks are removed or more than one are added // see https://github.com/tari-project/tari/issues/2101 if num_removed_blocks > 0 || num_added_blocks > 1 { info ! ( target LOG_TARGET " \
 " tip_header fork_header tip_header pow tip_header hash ( ) to_hex ( ) fork_header pow fork_tip_hash to_hex ( ) num_removed_blocks num_added_blocks ) ; Ok ( BlockAddResult :: ChainReorg ( removed_blocks , added_blocks ) ) } else { trace ! ( target LOG_TARGET " " num_removed_blocks num_added_blocks ) ; Ok ( BlockAddResult :: Ok ) } }
The handle_reorg function is triggered by the adding of orphaned blocks.
The handle_reorg function is triggered by the adding of orphaned blocks .
db
&mut T
block_validator
&StatefulValidator<Block, T>
chain_strength_comparer
&dyn ChainStrengthComparer
new_block
Arc<Block>