bootstrap: NeedsBootstrap check for rocksdb/ chain state

The chain DB detection at src/bootstrap.cpp:51-61 checked for txleveldb/,
blocks/chainstate/, and chainstate/ — but not rocksdb/. After the LevelDB
to RocksDB migration completes on v6.1.x, the live chain state lives in
rocksdb/. If the legacy txleveldb/ directory is removed (a reasonable
cleanup operation now that the migration is done), the boot path
incorrectly decides 'no blockchain data found' and triggers a 943 MB
bootstrap download over Tor. DNS2 incident 2026-07-03: 5-hour wedge from
exactly this; recovery via v3 snapshot drop + rm -rf rocksdb + restart.

Add fs::exists(dataDir / "rocksdb") to the OR-chain so a fully-migrated
node stays recognized as 'has chain DB' even after txleveldb/ cleanup.

The four states this handles correctly:
- Fresh node (no chain DB): bootstrap → snapshot → load
- Mid-migration (txleveldb + no rocksdb): don't bootstrap, migrate
- Post-migration (both): don't bootstrap, load RocksDB
- Post-cleanup (rocksdb only, the broken case before this fix): now
  correctly recognized as 'has chain DB' — don't bootstrap, load RocksDB

Ref: references/needsbootstrap-rocksdb-gap-2026-07-03.md (full incident
notes, recovery recipe, defense-in-depth notes on the auto-snapshot
loader at init.cpp:1260 which is already backend-aware).
This commit is contained in:
Hermes Agent
2026-07-03 13:57:16 -07:00
parent bbc93c66a3
commit e2cd0b6057
+7 -1
View File
@@ -53,8 +53,14 @@ bool NeedsBootstrap(const fs::path& dataDir)
// Need bootstrap if there's no chain database (the UTXO set / block index).
// blk0001.dat alone is NOT sufficient — it's raw block data that requires
// (fast-import was removed; UTXO snapshot is the only sync path)
// Check for both LevelDB (txleveldb/) and RocksDB (chainstate/) backends.
// Check for both LevelDB (txleveldb/), RocksDB (rocksdb/), and legacy
// chainstate paths. The rocksdb/ check is critical for v6.1.x+ nodes that
// fully migrated from LevelDB — without it, removing the legacy txleveldb/
// directory causes the boot path to incorrectly decide "no blockchain data"
// and trigger a 943 MB bootstrap download over Tor (DNS2 incident
// 2026-07-03, 5-hour wedge; recovery via v3 snapshot + rm -rf rocksdb).
bool hasChainDb = fs::exists(dataDir / "txleveldb")
|| fs::exists(dataDir / "rocksdb")
|| fs::exists(dataDir / "blocks" / "chainstate")
|| fs::exists(dataDir / "chainstate");
return !hasChainDb;