diff options
| author | bitflash <dev@bitflash.network> | 2026-08-18 14:33:40 -0300 |
|---|---|---|
| committer | bitflash <dev@bitflash.network> | 2026-08-18 14:33:40 -0300 |
| commit | 440d7f393d74f309d29e1d10ba1f11c41c869226 (patch) | |
| tree | 40c881b79ba7a2571cfd2bcfbb274031a5739071 /src/db.h | |
| parent | e58c4022e0ddd137b93cdb3464753bef5df1d456 (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/db.h')
| -rw-r--r-- | src/db.h | 20 |
1 files changed, 18 insertions, 2 deletions
| @@ -365,6 +365,9 @@ bool WalletSQLiteRuntimeWriteRecord(const vector<unsigned char>& vchKey, | |||
| 365 | const vector<unsigned char>& vchValue, | 365 | const vector<unsigned char>& vchValue, |
| 366 | bool fOverwrite); | 366 | bool fOverwrite); |
| 367 | bool WalletSQLiteRuntimeEraseRecord(const vector<unsigned char>& vchKey); | 367 | bool WalletSQLiteRuntimeEraseRecord(const vector<unsigned char>& vchKey); |
| 368 | bool WalletSQLiteRuntimeBeginTxn(); | ||
| 369 | bool WalletSQLiteRuntimeCommitTxn(); | ||
| 370 | void WalletSQLiteRuntimeRollbackTxn(); | ||
| 368 | 371 | ||
| 369 | 372 | ||
| 370 | 373 | ||
| @@ -531,8 +534,21 @@ public: | |||
| 531 | 534 | ||
| 532 | bool WriteHDMaster(const vector<unsigned char>& vchMaster, const vector<unsigned char>& vchChainCode) | 535 | bool WriteHDMaster(const vector<unsigned char>& vchMaster, const vector<unsigned char>& vchChainCode) |
| 533 | { | 536 | { |
| 534 | return WriteWalletRecord(string("hdmaster"), vchMaster) | 537 | // hdmaster and hdchaincode are halves of one seed: a wallet with one but |
| 535 | && WriteWalletRecord(string("hdchaincode"), vchChainCode); | 538 | // not the other is an incomplete HD wallet (-walletstoragecheck flags |
| 539 | // exactly that). Under the SQLite runtime write them in one transaction | ||
| 540 | // so a failure or crash between the two cannot leave that half-state. | ||
| 541 | // Berkeley DB keeps its existing per-write behaviour -- the bracket is a | ||
| 542 | // no-op there. | ||
| 543 | if (!WalletSQLiteRuntimeBeginTxn()) | ||
| 544 | return false; | ||
| 545 | if (!WriteWalletRecord(string("hdmaster"), vchMaster) || | ||
| 546 | !WriteWalletRecord(string("hdchaincode"), vchChainCode)) | ||
| 547 | { | ||
| 548 | WalletSQLiteRuntimeRollbackTxn(); | ||
| 549 | return false; | ||
| 550 | } | ||
| 551 | return WalletSQLiteRuntimeCommitTxn(); | ||
| 536 | } | 552 | } |
| 537 | 553 | ||
| 538 | bool WriteWalletMinVersion(int nVersion) | 554 | bool WriteWalletMinVersion(int nVersion) |