upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-13 17:36:09 -0300
committerNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-13 17:59:25 -0300
commit8e62e362e2fbe6fd6c5d66ddb354ec544f8cf0db (patch)
treed9773fd4b52f3ae7c316c6ff11fc2ab4d924a122
parentc0f886f0683ebd1321751355ddc0c757496320c9 (diff)
wallet: add storage audit command
-rw-r--r--src/main_gui.cpp9
-rw-r--r--src/selftest.cpp32
-rw-r--r--src/walletcmd.cpp178
-rw-r--r--src/walletcmd.h4
4 files changed, 223 insertions, 0 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 50c4f34..532bc3b 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -172,6 +172,8 @@ static void PrintUsage()
172 printf(" /recoveryaudit (show how much spendable balance is\n"); 172 printf(" /recoveryaudit (show how much spendable balance is\n");
173 printf(" covered by the recovery phrase;\n"); 173 printf(" covered by the recovery phrase;\n");
174 printf(" exits 2 when wallet.dat is still needed)\n"); 174 printf(" exits 2 when wallet.dat is still needed)\n");
175 printf(" /walletstorageaudit (count wallet.dat record types without\n");
176 printf(" printing wallet values, then exit)\n");
175 printf(" /rescan (walk the chain for coins this wallet owns\n"); 177 printf(" /rescan (walk the chain for coins this wallet owns\n");
176 printf(" but never recorded, then exit)\n"); 178 printf(" but never recorded, then exit)\n");
177 printf("\n"); 179 printf("\n");
@@ -704,6 +706,13 @@ int main(int argc, char* argv[])
704 return nRet; 706 return nRet;
705 } 707 }
706 708
709 if (arg(argc,argv,"/walletstorageaudit") || arg(argc,argv,"-walletstorageaudit"))
710 {
711 int nRet = CmdWalletStorageAudit();
712 DBFlush(true);
713 return nRet;
714 }
715
707 if (arg(argc,argv,"/newphrase") || arg(argc,argv,"-newphrase")) 716 if (arg(argc,argv,"/newphrase") || arg(argc,argv,"-newphrase"))
708 { 717 {
709 int nRet = CmdNewPhrase(); 718 int nRet = CmdNewPhrase();
diff --git a/src/selftest.cpp b/src/selftest.cpp
index 8a448f0..b112238 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -1314,6 +1314,21 @@ static int RunWalletPortabilitySelfTest()
1314 FileContainsText(strOriginalAudit, "SLIP-0044 BITFLASH"), 1314 FileContainsText(strOriginalAudit, "SLIP-0044 BITFLASH"),
1315 "the original wallet reports its recovery phrase") ? 0 : 1; 1315 "the original wallet reports its recovery phrase") ? 0 : 1;
1316 1316
1317 string strOriginalStorageAudit = tmp + "/original-storage-audit.txt";
1318 vector<string> vOriginalStorageAuditArgs;
1319 vOriginalStorageAuditArgs.push_back("-datadir=" + strOriginal);
1320 vOriginalStorageAuditArgs.push_back("-nomanagedtor");
1321 vOriginalStorageAuditArgs.push_back("-nogui");
1322 vOriginalStorageAuditArgs.push_back("-walletstorageaudit");
1323 int nOriginalStorageAuditRet =
1324 RunBitflashChild(strExe, vOriginalStorageAuditArgs, NULL, &strOriginalStorageAudit);
1325 nFail += Check(nOriginalStorageAuditRet == 0 &&
1326 FileContainsText(strOriginalStorageAudit, "Wallet storage audit") &&
1327 FileContainsText(strOriginalStorageAudit, "plain HD seed: complete") &&
1328 FileContainsText(strOriginalStorageAudit, "encrypted HD seed: none") &&
1329 FileContainsText(strOriginalStorageAudit, "malformed records: 0"),
1330 "the storage audit recognizes a plaintext phrase wallet") ? 0 : 1;
1331
1317 string strPortableWallet = tmp + "/portable-wallet.dat"; 1332 string strPortableWallet = tmp + "/portable-wallet.dat";
1318 string strBackupOut = tmp + "/backupwallet.txt"; 1333 string strBackupOut = tmp + "/backupwallet.txt";
1319 vector<string> vBackupArgs; 1334 vector<string> vBackupArgs;
@@ -1368,6 +1383,23 @@ static int RunWalletPortabilitySelfTest()
1368 nFail += Check(nEncryptRet == 0 && 1383 nFail += Check(nEncryptRet == 0 &&
1369 !DirectoryHasFileWithPrefix(strOriginal + "/database", "log."), 1384 !DirectoryHasFileWithPrefix(strOriginal + "/database", "log."),
1370 "the encryptwallet command purges Berkeley DB environment logs") ? 0 : 1; 1385 "the encryptwallet command purges Berkeley DB environment logs") ? 0 : 1;
1386
1387 string strEncryptedStorageAudit = tmp + "/encrypted-storage-audit.txt";
1388 vector<string> vEncryptedStorageAuditArgs;
1389 vEncryptedStorageAuditArgs.push_back("-datadir=" + strOriginal);
1390 vEncryptedStorageAuditArgs.push_back("-nomanagedtor");
1391 vEncryptedStorageAuditArgs.push_back("-nogui");
1392 vEncryptedStorageAuditArgs.push_back("-walletstorageaudit");
1393 int nEncryptedStorageAuditRet =
1394 RunBitflashChild(strExe, vEncryptedStorageAuditArgs, NULL, &strEncryptedStorageAudit);
1395 nFail += Check(nEncryptedStorageAuditRet == 0 &&
1396 FileContainsText(strEncryptedStorageAudit, "plain private keys: 0") &&
1397 FileContainsText(strEncryptedStorageAudit, "encrypted private keys:") &&
1398 FileContainsText(strEncryptedStorageAudit, "plain HD seed: none") &&
1399 FileContainsText(strEncryptedStorageAudit, "encrypted HD seed: complete") &&
1400 FileContainsText(strEncryptedStorageAudit, "wallet minimum version: present") &&
1401 FileContainsText(strEncryptedStorageAudit, "malformed records: 0"),
1402 "the storage audit recognizes an encrypted wallet") ? 0 : 1;
1371 } 1403 }
1372 catch (const std::exception& e) 1404 catch (const std::exception& e)
1373 { 1405 {
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp
index eccb9b3..896d593 100644
--- a/src/walletcmd.cpp
+++ b/src/walletcmd.cpp
@@ -470,6 +470,184 @@ int CmdRecoveryAudit()
470 return 0; 470 return 0;
471} 471}
472 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
608int CmdWalletStorageAudit()
609{
610 AttachTerminal();
611
612 WalletStorageAuditCounts counts;
613 std::string strError;
614 if (!ReadWalletStorageCounts(counts, strError))
615 {
616 fprintf(stderr, "Cannot audit wallet storage: %s\n", strError.c_str());
617 return 1;
618 }
619
620 printf("Wallet storage audit\n");
621 printf(" records total: %u\n", counts.nTotal);
622 printf(" malformed records: %u\n", counts.nMalformed);
623 printf(" unknown records: %u\n", counts.nUnknown);
624 printf(" plain private keys: %u\n", counts.nPlainKeys);
625 printf(" encrypted private keys: %u\n", counts.nEncryptedKeys);
626 printf(" encryption master keys: %u\n", counts.nMasterKeys);
627 printf(" default public key: %s\n", counts.nDefaultKey ? "present" : "none");
628 printf(" plain HD seed: %s\n",
629 HDSeedStorageState(counts.nPlainHDMaster,
630 counts.nPlainHDChainCode).c_str());
631 printf(" encrypted HD seed: %s\n",
632 HDSeedStorageState(counts.nCryptedHDMaster,
633 counts.nCryptedHDChainCode).c_str());
634 printf(" wallet minimum version: %s\n",
635 counts.nWalletMinVersion ? "present" : "none");
636 printf(" keypool entries: %u\n", counts.nPool);
637 printf(" wallet transactions: %u\n", counts.nTransactions);
638 printf(" address book labels: %u\n", counts.nNames);
639 printf(" settings: %u\n", counts.nSettings);
640 printf(" HD metadata records: %u\n",
641 counts.nHDNext + counts.nHDSchema + counts.nHDCoinType +
642 counts.nHDReceiveNext + counts.nHDChangeNext);
643 printf(" database version records: %u\n", counts.nVersion);
644 fflush(stdout);
645
646 if (counts.nMalformed > 0)
647 return 2;
648 return 0;
649}
650
473int CmdEncryptWallet(const std::string& strPassphrase) 651int CmdEncryptWallet(const std::string& strPassphrase)
474{ 652{
475 AttachTerminal(); 653 AttachTerminal();
diff --git a/src/walletcmd.h b/src/walletcmd.h
index 007b8b5..4a703db 100644
--- a/src/walletcmd.h
+++ b/src/walletcmd.h
@@ -62,6 +62,10 @@ int CmdShowDerived(int nCount);
62// by the installed recovery phrase or still depends on wallet.dat-only keys. 62// by the installed recovery phrase or still depends on wallet.dat-only keys.
63int CmdRecoveryAudit(); 63int CmdRecoveryAudit();
64 64
65// Diagnostic: count wallet.dat record types without printing keys,
66// addresses, transaction ids, labels, or other wallet values.
67int CmdWalletStorageAudit();
68
65// Rewrite wallet.dat so private keys and the HD seed are encrypted with the 69// Rewrite wallet.dat so private keys and the HD seed are encrypted with the
66// given passphrase. Returns 0 on success and exits without starting the node. 70// given passphrase. Returns 0 on success and exits without starting the node.
67int CmdEncryptWallet(const std::string& strPassphrase); 71int CmdEncryptWallet(const std::string& strPassphrase);