blocks/xor.dat holds the 8-byte key that block and undo data is obfuscated with. If the file goes missing, InitBlocksdirXorKey silently stores an all-zero key, every read of the existing data then fails, and the node reports:
Corrupted block database detected.
Please restart with -reindex or -reindex-chainstate to recover.
The data is not corrupt, only the key was lost. Restoring the key at that point brings the chain back untouched.
Following the suggested recovery instead makes things worse. -reindex reports no error at all: it completes, leaves the node at height 0 having silently discarded the chain, and rewrites the start of the block file with a null-key genesis. Restoring the key afterwards then still loads 0 blocks on the first reindex, and only recovers on a second one. A user who sees an empty chain twice concludes the data is gone and resyncs from scratch.
A missing key file is legitimate for a fresh blocksdir, and for one written before v28 where the data is unobfuscated and the null key is correct. Both are distinguishable from a lost key by whether the block files still start with the network magic, so check that and fail with an actionable error instead. The null key is not written in that case, so the original key can still be restored.
One file matching the magic proves the data is unobfuscated. Concluding the opposite takes every file disagreeing, so a single damaged file cannot misfire the check.
This guards the moment the key goes missing. It cannot help a node that already stored a null key on an earlier version, since xor.dat then exists and is read as-is.
Reproduce on master
Build a chain and keep a copy of the key:
bitcoind -regtest -daemonwait
bitcoin-cli -regtest createwallet w
bitcoin-cli -regtest generatetoaddress 20 $(bitcoin-cli -regtest -rpcwallet=w getnewaddress)
bitcoin-cli -regtest stop
cp <datadir>/regtest/blocks/xor.dat /tmp/xor.bak
The data is intact and the key alone recovers it:
rm <datadir>/regtest/blocks/xor.dat
bitcoind -regtest # "Corrupted block database detected."
cp /tmp/xor.bak <datadir>/regtest/blocks/xor.dat
bitcoind -regtest -daemonwait
bitcoin-cli -regtest getblockcount # 20
bitcoin-cli -regtest verifychain 4 20 # true
Following the error message instead:
rm <datadir>/regtest/blocks/xor.dat
bitcoind -regtest -reindex -daemonwait
bitcoin-cli -regtest getblockcount # 0, and no error was reported
With this change the node refuses to start on both paths, leaves the block file untouched, and does not write a null key.
Testing
Three cases added to feature_blocksxor.py: a lost key is detected and no null key is written over it; an unobfuscated blocksdir with no key still starts with XOR enabled (the pre-v28 upgrade path); and a single unreadable block file is not mistaken for a lost key. The first fails on master with the output above. The pre-existing steps still pass, and the full functional suite passes.