diff options
| author | Bitflash <btf01@proton.me> | 2026-07-30 11:27:49 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-30 11:27:49 -0300 |
| commit | 99f7c1dfebb1a15dc6f77a338a8b0aaa3962564b (patch) | |
| tree | 1e5a5bae21f75600d945efb3f56de68fdd33bce1 /src/main.cpp | |
| parent | de884e493145bd5cbd1d7a049968edcd110391af (diff) | |
wallet: make wallet.dat a file you can actually back up (#46)
* wallet: make wallet.dat a file you can actually back up
Issue #40: a wallet.dat copied out on its own will not open
anywhere else. The file is intact -- same bytes, same keys --
and the node refuses it with "Db::put: Invalid argument",
thrown from a path that aborted the process. The reporter
believes they lost funds to this, having saved the one file
anybody would think to save.
The chain, from the bottom:
_endthread() is pthread_exit() on POSIX, and glibc implements
that by throwing abi::__forced_unwind through the stack. That
is not a real exception; catching it without rethrowing is a
fatal error by contract. CATCH_PRINT_EXCEPTION ended in a bare
catch(...), so every Linux shutdown ended:
UNKNOWN EXCEPTION bitflash-node in ThreadSocketHandler()
FATAL: exception not rethrown
An abort, mid-shutdown, so nothing after StopNode() ran.
Which mattered because Berkeley DB stamps every page with a
log sequence number belonging to the environment that wrote
it. Carried elsewhere, those numbers point at logs that do not
exist and BDB refuses to write. DBFlush(true) calls lsn_reset,
which is precisely the cure -- and DBFlush has been declared
since 2009 and called from nowhere. Even had it been called,
the abort stood in front of it.
So the wallet was never released from its directory, and the
one file users copy was the one file that could not travel.
Three fixes, each covering a different case:
- Rethrow abi::__forced_unwind, and call DBFlush(true) on
both shutdown paths. Shutdown completes, and wallet.dat is
left portable by construction.
- Adopt orphaned files on open: a freshly created database
environment beside pre-existing .dat files means they came
from elsewhere, so reset their sequence numbers. This is
what recovers wallets already stranded by older builds --
including, if they still hold the file, the reporter's.
- BackupWallet(), reachable as /backupwallet=FILE, which
checkpoints, copies, and clears the copy's sequence
numbers. Bitcoin 0.1.0 had a backup command; this tree
lost it in the wxWidgets removal and has shipped with no
way to take a backup since.
DBFlush's own txn_checkpoint and lsn_reset are wrapped too --
they throw, and throwing on the way out is how this started.
Verified on Linux and Windows: shutdown exits 0 rather than
134, DBFlush runs, and a lone wallet.dat opens in an empty
directory with no exceptions and byte-identical key records
(defaultkey, key, name, setting, version) before and after.
Not addressed here, and the larger half of #40: keys are
generated as they are needed rather than derived from a seed,
so any backup is a snapshot and coins paid to addresses
created after it are not in it. /backupwallet says so on every
run. The real answer is deterministic derivation, which is a
wallet format change and its own piece of work.
* README: document what a wallet backup actually requires
The PR description claimed this section existed and it did not.
Covers the two things that cost money and are not obvious: a lone
wallet.dat is refused elsewhere because Berkeley DB ties it to the
database/ directory beside it, and every backup is a snapshot
because keys are made on demand rather than derived from a seed.
Diffstat (limited to 'src/main.cpp')
0 files changed, 0 insertions, 0 deletions