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 <dev@bitflash.network>2026-08-18 14:33:40 -0300
committerbitflash <dev@bitflash.network>2026-08-18 14:33:40 -0300
commit440d7f393d74f309d29e1d10ba1f11c41c869226 (patch)
tree40c881b79ba7a2571cfd2bcfbb274031a5739071 /src/selftest.cpp
parente58c4022e0ddd137b93cdb3464753bef5df1d456 (diff)
wallet: bracket WriteHDMaster in a SQLite runtime transaction
The SQLite runtime wrote each record as its own autocommit, so a group that must land together -- hdmaster and hdchaincode, the two halves of an HD seed -- could be left half-written by a failure or crash between them, the incomplete-seed state -walletstoragecheck exists to catch. Berkeley DB was no worse (it is not transactional here either) but no better. Add WalletSQLiteRuntimeBeginTxn/CommitTxn/RollbackTxn -- no-ops that succeed when the SQLite backend is not active, so Berkeley DB keeps its existing per-write behaviour -- and wrap WriteHDMaster in them so the seed lands all-or-nothing under SQLite. The one BDB-atomic wallet rewrite, the encrypt path, is already matched by its own transaction in the SQLite encrypt branch; every other wallet multi-write is ordering-based on both backends by design. wallet-sqlite self-test now proves the bracket: a rolled-back runtime write leaves no record, a committed one persists, and WriteHDMaster lands both halves of the seed.
Diffstat (limited to 'src/selftest.cpp')
-rw-r--r--src/selftest.cpp37
1 files changed, 37 insertions, 0 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp
index fc10947..7ad5a53 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -1231,6 +1231,43 @@ static int RunWalletSQLiteSelfTest()
1231 nFail += Check(!FileExists((strRuntimeDir + "/wallet.dat").c_str()), 1231 nFail += Check(!FileExists((strRuntimeDir + "/wallet.dat").c_str()),
1232 "active SQLite runtime does not create wallet.dat") ? 0 : 1; 1232 "active SQLite runtime does not create wallet.dat") ? 0 : 1;
1233 1233
1234 // Transaction bracket: a rolled-back group leaves nothing, a
1235 // committed group persists. This is the all-or-nothing that
1236 // WriteHDMaster relies on so a half-written seed cannot survive
1237 // a failure between its two records.
1238 bool fTxnBegan = WalletSQLiteRuntimeBeginTxn();
1239 bool fTxnWrote = fTxnBegan &&
1240 CWalletDB().WriteSetting("runtime-txn-rollback", (int64)111);
1241 if (fTxnBegan)
1242 WalletSQLiteRuntimeRollbackTxn();
1243 nFail += Check(fTxnBegan && fTxnWrote,
1244 "a SQLite runtime transaction opens and rolls back") ? 0 : 1;
1245 int64 nRolledBack = 0;
1246 nFail += Check(!CWalletDB("r").ReadSetting("runtime-txn-rollback",
1247 nRolledBack),
1248 "a rolled-back SQLite runtime write leaves no record") ? 0 : 1;
1249
1250 bool fCommitOk = WalletSQLiteRuntimeBeginTxn() &&
1251 CWalletDB().WriteSetting("runtime-txn-commit", (int64)222) &&
1252 WalletSQLiteRuntimeCommitTxn();
1253 int64 nCommitted = 0;
1254 nFail += Check(fCommitOk &&
1255 CWalletDB("r").ReadSetting("runtime-txn-commit",
1256 nCommitted) &&
1257 nCommitted == 222,
1258 "a committed SQLite runtime write persists") ? 0 : 1;
1259
1260 // WriteHDMaster brackets its two records in that transaction;
1261 // both halves of the seed must land.
1262 vector<unsigned char> vchTxnMaster(32, 0x5a);
1263 vector<unsigned char> vchTxnChainCode(32, 0xa5);
1264 vector<unsigned char> vchReadMaster, vchReadChainCode;
1265 nFail += Check(CWalletDB().WriteHDMaster(vchTxnMaster, vchTxnChainCode) &&
1266 CWalletDB("r").ReadHDMaster(vchReadMaster, vchReadChainCode) &&
1267 vchReadMaster == vchTxnMaster &&
1268 vchReadChainCode == vchTxnChainCode,
1269 "WriteHDMaster commits both halves of the seed under SQLite") ? 0 : 1;
1270
1234 // Durability: close the backend (checkpoints the WAL), reopen 1271 // Durability: close the backend (checkpoints the WAL), reopen
1235 // the same file, and confirm what CWalletDB wrote is still 1272 // the same file, and confirm what CWalletDB wrote is still
1236 // there. This is the write-survives-a-restart property the real 1273 // there. This is the write-survives-a-restart property the real