From 1017644bc8d4bf183086ffb816c5e3f18068568e Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Tue, 1 Sep 2026 17:16:04 +0000 Subject: wallet: refuse to mint a wallet a headless node never asked for v1.2.20 makes SQLite the default backend for a datadir that holds no wallet. On a desktop that is a first run. On a headless node it is usually a fault -- the datadir is not mounted, or wallet.dat was caught mid-rename by a backup -- and the new default turns that fault into a silent loss of custody rather than a loud failure. The damage is not the empty wallet, it is the marker. Creating the wallet also writes /wallet-backend = sqlite, and every later start reads that marker first: putting the real wallet.dat back no longer does anything, because the node stays on the empty SQLite wallet it made. 1.2.19 had the same fail-open on a missing wallet, but it created a wallet.dat, and restoring the backup over it recovered the funds. That runbook stops working here. So refuse when the startup is headless and no wallet of either kind is present, and say which flag makes it deliberate: /createwallet to start a new one, or /walletbackend=bdb for the Berkeley DB path. A node that meant to create a wallet says so; a node that lost its datadir gets an error instead of a fresh identity and a re-paid ledger. Upstream keeps the old behaviour; this is a fork divergence, and the three findings that would gate enabling the SQLite backend at all -- the non-atomic backend marker, WriteHDMaster's connection-wide transaction, and the encrypt-swap crash window -- remain open against upstream. --- src/main_gui.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'src/main_gui.cpp') diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 2743429..25cde1c 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -196,6 +196,9 @@ static void PrintUsage() printf(" /walletsqlite=FILE (stage-load a SQLite wallet export, then exit)\n"); printf(" /walletbackend=sqlite (opt-in: run the node on /wallet.sqlite\n"); printf(" instead of wallet.dat; export one first)\n"); + printf(" /createwallet (headless only: permit creating a new wallet in a\n"); + printf(" datadir that has none -- otherwise refused, since\n"); + printf(" a missing wallet is usually a mount/backup fault)\n"); printf(" /rescan (walk the chain for coins this wallet owns\n"); printf(" but never recorded, then exit)\n"); printf("\n"); @@ -835,6 +838,32 @@ int main(int argc, char* argv[]) else { fUseSQLite = true; fCreateSQLite = true; } } + // A headless node whose datadir holds no wallet of either kind is + // far more often an operations fault -- datadir not mounted, or + // wallet.dat caught mid-rename by a backup -- than a first run. + // Creating one here is not the harmless default it looks like: + // success also writes the backend marker, and from then on + // restoring the real wallet.dat no longer takes effect, because the + // marker keeps the node on the empty wallet it just made. Under + // 1.2.19 the same fault created a wallet.dat that a restore + // overwrote, so operators could recover by putting the file back. + // Refuse instead, and name the flag that makes it deliberate. + if (fCreateSQLite && fHeadlessStartup && + !arg(argc, argv, "/createwallet") && !arg(argc, argv, "-createwallet")) + { + AttachTerminal(); + fprintf(stderr, + "No wallet found in %s\n" + "Refusing to create one. On a headless node an absent wallet is\n" + "usually a mount or backup fault, and creating one would record a\n" + "backend marker that makes restoring the real wallet a no-op.\n" + "Pass -createwallet to start a new one deliberately, or\n" + "-walletbackend=bdb to run on a Berkeley DB wallet.dat.\n", + GetAppDir().c_str()); + fflush(stderr); + return 1; + } + if (fUseSQLite) { AttachTerminal(); -- cgit v1.2.3