upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/selftest.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-01 17:16:04 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-01 17:19:47 +0000
commit1017644bc8d4bf183086ffb816c5e3f18068568e (patch)
tree040e468018ad067811be744f55091294666f3fea /src/selftest.cpp
parentb4f0bc693ef7175702f82bcb2110b627922e8cbd (diff)
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 <datadir>/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.
Diffstat (limited to 'src/selftest.cpp')
-rw-r--r--src/selftest.cpp26
1 files changed, 21 insertions, 5 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp
index 2ada8f3..c62d70f 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -2580,7 +2580,11 @@ static int RunWalletBackendDefaultSelfTest()
2580 2580
2581 try 2581 try
2582 { 2582 {
2583 // 1. Fresh datadir, no flag -> new wallet.sqlite, marker "sqlite", no wallet.dat. 2583 // 1. Fresh datadir, no flag -> this fork refuses, where upstream creates.
2584 // An empty datadir on a headless node is far more often a mount or
2585 // backup fault than a first run, and creating a wallet also pins the
2586 // backend marker, after which restoring the real wallet is a no-op.
2587 // Nothing may be left behind: no wallet of either kind, no marker.
2584 string d1 = tmp + "/fresh"; 2588 string d1 = tmp + "/fresh";
2585 nFail += Check(MakeDirLocal(d1), "fresh scenario dir can be created") ? 0 : 1; 2589 nFail += Check(MakeDirLocal(d1), "fresh scenario dir can be created") ? 0 : 1;
2586 vector<string> a1; 2590 vector<string> a1;
@@ -2588,19 +2592,31 @@ static int RunWalletBackendDefaultSelfTest()
2588 a1.push_back("-nomanagedtor"); 2592 a1.push_back("-nomanagedtor");
2589 a1.push_back("-nogui"); 2593 a1.push_back("-nogui");
2590 a1.push_back("-newaddress"); 2594 a1.push_back("-newaddress");
2591 int r1 = RunBitflashChild(strExe, a1); 2595 int r0 = RunBitflashChild(strExe, a1);
2596 nFail += Check(r0 != 0 &&
2597 !FileExists((d1 + "/wallet.sqlite").c_str()) &&
2598 !FileExists((d1 + "/wallet.dat").c_str()) &&
2599 !FileExists((d1 + "/wallet-backend").c_str()),
2600 "a flagless headless start refuses to create a wallet") ? 0 : 1;
2601
2602 // 1a. -createwallet says it was meant: new wallet.sqlite, marker
2603 // "sqlite", no wallet.dat.
2604 vector<string> a1c = a1;
2605 a1c.push_back("-createwallet");
2606 int r1 = RunBitflashChild(strExe, a1c);
2592 nFail += Check(r1 == 0 && 2607 nFail += Check(r1 == 0 &&
2593 FileExists((d1 + "/wallet.sqlite").c_str()) && 2608 FileExists((d1 + "/wallet.sqlite").c_str()) &&
2594 !FileExists((d1 + "/wallet.dat").c_str()) && 2609 !FileExists((d1 + "/wallet.dat").c_str()) &&
2595 FileContainsText(d1 + "/wallet-backend", "sqlite"), 2610 FileContainsText(d1 + "/wallet-backend", "sqlite"),
2596 "a fresh install starts on SQLite (new default) and records the marker") ? 0 : 1; 2611 "-createwallet starts a fresh install on SQLite and records the marker") ? 0 : 1;
2597 2612
2598 // 1b. Second run reuses it, still SQLite, still no wallet.dat. 2613 // 1b. Second run reuses it without the flag -- the refusal only guards
2614 // creation, so an existing wallet still starts flagless.
2599 int r1b = RunBitflashChild(strExe, a1); 2615 int r1b = RunBitflashChild(strExe, a1);
2600 nFail += Check(r1b == 0 && 2616 nFail += Check(r1b == 0 &&
2601 FileExists((d1 + "/wallet.sqlite").c_str()) && 2617 FileExists((d1 + "/wallet.sqlite").c_str()) &&
2602 !FileExists((d1 + "/wallet.dat").c_str()), 2618 !FileExists((d1 + "/wallet.dat").c_str()),
2603 "the recorded SQLite choice survives a restart") ? 0 : 1; 2619 "the recorded SQLite choice survives a flagless restart") ? 0 : 1;
2604 2620
2605 // 2. Existing wallet.dat, no flag -> stays on Berkeley DB. 2621 // 2. Existing wallet.dat, no flag -> stays on Berkeley DB.
2606 string d2 = tmp + "/existing-bdb"; 2622 string d2 = tmp + "/existing-bdb";