upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/db.cpp')
-rw-r--r--src/db.cpp1251
1 files changed, 1141 insertions, 110 deletions
diff --git a/src/db.cpp b/src/db.cpp
index 405a3db..bf1958d 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -3,6 +3,7 @@
3// file license.txt or http://www.opensource.org/licenses/mit-license.php. 3// file license.txt or http://www.opensource.org/licenses/mit-license.php.
4 4
5#include "headers.h" 5#include "headers.h"
6#include "wallet_sqlite.h"
6 7
7 8
8 9
@@ -52,8 +53,34 @@ static bool fDbEnvInit = false;
52// Why LoadWallet() refused, in words meant for the person who has to act on it. 53// Why LoadWallet() refused, in words meant for the person who has to act on it.
53// Empty unless the reason is one we can name. 54// Empty unless the reason is one we can name.
54string strWalletLoadError; 55string strWalletLoadError;
55DbEnv dbenv(0u); 56static DbEnv* pdbenv = NULL;
57static FILE* pdbErrFile = NULL;
56static map<string, int> mapFileUseCount; 58static map<string, int> mapFileUseCount;
59static CWalletDBSQLite* pWalletSQLiteRuntime = NULL;
60
61DbEnv& GetDbEnv()
62{
63 if (!pdbenv)
64 pdbenv = new DbEnv(0u);
65 return *pdbenv;
66}
67
68static void CloseDbEnv()
69{
70 if (pdbenv)
71 {
72 try { pdbenv->close(0); }
73 catch (...) { }
74 delete pdbenv;
75 pdbenv = NULL;
76 }
77 if (pdbErrFile)
78 {
79 fclose(pdbErrFile);
80 pdbErrFile = NULL;
81 }
82 fDbEnvInit = false;
83}
57 84
58class CDBInit 85class CDBInit
59{ 86{
@@ -64,15 +91,189 @@ public:
64 ~CDBInit() 91 ~CDBInit()
65 { 92 {
66 if (fDbEnvInit) 93 if (fDbEnvInit)
67 { 94 CloseDbEnv();
68 try { dbenv.close(0); }
69 catch (...) { }
70 fDbEnvInit = false;
71 }
72 } 95 }
73} 96}
74instance_of_cdbinit; 97instance_of_cdbinit;
75 98
99bool WalletSQLiteRuntimeActive()
100{
101 return pWalletSQLiteRuntime != NULL;
102}
103
104bool WalletSQLiteRuntimeOpen(const string& strPath, string& strErrorRet, bool fMustExist)
105{
106 strErrorRet.clear();
107 // Callers that load an existing wallet want the clear "does not exist"
108 // message rather than silently creating a blank one; the create path passes
109 // fMustExist=false so Open() (SQLITE_OPEN_CREATE) can make a fresh file.
110 if (fMustExist && !FileExists(strPath.c_str()))
111 {
112 strErrorRet = strprintf("SQLite wallet does not exist: %s",
113 strPath.c_str());
114 return false;
115 }
116
117 WalletSQLiteRuntimeClose();
118 std::auto_ptr<CWalletDBSQLite> db(new CWalletDBSQLite());
119 if (!db->Open(strPath, strErrorRet))
120 return false;
121
122 pWalletSQLiteRuntime = db.release();
123 return true;
124}
125
126void WalletSQLiteRuntimeClose()
127{
128 if (pWalletSQLiteRuntime)
129 {
130 // Fold the write-ahead log into the main file on the way out. WAL +
131 // synchronous=FULL already makes every committed write durable across a
132 // kill, so this is housekeeping, not the thing that protects the data:
133 // if it fails, or the node is killed before it runs, the wallet is
134 // still whole. Best effort, never throws.
135 string strCheckpointError;
136 pWalletSQLiteRuntime->Checkpoint(strCheckpointError);
137 delete pWalletSQLiteRuntime;
138 pWalletSQLiteRuntime = NULL;
139 }
140}
141
142bool WalletSQLiteRuntimeReadRecord(const vector<unsigned char>& vchKey,
143 vector<unsigned char>& vchValueRet)
144{
145 if (!pWalletSQLiteRuntime)
146 return false;
147 string strError;
148 return pWalletSQLiteRuntime->ReadRecord(vchKey, vchValueRet, strError);
149}
150
151bool WalletSQLiteRuntimeWriteRecord(const vector<unsigned char>& vchKey,
152 const vector<unsigned char>& vchValue,
153 bool fOverwrite)
154{
155 if (!pWalletSQLiteRuntime)
156 return false;
157 string strError;
158 return pWalletSQLiteRuntime->WriteRecord(vchKey, vchValue, strError,
159 fOverwrite);
160}
161
162bool WalletSQLiteRuntimeEraseRecord(const vector<unsigned char>& vchKey)
163{
164 if (!pWalletSQLiteRuntime)
165 return false;
166 string strError;
167 return pWalletSQLiteRuntime->EraseRecord(vchKey, strError);
168}
169
170// Transaction bracket for the SQLite runtime. When the SQLite backend is not
171// active these are no-ops that succeed, so a caller can wrap a group of related
172// writes and Berkeley DB keeps its existing per-write behaviour unchanged while
173// SQLite gets all-or-nothing.
174bool WalletSQLiteRuntimeBeginTxn()
175{
176 if (!pWalletSQLiteRuntime)
177 return true;
178 string strError;
179 if (pWalletSQLiteRuntime->BeginTransaction(strError))
180 return true;
181 printf("WalletSQLiteRuntimeBeginTxn() : %s\n", strError.c_str());
182 return false;
183}
184
185bool WalletSQLiteRuntimeCommitTxn()
186{
187 if (!pWalletSQLiteRuntime)
188 return true;
189 string strError;
190 if (pWalletSQLiteRuntime->CommitTransaction(strError))
191 return true;
192 printf("WalletSQLiteRuntimeCommitTxn() : %s\n", strError.c_str());
193 return false;
194}
195
196void WalletSQLiteRuntimeRollbackTxn()
197{
198 if (!pWalletSQLiteRuntime)
199 return;
200 string strError;
201 pWalletSQLiteRuntime->RollbackTransaction(strError);
202}
203
204// Open the SQLite wallet read-only as the active runtime. Used by the one-shot
205// storage diagnostics, which run before the normal wallet load and must be able
206// to inspect a wallet that the loader would refuse -- so this opens the file for
207// scanning without loading or validating its records.
208bool WalletSQLiteRuntimeOpenReadOnly(const string& strPath, string& strErrorRet)
209{
210 strErrorRet.clear();
211 if (!FileExists(strPath.c_str()))
212 {
213 strErrorRet = strprintf("SQLite wallet does not exist: %s", strPath.c_str());
214 return false;
215 }
216
217 WalletSQLiteRuntimeClose();
218 std::auto_ptr<CWalletDBSQLite> db(new CWalletDBSQLite());
219 if (!db->OpenReadOnly(strPath, strErrorRet))
220 return false;
221
222 pWalletSQLiteRuntime = db.release();
223 return true;
224}
225
226bool WalletSQLiteRuntimeCheckpoint(string& strErrorRet)
227{
228 if (!pWalletSQLiteRuntime)
229 {
230 strErrorRet = "SQLite wallet backend is not active";
231 return false;
232 }
233 return pWalletSQLiteRuntime->Checkpoint(strErrorRet);
234}
235
236// The persistent record of which backend this datadir uses, so the choice
237// survives a restart without a command-line flag. A plain text file holding
238// "sqlite" or "bdb" -- visible and hand-editable on purpose, no hidden state.
239static string WalletBackendMarkerPath()
240{
241 return GetAppDir() + "/wallet-backend";
242}
243
244string ReadWalletBackendMarker()
245{
246 FILE* pf = fopen(WalletBackendMarkerPath().c_str(), "rb");
247 if (!pf)
248 return "";
249 char buf[16] = {0};
250 size_t n = fread(buf, 1, sizeof(buf) - 1, pf);
251 fclose(pf);
252 string s(buf, n);
253 while (!s.empty() && isspace((unsigned char)s[s.size() - 1]))
254 s.erase(s.size() - 1);
255 while (!s.empty() && isspace((unsigned char)s[0]))
256 s.erase(0, 1);
257 if (s == "sqlite" || s == "bdb")
258 return s;
259 return "";
260}
261
262bool WriteWalletBackendMarker(const string& strBackend)
263{
264 if (strBackend != "sqlite" && strBackend != "bdb")
265 return false;
266 FILE* pf = fopen(WalletBackendMarkerPath().c_str(), "wb");
267 if (!pf)
268 return false;
269 bool fOk = fwrite(strBackend.c_str(), 1, strBackend.size(), pf) == strBackend.size();
270 if (fputc('\n', pf) == EOF)
271 fOk = false;
272 if (fclose(pf) != 0)
273 fOk = false;
274 return fOk;
275}
276
76 277
77CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) 278CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL)
78{ 279{
@@ -104,24 +305,30 @@ CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL)
104 305
105 printf("dbenv.open strAppDir=%s\n", strAppDir.c_str()); 306 printf("dbenv.open strAppDir=%s\n", strAppDir.c_str());
106 307
107 dbenv.set_lg_dir(strLogDir.c_str()); 308 DbEnv& env = GetDbEnv();
108 dbenv.set_lg_max(10000000); 309 env.set_lg_dir(strLogDir.c_str());
109 dbenv.set_lk_max_locks(10000); 310 env.set_lg_max(10000000);
110 dbenv.set_lk_max_objects(10000); 311 env.set_lk_max_locks(10000);
111 dbenv.set_errfile(fopen("db.log", "a")); /// debug 312 env.set_lk_max_objects(10000);
112 ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption 313 pdbErrFile = fopen("db.log", "a"); /// debug
113 ret = dbenv.open(strAppDir.c_str(), 314 if (pdbErrFile)
114 DB_CREATE | 315 env.set_errfile(pdbErrFile);
115 DB_INIT_LOCK | 316 ///env.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption
116 DB_INIT_LOG | 317 ret = env.open(strAppDir.c_str(),
117 DB_INIT_MPOOL | 318 DB_CREATE |
118 DB_INIT_TXN | 319 DB_INIT_LOCK |
119 DB_THREAD | 320 DB_INIT_LOG |
120 DB_PRIVATE | 321 DB_INIT_MPOOL |
121 DB_RECOVER, 322 DB_INIT_TXN |
122 0); 323 DB_THREAD |
324 DB_PRIVATE |
325 DB_RECOVER,
326 0);
123 if (ret > 0) 327 if (ret > 0)
328 {
329 CloseDbEnv();
124 throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret)); 330 throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret));
331 }
125 fDbEnvInit = true; 332 fDbEnvInit = true;
126 333
127 // A Berkeley DB file records, in every page header, a log sequence 334 // A Berkeley DB file records, in every page header, a log sequence
@@ -151,7 +358,7 @@ CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL)
151 continue; 358 continue;
152 try 359 try
153 { 360 {
154 if (dbenv.lsn_reset(pszName, 0) == 0) 361 if (env.lsn_reset(pszName, 0) == 0)
155 printf("CDB() : adopted %s from another environment " 362 printf("CDB() : adopted %s from another environment "
156 "(log sequence numbers reset)\n", pszName); 363 "(log sequence numbers reset)\n", pszName);
157 } 364 }
@@ -164,7 +371,7 @@ CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL)
164 ++mapFileUseCount[strFile]; 371 ++mapFileUseCount[strFile];
165 } 372 }
166 373
167 pdb = new Db(&dbenv, 0); 374 pdb = new Db(&GetDbEnv(), 0);
168 375
169 ret = pdb->open(NULL, // Txn pointer 376 ret = pdb->open(NULL, // Txn pointer
170 pszFile, // Filename 377 pszFile, // Filename
@@ -199,7 +406,8 @@ void CDB::Close()
199 try { pdb->close(0); } catch (...) { } 406 try { pdb->close(0); } catch (...) { }
200 delete pdb; 407 delete pdb;
201 pdb = NULL; 408 pdb = NULL;
202 try { dbenv.txn_checkpoint(0, 0, 0); } catch (...) { } 409 if (fDbEnvInit)
410 try { GetDbEnv().txn_checkpoint(0, 0, 0); } catch (...) { }
203 411
204 CRITICAL_BLOCK(cs_db) 412 CRITICAL_BLOCK(cs_db)
205 --mapFileUseCount[strFile]; 413 --mapFileUseCount[strFile];
@@ -279,7 +487,8 @@ void DBFlush(bool fShutdown)
279 // takes the process down before the wallet has been put down cleanly, 487 // takes the process down before the wallet has been put down cleanly,
280 // which is the difference between a portable wallet.dat and one welded 488 // which is the difference between a portable wallet.dat and one welded
281 // to this directory -- the lsn_reset just below is what frees it. 489 // to this directory -- the lsn_reset just below is what frees it.
282 try { dbenv.txn_checkpoint(0, 0, 0); } 490 DbEnv& env = GetDbEnv();
491 try { env.txn_checkpoint(0, 0, 0); }
283 catch (const std::exception& e) 492 catch (const std::exception& e)
284 { printf("DBFlush() : checkpoint failed: %s\n", e.what()); } 493 { printf("DBFlush() : checkpoint failed: %s\n", e.what()); }
285 494
@@ -290,7 +499,7 @@ void DBFlush(bool fShutdown)
290 int nRefCount = (*mi).second; 499 int nRefCount = (*mi).second;
291 if (nRefCount == 0) 500 if (nRefCount == 0)
292 { 501 {
293 try { dbenv.lsn_reset(strFile.c_str(), 0); } 502 try { env.lsn_reset(strFile.c_str(), 0); }
294 catch (const std::exception& e) 503 catch (const std::exception& e)
295 { printf("DBFlush() : lsn_reset(%s) failed: %s\n", strFile.c_str(), e.what()); } 504 { printf("DBFlush() : lsn_reset(%s) failed: %s\n", strFile.c_str(), e.what()); }
296 mapFileUseCount.erase(mi++); 505 mapFileUseCount.erase(mi++);
@@ -310,9 +519,8 @@ void DBFlush(bool fShutdown)
310 // Found while making wallet encryption clean up after itself: 519 // Found while making wallet encryption clean up after itself:
311 // calling DBFlush(true) a second time crashed here every time. 520 // calling DBFlush(true) a second time crashed here every time.
312 if (mapFileUseCount.empty()) 521 if (mapFileUseCount.empty())
313 try { dbenv.log_archive(NULL, DB_ARCH_REMOVE); } catch (...) { } 522 try { env.log_archive(NULL, DB_ARCH_REMOVE); } catch (...) { }
314 try { dbenv.close(0); } catch (...) { } 523 CloseDbEnv();
315 fDbEnvInit = false;
316 } 524 }
317 } 525 }
318} 526}
@@ -614,6 +822,294 @@ bool CTxDB::LoadBlockIndex()
614// CWalletDB 822// CWalletDB
615// 823//
616 824
825CWalletDB::CWalletDB(const char* pszMode, bool fTxn) :
826 CDB(WalletSQLiteRuntimeActive() ? NULL : "wallet.dat", pszMode, fTxn)
827{
828}
829
830class CSQLiteWalletRuntimeLoadVisitor : public CWalletRecordVisitor
831{
832public:
833 bool fHaveStoredMineMode;
834 bool fHaveStoredHDCoinType;
835 string strLastType;
836
837 CSQLiteWalletRuntimeLoadVisitor() :
838 fHaveStoredMineMode(false),
839 fHaveStoredHDCoinType(false)
840 {
841 }
842
843 bool VisitWalletRecord(const CDataStream& ssKeyIn,
844 const CDataStream& ssValueIn,
845 string& strErrorRet)
846 {
847 CDataStream ssKey = ssKeyIn;
848 CDataStream ssValue = ssValueIn;
849 string strType;
850 ssKey >> strType;
851 strLastType = strType;
852
853 if (strType == "name")
854 {
855 string strAddress;
856 ssKey >> strAddress;
857 ssValue >> mapAddressBook[strAddress];
858 }
859 else if (strType == "tx")
860 {
861 uint256 hash;
862 ssKey >> hash;
863 CWalletTx& wtx = mapWallet[hash];
864 ssValue >> wtx;
865
866 if (wtx.GetHash() != hash)
867 printf("Error in SQLite wallet export, hash mismatch\n");
868 }
869 else if (strType == "key")
870 {
871 vector<unsigned char> vchPubKey;
872 ssKey >> vchPubKey;
873 CPrivKey vchPrivKey;
874 ssValue >> vchPrivKey;
875
876 mapKeys[vchPubKey] = vchPrivKey;
877 mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
878 }
879 else if (strType == "mkey")
880 {
881 unsigned int nID = 0;
882 ssKey >> nID;
883 CWalletMasterKey kMasterKey;
884 ssValue >> kMasterKey;
885 mapMasterKeys[nID] = kMasterKey;
886 nWalletMasterKeyMaxID = max(nWalletMasterKeyMaxID, nID);
887 fWalletEncrypted = true;
888 fWalletLocked = true;
889 }
890 else if (strType == "ckey")
891 {
892 vector<unsigned char> vchPubKey;
893 ssKey >> vchPubKey;
894 vector<unsigned char> vchCryptedSecret;
895 ssValue >> vchCryptedSecret;
896 if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
897 {
898 strErrorRet = "SQLite wallet export has an unreadable encrypted key record";
899 return false;
900 }
901 }
902 else if (strType == "defaultkey")
903 {
904 ssValue >> vchDefaultKey;
905 }
906 else if (strType == "hdmaster")
907 {
908 ssValue >> vchHDMaster;
909 }
910 else if (strType == "hdchaincode")
911 {
912 ssValue >> vchHDChainCode;
913 }
914 else if (strType == "hdnext")
915 {
916 ssValue >> nHDNext;
917 }
918 else if (strType == "hdschema")
919 {
920 ssValue >> nHDKeySchema;
921 }
922 else if (strType == "hdcointype")
923 {
924 ssValue >> nHDCoinType;
925 fHaveStoredHDCoinType = true;
926 }
927 else if (strType == "hdreceivenext")
928 {
929 ssValue >> nHDReceiveNext;
930 }
931 else if (strType == "hdchangenext")
932 {
933 ssValue >> nHDChangeNext;
934 }
935 else if (strType == "cryptedhdmaster")
936 {
937 ssValue >> vchCryptedHDMaster;
938 fWalletEncrypted = true;
939 fWalletLocked = true;
940 }
941 else if (strType == "cryptedhdchaincode")
942 {
943 ssValue >> vchCryptedHDChainCode;
944 fWalletEncrypted = true;
945 fWalletLocked = true;
946 }
947 else if (strType == "walletminversion")
948 {
949 int nMinVersion = 0;
950 ssValue >> nMinVersion;
951 if (nMinVersion > WALLET_FORMAT_SUPPORTED)
952 {
953 strWalletLoadError = strprintf(
954 "This SQLite wallet export was written by a newer version of "
955 "Bitflash (wallet format %d; this build understands %d). "
956 "Upgrade Bitflash before opening it.",
957 nMinVersion, WALLET_FORMAT_SUPPORTED);
958 strErrorRet = strWalletLoadError;
959 return false;
960 }
961 }
962 else if (strType == "pool")
963 {
964 int64 nIndex;
965 ssKey >> nIndex;
966 vector<unsigned char> vchPubKey;
967 ssValue >> vchPubKey;
968 mapKeyPool[nIndex] = vchPubKey;
969 }
970 else if (strType == "setting")
971 {
972 string strKey;
973 ssKey >> strKey;
974 if (strKey == "nTransactionFee") ssValue >> nTransactionFee;
975 if (strKey == "addrIncoming") ssValue >> addrIncoming;
976
977 if (!fMineModeFromCommandLine)
978 {
979 if (strKey == "nMineMode") { ssValue >> nMineMode; fHaveStoredMineMode = true; }
980 if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins;
981 if (strKey == "strParticipantPool") ssValue >> strParticipantPool;
982 }
983 }
984
985 if (ssKey.fail())
986 {
987 strErrorRet = strprintf("malformed SQLite wallet record key while handling '%s'",
988 strLastType.c_str());
989 return false;
990 }
991 if (ssValue.fail())
992 {
993 strErrorRet = strprintf("malformed SQLite wallet record value while handling '%s'",
994 strLastType.c_str());
995 return false;
996 }
997 return true;
998 }
999
1000 vector<unsigned char> vchDefaultKey;
1001};
1002
1003static bool FinishLoadedWallet(const vector<unsigned char>& vchDefaultKey,
1004 bool fMayCreateDefaultKey)
1005{
1006 if (!vchDefaultKey.empty() && mapPubKeys.count(Hash160(vchDefaultKey)))
1007 {
1008 keyUser.SetPubKey(vchDefaultKey);
1009 if (mapKeys.count(vchDefaultKey))
1010 keyUser.SetPrivKey(mapKeys[vchDefaultKey]);
1011 }
1012 else if (IsWalletEncrypted())
1013 {
1014 printf("LoadWallet: encrypted wallet has no usable default public key. "
1015 "The file was left untouched.\n");
1016 return false;
1017 }
1018 else
1019 {
1020 if (!fMayCreateDefaultKey)
1021 {
1022 strWalletLoadError =
1023 "SQLite wallet export has no usable default public key";
1024 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
1025 return false;
1026 }
1027
1028 keyUser.MakeNewKey();
1029 if (!AddKey(keyUser))
1030 return false;
1031 if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address"))
1032 return false;
1033 CWalletDB().WriteDefaultKey(keyUser.GetPubKey());
1034 }
1035
1036 return true;
1037}
1038
1039static bool ReconcileLoadedWalletMetadata(bool fHaveStoredMineMode,
1040 bool fHaveStoredHDCoinType)
1041{
1042 // An encrypted wallet has a seed; it just has not been decrypted yet.
1043 //
1044 // The reset in the else branch was written when "no seed in memory after
1045 // loading" could only mean "this wallet has no seed". With encryption that
1046 // is no longer true, and taking the else branch here wiped the schema and
1047 // the derivation counters that had just been read correctly out of the
1048 // file. Unlocking later restores the seed but nothing restores those, so a
1049 // BIP44 wallet came back deriving legacy addresses -- different addresses
1050 // from the ones its own recovery phrase produces, while the audit reported
1051 // the phrase as covering nothing.
1052 if (HaveHDSeed() || !vchCryptedHDMaster.empty())
1053 {
1054 if (nHDKeySchema == HD_SCHEMA_NONE)
1055 {
1056 nHDKeySchema = HD_SCHEMA_LEGACY;
1057 printf("LoadWallet: deterministic seed has no schema metadata; "
1058 "treating it as legacy m/index'\n");
1059 }
1060 else if (nHDKeySchema != HD_SCHEMA_LEGACY &&
1061 nHDKeySchema != HD_SCHEMA_BIP44)
1062 {
1063 printf("LoadWallet: deterministic wallet schema %d (%s) is not "
1064 "supported by this build\n",
1065 nHDKeySchema, HDKeySchemaName(nHDKeySchema).c_str());
1066 return false;
1067 }
1068 if (!fHaveStoredHDCoinType)
1069 {
1070 nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH;
1071 printf("LoadWallet: deterministic seed has no BIP44 coin type metadata; "
1072 "using Bitflash coin type %u\n", nHDCoinType);
1073 }
1074 }
1075 else
1076 {
1077 nHDKeySchema = HD_SCHEMA_NONE;
1078 nHDNext = 0;
1079 nHDReceiveNext = 0;
1080 nHDChangeNext = 0;
1081 nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH;
1082 }
1083
1084 // fGenerateBitcoins and nMineMode only mean anything together, and a
1085 // wallet.dat can easily hold one without the other: Bitcoin 0.1.0 already
1086 // wrote fGenerateBitcoins when mining was toggled, years before this fork
1087 // had a mode at all. Restoring that lone flag put a node into the one state
1088 // the miner cannot act on -- generate on, mode relay -- so every miner
1089 // thread started and returned at the relay guard, and the machine sat there
1090 // looking like it was mining while hashing nothing. That happened to a
1091 // 32-core node on the first 1.2.11 start.
1092 //
1093 // So the flag is only believed when the mode it belongs to was stored with
1094 // it, and the pair is made coherent either way.
1095 if (!fMineModeFromCommandLine)
1096 {
1097 if (!fHaveStoredMineMode)
1098 {
1099 if (fGenerateBitcoins)
1100 printf("LoadWallet: ignoring a stored fGenerateBitcoins with no mining mode "
1101 "beside it -- it predates this setting and cannot be read on its own\n");
1102 fGenerateBitcoins = 0;
1103 }
1104 else
1105 {
1106 fGenerateBitcoins = (nMineMode != MINE_RELAY) ? 1 : 0;
1107 }
1108 }
1109
1110 return true;
1111}
1112
617bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet) 1113bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
618{ 1114{
619 // Whether wallet.dat actually carried a mining mode, as opposed to just the 1115 // Whether wallet.dat actually carried a mining mode, as opposed to just the
@@ -924,34 +1420,269 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
924 return true; 1420 return true;
925} 1421}
926 1422
1423bool ScanWalletRecords(CWalletRecordVisitor& visitor, string& strErrorRet)
1424{
1425 // Under the SQLite backend there is no Berkeley DB cursor to walk; scan the
1426 // active SQLite store instead. This is the single choke point every "read
1427 // every wallet record" caller goes through (storage audit and check among
1428 // them), so routing it here makes those honour -walletbackend=sqlite.
1429 if (WalletSQLiteRuntimeActive())
1430 {
1431 strErrorRet.clear();
1432 return pWalletSQLiteRuntime->ScanRecords(visitor, strErrorRet);
1433 }
1434
1435 class CWalletScanDB : public CWalletDB
1436 {
1437 public:
1438 CWalletScanDB() : CWalletDB("r") { }
1439 using CDB::GetCursor;
1440 using CDB::ReadAtCursor;
1441 };
1442
1443 strErrorRet.clear();
1444 CWalletScanDB dbWallet;
1445 Dbc* pcursor = dbWallet.GetCursor();
1446 if (!pcursor)
1447 {
1448 strErrorRet = "could not open a wallet cursor";
1449 return false;
1450 }
1451 CAutoCursor cursorGuard(pcursor);
1452
1453 loop
1454 {
1455 CDataStream ssKey(SER_DISK);
1456 CDataStream ssValue(SER_DISK);
1457 int ret = dbWallet.ReadAtCursor(pcursor, ssKey, ssValue);
1458 if (ret == DB_NOTFOUND)
1459 break;
1460 if (ret != 0)
1461 {
1462 strErrorRet = strprintf("could not read wallet record: Berkeley DB error %d", ret);
1463 return false;
1464 }
1465 if (!visitor.VisitWalletRecord(ssKey, ssValue, strErrorRet))
1466 return false;
1467 }
1468
1469 return true;
1470}
1471
927bool LoadWallet() 1472bool LoadWallet()
928{ 1473{
929 vector<unsigned char> vchDefaultKey; 1474 vector<unsigned char> vchDefaultKey;
930 if (!CWalletDB("cr").LoadWallet(vchDefaultKey)) 1475 if (!CWalletDB("cr").LoadWallet(vchDefaultKey))
931 return false; 1476 return false;
932 1477
933 if (!vchDefaultKey.empty() && mapPubKeys.count(Hash160(vchDefaultKey))) 1478 return FinishLoadedWallet(vchDefaultKey, true);
1479}
1480
1481bool LoadWalletFromSQLite(const string& strPath)
1482{
1483 strWalletLoadError.clear();
1484
1485 if (!FileExists(strPath.c_str()))
934 { 1486 {
935 // Set keyUser 1487 strWalletLoadError = strprintf("SQLite wallet export does not exist: %s",
936 keyUser.SetPubKey(vchDefaultKey); 1488 strPath.c_str());
937 if (mapKeys.count(vchDefaultKey)) 1489 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
938 keyUser.SetPrivKey(mapKeys[vchDefaultKey]); 1490 return false;
939 } 1491 }
940 else if (IsWalletEncrypted()) 1492
1493 CWalletDBSQLite sqlite;
1494 string strError;
1495 if (!sqlite.OpenReadOnly(strPath, strError))
941 { 1496 {
942 printf("LoadWallet: encrypted wallet has no usable default public key. " 1497 strWalletLoadError = strError;
943 "The file was left untouched.\n"); 1498 printf("LoadWallet: cannot open SQLite wallet export: %s\n",
1499 strError.c_str());
944 return false; 1500 return false;
945 } 1501 }
946 else 1502
1503 CSQLiteWalletRuntimeLoadVisitor visitor;
1504 try
947 { 1505 {
948 // Create new keyUser and set as default key 1506 CRITICAL_BLOCK(cs_mapKeys)
949 keyUser.MakeNewKey(); 1507 CRITICAL_BLOCK(cs_mapWallet)
950 if (!AddKey(keyUser)) 1508 {
951 return false; 1509 if (!sqlite.ScanRecords(visitor, strError))
952 if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address")) 1510 {
953 return false; 1511 if (strWalletLoadError.empty())
954 CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); 1512 strWalletLoadError = strError;
1513 printf("LoadWallet: SQLite wallet export could not be read. "
1514 "Failed while handling a '%s' record: %s\n",
1515 visitor.strLastType.c_str(), strError.c_str());
1516 return false;
1517 }
1518 }
1519 }
1520 catch (const std::exception& e)
1521 {
1522 strWalletLoadError = strprintf(
1523 "SQLite wallet export could not be read while handling a '%s' record: %s",
1524 visitor.strLastType.c_str(), e.what());
1525 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
1526 return false;
1527 }
1528 catch (...)
1529 {
1530 strWalletLoadError = strprintf(
1531 "SQLite wallet export could not be read while handling a '%s' record",
1532 visitor.strLastType.c_str());
1533 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
1534 return false;
1535 }
1536
1537 if (!ReconcileLoadedWalletMetadata(visitor.fHaveStoredMineMode,
1538 visitor.fHaveStoredHDCoinType))
1539 {
1540 if (strWalletLoadError.empty())
1541 strWalletLoadError = "SQLite wallet metadata is not supported by this build";
1542 return false;
1543 }
1544
1545 return FinishLoadedWallet(visitor.vchDefaultKey, false);
1546}
1547
1548// Open a SQLite wallet read-write and keep it open as the active runtime
1549// backend, then load its records into memory the same way LoadWalletFromSQLite
1550// does. Unlike the read-only staging path, this leaves pWalletSQLiteRuntime set
1551// so every later CWalletDB read/write/erase routes to this file for the life of
1552// the process. The Berkeley DB wallet.dat is never opened.
1553bool LoadWalletFromSQLiteRuntime(const string& strPath)
1554{
1555 strWalletLoadError.clear();
1556
1557 if (!FileExists(strPath.c_str()))
1558 {
1559 strWalletLoadError = strprintf("SQLite wallet does not exist: %s",
1560 strPath.c_str());
1561 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
1562 return false;
1563 }
1564
1565 string strError;
1566 if (!WalletSQLiteRuntimeOpen(strPath, strError))
1567 {
1568 strWalletLoadError = strError;
1569 printf("LoadWallet: cannot open SQLite wallet backend: %s\n",
1570 strError.c_str());
1571 return false;
1572 }
1573
1574 CSQLiteWalletRuntimeLoadVisitor visitor;
1575 try
1576 {
1577 CRITICAL_BLOCK(cs_mapKeys)
1578 CRITICAL_BLOCK(cs_mapWallet)
1579 {
1580 if (!pWalletSQLiteRuntime->ScanRecords(visitor, strError))
1581 {
1582 WalletSQLiteRuntimeClose();
1583 if (strWalletLoadError.empty())
1584 strWalletLoadError = strError;
1585 printf("LoadWallet: SQLite wallet backend could not be read. "
1586 "Failed while handling a '%s' record: %s\n",
1587 visitor.strLastType.c_str(), strError.c_str());
1588 return false;
1589 }
1590 }
1591 }
1592 catch (const std::exception& e)
1593 {
1594 WalletSQLiteRuntimeClose();
1595 strWalletLoadError = strprintf(
1596 "SQLite wallet backend could not be read while handling a '%s' record: %s",
1597 visitor.strLastType.c_str(), e.what());
1598 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
1599 return false;
1600 }
1601 catch (...)
1602 {
1603 WalletSQLiteRuntimeClose();
1604 strWalletLoadError = strprintf(
1605 "SQLite wallet backend could not be read while handling a '%s' record",
1606 visitor.strLastType.c_str());
1607 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
1608 return false;
1609 }
1610
1611 // Refuse an empty or non-wallet SQLite file up front, with a message that
1612 // names the likely cause. FinishLoadedWallet below already rejects a
1613 // missing default key (it is the fMayCreateDefaultKey=false path), but its
1614 // wording -- "no usable default public key" -- reads as corruption when the
1615 // real story is usually "this file was never an exported wallet". A blank
1616 // wallet.sqlite left by a typo or a half-run export should say so.
1617 if (visitor.vchDefaultKey.empty())
1618 {
1619 WalletSQLiteRuntimeClose();
1620 strWalletLoadError =
1621 "SQLite wallet has no default key record; it may be empty or not a "
1622 "wallet export. Create it with -walletsqliteexport.";
1623 printf("LoadWallet: %s\n", strWalletLoadError.c_str());
1624 return false;
1625 }
1626
1627 if (!ReconcileLoadedWalletMetadata(visitor.fHaveStoredMineMode,
1628 visitor.fHaveStoredHDCoinType))
1629 {
1630 WalletSQLiteRuntimeClose();
1631 if (strWalletLoadError.empty())
1632 strWalletLoadError = "SQLite wallet metadata is not supported by this build";
1633 return false;
1634 }
1635
1636 // Never fabricate a default key: an export always carries one, and a file
1637 // that does not is refused with the wallet left open to no writes rather
1638 // than quietly minted into a new identity.
1639 if (!FinishLoadedWallet(visitor.vchDefaultKey, false))
1640 {
1641 WalletSQLiteRuntimeClose();
1642 return false;
1643 }
1644
1645 return true;
1646}
1647
1648// Create a brand-new wallet directly in the SQLite backend. For a fresh install
1649// where SQLite is the default there is no wallet.dat to export from, so open a
1650// new wallet.sqlite (Open() creates the file and its schema) and let
1651// FinishLoadedWallet mint the first default key straight into it -- the same
1652// call the Berkeley DB create path uses, so a new wallet is identical either
1653// way. Refuses to touch an existing file, and cleans up on failure so a broken
1654// half-initialised wallet.sqlite is never left for the next start to load.
1655bool CreateNewSQLiteWallet(const string& strPath)
1656{
1657 strWalletLoadError.clear();
1658
1659 if (FileExists(strPath.c_str()))
1660 {
1661 strWalletLoadError = strprintf("SQLite wallet already exists: %s",
1662 strPath.c_str());
1663 printf("CreateNewSQLiteWallet: %s\n", strWalletLoadError.c_str());
1664 return false;
1665 }
1666
1667 string strError;
1668 if (!WalletSQLiteRuntimeOpen(strPath, strError, false))
1669 {
1670 strWalletLoadError = strError;
1671 printf("CreateNewSQLiteWallet: cannot create SQLite wallet: %s\n",
1672 strError.c_str());
1673 return false;
1674 }
1675
1676 vector<unsigned char> vchNoDefaultKey;
1677 if (!FinishLoadedWallet(vchNoDefaultKey, true))
1678 {
1679 WalletSQLiteRuntimeClose();
1680 remove(strPath.c_str());
1681 remove((strPath + "-wal").c_str());
1682 remove((strPath + "-shm").c_str());
1683 if (strWalletLoadError.empty())
1684 strWalletLoadError = "could not initialise the new SQLite wallet";
1685 return false;
955 } 1686 }
956 1687
957 return true; 1688 return true;
@@ -974,21 +1705,99 @@ bool LoadWallet()
974// this file was written are not spendable from it. Backing up once is not 1705// this file was written are not spendable from it. Backing up once is not
975// enough, and that is a property of the wallet format rather than of this 1706// enough, and that is a property of the wallet format rather than of this
976// function. 1707// function.
1708// Back up the active SQLite wallet. The store is checkpointed so the WAL is
1709// folded into the main file, then the single self-contained wallet.sqlite is
1710// copied through a temp file and renamed into place (same temp/old/rename dance
1711// as the Berkeley DB path, for Windows' non-overwriting rename). No lsn_reset:
1712// a checkpointed SQLite file needs nothing beside it to open.
1713static bool BackupWalletSQLite(const string& strDest)
1714{
1715 string strSrc = GetAppDir() + "/wallet.sqlite";
1716 if (!FileExists(strSrc.c_str()))
1717 return error("BackupWallet() : %s does not exist\n", strSrc.c_str());
1718
1719 string strCheckpointError;
1720 if (!WalletSQLiteRuntimeCheckpoint(strCheckpointError))
1721 return error("BackupWallet() : checkpoint failed: %s\n", strCheckpointError.c_str());
1722
1723 string strTemp = strprintf("%s.tmp.%lld", strDest.c_str(), GetTime());
1724 for (int i = 1; FileExists(strTemp.c_str()); i++)
1725 strTemp = strprintf("%s.tmp.%lld.%d", strDest.c_str(), GetTime(), i);
1726 string strOld = strprintf("%s.old.%lld", strDest.c_str(), GetTime());
1727 for (int i = 1; FileExists(strOld.c_str()); i++)
1728 strOld = strprintf("%s.old.%lld.%d", strDest.c_str(), GetTime(), i);
1729
1730 FILE* pfIn = fopen(strSrc.c_str(), "rb");
1731 if (!pfIn)
1732 return error("BackupWallet() : cannot read %s\n", strSrc.c_str());
1733 FILE* pfOut = fopen(strTemp.c_str(), "wb");
1734 if (!pfOut)
1735 {
1736 fclose(pfIn);
1737 return error("BackupWallet() : cannot write %s\n", strTemp.c_str());
1738 }
1739
1740 char buf[65536];
1741 size_t n;
1742 bool fOk = true;
1743 while ((n = fread(buf, 1, sizeof(buf), pfIn)) > 0)
1744 if (fwrite(buf, 1, n, pfOut) != n) { fOk = false; break; }
1745 if (ferror(pfIn))
1746 fOk = false;
1747 fclose(pfIn);
1748 if (fclose(pfOut) != 0)
1749 fOk = false;
1750 if (!fOk)
1751 {
1752 remove(strTemp.c_str()); // half a wallet is worse than none
1753 return error("BackupWallet() : copy to %s failed\n", strTemp.c_str());
1754 }
1755
1756 bool fHadOldDest = FileExists(strDest.c_str());
1757 if (fHadOldDest && rename(strDest.c_str(), strOld.c_str()) != 0)
1758 {
1759 remove(strTemp.c_str());
1760 return error("BackupWallet() : could not move old %s aside\n", strDest.c_str());
1761 }
1762 if (rename(strTemp.c_str(), strDest.c_str()) != 0)
1763 {
1764 remove(strTemp.c_str());
1765 if (fHadOldDest)
1766 rename(strOld.c_str(), strDest.c_str());
1767 return error("BackupWallet() : could not install %s\n", strDest.c_str());
1768 }
1769 if (fHadOldDest)
1770 remove(strOld.c_str());
1771 return true;
1772}
1773
977bool BackupWallet(const string& strDest) 1774bool BackupWallet(const string& strDest)
978{ 1775{
979 if (strDest.empty()) 1776 if (strDest.empty())
980 return error("BackupWallet() : no destination given\n"); 1777 return error("BackupWallet() : no destination given\n");
981 1778
1779 // Under the SQLite backend the live wallet is wallet.sqlite, not wallet.dat.
1780 if (WalletSQLiteRuntimeActive())
1781 return BackupWalletSQLite(strDest);
1782
982 string strSrc = GetAppDir() + "/wallet.dat"; 1783 string strSrc = GetAppDir() + "/wallet.dat";
983 if (!FileExists(strSrc.c_str())) 1784 if (!FileExists(strSrc.c_str()))
984 return error("BackupWallet() : %s does not exist\n", strSrc.c_str()); 1785 return error("BackupWallet() : %s does not exist\n", strSrc.c_str());
985 1786
1787 string strTemp = strprintf("%s.tmp.%lld", strDest.c_str(), GetTime());
1788 for (int i = 1; FileExists(strTemp.c_str()); i++)
1789 strTemp = strprintf("%s.tmp.%lld.%d", strDest.c_str(), GetTime(), i);
1790 string strOld = strprintf("%s.old.%lld", strDest.c_str(), GetTime());
1791 for (int i = 1; FileExists(strOld.c_str()); i++)
1792 strOld = strprintf("%s.old.%lld.%d", strDest.c_str(), GetTime(), i);
1793
986 CRITICAL_BLOCK(cs_db) 1794 CRITICAL_BLOCK(cs_db)
987 { 1795 {
988 // Committing first, so the copy is not missing the newest records. 1796 // Committing first, so the copy is not missing the newest records.
989 // Both calls can throw, and a failed backup must not take the node 1797 // Both calls can throw, and a failed backup must not take the node
990 // down with it. 1798 // down with it.
991 try { dbenv.txn_checkpoint(0, 0, 0); } 1799 DbEnv& env = GetDbEnv();
1800 try { env.txn_checkpoint(0, 0, 0); }
992 catch (const std::exception& e) 1801 catch (const std::exception& e)
993 { return error("BackupWallet() : checkpoint failed: %s\n", e.what()); } 1802 { return error("BackupWallet() : checkpoint failed: %s\n", e.what()); }
994 1803
@@ -997,11 +1806,11 @@ bool BackupWallet(const string& strDest)
997 FILE* pfIn = fopen(strSrc.c_str(), "rb"); 1806 FILE* pfIn = fopen(strSrc.c_str(), "rb");
998 if (!pfIn) 1807 if (!pfIn)
999 return error("BackupWallet() : cannot read %s\n", strSrc.c_str()); 1808 return error("BackupWallet() : cannot read %s\n", strSrc.c_str());
1000 FILE* pfOut = fopen(strDest.c_str(), "wb"); 1809 FILE* pfOut = fopen(strTemp.c_str(), "wb");
1001 if (!pfOut) 1810 if (!pfOut)
1002 { 1811 {
1003 fclose(pfIn); 1812 fclose(pfIn);
1004 return error("BackupWallet() : cannot write %s\n", strDest.c_str()); 1813 return error("BackupWallet() : cannot write %s\n", strTemp.c_str());
1005 } 1814 }
1006 1815
1007 char buf[65536]; 1816 char buf[65536];
@@ -1018,18 +1827,39 @@ bool BackupWallet(const string& strDest)
1018 1827
1019 if (!fOk) 1828 if (!fOk)
1020 { 1829 {
1021 remove(strDest.c_str()); // half a wallet is worse than none 1830 remove(strTemp.c_str()); // half a wallet is worse than none
1022 return error("BackupWallet() : copy to %s failed\n", strDest.c_str()); 1831 return error("BackupWallet() : copy to %s failed\n", strTemp.c_str());
1023 } 1832 }
1024 1833
1025 // Without this the copy is welded to this node's database/ dir. 1834 // Without this the copy is welded to this node's database/ dir.
1026 int ret = dbenv.lsn_reset(strDest.c_str(), 0); 1835 int ret = env.lsn_reset(strTemp.c_str(), 0);
1027 if (ret != 0) 1836 if (ret != 0)
1028 printf("BackupWallet() : warning -- lsn_reset returned %d; the copy " 1837 printf("BackupWallet() : warning -- lsn_reset returned %d; the copy "
1029 "may only open beside this node's database/ directory\n", ret); 1838 "may only open beside this node's database/ directory\n", ret);
1839
1840 bool fHadOldDest = FileExists(strDest.c_str());
1841 if (fHadOldDest && rename(strDest.c_str(), strOld.c_str()) != 0)
1842 {
1843 remove(strTemp.c_str());
1844 return error("BackupWallet() : could not move old %s aside\n", strDest.c_str());
1845 }
1846 if (rename(strTemp.c_str(), strDest.c_str()) != 0)
1847 {
1848 remove(strTemp.c_str());
1849 if (fHadOldDest)
1850 rename(strOld.c_str(), strDest.c_str());
1851 return error("BackupWallet() : could not install %s\n", strDest.c_str());
1852 }
1853 if (fHadOldDest)
1854 remove(strOld.c_str());
1030 } 1855 }
1031 catch (const std::exception& e) 1856 catch (const std::exception& e)
1032 { return error("BackupWallet() : %s\n", e.what()); } 1857 {
1858 remove(strTemp.c_str());
1859 if (FileExists(strOld.c_str()) && !FileExists(strDest.c_str()))
1860 rename(strOld.c_str(), strDest.c_str());
1861 return error("BackupWallet() : %s\n", e.what());
1862 }
1033 } 1863 }
1034 1864
1035 // Recorded here rather than by whoever called, so that a backup taken with 1865 // Recorded here rather than by whoever called, so that a backup taken with
@@ -1307,73 +2137,71 @@ public:
1307 { 2137 {
1308 return Write(make_pair(string("pool"), nIndex), vchPubKey); 2138 return Write(make_pair(string("pool"), nIndex), vchPubKey);
1309 } 2139 }
2140};
2141
2142static bool IsWalletEncryptionPrivateRecord(const string& strType)
2143{
2144 return strType == "key" ||
2145 strType == "pool" ||
2146 strType == "hdmaster" ||
2147 strType == "hdchaincode" ||
2148 strType == "mkey" ||
2149 strType == "ckey" ||
2150 strType == "cryptedhdmaster" ||
2151 strType == "cryptedhdchaincode" ||
2152 strType == "walletminversion";
2153}
2154
2155class CWalletPublicRecordCopyVisitor : public CWalletRecordVisitor
2156{
2157public:
2158 CWalletRewriteDB& dbTo;
2159 int& nCopied;
1310 2160
1311 bool CopyPublicRecordsTo(CWalletRewriteDB& dbTo, int& nCopiedRet, string& strErrorRet) 2161 CWalletPublicRecordCopyVisitor(CWalletRewriteDB& dbToIn, int& nCopiedIn)
2162 : dbTo(dbToIn), nCopied(nCopiedIn) { }
2163
2164 bool VisitWalletRecord(const CDataStream& ssKey,
2165 const CDataStream& ssValue,
2166 string& strErrorRet)
1312 { 2167 {
1313 nCopiedRet = 0; 2168 string strType;
1314 Dbc* pcursor = GetCursor(); 2169 try
1315 if (!pcursor)
1316 { 2170 {
1317 strErrorRet = "could not open a wallet cursor"; 2171 CDataStream ssType = ssKey;
1318 return false; 2172 ssType >> strType;
1319 } 2173 }
1320 2174 catch (...)
1321 unsigned int fFlags = DB_NEXT;
1322 loop
1323 { 2175 {
1324 CDataStream ssKey; 2176 strErrorRet = "wallet.dat has a record whose key type cannot be read";
1325 CDataStream ssValue; 2177 return false;
1326 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); 2178 }
1327 if (ret == DB_NOTFOUND)
1328 break;
1329 if (ret != 0)
1330 {
1331 pcursor->close();
1332 strErrorRet = strprintf("could not read wallet record: Berkeley DB error %d", ret);
1333 return false;
1334 }
1335
1336 string strType;
1337 try
1338 {
1339 CDataStream ssType = ssKey;
1340 ssType >> strType;
1341 }
1342 catch (...)
1343 {
1344 pcursor->close();
1345 strErrorRet = "wallet.dat has a record whose key type cannot be read";
1346 return false;
1347 }
1348 2179
1349 if (strType == "key" || 2180 if (IsWalletEncryptionPrivateRecord(strType))
1350 strType == "pool" || 2181 return true;
1351 strType == "hdmaster" ||
1352 strType == "hdchaincode" ||
1353 strType == "mkey" ||
1354 strType == "ckey" ||
1355 strType == "cryptedhdmaster" ||
1356 strType == "cryptedhdchaincode" ||
1357 strType == "walletminversion")
1358 continue;
1359 2182
1360 if (!dbTo.WriteRaw(ssKey, ssValue)) 2183 if (!dbTo.WriteRaw(ssKey, ssValue))
1361 { 2184 {
1362 pcursor->close(); 2185 strErrorRet = strprintf("could not copy wallet record '%s'", strType.c_str());
1363 strErrorRet = strprintf("could not copy wallet record '%s'", strType.c_str()); 2186 return false;
1364 return false;
1365 }
1366 nCopiedRet++;
1367 } 2187 }
1368 2188 nCopied++;
1369 pcursor->close();
1370 return true; 2189 return true;
1371 } 2190 }
1372}; 2191};
1373 2192
1374static string WalletEncryptBackupPath() 2193static bool CopyPublicWalletRecordsTo(CWalletRewriteDB& dbTo,
2194 int& nCopiedRet,
2195 string& strErrorRet)
1375{ 2196{
1376 string strBase = GetAppDir() + "/wallet.dat.before-encrypt."; 2197 nCopiedRet = 0;
2198 CWalletPublicRecordCopyVisitor visitor(dbTo, nCopiedRet);
2199 return ScanWalletRecords(visitor, strErrorRet);
2200}
2201
2202static string WalletEncryptBackupPathFor(const char* pszBaseName)
2203{
2204 string strBase = GetAppDir() + "/" + pszBaseName + ".before-encrypt.";
1377 string strPath = strBase + i64tostr(GetTime()) + ".bak"; 2205 string strPath = strBase + i64tostr(GetTime()) + ".bak";
1378 for (int i = 1; FileExists(strPath.c_str()); i++) 2206 for (int i = 1; FileExists(strPath.c_str()); i++)
1379 strPath = strBase + i64tostr(GetTime()) + "." + itostr(i) + ".bak"; 2207 strPath = strBase + i64tostr(GetTime()) + "." + itostr(i) + ".bak";
@@ -1403,6 +2231,192 @@ static void SecureClearBytes(vector<unsigned char>& vch)
1403 vch.clear(); 2231 vch.clear();
1404} 2232}
1405 2233
2234// The SQLite counterpart of CWalletPublicRecordCopyVisitor: same skip list,
2235// same byte-for-byte copy, but the destination is a CWalletDBSQLite record
2236// store rather than a Berkeley DB file.
2237class CWalletPublicRecordCopyToSQLiteVisitor : public CWalletRecordVisitor
2238{
2239public:
2240 CWalletDBSQLite& dbTo;
2241 int& nCopied;
2242
2243 CWalletPublicRecordCopyToSQLiteVisitor(CWalletDBSQLite& dbToIn, int& nCopiedIn)
2244 : dbTo(dbToIn), nCopied(nCopiedIn) { }
2245
2246 bool VisitWalletRecord(const CDataStream& ssKey,
2247 const CDataStream& ssValue,
2248 string& strErrorRet)
2249 {
2250 string strType;
2251 try
2252 {
2253 CDataStream ssType = ssKey;
2254 ssType >> strType;
2255 }
2256 catch (...)
2257 {
2258 strErrorRet = "SQLite wallet has a record whose key type cannot be read";
2259 return false;
2260 }
2261
2262 if (IsWalletEncryptionPrivateRecord(strType))
2263 return true;
2264
2265 vector<unsigned char> vchKey(ssKey.begin(), ssKey.end());
2266 vector<unsigned char> vchValue(ssValue.begin(), ssValue.end());
2267 bool fOk = dbTo.WriteRecord(vchKey, vchValue, strErrorRet, true);
2268 if (!vchKey.empty())
2269 memset(&vchKey[0], 0, vchKey.size());
2270 if (!vchValue.empty())
2271 memset(&vchValue[0], 0, vchValue.size());
2272 if (!fOk)
2273 {
2274 strErrorRet = strprintf("could not copy wallet record '%s': %s",
2275 strType.c_str(), strErrorRet.c_str());
2276 return false;
2277 }
2278 nCopied++;
2279 return true;
2280 }
2281};
2282
2283// Persist an encrypted wallet under the SQLite backend. Everything the caller
2284// computed -- the master key, the re-encrypted private keys, the fresh crypted
2285// key pool, the encrypted HD seed -- lands in a brand new wallet.sqlite built
2286// in one transaction, which is then swapped in for the plaintext original.
2287//
2288// A fresh file rather than an in-place rewrite for two reasons: it keeps the
2289// whole change atomic (a crash before the rename leaves the old wallet whole),
2290// and it guarantees no plaintext key survives in a freed page the way an
2291// in-place DELETE would leave behind until a VACUUM.
2292static bool EncryptWalletPersistSQLite(
2293 const vector<pair<vector<unsigned char>, vector<unsigned char> > >& vCryptedKeys,
2294 const vector<pair<vector<unsigned char>, vector<unsigned char> > >& vNewPoolKeys,
2295 const vector<pair<int64, vector<unsigned char> > >& vNewPool,
2296 const CWalletMasterKey& kMasterKey,
2297 const vector<unsigned char>& vchCryptedHDMasterNew,
2298 const vector<unsigned char>& vchCryptedHDChainCodeNew,
2299 unsigned int nHDNextNew,
2300 int& nCopiedRet,
2301 string& strBackupRet,
2302 string& strErrorRet)
2303{
2304 nCopiedRet = 0;
2305 if (!pWalletSQLiteRuntime)
2306 {
2307 strErrorRet = "SQLite wallet backend is not active";
2308 return false;
2309 }
2310
2311 string strWalletPath = GetAppDir() + "/wallet.sqlite";
2312 string strTempPath = GetAppDir() + "/" +
2313 strprintf("wallet.sqlite.encrypting.%lld", GetTime());
2314 remove(strTempPath.c_str());
2315 remove((strTempPath + "-wal").c_str());
2316 remove((strTempPath + "-shm").c_str());
2317
2318 {
2319 CWalletDBSQLite dbTmp;
2320 if (!dbTmp.Open(strTempPath, strErrorRet))
2321 {
2322 remove(strTempPath.c_str());
2323 return false;
2324 }
2325 if (!dbTmp.BeginTransaction(strErrorRet))
2326 {
2327 dbTmp.Close();
2328 remove(strTempPath.c_str());
2329 return false;
2330 }
2331
2332 // The public records come from the live SQLite store -- under this
2333 // backend ScanWalletRecords() has no Berkeley DB behind it to read.
2334 CWalletPublicRecordCopyToSQLiteVisitor visitor(dbTmp, nCopiedRet);
2335 bool fOk = pWalletSQLiteRuntime->ScanRecords(visitor, strErrorRet);
2336
2337 // Encrypted metadata, serialized byte-for-byte the way the Berkeley DB
2338 // path (CWalletRewriteDB) writes it, so a wallet encrypted under either
2339 // backend is the same set of records. ckey uses INSERT-only to match
2340 // WriteCryptedKey; the rest overwrite.
2341 fOk = fOk &&
2342 dbTmp.WriteTypedRecord(string("walletminversion"),
2343 (int)WALLET_FORMAT_ENCRYPTED, strErrorRet, true) &&
2344 dbTmp.WriteTypedRecord(make_pair(string("mkey"),
2345 (unsigned int)(nWalletMasterKeyMaxID + 1)),
2346 kMasterKey, strErrorRet, true);
2347 for (size_t i = 0; fOk && i < vCryptedKeys.size(); i++)
2348 fOk = dbTmp.WriteTypedRecord(make_pair(string("ckey"), vCryptedKeys[i].first),
2349 vCryptedKeys[i].second, strErrorRet, false);
2350 for (size_t i = 0; fOk && i < vNewPoolKeys.size(); i++)
2351 fOk = dbTmp.WriteTypedRecord(make_pair(string("ckey"), vNewPoolKeys[i].first),
2352 vNewPoolKeys[i].second, strErrorRet, false);
2353 if (fOk && !vchCryptedHDMasterNew.empty())
2354 fOk = dbTmp.WriteTypedRecord(string("cryptedhdmaster"),
2355 vchCryptedHDMasterNew, strErrorRet, true) &&
2356 dbTmp.WriteTypedRecord(string("cryptedhdchaincode"),
2357 vchCryptedHDChainCodeNew, strErrorRet, true);
2358 if (fOk)
2359 fOk = dbTmp.WriteTypedRecord(string("hdnext"), nHDNextNew, strErrorRet, true);
2360 for (size_t i = 0; fOk && i < vNewPool.size(); i++)
2361 fOk = dbTmp.WriteTypedRecord(make_pair(string("pool"), vNewPool[i].first),
2362 vNewPool[i].second, strErrorRet, true);
2363
2364 if (!fOk)
2365 {
2366 string strIgnore;
2367 dbTmp.RollbackTransaction(strIgnore);
2368 dbTmp.Close();
2369 remove(strTempPath.c_str());
2370 remove((strTempPath + "-wal").c_str());
2371 remove((strTempPath + "-shm").c_str());
2372 return false;
2373 }
2374
2375 if (!dbTmp.CommitTransaction(strErrorRet))
2376 {
2377 dbTmp.Close();
2378 remove(strTempPath.c_str());
2379 remove((strTempPath + "-wal").c_str());
2380 remove((strTempPath + "-shm").c_str());
2381 return false;
2382 }
2383 string strCheckpointError;
2384 dbTmp.Checkpoint(strCheckpointError);
2385 dbTmp.Close();
2386 }
2387
2388 // The rebuilt wallet is committed and self-contained. Close the live handle
2389 // so the file can be swapped: Windows will not rename a file that still has
2390 // an open handle, and the node exits right after this.
2391 WalletSQLiteRuntimeClose();
2392
2393 string strBackupPath = WalletEncryptBackupPathFor("wallet.sqlite");
2394 if (rename(strWalletPath.c_str(), strBackupPath.c_str()) != 0)
2395 {
2396 strErrorRet = strprintf("could not move the original SQLite wallet to %s",
2397 strBackupPath.c_str());
2398 remove(strTempPath.c_str());
2399 return false;
2400 }
2401 // The moved-aside wallet owns any leftover write-ahead log or shared-memory
2402 // sidecar; drop stale ones so they cannot shadow the new wallet.
2403 remove((strWalletPath + "-wal").c_str());
2404 remove((strWalletPath + "-shm").c_str());
2405
2406 if (rename(strTempPath.c_str(), strWalletPath.c_str()) != 0)
2407 {
2408 rename(strBackupPath.c_str(), strWalletPath.c_str());
2409 strErrorRet = "could not install the encrypted SQLite wallet; the original was restored";
2410 remove(strTempPath.c_str());
2411 return false;
2412 }
2413 remove((strTempPath + "-wal").c_str());
2414 remove((strTempPath + "-shm").c_str());
2415
2416 strBackupRet = strBackupPath;
2417 return true;
2418}
2419
1406bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& strErrorRet) 2420bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& strErrorRet)
1407{ 2421{
1408 strBackupRet.clear(); 2422 strBackupRet.clear();
@@ -1541,15 +2555,32 @@ bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& st
1541 vNewPool.push_back(make_pair((int64)i + 1, vchPubKey)); 2555 vNewPool.push_back(make_pair((int64)i + 1, vchPubKey));
1542 } 2556 }
1543 2557
2558 // Every ciphertext is computed; nothing has touched the store yet. Under
2559 // the SQLite backend, persist by rebuilding wallet.sqlite in one atomic
2560 // transaction and swapping it in. The Berkeley DB path below is unchanged.
2561 if (WalletSQLiteRuntimeActive())
2562 {
2563 int nCopiedSQLite = 0;
2564 if (!EncryptWalletPersistSQLite(vCryptedKeys, vNewPoolKeys, vNewPool,
2565 kMasterKey, vchCryptedHDMasterNew,
2566 vchCryptedHDChainCodeNew, nHDNextNew,
2567 nCopiedSQLite, strBackupRet, strErrorRet))
2568 return false;
2569 printf("EncryptWallet() : copied %d public record(s), encrypted %u key(s), "
2570 "rebuilt %d key-pool entry(s) [SQLite]\n",
2571 nCopiedSQLite, (unsigned int)(vCryptedKeys.size() + vNewPoolKeys.size()),
2572 (int)vNewPool.size());
2573 return true;
2574 }
2575
1544 string strTempFile = strprintf("wallet.encrypting.%lld.dat", GetTime()); 2576 string strTempFile = strprintf("wallet.encrypting.%lld.dat", GetTime());
1545 string strTempPath = GetAppDir() + "/" + strTempFile; 2577 string strTempPath = GetAppDir() + "/" + strTempFile;
1546 remove(strTempPath.c_str()); 2578 remove(strTempPath.c_str());
1547 2579
1548 int nCopied = 0; 2580 int nCopied = 0;
1549 { 2581 {
1550 CWalletRewriteDB dbSource("wallet.dat", "r");
1551 CWalletRewriteDB dbTarget(strTempFile.c_str(), "c+"); 2582 CWalletRewriteDB dbTarget(strTempFile.c_str(), "c+");
1552 if (!dbSource.CopyPublicRecordsTo(dbTarget, nCopied, strErrorRet)) 2583 if (!CopyPublicWalletRecordsTo(dbTarget, nCopied, strErrorRet))
1553 { 2584 {
1554 remove(strTempPath.c_str()); 2585 remove(strTempPath.c_str());
1555 return false; 2586 return false;
@@ -1609,7 +2640,7 @@ bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& st
1609 DBFlush(true); 2640 DBFlush(true);
1610 2641
1611 string strWalletPath = GetAppDir() + "/wallet.dat"; 2642 string strWalletPath = GetAppDir() + "/wallet.dat";
1612 string strBackupPath = WalletEncryptBackupPath(); 2643 string strBackupPath = WalletEncryptBackupPathFor("wallet.dat");
1613 if (rename(strWalletPath.c_str(), strBackupPath.c_str()) != 0) 2644 if (rename(strWalletPath.c_str(), strBackupPath.c_str()) != 0)
1614 { 2645 {
1615 strErrorRet = strprintf("could not move the original wallet to %s", strBackupPath.c_str()); 2646 strErrorRet = strprintf("could not move the original wallet to %s", strBackupPath.c_str());