Context: Recent discussions highlighted that many nodes that synced before Bitcoin Core v28 have their block and undo files stored effectively in the clear (zero XOR key). This patch adds a simple, resumable maintenance tool to obfuscate previously raw block files, rotate an existing key to a fresh random one, or de-obfuscate (set key to zero) if consciously chosen, all without requiring a resync. The operation can be cancelled and restarted safely.
Implementation: The new startup option -reobfuscate-blocks[=VALUE] accepts either 16 hex characters as an exact 8-byte XOR key (little-endian in-memory layout) or a boolean to generate a random 64-bit key. For example, -reobfuscate-blocks=0000000000000000 sets the key to zero, effectively removing obfuscation. Malformed explicit key values fail early.
If unobfuscated blocks are detected at start time, a log message points to this new option.
At startup, before chainstate loading, we iterate over all undo and block files (grouping them for more uniform iteration), read them with the old XOR key, and write them back with the new key (as <name>.reobfuscated). The implementation combines the two keys and reads directly into the new obfuscated version to do only a single pass over the data. This works whether the original blocks are obfuscated, the new blocks are not, or neither are.
After a successful write, the old file is deleted immediately. File modification times are preserved on the staged replacement files. Once all files are staged, they are renamed back, and xor.dat.reobfuscated is atomically swapped to xor.dat before startup continues.
Progress is logged roughly per-percent as files complete (i.e. max 100 progress log lines), and GUI builds receive resumable kernel progress notifications.
Constraints:
- Re-obfuscation resumes automatically (detected via
xor.dat.reobfuscated) even without the flag. In the worst case, a crash should only force redoing previous work. - Single-threaded, processing one file at a time to keep code simple and avoid the complexity of interleaving renames and key swaps across threads.
- Fast in practice with sequential read/modify/write per block file - after recent obfuscation vectorization, this path is very quick.
Reproducer:
cmake -B build -DBUILD_GUI=ON && cmake --build build -j$(nproc)
# command line
./build/bin/bitcoind -reobfuscate-blocks -stopatheight=1
# same with GUI
./build/bin/bitcoin-qt -reobfuscate-blocks -stopatheight=1
Single-threaded Performance:
| cpu | hdd/ssd | block count | size | files | time (min) | blocks/min |
|---|---|---|---|---|---|---|
| Apple M4 Max laptop | SSD | ~954k | ~852 GB | 11,206 | 16.6 | 57,536 |
| Intel Core i9 | SSD | ~909k | ~725 GB | 10,238 | 23.1 | 39,351 |
| Raspberry Pi 5 | SSD | ~914k | ~728 GB | 10,276 | 72.78 | 12,558 |
| Intel Core i7 | HDD | ~909k | ~720 GB | 10,156 | 208.7 | 4,356 |
| Raspberry Pi 4B | HDD | ~915k | ~730 GB | 10,304 | 1467 | 624 |
Similar work: