diff options
| author | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-13 17:40:20 -0300 |
|---|---|---|
| committer | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-13 17:59:25 -0300 |
| commit | 805c5757b801d80ea575c6a8b58e2855254f9046 (patch) | |
| tree | 5faf35daa569d987b87dbb161bb18ec170f944b0 /src | |
| parent | 8e62e362e2fbe6fd6c5d66ddb354ec544f8cf0db (diff) | |
wallet: export storage audit json
Diffstat (limited to 'src')
| -rw-r--r-- | src/main_gui.cpp | 10 | ||||
| -rw-r--r-- | src/selftest.cpp | 38 | ||||
| -rw-r--r-- | src/walletcmd.cpp | 71 | ||||
| -rw-r--r-- | src/walletcmd.h | 2 |
4 files changed, 117 insertions, 4 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 532bc3b..395cc0f 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -174,6 +174,8 @@ static void PrintUsage() | |||
| 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"); | 175 | printf(" /walletstorageaudit (count wallet.dat record types without\n"); |
| 176 | printf(" printing wallet values, then exit)\n"); | 176 | printf(" printing wallet values, then exit)\n"); |
| 177 | printf(" /walletstorageauditjson=FILE\n"); | ||
| 178 | printf(" (write the same audit as deterministic JSON)\n"); | ||
| 177 | printf(" /rescan (walk the chain for coins this wallet owns\n"); | 179 | printf(" /rescan (walk the chain for coins this wallet owns\n"); |
| 178 | printf(" but never recorded, then exit)\n"); | 180 | printf(" but never recorded, then exit)\n"); |
| 179 | printf("\n"); | 181 | printf("\n"); |
| @@ -706,9 +708,13 @@ int main(int argc, char* argv[]) | |||
| 706 | return nRet; | 708 | return nRet; |
| 707 | } | 709 | } |
| 708 | 710 | ||
| 709 | if (arg(argc,argv,"/walletstorageaudit") || arg(argc,argv,"-walletstorageaudit")) | 711 | string strWalletStorageAuditJson = |
| 712 | argval2(argc, argv, "/walletstorageauditjson", "-walletstorageauditjson"); | ||
| 713 | if (arg(argc,argv,"/walletstorageaudit") || | ||
| 714 | arg(argc,argv,"-walletstorageaudit") || | ||
| 715 | !strWalletStorageAuditJson.empty()) | ||
| 710 | { | 716 | { |
| 711 | int nRet = CmdWalletStorageAudit(); | 717 | int nRet = CmdWalletStorageAudit(strWalletStorageAuditJson); |
| 712 | DBFlush(true); | 718 | DBFlush(true); |
| 713 | return nRet; | 719 | return nRet; |
| 714 | } | 720 | } |
diff --git a/src/selftest.cpp b/src/selftest.cpp index b112238..73c2935 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -1329,6 +1329,24 @@ static int RunWalletPortabilitySelfTest() | |||
| 1329 | FileContainsText(strOriginalStorageAudit, "malformed records: 0"), | 1329 | FileContainsText(strOriginalStorageAudit, "malformed records: 0"), |
| 1330 | "the storage audit recognizes a plaintext phrase wallet") ? 0 : 1; | 1330 | "the storage audit recognizes a plaintext phrase wallet") ? 0 : 1; |
| 1331 | 1331 | ||
| 1332 | string strOriginalStorageAuditJson = tmp + "/original-storage-audit.json"; | ||
| 1333 | vector<string> vOriginalStorageAuditJsonArgs; | ||
| 1334 | vOriginalStorageAuditJsonArgs.push_back("-datadir=" + strOriginal); | ||
| 1335 | vOriginalStorageAuditJsonArgs.push_back("-nomanagedtor"); | ||
| 1336 | vOriginalStorageAuditJsonArgs.push_back("-nogui"); | ||
| 1337 | vOriginalStorageAuditJsonArgs.push_back("-walletstorageauditjson=" + | ||
| 1338 | strOriginalStorageAuditJson); | ||
| 1339 | int nOriginalStorageAuditJsonRet = | ||
| 1340 | RunBitflashChild(strExe, vOriginalStorageAuditJsonArgs); | ||
| 1341 | nFail += Check(nOriginalStorageAuditJsonRet == 0 && | ||
| 1342 | FileContainsText(strOriginalStorageAuditJson, | ||
| 1343 | "\"format\": \"bitflash-wallet-storage-audit-v1\"") && | ||
| 1344 | FileContainsText(strOriginalStorageAuditJson, | ||
| 1345 | "\"plain_hd_seed\": \"complete\"") && | ||
| 1346 | FileContainsText(strOriginalStorageAuditJson, | ||
| 1347 | "\"encrypted_hd_seed\": \"none\""), | ||
| 1348 | "the storage audit can write plaintext-wallet JSON") ? 0 : 1; | ||
| 1349 | |||
| 1332 | string strPortableWallet = tmp + "/portable-wallet.dat"; | 1350 | string strPortableWallet = tmp + "/portable-wallet.dat"; |
| 1333 | string strBackupOut = tmp + "/backupwallet.txt"; | 1351 | string strBackupOut = tmp + "/backupwallet.txt"; |
| 1334 | vector<string> vBackupArgs; | 1352 | vector<string> vBackupArgs; |
| @@ -1400,6 +1418,26 @@ static int RunWalletPortabilitySelfTest() | |||
| 1400 | FileContainsText(strEncryptedStorageAudit, "wallet minimum version: present") && | 1418 | FileContainsText(strEncryptedStorageAudit, "wallet minimum version: present") && |
| 1401 | FileContainsText(strEncryptedStorageAudit, "malformed records: 0"), | 1419 | FileContainsText(strEncryptedStorageAudit, "malformed records: 0"), |
| 1402 | "the storage audit recognizes an encrypted wallet") ? 0 : 1; | 1420 | "the storage audit recognizes an encrypted wallet") ? 0 : 1; |
| 1421 | |||
| 1422 | string strEncryptedStorageAuditJson = tmp + "/encrypted-storage-audit.json"; | ||
| 1423 | vector<string> vEncryptedStorageAuditJsonArgs; | ||
| 1424 | vEncryptedStorageAuditJsonArgs.push_back("-datadir=" + strOriginal); | ||
| 1425 | vEncryptedStorageAuditJsonArgs.push_back("-nomanagedtor"); | ||
| 1426 | vEncryptedStorageAuditJsonArgs.push_back("-nogui"); | ||
| 1427 | vEncryptedStorageAuditJsonArgs.push_back("-walletstorageauditjson=" + | ||
| 1428 | strEncryptedStorageAuditJson); | ||
| 1429 | int nEncryptedStorageAuditJsonRet = | ||
| 1430 | RunBitflashChild(strExe, vEncryptedStorageAuditJsonArgs); | ||
| 1431 | nFail += Check(nEncryptedStorageAuditJsonRet == 0 && | ||
| 1432 | FileContainsText(strEncryptedStorageAuditJson, | ||
| 1433 | "\"plain_private_keys\": 0") && | ||
| 1434 | FileContainsText(strEncryptedStorageAuditJson, | ||
| 1435 | "\"plain_hd_seed\": \"none\"") && | ||
| 1436 | FileContainsText(strEncryptedStorageAuditJson, | ||
| 1437 | "\"encrypted_hd_seed\": \"complete\"") && | ||
| 1438 | FileContainsText(strEncryptedStorageAuditJson, | ||
| 1439 | "\"wallet_minimum_version\": \"present\""), | ||
| 1440 | "the storage audit can write encrypted-wallet JSON") ? 0 : 1; | ||
| 1403 | } | 1441 | } |
| 1404 | catch (const std::exception& e) | 1442 | catch (const std::exception& e) |
| 1405 | { | 1443 | { |
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 896d593..6f8258d 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp | |||
| @@ -605,7 +605,60 @@ static bool ReadWalletStorageCounts(WalletStorageAuditCounts& counts, | |||
| 605 | return true; | 605 | return true; |
| 606 | } | 606 | } |
| 607 | 607 | ||
| 608 | int CmdWalletStorageAudit() | 608 | static 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 | |||
| 641 | static 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 | |||
| 661 | int CmdWalletStorageAudit(const std::string& strJsonOut) | ||
| 609 | { | 662 | { |
| 610 | AttachTerminal(); | 663 | AttachTerminal(); |
| 611 | 664 | ||
| @@ -617,6 +670,22 @@ int CmdWalletStorageAudit() | |||
| 617 | return 1; | 670 | return 1; |
| 618 | } | 671 | } |
| 619 | 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 | |||
| 620 | printf("Wallet storage audit\n"); | 689 | printf("Wallet storage audit\n"); |
| 621 | printf(" records total: %u\n", counts.nTotal); | 690 | printf(" records total: %u\n", counts.nTotal); |
| 622 | printf(" malformed records: %u\n", counts.nMalformed); | 691 | printf(" malformed records: %u\n", counts.nMalformed); |
diff --git a/src/walletcmd.h b/src/walletcmd.h index 4a703db..c6342a6 100644 --- a/src/walletcmd.h +++ b/src/walletcmd.h | |||
| @@ -64,7 +64,7 @@ int CmdRecoveryAudit(); | |||
| 64 | 64 | ||
| 65 | // Diagnostic: count wallet.dat record types without printing keys, | 65 | // Diagnostic: count wallet.dat record types without printing keys, |
| 66 | // addresses, transaction ids, labels, or other wallet values. | 66 | // addresses, transaction ids, labels, or other wallet values. |
| 67 | int CmdWalletStorageAudit(); | 67 | int CmdWalletStorageAudit(const std::string& strJsonOut = ""); |
| 68 | 68 | ||
| 69 | // 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 |
| 70 | // 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. |