Aimeedeer / realtps

https://realtps.net
MIT License
93 stars 25 forks source link

Failure to handle a complete chain reorg #57

Open brson opened 1 year ago

brson commented 1 year ago

The import logic does not handle the case where our entire loaded chain is reorged.

Here's the logic:

        let block_number_to_fetch_next = if let Some(prev_stored_block) = prev_stored_block {
            let chain_reorg = prev_stored_block.hash != prev_block_hash;
            if !chain_reorg {
                if prev_block_number <= highest_known_block_number {
                    // We did it!
                    joined_chain_block_number = prev_block_number;
                    joined_chain_block_hash = prev_block_hash;
                    break;
                } else {
                    // This is a block we've seen before, but it has a higher block
                    // number than our highest_known_block. This indicates a previous
                    // incomplete import. To avoid wasting a lot of time and bandwidth
                    // "fast-forward" through all the blocks we already know.
                    let highest_unknown_block = fast_forward(chain, db, prev_stored_block).await?;
                    highest_unknown_block
                }
            } else {
                warn!(
                    "reorg of chain {} at block {}; old hash: {}; new hash: {}",
                    chain, prev_block_number, prev_stored_block.hash, prev_block_hash
                );
                // continue - have wrong version of prev block
                prev_block_number
            }
        } else {
            prev_block_number
        };

Usually we expect that prev_stored_block is Some, and handle a bunch of cases. We only have one case when the prev block is None, and that is "keep going".

But it is possible that every single previous block we load will have the wrong hash, a complete reorg of all the blocks we've previously seen. In that case this loop will hit the "keep going" phase forever, having passed the entire imported chain, until it reaches the genesis block.

Various cases in the code expect to never load the genesis block.

Probably we have never hit this case, but it is possible.