upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/walletcmd.cpp
diff options
context:
space:
mode:
authorBitflash <admin@bitflash.network>2026-08-14 08:56:40 -0300
committerBitflash <admin@bitflash.network>2026-08-14 08:56:40 -0300
commit5306542f6121e0c0b4ed1210e24d55fa4fbb7127 (patch)
tree5faf35daa569d987b87dbb161bb18ec170f944b0 /src/walletcmd.cpp
parent9e39a50d1f110020b0841d31e4b931aa877cc448 (diff)
parent805c5757b801d80ea575c6a8b58e2855254f9046 (diff)
Merge pull request 'wallet: audit Berkeley DB wallet storage' (#38) from wallet-storage-audit-bdb into main
Diffstat (limited to 'src/walletcmd.cpp')
-rw-r--r--src/walletcmd.cpp263
1 files changed, 262 insertions, 1 deletions
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp
index e8ac4bf..6f8258d 100644
--- a/src/walletcmd.cpp
+++ b/src/walletcmd.cpp
@@ -400,7 +400,12 @@ int CmdRecoveryAudit()
400 int64 nImmatureTotal = audit.nRecoverableImmatureCredit + audit.nLegacyImmatureCredit; 400 int64 nImmatureTotal = audit.nRecoverableImmatureCredit + audit.nLegacyImmatureCredit;
401 401
402 printf("Wallet recovery audit\n"); 402 printf("Wallet recovery audit\n");
403 printf(" recovery phrase: %s\n", audit.fHaveSeed ? "present" : "not installed"); 403 if (audit.fHaveSeed)
404 printf(" recovery phrase: present\n");
405 else if (audit.fSeedEncryptedLocked)
406 printf(" recovery phrase: encrypted, unlock wallet to audit\n");
407 else
408 printf(" recovery phrase: not installed\n");
404 if (audit.fHaveSeed) 409 if (audit.fHaveSeed)
405 { 410 {
406 printf(" derivation schema: %s\n", HDKeySchemaName(audit.nSchema).c_str()); 411 printf(" derivation schema: %s\n", HDKeySchemaName(audit.nSchema).c_str());
@@ -412,6 +417,15 @@ int CmdRecoveryAudit()
412 if (!audit.fDeriveComplete) 417 if (!audit.fDeriveComplete)
413 printf(" derivation warning: %s\n", audit.strDeriveError.c_str()); 418 printf(" derivation warning: %s\n", audit.strDeriveError.c_str());
414 printf(" total spendable balance: %s BTF\n", FormatMoney(nTotal).c_str()); 419 printf(" total spendable balance: %s BTF\n", FormatMoney(nTotal).c_str());
420 if (audit.fSeedEncryptedLocked)
421 {
422 printf(" recovery coverage: unavailable while wallet is locked\n");
423 printf(" immature mining rewards: %s BTF\n", FormatMoney(nImmatureTotal).c_str());
424 printf("\n");
425 printf("Unlock the wallet with /walletpassphrase or /walletpassphrase=@FILE to audit phrase coverage.\n");
426 fflush(stdout);
427 return 2;
428 }
415 printf(" covered by recovery phrase: %s BTF (%d transaction(s))\n", 429 printf(" covered by recovery phrase: %s BTF (%d transaction(s))\n",
416 FormatMoney(audit.nRecoverableCredit).c_str(), audit.nRecoverableTx); 430 FormatMoney(audit.nRecoverableCredit).c_str(), audit.nRecoverableTx);
417 printf(" wallet.dat-only balance: %s BTF (%d transaction(s))\n", 431 printf(" wallet.dat-only balance: %s BTF (%d transaction(s))\n",
@@ -456,6 +470,253 @@ int CmdRecoveryAudit()
456 return 0; 470 return 0;
457} 471}
458 472
473struct WalletStorageAuditCounts
474{
475 unsigned int nTotal;
476 unsigned int nMalformed;
477 unsigned int nUnknown;
478 unsigned int nVersion;
479 unsigned int nNames;
480 unsigned int nTransactions;
481 unsigned int nPlainKeys;
482 unsigned int nEncryptedKeys;
483 unsigned int nMasterKeys;
484 unsigned int nDefaultKey;
485 unsigned int nPlainHDMaster;
486 unsigned int nPlainHDChainCode;
487 unsigned int nCryptedHDMaster;
488 unsigned int nCryptedHDChainCode;
489 unsigned int nWalletMinVersion;
490 unsigned int nPool;
491 unsigned int nSettings;
492 unsigned int nHDNext;
493 unsigned int nHDSchema;
494 unsigned int nHDCoinType;
495 unsigned int nHDReceiveNext;
496 unsigned int nHDChangeNext;
497
498 WalletStorageAuditCounts()
499 {
500 memset(this, 0, sizeof(*this));
501 }
502};
503
504static std::string HDSeedStorageState(unsigned int nMaster, unsigned int nChain)
505{
506 if (nMaster == 0 && nChain == 0)
507 return "none";
508 if (nMaster > 0 && nChain > 0)
509 return "complete";
510 return "incomplete";
511}
512
513static void CountWalletStorageType(const std::string& strType,
514 WalletStorageAuditCounts& c)
515{
516 if (strType == "version")
517 c.nVersion++;
518 else if (strType == "name")
519 c.nNames++;
520 else if (strType == "tx")
521 c.nTransactions++;
522 else if (strType == "key")
523 c.nPlainKeys++;
524 else if (strType == "ckey")
525 c.nEncryptedKeys++;
526 else if (strType == "mkey")
527 c.nMasterKeys++;
528 else if (strType == "defaultkey")
529 c.nDefaultKey++;
530 else if (strType == "hdmaster")
531 c.nPlainHDMaster++;
532 else if (strType == "hdchaincode")
533 c.nPlainHDChainCode++;
534 else if (strType == "cryptedhdmaster")
535 c.nCryptedHDMaster++;
536 else if (strType == "cryptedhdchaincode")
537 c.nCryptedHDChainCode++;
538 else if (strType == "walletminversion")
539 c.nWalletMinVersion++;
540 else if (strType == "pool")
541 c.nPool++;
542 else if (strType == "setting")
543 c.nSettings++;
544 else if (strType == "hdnext")
545 c.nHDNext++;
546 else if (strType == "hdschema")
547 c.nHDSchema++;
548 else if (strType == "hdcointype")
549 c.nHDCoinType++;
550 else if (strType == "hdreceivenext")
551 c.nHDReceiveNext++;
552 else if (strType == "hdchangenext")
553 c.nHDChangeNext++;
554 else
555 c.nUnknown++;
556}
557
558static bool ReadWalletStorageCounts(WalletStorageAuditCounts& counts,
559 std::string& strError)
560{
561 class CWalletAuditDB : public CWalletDB
562 {
563 public:
564 CWalletAuditDB() : CWalletDB("r") { }
565 using CDB::GetCursor;
566 using CDB::ReadAtCursor;
567 };
568
569 CWalletAuditDB walletdb;
570 Dbc* pcursor = walletdb.GetCursor();
571 if (!pcursor)
572 {
573 strError = "cannot open wallet.dat cursor";
574 return false;
575 }
576
577 for (;;)
578 {
579 CDataStream ssKey(SER_DISK);
580 CDataStream ssValue(SER_DISK);
581 int ret = walletdb.ReadAtCursor(pcursor, ssKey, ssValue);
582 if (ret == DB_NOTFOUND)
583 break;
584 if (ret != 0)
585 {
586 pcursor->close();
587 strError = strprintf("Berkeley DB cursor read failed (%d)", ret);
588 return false;
589 }
590
591 counts.nTotal++;
592 try
593 {
594 std::string strType;
595 ssKey >> strType;
596 CountWalletStorageType(strType, counts);
597 }
598 catch (...)
599 {
600 counts.nMalformed++;
601 }
602 }
603
604 pcursor->close();
605 return true;
606}
607
608static std::string WalletStorageAuditJson(const WalletStorageAuditCounts& counts)
609{
610 std::string strJson;
611 strJson += "{\n";
612 strJson += " \"format\": \"bitflash-wallet-storage-audit-v1\",\n";
613 strJson += strprintf(" \"records_total\": %u,\n", counts.nTotal);
614 strJson += strprintf(" \"malformed_records\": %u,\n", counts.nMalformed);
615 strJson += strprintf(" \"unknown_records\": %u,\n", counts.nUnknown);
616 strJson += strprintf(" \"plain_private_keys\": %u,\n", counts.nPlainKeys);
617 strJson += strprintf(" \"encrypted_private_keys\": %u,\n", counts.nEncryptedKeys);
618 strJson += strprintf(" \"encryption_master_keys\": %u,\n", counts.nMasterKeys);
619 strJson += strprintf(" \"default_public_key\": \"%s\",\n",
620 counts.nDefaultKey ? "present" : "none");
621 strJson += strprintf(" \"plain_hd_seed\": \"%s\",\n",
622 HDSeedStorageState(counts.nPlainHDMaster,
623 counts.nPlainHDChainCode).c_str());
624 strJson += strprintf(" \"encrypted_hd_seed\": \"%s\",\n",
625 HDSeedStorageState(counts.nCryptedHDMaster,
626 counts.nCryptedHDChainCode).c_str());
627 strJson += strprintf(" \"wallet_minimum_version\": \"%s\",\n",
628 counts.nWalletMinVersion ? "present" : "none");
629 strJson += strprintf(" \"keypool_entries\": %u,\n", counts.nPool);
630 strJson += strprintf(" \"wallet_transactions\": %u,\n", counts.nTransactions);
631 strJson += strprintf(" \"address_book_labels\": %u,\n", counts.nNames);
632 strJson += strprintf(" \"settings\": %u,\n", counts.nSettings);
633 strJson += strprintf(" \"hd_metadata_records\": %u,\n",
634 counts.nHDNext + counts.nHDSchema + counts.nHDCoinType +
635 counts.nHDReceiveNext + counts.nHDChangeNext);
636 strJson += strprintf(" \"database_version_records\": %u\n", counts.nVersion);
637 strJson += "}\n";
638 return strJson;
639}
640
641static bool WriteAuditTextFile(const std::string& strPath,
642 const std::string& strText,
643 std::string& strError)
644{
645 FILE* pf = fopen(strPath.c_str(), "wb");
646 if (!pf)
647 {
648 strError = "cannot open output file";
649 return false;
650 }
651 size_t nWritten = fwrite(strText.data(), 1, strText.size(), pf);
652 bool fCloseOk = fclose(pf) == 0;
653 if (nWritten != strText.size() || !fCloseOk)
654 {
655 strError = "could not write the complete output file";
656 return false;
657 }
658 return true;
659}
660
661int CmdWalletStorageAudit(const std::string& strJsonOut)
662{
663 AttachTerminal();
664
665 WalletStorageAuditCounts counts;
666 std::string strError;
667 if (!ReadWalletStorageCounts(counts, strError))
668 {
669 fprintf(stderr, "Cannot audit wallet storage: %s\n", strError.c_str());
670 return 1;
671 }
672
673 if (!strJsonOut.empty())
674 {
675 std::string strWriteError;
676 if (!WriteAuditTextFile(strJsonOut,
677 WalletStorageAuditJson(counts),
678 strWriteError))
679 {
680 fprintf(stderr, "Cannot write wallet storage audit JSON: %s\n",
681 strWriteError.c_str());
682 return 1;
683 }
684 printf("Wallet storage audit written to %s\n", strJsonOut.c_str());
685 fflush(stdout);
686 return counts.nMalformed > 0 ? 2 : 0;
687 }
688
689 printf("Wallet storage audit\n");
690 printf(" records total: %u\n", counts.nTotal);
691 printf(" malformed records: %u\n", counts.nMalformed);
692 printf(" unknown records: %u\n", counts.nUnknown);
693 printf(" plain private keys: %u\n", counts.nPlainKeys);
694 printf(" encrypted private keys: %u\n", counts.nEncryptedKeys);
695 printf(" encryption master keys: %u\n", counts.nMasterKeys);
696 printf(" default public key: %s\n", counts.nDefaultKey ? "present" : "none");
697 printf(" plain HD seed: %s\n",
698 HDSeedStorageState(counts.nPlainHDMaster,
699 counts.nPlainHDChainCode).c_str());
700 printf(" encrypted HD seed: %s\n",
701 HDSeedStorageState(counts.nCryptedHDMaster,
702 counts.nCryptedHDChainCode).c_str());
703 printf(" wallet minimum version: %s\n",
704 counts.nWalletMinVersion ? "present" : "none");
705 printf(" keypool entries: %u\n", counts.nPool);
706 printf(" wallet transactions: %u\n", counts.nTransactions);
707 printf(" address book labels: %u\n", counts.nNames);
708 printf(" settings: %u\n", counts.nSettings);
709 printf(" HD metadata records: %u\n",
710 counts.nHDNext + counts.nHDSchema + counts.nHDCoinType +
711 counts.nHDReceiveNext + counts.nHDChangeNext);
712 printf(" database version records: %u\n", counts.nVersion);
713 fflush(stdout);
714
715 if (counts.nMalformed > 0)
716 return 2;
717 return 0;
718}
719
459int CmdEncryptWallet(const std::string& strPassphrase) 720int CmdEncryptWallet(const std::string& strPassphrase)
460{ 721{
461 AttachTerminal(); 722 AttachTerminal();