diff options
| author | Bitflash <admin@bitflash.network> | 2026-08-14 16:53:01 -0300 |
|---|---|---|
| committer | Bitflash <admin@bitflash.network> | 2026-08-14 16:53:01 -0300 |
| commit | 35f6c752f9f879ca94478985978e4a9c862ede3e (patch) | |
| tree | a32b59dde39832f1fc7a21a29681b77c95de40cb /src/walletcmd.cpp | |
| parent | 0c58896a045246c3518d5d379f217172defdfd91 (diff) | |
| parent | 99c6b5f2971da51cca1c739b5e982ae2022acc85 (diff) | |
Merge pull request 'wallet: verify SQLite wallet exports' (#50) from wallet-sqlite-verify into main
Diffstat (limited to 'src/walletcmd.cpp')
| -rw-r--r-- | src/walletcmd.cpp | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 20e7e33..36f6783 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp | |||
| @@ -796,6 +796,8 @@ static vector<unsigned char> DataStreamBytesLocal(const CDataStream& ss) | |||
| 796 | return vector<unsigned char>(ss.begin(), ss.end()); | 796 | return vector<unsigned char>(ss.begin(), ss.end()); |
| 797 | } | 797 | } |
| 798 | 798 | ||
| 799 | namespace { | ||
| 800 | |||
| 799 | class CWalletSQLiteExportVisitor : public CWalletRecordVisitor | 801 | class CWalletSQLiteExportVisitor : public CWalletRecordVisitor |
| 800 | { | 802 | { |
| 801 | public: | 803 | public: |
| @@ -818,6 +820,32 @@ public: | |||
| 818 | } | 820 | } |
| 819 | }; | 821 | }; |
| 820 | 822 | ||
| 823 | class CWalletRecordMapVisitor : public CWalletRecordVisitor | ||
| 824 | { | ||
| 825 | public: | ||
| 826 | std::map<vector<unsigned char>, vector<unsigned char> >& mapRecords; | ||
| 827 | |||
| 828 | explicit CWalletRecordMapVisitor( | ||
| 829 | std::map<vector<unsigned char>, vector<unsigned char> >& mapRecordsIn) | ||
| 830 | : mapRecords(mapRecordsIn) { } | ||
| 831 | |||
| 832 | bool VisitWalletRecord(const CDataStream& ssKey, | ||
| 833 | const CDataStream& ssValue, | ||
| 834 | string& strErrorRet) | ||
| 835 | { | ||
| 836 | vector<unsigned char> vchKey = DataStreamBytesLocal(ssKey); | ||
| 837 | if (mapRecords.count(vchKey)) | ||
| 838 | { | ||
| 839 | strErrorRet = "duplicate serialized wallet key"; | ||
| 840 | return false; | ||
| 841 | } | ||
| 842 | mapRecords[vchKey] = DataStreamBytesLocal(ssValue); | ||
| 843 | return true; | ||
| 844 | } | ||
| 845 | }; | ||
| 846 | |||
| 847 | } // namespace | ||
| 848 | |||
| 821 | int CmdWalletSQLiteExport(const std::string& strDest) | 849 | int CmdWalletSQLiteExport(const std::string& strDest) |
| 822 | { | 850 | { |
| 823 | AttachTerminal(); | 851 | AttachTerminal(); |
| @@ -887,6 +915,85 @@ int CmdWalletSQLiteExport(const std::string& strDest) | |||
| 887 | return 0; | 915 | return 0; |
| 888 | } | 916 | } |
| 889 | 917 | ||
| 918 | int CmdWalletSQLiteVerify(const std::string& strPath) | ||
| 919 | { | ||
| 920 | AttachTerminal(); | ||
| 921 | |||
| 922 | if (strPath.empty()) | ||
| 923 | { | ||
| 924 | fprintf(stderr, "Missing SQLite wallet export path.\n"); | ||
| 925 | return 1; | ||
| 926 | } | ||
| 927 | if (!FileExists(strPath.c_str())) | ||
| 928 | { | ||
| 929 | fprintf(stderr, "SQLite wallet export does not exist: %s\n", | ||
| 930 | strPath.c_str()); | ||
| 931 | return 1; | ||
| 932 | } | ||
| 933 | |||
| 934 | std::map<vector<unsigned char>, vector<unsigned char> > mapBDB; | ||
| 935 | std::map<vector<unsigned char>, vector<unsigned char> > mapSQLite; | ||
| 936 | string strError; | ||
| 937 | |||
| 938 | CWalletRecordMapVisitor bdbVisitor(mapBDB); | ||
| 939 | if (!ScanWalletRecords(bdbVisitor, strError)) | ||
| 940 | { | ||
| 941 | fprintf(stderr, "Cannot scan wallet.dat records: %s\n", strError.c_str()); | ||
| 942 | return 1; | ||
| 943 | } | ||
| 944 | |||
| 945 | CWalletDBSQLite db; | ||
| 946 | if (!db.Open(strPath, strError)) | ||
| 947 | { | ||
| 948 | fprintf(stderr, "Cannot open SQLite wallet export: %s\n", strError.c_str()); | ||
| 949 | return 1; | ||
| 950 | } | ||
| 951 | CWalletRecordMapVisitor sqliteVisitor(mapSQLite); | ||
| 952 | if (!db.ScanRecords(sqliteVisitor, strError)) | ||
| 953 | { | ||
| 954 | fprintf(stderr, "Cannot scan SQLite wallet export: %s\n", strError.c_str()); | ||
| 955 | return 1; | ||
| 956 | } | ||
| 957 | |||
| 958 | int nMissing = 0; | ||
| 959 | int nMismatched = 0; | ||
| 960 | for (std::map<vector<unsigned char>, vector<unsigned char> >::const_iterator it = | ||
| 961 | mapBDB.begin(); it != mapBDB.end(); ++it) | ||
| 962 | { | ||
| 963 | std::map<vector<unsigned char>, vector<unsigned char> >::const_iterator sit = | ||
| 964 | mapSQLite.find(it->first); | ||
| 965 | if (sit == mapSQLite.end()) | ||
| 966 | nMissing++; | ||
| 967 | else if (sit->second != it->second) | ||
| 968 | nMismatched++; | ||
| 969 | } | ||
| 970 | |||
| 971 | int nExtra = 0; | ||
| 972 | for (std::map<vector<unsigned char>, vector<unsigned char> >::const_iterator it = | ||
| 973 | mapSQLite.begin(); it != mapSQLite.end(); ++it) | ||
| 974 | { | ||
| 975 | if (!mapBDB.count(it->first)) | ||
| 976 | nExtra++; | ||
| 977 | } | ||
| 978 | |||
| 979 | printf("SQLite wallet export verification\n"); | ||
| 980 | printf(" wallet.dat records: %u\n", (unsigned int)mapBDB.size()); | ||
| 981 | printf(" SQLite records: %u\n", (unsigned int)mapSQLite.size()); | ||
| 982 | printf(" missing records: %d\n", nMissing); | ||
| 983 | printf(" extra records: %d\n", nExtra); | ||
| 984 | printf(" record value mismatches: %d\n", nMismatched); | ||
| 985 | if (nMissing == 0 && nExtra == 0 && nMismatched == 0) | ||
| 986 | { | ||
| 987 | printf(" verification: ok\n"); | ||
| 988 | fflush(stdout); | ||
| 989 | return 0; | ||
| 990 | } | ||
| 991 | |||
| 992 | printf(" verification: failed\n"); | ||
| 993 | fflush(stdout); | ||
| 994 | return 2; | ||
| 995 | } | ||
| 996 | |||
| 890 | int CmdEncryptWallet(const std::string& strPassphrase) | 997 | int CmdEncryptWallet(const std::string& strPassphrase) |
| 891 | { | 998 | { |
| 892 | AttachTerminal(); | 999 | AttachTerminal(); |