diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-06 01:53:03 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-06 01:53:03 +0000 |
| commit | ef9f06d43fc6577c15df2256abbb25b20d4227b8 (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/db.h | |
| parent | be63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff) | |
| parent | ceae3d076b5ad11ee84729ae43f14e9312896646 (diff) | |
Bring main onto the line this project actually ships (incl. the 1.2.23 compactblock port)
Closes #6, #7, #8, #11, #35, #34, #29, #28, #27, and #16
See merge request !16
Diffstat (limited to 'src/db.h')
| -rw-r--r-- | src/db.h | 183 |
1 files changed, 152 insertions, 31 deletions
| @@ -46,7 +46,7 @@ public: | |||
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | 48 | ||
| 49 | extern DbEnv dbenv; | 49 | extern DbEnv& GetDbEnv(); |
| 50 | extern void DBFlush(bool fShutdown); | 50 | extern void DBFlush(bool fShutdown); |
| 51 | // Close the environment and delete its logs. Only the encryption command calls | 51 | // Close the environment and delete its logs. Only the encryption command calls |
| 52 | // this: the logs hold the plaintext records the old wallet wrote. | 52 | // this: the logs hold the plaintext records the old wallet wrote. |
| @@ -235,7 +235,7 @@ public: | |||
| 235 | if (!pdb) | 235 | if (!pdb) |
| 236 | return false; | 236 | return false; |
| 237 | DbTxn* ptxn = NULL; | 237 | DbTxn* ptxn = NULL; |
| 238 | int ret = dbenv.txn_begin(GetTxn(), &ptxn, 0); | 238 | int ret = GetDbEnv().txn_begin(GetTxn(), &ptxn, 0); |
| 239 | if (!ptxn || ret != 0) | 239 | if (!ptxn || ret != 0) |
| 240 | return false; | 240 | return false; |
| 241 | vTxn.push_back(ptxn); | 241 | vTxn.push_back(ptxn); |
| @@ -333,6 +333,24 @@ private: | |||
| 333 | // Legacy IP-based peer address database (CAddrDB / addr.dat) removed -- | 333 | // Legacy IP-based peer address database (CAddrDB / addr.dat) removed -- |
| 334 | // peer discovery is entirely Nostr/.btf-based now. | 334 | // peer discovery is entirely Nostr/.btf-based now. |
| 335 | 335 | ||
| 336 | bool WalletSQLiteRuntimeActive(); | ||
| 337 | bool WalletSQLiteRuntimeOpen(const string& strPath, string& strErrorRet, bool fMustExist=true); | ||
| 338 | void WalletSQLiteRuntimeClose(); | ||
| 339 | bool WalletSQLiteRuntimeReadRecord(const vector<unsigned char>& vchKey, | ||
| 340 | vector<unsigned char>& vchValueRet); | ||
| 341 | bool WalletSQLiteRuntimeWriteRecord(const vector<unsigned char>& vchKey, | ||
| 342 | const vector<unsigned char>& vchValue, | ||
| 343 | bool fOverwrite); | ||
| 344 | bool WalletSQLiteRuntimeEraseRecord(const vector<unsigned char>& vchKey); | ||
| 345 | bool WalletSQLiteRuntimeBeginTxn(); | ||
| 346 | bool WalletSQLiteRuntimeCommitTxn(); | ||
| 347 | void WalletSQLiteRuntimeRollbackTxn(); | ||
| 348 | bool WalletSQLiteRuntimeOpenReadOnly(const string& strPath, string& strErrorRet); | ||
| 349 | bool WalletSQLiteRuntimeCheckpoint(string& strErrorRet); | ||
| 350 | string ReadWalletBackendMarker(); | ||
| 351 | bool WriteWalletBackendMarker(const string& strBackend); | ||
| 352 | bool CreateNewSQLiteWallet(const string& strPath); | ||
| 353 | |||
| 336 | 354 | ||
| 337 | 355 | ||
| 338 | 356 | ||
| @@ -340,64 +358,141 @@ private: | |||
| 340 | class CWalletDB : public CDB | 358 | class CWalletDB : public CDB |
| 341 | { | 359 | { |
| 342 | public: | 360 | public: |
| 343 | CWalletDB(const char* pszMode="r+", bool fTxn=false) : CDB("wallet.dat", pszMode, fTxn) { } | 361 | CWalletDB(const char* pszMode="r+", bool fTxn=false); |
| 344 | private: | 362 | private: |
| 345 | CWalletDB(const CWalletDB&); | 363 | CWalletDB(const CWalletDB&); |
| 346 | void operator=(const CWalletDB&); | 364 | void operator=(const CWalletDB&); |
| 365 | |||
| 366 | template<typename K, typename T> | ||
| 367 | bool ReadWalletRecord(const K& key, T& value) | ||
| 368 | { | ||
| 369 | if (!WalletSQLiteRuntimeActive()) | ||
| 370 | return Read(key, value); | ||
| 371 | |||
| 372 | CDataStream ssKey(SER_DISK); | ||
| 373 | ssKey.reserve(1000); | ||
| 374 | ssKey << key; | ||
| 375 | vector<unsigned char> vchKey(ssKey.begin(), ssKey.end()); | ||
| 376 | vector<unsigned char> vchValue; | ||
| 377 | |||
| 378 | bool fOk = WalletSQLiteRuntimeReadRecord(vchKey, vchValue); | ||
| 379 | if (!vchKey.empty()) | ||
| 380 | memset(&vchKey[0], 0, vchKey.size()); | ||
| 381 | if (!ssKey.empty()) | ||
| 382 | memset(&ssKey[0], 0, ssKey.size()); | ||
| 383 | if (!fOk) | ||
| 384 | return false; | ||
| 385 | |||
| 386 | CDataStream ssValue(vchValue, SER_DISK); | ||
| 387 | ssValue >> value; | ||
| 388 | bool fReadOk = !ssValue.fail(); | ||
| 389 | if (!vchValue.empty()) | ||
| 390 | memset(&vchValue[0], 0, vchValue.size()); | ||
| 391 | if (!ssValue.empty()) | ||
| 392 | memset(&ssValue[0], 0, ssValue.size()); | ||
| 393 | return fReadOk; | ||
| 394 | } | ||
| 395 | |||
| 396 | template<typename K, typename T> | ||
| 397 | bool WriteWalletRecord(const K& key, const T& value, bool fOverwrite=true) | ||
| 398 | { | ||
| 399 | if (!WalletSQLiteRuntimeActive()) | ||
| 400 | return Write(key, value, fOverwrite); | ||
| 401 | |||
| 402 | CDataStream ssKey(SER_DISK); | ||
| 403 | ssKey.reserve(1000); | ||
| 404 | ssKey << key; | ||
| 405 | CDataStream ssValue(SER_DISK); | ||
| 406 | ssValue.reserve(10000); | ||
| 407 | ssValue << value; | ||
| 408 | |||
| 409 | vector<unsigned char> vchKey(ssKey.begin(), ssKey.end()); | ||
| 410 | vector<unsigned char> vchValue(ssValue.begin(), ssValue.end()); | ||
| 411 | bool fOk = WalletSQLiteRuntimeWriteRecord(vchKey, vchValue, fOverwrite); | ||
| 412 | |||
| 413 | if (!vchKey.empty()) | ||
| 414 | memset(&vchKey[0], 0, vchKey.size()); | ||
| 415 | if (!vchValue.empty()) | ||
| 416 | memset(&vchValue[0], 0, vchValue.size()); | ||
| 417 | if (!ssKey.empty()) | ||
| 418 | memset(&ssKey[0], 0, ssKey.size()); | ||
| 419 | if (!ssValue.empty()) | ||
| 420 | memset(&ssValue[0], 0, ssValue.size()); | ||
| 421 | return fOk; | ||
| 422 | } | ||
| 423 | |||
| 424 | template<typename K> | ||
| 425 | bool EraseWalletRecord(const K& key) | ||
| 426 | { | ||
| 427 | if (!WalletSQLiteRuntimeActive()) | ||
| 428 | return Erase(key); | ||
| 429 | |||
| 430 | CDataStream ssKey(SER_DISK); | ||
| 431 | ssKey.reserve(1000); | ||
| 432 | ssKey << key; | ||
| 433 | vector<unsigned char> vchKey(ssKey.begin(), ssKey.end()); | ||
| 434 | bool fOk = WalletSQLiteRuntimeEraseRecord(vchKey); | ||
| 435 | |||
| 436 | if (!vchKey.empty()) | ||
| 437 | memset(&vchKey[0], 0, vchKey.size()); | ||
| 438 | if (!ssKey.empty()) | ||
| 439 | memset(&ssKey[0], 0, ssKey.size()); | ||
| 440 | return fOk; | ||
| 441 | } | ||
| 347 | public: | 442 | public: |
| 348 | bool ReadName(const string& strAddress, string& strName) | 443 | bool ReadName(const string& strAddress, string& strName) |
| 349 | { | 444 | { |
| 350 | strName = ""; | 445 | strName = ""; |
| 351 | return Read(make_pair(string("name"), strAddress), strName); | 446 | return ReadWalletRecord(make_pair(string("name"), strAddress), strName); |
| 352 | } | 447 | } |
| 353 | 448 | ||
| 354 | bool WriteName(const string& strAddress, const string& strName) | 449 | bool WriteName(const string& strAddress, const string& strName) |
| 355 | { | 450 | { |
| 356 | mapAddressBook[strAddress] = strName; | 451 | mapAddressBook[strAddress] = strName; |
| 357 | return Write(make_pair(string("name"), strAddress), strName); | 452 | return WriteWalletRecord(make_pair(string("name"), strAddress), strName); |
| 358 | } | 453 | } |
| 359 | 454 | ||
| 360 | bool EraseName(const string& strAddress) | 455 | bool EraseName(const string& strAddress) |
| 361 | { | 456 | { |
| 362 | mapAddressBook.erase(strAddress); | 457 | mapAddressBook.erase(strAddress); |
| 363 | return Erase(make_pair(string("name"), strAddress)); | 458 | return EraseWalletRecord(make_pair(string("name"), strAddress)); |
| 364 | } | 459 | } |
| 365 | 460 | ||
| 366 | bool ReadTx(uint256 hash, CWalletTx& wtx) | 461 | bool ReadTx(uint256 hash, CWalletTx& wtx) |
| 367 | { | 462 | { |
| 368 | return Read(make_pair(string("tx"), hash), wtx); | 463 | return ReadWalletRecord(make_pair(string("tx"), hash), wtx); |
| 369 | } | 464 | } |
| 370 | 465 | ||
| 371 | bool WriteTx(uint256 hash, const CWalletTx& wtx) | 466 | bool WriteTx(uint256 hash, const CWalletTx& wtx) |
| 372 | { | 467 | { |
| 373 | return Write(make_pair(string("tx"), hash), wtx); | 468 | return WriteWalletRecord(make_pair(string("tx"), hash), wtx); |
| 374 | } | 469 | } |
| 375 | 470 | ||
| 376 | bool EraseTx(uint256 hash) | 471 | bool EraseTx(uint256 hash) |
| 377 | { | 472 | { |
| 378 | return Erase(make_pair(string("tx"), hash)); | 473 | return EraseWalletRecord(make_pair(string("tx"), hash)); |
| 379 | } | 474 | } |
| 380 | 475 | ||
| 381 | bool ReadKey(const vector<unsigned char>& vchPubKey, CPrivKey& vchPrivKey) | 476 | bool ReadKey(const vector<unsigned char>& vchPubKey, CPrivKey& vchPrivKey) |
| 382 | { | 477 | { |
| 383 | vchPrivKey.clear(); | 478 | vchPrivKey.clear(); |
| 384 | return Read(make_pair(string("key"), vchPubKey), vchPrivKey); | 479 | return ReadWalletRecord(make_pair(string("key"), vchPubKey), vchPrivKey); |
| 385 | } | 480 | } |
| 386 | 481 | ||
| 387 | bool WriteKey(const vector<unsigned char>& vchPubKey, const CPrivKey& vchPrivKey) | 482 | bool WriteKey(const vector<unsigned char>& vchPubKey, const CPrivKey& vchPrivKey) |
| 388 | { | 483 | { |
| 389 | return Write(make_pair(string("key"), vchPubKey), vchPrivKey, false); | 484 | return WriteWalletRecord(make_pair(string("key"), vchPubKey), vchPrivKey, false); |
| 390 | } | 485 | } |
| 391 | 486 | ||
| 392 | bool ReadDefaultKey(vector<unsigned char>& vchPubKey) | 487 | bool ReadDefaultKey(vector<unsigned char>& vchPubKey) |
| 393 | { | 488 | { |
| 394 | vchPubKey.clear(); | 489 | vchPubKey.clear(); |
| 395 | return Read(string("defaultkey"), vchPubKey); | 490 | return ReadWalletRecord(string("defaultkey"), vchPubKey); |
| 396 | } | 491 | } |
| 397 | 492 | ||
| 398 | bool WriteDefaultKey(const vector<unsigned char>& vchPubKey) | 493 | bool WriteDefaultKey(const vector<unsigned char>& vchPubKey) |
| 399 | { | 494 | { |
| 400 | return Write(string("defaultkey"), vchPubKey); | 495 | return WriteWalletRecord(string("defaultkey"), vchPubKey); |
| 401 | } | 496 | } |
| 402 | 497 | ||
| 403 | // --- Deterministic (BIP32) seed --------------------------------------- | 498 | // --- Deterministic (BIP32) seed --------------------------------------- |
| @@ -415,62 +510,75 @@ public: | |||
| 415 | { | 510 | { |
| 416 | vchMasterRet.clear(); | 511 | vchMasterRet.clear(); |
| 417 | vchChainCodeRet.clear(); | 512 | vchChainCodeRet.clear(); |
| 418 | return Read(string("hdmaster"), vchMasterRet) | 513 | return ReadWalletRecord(string("hdmaster"), vchMasterRet) |
| 419 | && Read(string("hdchaincode"), vchChainCodeRet); | 514 | && ReadWalletRecord(string("hdchaincode"), vchChainCodeRet); |
| 420 | } | 515 | } |
| 421 | 516 | ||
| 422 | bool WriteHDMaster(const vector<unsigned char>& vchMaster, const vector<unsigned char>& vchChainCode) | 517 | bool WriteHDMaster(const vector<unsigned char>& vchMaster, const vector<unsigned char>& vchChainCode) |
| 423 | { | 518 | { |
| 424 | return Write(string("hdmaster"), vchMaster) | 519 | // hdmaster and hdchaincode are halves of one seed: a wallet with one but |
| 425 | && Write(string("hdchaincode"), vchChainCode); | 520 | // not the other is an incomplete HD wallet (-walletstoragecheck flags |
| 521 | // exactly that). Under the SQLite runtime write them in one transaction | ||
| 522 | // so a failure or crash between the two cannot leave that half-state. | ||
| 523 | // Berkeley DB keeps its existing per-write behaviour -- the bracket is a | ||
| 524 | // no-op there. | ||
| 525 | if (!WalletSQLiteRuntimeBeginTxn()) | ||
| 526 | return false; | ||
| 527 | if (!WriteWalletRecord(string("hdmaster"), vchMaster) || | ||
| 528 | !WriteWalletRecord(string("hdchaincode"), vchChainCode)) | ||
| 529 | { | ||
| 530 | WalletSQLiteRuntimeRollbackTxn(); | ||
| 531 | return false; | ||
| 532 | } | ||
| 533 | return WalletSQLiteRuntimeCommitTxn(); | ||
| 426 | } | 534 | } |
| 427 | 535 | ||
| 428 | bool WriteWalletMinVersion(int nVersion) | 536 | bool WriteWalletMinVersion(int nVersion) |
| 429 | { | 537 | { |
| 430 | return Write(string("walletminversion"), nVersion); | 538 | return WriteWalletRecord(string("walletminversion"), nVersion); |
| 431 | } | 539 | } |
| 432 | 540 | ||
| 433 | bool WriteMasterKey(unsigned int nID, const CWalletMasterKey& kMasterKey) | 541 | bool WriteMasterKey(unsigned int nID, const CWalletMasterKey& kMasterKey) |
| 434 | { | 542 | { |
| 435 | return Write(make_pair(string("mkey"), nID), kMasterKey, true); | 543 | return WriteWalletRecord(make_pair(string("mkey"), nID), kMasterKey, true); |
| 436 | } | 544 | } |
| 437 | 545 | ||
| 438 | bool WriteCryptedKey(const vector<unsigned char>& vchPubKey, | 546 | bool WriteCryptedKey(const vector<unsigned char>& vchPubKey, |
| 439 | const vector<unsigned char>& vchCryptedSecret) | 547 | const vector<unsigned char>& vchCryptedSecret) |
| 440 | { | 548 | { |
| 441 | return Write(make_pair(string("ckey"), vchPubKey), vchCryptedSecret, false); | 549 | return WriteWalletRecord(make_pair(string("ckey"), vchPubKey), vchCryptedSecret, false); |
| 442 | } | 550 | } |
| 443 | 551 | ||
| 444 | bool WriteCryptedHDMaster(const vector<unsigned char>& vchCryptedMaster, | 552 | bool WriteCryptedHDMaster(const vector<unsigned char>& vchCryptedMaster, |
| 445 | const vector<unsigned char>& vchCryptedChainCode) | 553 | const vector<unsigned char>& vchCryptedChainCode) |
| 446 | { | 554 | { |
| 447 | return Write(string("cryptedhdmaster"), vchCryptedMaster) | 555 | return WriteWalletRecord(string("cryptedhdmaster"), vchCryptedMaster) |
| 448 | && Write(string("cryptedhdchaincode"), vchCryptedChainCode); | 556 | && WriteWalletRecord(string("cryptedhdchaincode"), vchCryptedChainCode); |
| 449 | } | 557 | } |
| 450 | 558 | ||
| 451 | bool WriteHDNext(unsigned int nNext) | 559 | bool WriteHDNext(unsigned int nNext) |
| 452 | { | 560 | { |
| 453 | return Write(string("hdnext"), nNext); | 561 | return WriteWalletRecord(string("hdnext"), nNext); |
| 454 | } | 562 | } |
| 455 | 563 | ||
| 456 | bool WriteHDSchema(int nSchema) | 564 | bool WriteHDSchema(int nSchema) |
| 457 | { | 565 | { |
| 458 | return Write(string("hdschema"), nSchema); | 566 | return WriteWalletRecord(string("hdschema"), nSchema); |
| 459 | } | 567 | } |
| 460 | 568 | ||
| 461 | bool WriteHDCoinType(unsigned int nCoinType) | 569 | bool WriteHDCoinType(unsigned int nCoinType) |
| 462 | { | 570 | { |
| 463 | return Write(string("hdcointype"), nCoinType); | 571 | return WriteWalletRecord(string("hdcointype"), nCoinType); |
| 464 | } | 572 | } |
| 465 | 573 | ||
| 466 | bool WriteHDReceiveNext(unsigned int nNext) | 574 | bool WriteHDReceiveNext(unsigned int nNext) |
| 467 | { | 575 | { |
| 468 | return Write(string("hdreceivenext"), nNext); | 576 | return WriteWalletRecord(string("hdreceivenext"), nNext); |
| 469 | } | 577 | } |
| 470 | 578 | ||
| 471 | bool WriteHDChangeNext(unsigned int nNext) | 579 | bool WriteHDChangeNext(unsigned int nNext) |
| 472 | { | 580 | { |
| 473 | return Write(string("hdchangenext"), nNext); | 581 | return WriteWalletRecord(string("hdchangenext"), nNext); |
| 474 | } | 582 | } |
| 475 | 583 | ||
| 476 | // Keys generated ahead of time and not yet handed out. The private key of | 584 | // Keys generated ahead of time and not yet handed out. The private key of |
| @@ -478,30 +586,43 @@ public: | |||
| 478 | // record only says that this one is still unspoken for. | 586 | // record only says that this one is still unspoken for. |
| 479 | bool WritePool(int64 nIndex, const vector<unsigned char>& vchPubKey) | 587 | bool WritePool(int64 nIndex, const vector<unsigned char>& vchPubKey) |
| 480 | { | 588 | { |
| 481 | return Write(make_pair(string("pool"), nIndex), vchPubKey); | 589 | return WriteWalletRecord(make_pair(string("pool"), nIndex), vchPubKey); |
| 482 | } | 590 | } |
| 483 | 591 | ||
| 484 | bool ErasePool(int64 nIndex) | 592 | bool ErasePool(int64 nIndex) |
| 485 | { | 593 | { |
| 486 | return Erase(make_pair(string("pool"), nIndex)); | 594 | return EraseWalletRecord(make_pair(string("pool"), nIndex)); |
| 487 | } | 595 | } |
| 488 | 596 | ||
| 489 | template<typename T> | 597 | template<typename T> |
| 490 | bool ReadSetting(const string& strKey, T& value) | 598 | bool ReadSetting(const string& strKey, T& value) |
| 491 | { | 599 | { |
| 492 | return Read(make_pair(string("setting"), strKey), value); | 600 | return ReadWalletRecord(make_pair(string("setting"), strKey), value); |
| 493 | } | 601 | } |
| 494 | 602 | ||
| 495 | template<typename T> | 603 | template<typename T> |
| 496 | bool WriteSetting(const string& strKey, const T& value) | 604 | bool WriteSetting(const string& strKey, const T& value) |
| 497 | { | 605 | { |
| 498 | return Write(make_pair(string("setting"), strKey), value); | 606 | return WriteWalletRecord(make_pair(string("setting"), strKey), value); |
| 499 | } | 607 | } |
| 500 | 608 | ||
| 501 | bool LoadWallet(vector<unsigned char>& vchDefaultKeyRet); | 609 | bool LoadWallet(vector<unsigned char>& vchDefaultKeyRet); |
| 502 | }; | 610 | }; |
| 503 | 611 | ||
| 612 | class CWalletRecordVisitor | ||
| 613 | { | ||
| 614 | public: | ||
| 615 | virtual ~CWalletRecordVisitor() { } | ||
| 616 | virtual bool VisitWalletRecord(const CDataStream& ssKey, | ||
| 617 | const CDataStream& ssValue, | ||
| 618 | string& strErrorRet) = 0; | ||
| 619 | }; | ||
| 620 | |||
| 621 | bool ScanWalletRecords(CWalletRecordVisitor& visitor, string& strErrorRet); | ||
| 622 | |||
| 504 | bool LoadWallet(); | 623 | bool LoadWallet(); |
| 624 | bool LoadWalletFromSQLite(const string& strPath); | ||
| 625 | bool LoadWalletFromSQLiteRuntime(const string& strPath); | ||
| 505 | 626 | ||
| 506 | // Writes a wallet.dat that opens on its own, anywhere -- see the definition in | 627 | // Writes a wallet.dat that opens on its own, anywhere -- see the definition in |
| 507 | // db.cpp for why a plain file copy does not. Still a point-in-time snapshot: | 628 | // db.cpp for why a plain file copy does not. Still a point-in-time snapshot: |