Neptune-Crypto / neptune-core

anonymous peer-to-peer cash
Apache License 2.0
23 stars 7 forks source link

Tolerate arbitrarily deep reorganizations #77

Open aszepieniec opened 7 months ago

aszepieniec commented 7 months ago

Currently, nodes refuse to persist blocks to disk until they have verified that they belong to the (currently) canonical chain. Simultaneously, there is a bound on the number of blocks that can be stored in RAM. If the reorganization is deeper than this bound, nodes will be unable to switch to the longer fork because they cannot test the longest chain rule.

Potential solutions:

This problem should be solved when we have recursive block validation. So this issue more like something to keep in mind than like something to be fixed urgently. That said, it would be nice to have a solution that does not rely on recursive block validation.

dan-da commented 5 months ago

Currently, nodes refuse to persist blocks to disk until they have verified that they belong to the (currently) canonical chain

I have a few thoughts:

  1. The quickest fix (for now) may be to simply write all received blocks to disk, without canonical chain check.
  2. Old blocks that are not part of canonical chain could be pruned later after reaching some height. (an optimization, probably not worth doing. obsoleted by [3])
  3. Probably the best fix would be to implement headers-first sync, as bitcoin-core now does.
  4. I'm unclear how recursive block validation would fix this, given that we presently receive block-header and block-data together from each peer. If it does though, then great.

I guess the biggest concern with [1] is that it opens the door to a DOS attack where a peer feeds a continuous stream of non-canonical blocks that could lead to out-of-disk-space issues.

afaict, our problem is that we obtain the block headers and data together from each peer. So we have to somehow store all the block data until we have enough block headers to verify a given block is part of canonical chain.

note: block header and block data are both written in ArchivalState::write_block(), from a single Block argument.

bitcoin-core handles this via headers-first sync, where all headers are obtained and stored in a tree of all chains. Then (or concurrently) blocks are requested, but only if/when enough headers are available to indicates the block is part of the canonical (most work) chain.

sipa has a detailed writeup of bitcoin-core's behavior at: https://bitcoin.stackexchange.com/a/121293/49396