diff options
Diffstat (limited to 'src/walletcmd.cpp')
| -rw-r--r-- | src/walletcmd.cpp | 263 |
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 | ||
| 473 | struct 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 | |||
| 504 | static 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 | |||
| 513 | static 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 | |||
| 558 | static 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 | |||
| 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) | ||
| 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 | |||
| 459 | int CmdEncryptWallet(const std::string& strPassphrase) | 720 | int CmdEncryptWallet(const std::string& strPassphrase) |
| 460 | { | 721 | { |
| 461 | AttachTerminal(); | 722 | AttachTerminal(); |