]>
Commit | Line | Data |
---|---|---|
9eace6b1 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
78253fcb | 3 | // Distributed under the MIT software license, see the accompanying |
3a25a2b9 | 4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
9eace6b1 | 5 | |
50c72f23 | 6 | #include "wallet/walletdb.h" |
51ed9ec9 BD |
7 | |
8 | #include "base58.h" | |
da29ecbc | 9 | #include "consensus/validation.h" |
8a893c94 | 10 | #include "main.h" |
51ed9ec9 BD |
11 | #include "protocol.h" |
12 | #include "serialize.h" | |
13 | #include "sync.h" | |
ad49c256 | 14 | #include "util.h" |
611116d4 | 15 | #include "utiltime.h" |
50c72f23 | 16 | #include "wallet/wallet.h" |
51ed9ec9 | 17 | |
9eace6b1 | 18 | #include <boost/filesystem.hpp> |
51ed9ec9 | 19 | #include <boost/foreach.hpp> |
f606bb9b | 20 | #include <boost/scoped_ptr.hpp> |
ad49c256 | 21 | #include <boost/thread.hpp> |
9eace6b1 | 22 | |
f606bb9b | 23 | using namespace std; |
9eace6b1 | 24 | |
51ed9ec9 | 25 | static uint64_t nAccountingEntryNumber = 0; |
9eace6b1 | 26 | |
9eace6b1 JG |
27 | // |
28 | // CWalletDB | |
29 | // | |
30 | ||
31 | bool CWalletDB::WriteName(const string& strAddress, const string& strName) | |
32 | { | |
33 | nWalletDBUpdated++; | |
34 | return Write(make_pair(string("name"), strAddress), strName); | |
35 | } | |
36 | ||
37 | bool CWalletDB::EraseName(const string& strAddress) | |
38 | { | |
39 | // This should only be used for sending addresses, never for receiving addresses, | |
40 | // receiving addresses must always have an address book entry if they're not change return. | |
41 | nWalletDBUpdated++; | |
42 | return Erase(make_pair(string("name"), strAddress)); | |
43 | } | |
44 | ||
a41d5fe0 GA |
45 | bool CWalletDB::WritePurpose(const string& strAddress, const string& strPurpose) |
46 | { | |
47 | nWalletDBUpdated++; | |
48 | return Write(make_pair(string("purpose"), strAddress), strPurpose); | |
49 | } | |
50 | ||
51 | bool CWalletDB::ErasePurpose(const string& strPurpose) | |
52 | { | |
53 | nWalletDBUpdated++; | |
54 | return Erase(make_pair(string("purpose"), strPurpose)); | |
55 | } | |
56 | ||
51ed9ec9 BD |
57 | bool CWalletDB::WriteTx(uint256 hash, const CWalletTx& wtx) |
58 | { | |
59 | nWalletDBUpdated++; | |
60 | return Write(std::make_pair(std::string("tx"), hash), wtx); | |
61 | } | |
62 | ||
63 | bool CWalletDB::EraseTx(uint256 hash) | |
64 | { | |
65 | nWalletDBUpdated++; | |
66 | return Erase(std::make_pair(std::string("tx"), hash)); | |
67 | } | |
68 | ||
69 | bool CWalletDB::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta) | |
70 | { | |
71 | nWalletDBUpdated++; | |
72 | ||
73 | if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), | |
74 | keyMeta, false)) | |
75 | return false; | |
76 | ||
77 | // hash pubkey/privkey to accelerate wallet load | |
78 | std::vector<unsigned char> vchKey; | |
79 | vchKey.reserve(vchPubKey.size() + vchPrivKey.size()); | |
80 | vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); | |
81 | vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end()); | |
82 | ||
83 | return Write(std::make_pair(std::string("key"), vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey.begin(), vchKey.end())), false); | |
84 | } | |
85 | ||
fabba0e6 PK |
86 | bool CWalletDB::WriteCryptedKey(const CPubKey& vchPubKey, |
87 | const std::vector<unsigned char>& vchCryptedSecret, | |
51ed9ec9 BD |
88 | const CKeyMetadata &keyMeta) |
89 | { | |
90 | const bool fEraseUnencryptedKey = true; | |
91 | nWalletDBUpdated++; | |
92 | ||
93 | if (!Write(std::make_pair(std::string("keymeta"), vchPubKey), | |
94 | keyMeta)) | |
95 | return false; | |
96 | ||
97 | if (!Write(std::make_pair(std::string("ckey"), vchPubKey), vchCryptedSecret, false)) | |
98 | return false; | |
99 | if (fEraseUnencryptedKey) | |
100 | { | |
101 | Erase(std::make_pair(std::string("key"), vchPubKey)); | |
102 | Erase(std::make_pair(std::string("wkey"), vchPubKey)); | |
103 | } | |
104 | return true; | |
105 | } | |
106 | ||
107 | bool CWalletDB::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey) | |
108 | { | |
109 | nWalletDBUpdated++; | |
110 | return Write(std::make_pair(std::string("mkey"), nID), kMasterKey, true); | |
111 | } | |
112 | ||
c1c45943 S |
113 | bool CWalletDB::WriteZKey(const libzcash::PaymentAddress& addr, const libzcash::SpendingKey& key, const CKeyMetadata &keyMeta) |
114 | { | |
115 | nWalletDBUpdated++; | |
116 | ||
117 | if (!Write(std::make_pair(std::string("zkeymeta"), addr), keyMeta)) | |
118 | return false; | |
119 | ||
2e0cc87a | 120 | // pair is: tuple_key("zkey", paymentaddress) --> secretkey |
c1c45943 S |
121 | return Write(std::make_pair(std::string("zkey"), addr), key, false); |
122 | } | |
123 | ||
51ed9ec9 BD |
124 | bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) |
125 | { | |
126 | nWalletDBUpdated++; | |
127 | return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); | |
128 | } | |
129 | ||
d5087d1b | 130 | bool CWalletDB::WriteWatchOnly(const CScript &dest) |
c8988460 PW |
131 | { |
132 | nWalletDBUpdated++; | |
d5087d1b | 133 | return Write(std::make_pair(std::string("watchs"), dest), '1'); |
c8988460 PW |
134 | } |
135 | ||
ccca27a7 CL |
136 | bool CWalletDB::EraseWatchOnly(const CScript &dest) |
137 | { | |
138 | nWalletDBUpdated++; | |
139 | return Erase(std::make_pair(std::string("watchs"), dest)); | |
140 | } | |
141 | ||
51ed9ec9 BD |
142 | bool CWalletDB::WriteBestBlock(const CBlockLocator& locator) |
143 | { | |
144 | nWalletDBUpdated++; | |
145 | return Write(std::string("bestblock"), locator); | |
146 | } | |
147 | ||
148 | bool CWalletDB::ReadBestBlock(CBlockLocator& locator) | |
149 | { | |
150 | return Read(std::string("bestblock"), locator); | |
151 | } | |
152 | ||
153 | bool CWalletDB::WriteOrderPosNext(int64_t nOrderPosNext) | |
154 | { | |
155 | nWalletDBUpdated++; | |
156 | return Write(std::string("orderposnext"), nOrderPosNext); | |
157 | } | |
158 | ||
159 | bool CWalletDB::WriteDefaultKey(const CPubKey& vchPubKey) | |
160 | { | |
161 | nWalletDBUpdated++; | |
162 | return Write(std::string("defaultkey"), vchPubKey); | |
163 | } | |
164 | ||
165 | bool CWalletDB::ReadPool(int64_t nPool, CKeyPool& keypool) | |
166 | { | |
167 | return Read(std::make_pair(std::string("pool"), nPool), keypool); | |
168 | } | |
169 | ||
170 | bool CWalletDB::WritePool(int64_t nPool, const CKeyPool& keypool) | |
171 | { | |
172 | nWalletDBUpdated++; | |
173 | return Write(std::make_pair(std::string("pool"), nPool), keypool); | |
174 | } | |
175 | ||
176 | bool CWalletDB::ErasePool(int64_t nPool) | |
177 | { | |
178 | nWalletDBUpdated++; | |
179 | return Erase(std::make_pair(std::string("pool"), nPool)); | |
180 | } | |
181 | ||
51ed9ec9 BD |
182 | bool CWalletDB::WriteMinVersion(int nVersion) |
183 | { | |
184 | return Write(std::string("minversion"), nVersion); | |
185 | } | |
186 | ||
9eace6b1 JG |
187 | bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) |
188 | { | |
189 | account.SetNull(); | |
190 | return Read(make_pair(string("acc"), strAccount), account); | |
191 | } | |
192 | ||
193 | bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) | |
194 | { | |
195 | return Write(make_pair(string("acc"), strAccount), account); | |
196 | } | |
197 | ||
51ed9ec9 | 198 | bool CWalletDB::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) |
9c7722b7 | 199 | { |
52955068 | 200 | return Write(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); |
9c7722b7 LD |
201 | } |
202 | ||
9eace6b1 JG |
203 | bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) |
204 | { | |
9c7722b7 | 205 | return WriteAccountingEntry(++nAccountingEntryNumber, acentry); |
9eace6b1 JG |
206 | } |
207 | ||
a372168e | 208 | CAmount CWalletDB::GetAccountCreditDebit(const string& strAccount) |
9eace6b1 JG |
209 | { |
210 | list<CAccountingEntry> entries; | |
211 | ListAccountCreditDebit(strAccount, entries); | |
212 | ||
a372168e | 213 | CAmount nCreditDebit = 0; |
9eace6b1 JG |
214 | BOOST_FOREACH (const CAccountingEntry& entry, entries) |
215 | nCreditDebit += entry.nCreditDebit; | |
216 | ||
217 | return nCreditDebit; | |
218 | } | |
219 | ||
220 | void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries) | |
221 | { | |
222 | bool fAllAccounts = (strAccount == "*"); | |
223 | ||
224 | Dbc* pcursor = GetCursor(); | |
225 | if (!pcursor) | |
5262fde0 | 226 | throw runtime_error("CWalletDB::ListAccountCreditDebit(): cannot create DB cursor"); |
9eace6b1 | 227 | unsigned int fFlags = DB_SET_RANGE; |
050d2e95 | 228 | while (true) |
9eace6b1 JG |
229 | { |
230 | // Read next record | |
6b6aaa16 | 231 | CDataStream ssKey(SER_DISK, CLIENT_VERSION); |
9eace6b1 | 232 | if (fFlags == DB_SET_RANGE) |
52955068 | 233 | ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? string("") : strAccount), uint64_t(0))); |
6b6aaa16 | 234 | CDataStream ssValue(SER_DISK, CLIENT_VERSION); |
9eace6b1 JG |
235 | int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); |
236 | fFlags = DB_NEXT; | |
237 | if (ret == DB_NOTFOUND) | |
238 | break; | |
239 | else if (ret != 0) | |
240 | { | |
241 | pcursor->close(); | |
5262fde0 | 242 | throw runtime_error("CWalletDB::ListAccountCreditDebit(): error scanning DB"); |
9eace6b1 JG |
243 | } |
244 | ||
245 | // Unserialize | |
246 | string strType; | |
247 | ssKey >> strType; | |
248 | if (strType != "acentry") | |
249 | break; | |
250 | CAccountingEntry acentry; | |
251 | ssKey >> acentry.strAccount; | |
252 | if (!fAllAccounts && acentry.strAccount != strAccount) | |
253 | break; | |
254 | ||
255 | ssValue >> acentry; | |
9c7722b7 | 256 | ssKey >> acentry.nEntryNo; |
9eace6b1 JG |
257 | entries.push_back(acentry); |
258 | } | |
259 | ||
260 | pcursor->close(); | |
261 | } | |
262 | ||
93f84d04 | 263 | DBErrors CWalletDB::ReorderTransactions(CWallet* pwallet) |
9c7722b7 LD |
264 | { |
265 | LOCK(pwallet->cs_wallet); | |
266 | // Old wallets didn't have any defined order for transactions | |
267 | // Probably a bad idea to change the output of this | |
268 | ||
269 | // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. | |
270 | typedef pair<CWalletTx*, CAccountingEntry*> TxPair; | |
51ed9ec9 | 271 | typedef multimap<int64_t, TxPair > TxItems; |
9c7722b7 LD |
272 | TxItems txByTime; |
273 | ||
274 | for (map<uint256, CWalletTx>::iterator it = pwallet->mapWallet.begin(); it != pwallet->mapWallet.end(); ++it) | |
275 | { | |
276 | CWalletTx* wtx = &((*it).second); | |
277 | txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); | |
278 | } | |
279 | list<CAccountingEntry> acentries; | |
280 | ListAccountCreditDebit("", acentries); | |
281 | BOOST_FOREACH(CAccountingEntry& entry, acentries) | |
282 | { | |
283 | txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); | |
284 | } | |
285 | ||
51ed9ec9 | 286 | int64_t& nOrderPosNext = pwallet->nOrderPosNext; |
9c7722b7 | 287 | nOrderPosNext = 0; |
51ed9ec9 | 288 | std::vector<int64_t> nOrderPosOffsets; |
9c7722b7 LD |
289 | for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) |
290 | { | |
291 | CWalletTx *const pwtx = (*it).second.first; | |
292 | CAccountingEntry *const pacentry = (*it).second.second; | |
51ed9ec9 | 293 | int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; |
9c7722b7 LD |
294 | |
295 | if (nOrderPos == -1) | |
296 | { | |
297 | nOrderPos = nOrderPosNext++; | |
298 | nOrderPosOffsets.push_back(nOrderPos); | |
299 | ||
da2ede2a CL |
300 | if (pwtx) |
301 | { | |
10d2c57c | 302 | if (!WriteTx(pwtx->GetTxid(), *pwtx)) |
da2ede2a CL |
303 | return DB_LOAD_FAIL; |
304 | } | |
305 | else | |
9c7722b7 LD |
306 | if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) |
307 | return DB_LOAD_FAIL; | |
308 | } | |
309 | else | |
310 | { | |
51ed9ec9 BD |
311 | int64_t nOrderPosOff = 0; |
312 | BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) | |
9c7722b7 LD |
313 | { |
314 | if (nOrderPos >= nOffsetStart) | |
315 | ++nOrderPosOff; | |
316 | } | |
317 | nOrderPos += nOrderPosOff; | |
318 | nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); | |
319 | ||
320 | if (!nOrderPosOff) | |
321 | continue; | |
322 | ||
323 | // Since we're changing the order, write it back | |
324 | if (pwtx) | |
325 | { | |
10d2c57c | 326 | if (!WriteTx(pwtx->GetTxid(), *pwtx)) |
9c7722b7 LD |
327 | return DB_LOAD_FAIL; |
328 | } | |
329 | else | |
330 | if (!WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) | |
331 | return DB_LOAD_FAIL; | |
332 | } | |
333 | } | |
da2ede2a | 334 | WriteOrderPosNext(nOrderPosNext); |
9c7722b7 LD |
335 | |
336 | return DB_LOAD_OK; | |
337 | } | |
338 | ||
3869fb89 JG |
339 | class CWalletScanState { |
340 | public: | |
341 | unsigned int nKeys; | |
342 | unsigned int nCKeys; | |
343 | unsigned int nKeyMeta; | |
c1c45943 S |
344 | unsigned int nZKeys; |
345 | unsigned int nZKeyMeta; | |
3869fb89 JG |
346 | bool fIsEncrypted; |
347 | bool fAnyUnordered; | |
348 | int nFileVersion; | |
349 | vector<uint256> vWalletUpgrade; | |
350 | ||
351 | CWalletScanState() { | |
c1c45943 | 352 | nKeys = nCKeys = nKeyMeta = nZKeys = nZKeyMeta = 0; |
3869fb89 JG |
353 | fIsEncrypted = false; |
354 | fAnyUnordered = false; | |
355 | nFileVersion = 0; | |
356 | } | |
357 | }; | |
9c7722b7 | 358 | |
eed1785f GA |
359 | bool |
360 | ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, | |
3869fb89 | 361 | CWalletScanState &wss, string& strType, string& strErr) |
eed1785f GA |
362 | { |
363 | try { | |
364 | // Unserialize | |
365 | // Taking advantage of the fact that pair serialization | |
366 | // is just the two items serialized one after the other | |
367 | ssKey >> strType; | |
368 | if (strType == "name") | |
369 | { | |
370 | string strAddress; | |
371 | ssKey >> strAddress; | |
61885513 | 372 | ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; |
eed1785f | 373 | } |
a41d5fe0 GA |
374 | else if (strType == "purpose") |
375 | { | |
376 | string strAddress; | |
377 | ssKey >> strAddress; | |
378 | ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; | |
379 | } | |
eed1785f GA |
380 | else if (strType == "tx") |
381 | { | |
382 | uint256 hash; | |
383 | ssKey >> hash; | |
16ec9044 | 384 | CWalletTx wtx; |
eed1785f | 385 | ssValue >> wtx; |
ef3988ca | 386 | CValidationState state; |
10d2c57c | 387 | if (!(CheckTransaction(wtx, state) && (wtx.GetTxid() == hash) && state.IsValid())) |
eed1785f | 388 | return false; |
eed1785f GA |
389 | |
390 | // Undo serialize changes in 31600 | |
391 | if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) | |
392 | { | |
393 | if (!ssValue.empty()) | |
394 | { | |
395 | char fTmp; | |
396 | char fUnused; | |
397 | ssValue >> fTmp >> fUnused >> wtx.strFromAccount; | |
398 | strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", | |
7d9d134b | 399 | wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString()); |
eed1785f GA |
400 | wtx.fTimeReceivedIsTxTime = fTmp; |
401 | } | |
402 | else | |
403 | { | |
7d9d134b | 404 | strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString()); |
eed1785f GA |
405 | wtx.fTimeReceivedIsTxTime = 0; |
406 | } | |
3869fb89 | 407 | wss.vWalletUpgrade.push_back(hash); |
eed1785f GA |
408 | } |
409 | ||
410 | if (wtx.nOrderPos == -1) | |
3869fb89 | 411 | wss.fAnyUnordered = true; |
eed1785f | 412 | |
44bc988e | 413 | pwallet->AddToWallet(wtx, true, NULL); |
eed1785f GA |
414 | } |
415 | else if (strType == "acentry") | |
416 | { | |
417 | string strAccount; | |
418 | ssKey >> strAccount; | |
51ed9ec9 | 419 | uint64_t nNumber; |
eed1785f GA |
420 | ssKey >> nNumber; |
421 | if (nNumber > nAccountingEntryNumber) | |
422 | nAccountingEntryNumber = nNumber; | |
423 | ||
3869fb89 | 424 | if (!wss.fAnyUnordered) |
eed1785f GA |
425 | { |
426 | CAccountingEntry acentry; | |
427 | ssValue >> acentry; | |
428 | if (acentry.nOrderPos == -1) | |
3869fb89 | 429 | wss.fAnyUnordered = true; |
eed1785f GA |
430 | } |
431 | } | |
d5087d1b | 432 | else if (strType == "watchs") |
c8988460 | 433 | { |
d5087d1b PW |
434 | CScript script; |
435 | ssKey >> script; | |
c8988460 PW |
436 | char fYes; |
437 | ssValue >> fYes; | |
438 | if (fYes == '1') | |
d5087d1b | 439 | pwallet->LoadWatchOnly(script); |
c8988460 PW |
440 | |
441 | // Watch-only addresses have no birthday information for now, | |
442 | // so set the wallet birthday to the beginning of time. | |
443 | pwallet->nTimeFirstKey = 1; | |
444 | } | |
c1c45943 S |
445 | else if (strType == "zkey") |
446 | { | |
447 | libzcash::PaymentAddress addr; | |
448 | ssKey >> addr; | |
449 | libzcash::SpendingKey key; | |
450 | ssValue >> key; | |
451 | ||
452 | if (!pwallet->LoadZKey(key)) | |
453 | { | |
454 | strErr = "Error reading wallet database: LoadZKey failed"; | |
455 | return false; | |
456 | } | |
457 | ||
458 | wss.nZKeys++; | |
c1c45943 | 459 | } |
eed1785f GA |
460 | else if (strType == "key" || strType == "wkey") |
461 | { | |
dfa23b94 | 462 | CPubKey vchPubKey; |
eed1785f | 463 | ssKey >> vchPubKey; |
dfa23b94 PW |
464 | if (!vchPubKey.IsValid()) |
465 | { | |
466 | strErr = "Error reading wallet database: CPubKey corrupt"; | |
467 | return false; | |
468 | } | |
eed1785f | 469 | CKey key; |
dfa23b94 | 470 | CPrivKey pkey; |
4f152496 | 471 | uint256 hash; |
fabba0e6 | 472 | |
eed1785f | 473 | if (strType == "key") |
3869fb89 JG |
474 | { |
475 | wss.nKeys++; | |
eed1785f | 476 | ssValue >> pkey; |
3869fb89 | 477 | } else { |
eed1785f GA |
478 | CWalletKey wkey; |
479 | ssValue >> wkey; | |
dfa23b94 PW |
480 | pkey = wkey.vchPrivKey; |
481 | } | |
fabba0e6 | 482 | |
bc687883 | 483 | // Old wallets store keys as "key" [pubkey] => [privkey] |
484 | // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key | |
485 | // using EC operations as a checksum. | |
486 | // Newer wallets store keys as "key"[pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while | |
487 | // remaining backwards-compatible. | |
6e51b3bd | 488 | try |
dfa23b94 | 489 | { |
6e51b3bd | 490 | ssValue >> hash; |
dfa23b94 | 491 | } |
27df4123 | 492 | catch (...) {} |
fabba0e6 | 493 | |
6e51b3bd | 494 | bool fSkipCheck = false; |
fabba0e6 | 495 | |
4f152496 | 496 | if (!hash.IsNull()) |
dfa23b94 | 497 | { |
6e51b3bd | 498 | // hash pubkey/privkey to accelerate wallet load |
499 | std::vector<unsigned char> vchKey; | |
500 | vchKey.reserve(vchPubKey.size() + pkey.size()); | |
501 | vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end()); | |
502 | vchKey.insert(vchKey.end(), pkey.begin(), pkey.end()); | |
fabba0e6 | 503 | |
6e51b3bd | 504 | if (Hash(vchKey.begin(), vchKey.end()) != hash) |
505 | { | |
506 | strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt"; | |
507 | return false; | |
508 | } | |
fabba0e6 | 509 | |
6e51b3bd | 510 | fSkipCheck = true; |
511 | } | |
fabba0e6 | 512 | |
6e51b3bd | 513 | if (!key.Load(pkey, vchPubKey, fSkipCheck)) |
514 | { | |
515 | strErr = "Error reading wallet database: CPrivKey corrupt"; | |
dfa23b94 | 516 | return false; |
eed1785f | 517 | } |
dfa23b94 | 518 | if (!pwallet->LoadKey(key, vchPubKey)) |
eed1785f GA |
519 | { |
520 | strErr = "Error reading wallet database: LoadKey failed"; | |
521 | return false; | |
522 | } | |
523 | } | |
524 | else if (strType == "mkey") | |
525 | { | |
526 | unsigned int nID; | |
527 | ssKey >> nID; | |
528 | CMasterKey kMasterKey; | |
529 | ssValue >> kMasterKey; | |
530 | if(pwallet->mapMasterKeys.count(nID) != 0) | |
531 | { | |
532 | strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID); | |
533 | return false; | |
534 | } | |
535 | pwallet->mapMasterKeys[nID] = kMasterKey; | |
536 | if (pwallet->nMasterKeyMaxID < nID) | |
537 | pwallet->nMasterKeyMaxID = nID; | |
538 | } | |
539 | else if (strType == "ckey") | |
540 | { | |
541 | vector<unsigned char> vchPubKey; | |
542 | ssKey >> vchPubKey; | |
543 | vector<unsigned char> vchPrivKey; | |
544 | ssValue >> vchPrivKey; | |
3869fb89 JG |
545 | wss.nCKeys++; |
546 | ||
eed1785f GA |
547 | if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) |
548 | { | |
549 | strErr = "Error reading wallet database: LoadCryptedKey failed"; | |
550 | return false; | |
551 | } | |
3869fb89 JG |
552 | wss.fIsEncrypted = true; |
553 | } | |
554 | else if (strType == "keymeta") | |
555 | { | |
4addb2c0 | 556 | CPubKey vchPubKey; |
3869fb89 JG |
557 | ssKey >> vchPubKey; |
558 | CKeyMetadata keyMeta; | |
559 | ssValue >> keyMeta; | |
560 | wss.nKeyMeta++; | |
561 | ||
4addb2c0 PW |
562 | pwallet->LoadKeyMetadata(vchPubKey, keyMeta); |
563 | ||
3869fb89 JG |
564 | // find earliest key creation time, as wallet birthday |
565 | if (!pwallet->nTimeFirstKey || | |
566 | (keyMeta.nCreateTime < pwallet->nTimeFirstKey)) | |
567 | pwallet->nTimeFirstKey = keyMeta.nCreateTime; | |
eed1785f | 568 | } |
c1c45943 S |
569 | else if (strType == "zkeymeta") |
570 | { | |
571 | libzcash::PaymentAddress addr; | |
572 | ssKey >> addr; | |
573 | CKeyMetadata keyMeta; | |
574 | ssValue >> keyMeta; | |
575 | wss.nZKeyMeta++; | |
576 | ||
577 | pwallet->LoadZKeyMetadata(addr, keyMeta); | |
578 | ||
579 | // ignore earliest key creation time as taddr will exist before any zaddr | |
580 | } | |
eed1785f GA |
581 | else if (strType == "defaultkey") |
582 | { | |
583 | ssValue >> pwallet->vchDefaultKey; | |
584 | } | |
585 | else if (strType == "pool") | |
586 | { | |
51ed9ec9 | 587 | int64_t nIndex; |
eed1785f | 588 | ssKey >> nIndex; |
434e4273 PW |
589 | CKeyPool keypool; |
590 | ssValue >> keypool; | |
eed1785f | 591 | pwallet->setKeyPool.insert(nIndex); |
434e4273 PW |
592 | |
593 | // If no metadata exists yet, create a default with the pool key's | |
594 | // creation time. Note that this may be overwritten by actually | |
595 | // stored metadata for that key later, which is fine. | |
596 | CKeyID keyid = keypool.vchPubKey.GetID(); | |
597 | if (pwallet->mapKeyMetadata.count(keyid) == 0) | |
598 | pwallet->mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); | |
eed1785f GA |
599 | } |
600 | else if (strType == "version") | |
601 | { | |
3869fb89 JG |
602 | ssValue >> wss.nFileVersion; |
603 | if (wss.nFileVersion == 10300) | |
604 | wss.nFileVersion = 300; | |
eed1785f GA |
605 | } |
606 | else if (strType == "cscript") | |
607 | { | |
608 | uint160 hash; | |
609 | ssKey >> hash; | |
610 | CScript script; | |
611 | ssValue >> script; | |
612 | if (!pwallet->LoadCScript(script)) | |
613 | { | |
614 | strErr = "Error reading wallet database: LoadCScript failed"; | |
615 | return false; | |
616 | } | |
617 | } | |
618 | else if (strType == "orderposnext") | |
619 | { | |
620 | ssValue >> pwallet->nOrderPosNext; | |
621 | } | |
b10e1470 WL |
622 | else if (strType == "destdata") |
623 | { | |
624 | std::string strAddress, strKey, strValue; | |
625 | ssKey >> strAddress; | |
626 | ssKey >> strKey; | |
627 | ssValue >> strValue; | |
628 | if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) | |
629 | { | |
630 | strErr = "Error reading wallet database: LoadDestData failed"; | |
631 | return false; | |
632 | } | |
633 | } | |
eed1785f GA |
634 | } catch (...) |
635 | { | |
636 | return false; | |
637 | } | |
638 | return true; | |
639 | } | |
640 | ||
641 | static bool IsKeyType(string strType) | |
642 | { | |
643 | return (strType== "key" || strType == "wkey" || | |
309b0ecd | 644 | strType == "zkey" || |
eed1785f GA |
645 | strType == "mkey" || strType == "ckey"); |
646 | } | |
647 | ||
648 | DBErrors CWalletDB::LoadWallet(CWallet* pwallet) | |
9eace6b1 | 649 | { |
fd61d6f5 | 650 | pwallet->vchDefaultKey = CPubKey(); |
3869fb89 | 651 | CWalletScanState wss; |
eed1785f GA |
652 | bool fNoncriticalErrors = false; |
653 | DBErrors result = DB_LOAD_OK; | |
9eace6b1 | 654 | |
eed1785f | 655 | try { |
9eace6b1 JG |
656 | LOCK(pwallet->cs_wallet); |
657 | int nMinVersion = 0; | |
658 | if (Read((string)"minversion", nMinVersion)) | |
659 | { | |
660 | if (nMinVersion > CLIENT_VERSION) | |
661 | return DB_TOO_NEW; | |
662 | pwallet->LoadMinVersion(nMinVersion); | |
663 | } | |
664 | ||
665 | // Get cursor | |
666 | Dbc* pcursor = GetCursor(); | |
667 | if (!pcursor) | |
668 | { | |
881a85a2 | 669 | LogPrintf("Error getting wallet database cursor\n"); |
9eace6b1 JG |
670 | return DB_CORRUPT; |
671 | } | |
672 | ||
050d2e95 | 673 | while (true) |
9eace6b1 JG |
674 | { |
675 | // Read next record | |
6b6aaa16 PW |
676 | CDataStream ssKey(SER_DISK, CLIENT_VERSION); |
677 | CDataStream ssValue(SER_DISK, CLIENT_VERSION); | |
9eace6b1 JG |
678 | int ret = ReadAtCursor(pcursor, ssKey, ssValue); |
679 | if (ret == DB_NOTFOUND) | |
680 | break; | |
681 | else if (ret != 0) | |
682 | { | |
881a85a2 | 683 | LogPrintf("Error reading next record from wallet database\n"); |
9eace6b1 JG |
684 | return DB_CORRUPT; |
685 | } | |
686 | ||
eed1785f GA |
687 | // Try to be tolerant of single corrupt records: |
688 | string strType, strErr; | |
3869fb89 | 689 | if (!ReadKeyValue(pwallet, ssKey, ssValue, wss, strType, strErr)) |
9eace6b1 | 690 | { |
eed1785f GA |
691 | // losing keys is considered a catastrophic error, anything else |
692 | // we assume the user can live with: | |
693 | if (IsKeyType(strType)) | |
694 | result = DB_CORRUPT; | |
9eace6b1 JG |
695 | else |
696 | { | |
eed1785f GA |
697 | // Leave other errors alone, if we try to fix them we might make things worse. |
698 | fNoncriticalErrors = true; // ... but do warn the user there is something wrong. | |
699 | if (strType == "tx") | |
700 | // Rescan if there is a bad transaction record: | |
701 | SoftSetBoolArg("-rescan", true); | |
9eace6b1 | 702 | } |
da7b8c12 | 703 | } |
eed1785f | 704 | if (!strErr.empty()) |
7d9d134b | 705 | LogPrintf("%s\n", strErr); |
9eace6b1 JG |
706 | } |
707 | pcursor->close(); | |
708 | } | |
27df4123 | 709 | catch (const boost::thread_interrupted&) { |
b31499ec GA |
710 | throw; |
711 | } | |
712 | catch (...) { | |
eed1785f GA |
713 | result = DB_CORRUPT; |
714 | } | |
9eace6b1 | 715 | |
eed1785f GA |
716 | if (fNoncriticalErrors && result == DB_LOAD_OK) |
717 | result = DB_NONCRITICAL_ERROR; | |
718 | ||
719 | // Any wallet corruption at all: skip any rewriting or | |
720 | // upgrading, we don't want to make it worse. | |
721 | if (result != DB_LOAD_OK) | |
722 | return result; | |
9eace6b1 | 723 | |
881a85a2 | 724 | LogPrintf("nFileVersion = %d\n", wss.nFileVersion); |
9eace6b1 | 725 | |
881a85a2 | 726 | LogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total\n", |
3869fb89 JG |
727 | wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys); |
728 | ||
c1c45943 S |
729 | // TODO: Keep track of encrypted ZKeys |
730 | LogPrintf("ZKeys: %u plaintext, -- encrypted, %u w/metadata, %u total\n", | |
731 | wss.nZKeys, wss.nZKeyMeta, wss.nZKeys + 0); | |
732 | ||
3869fb89 JG |
733 | // nTimeFirstKey is only reliable if all keys have metadata |
734 | if ((wss.nKeys + wss.nCKeys) != wss.nKeyMeta) | |
4addb2c0 | 735 | pwallet->nTimeFirstKey = 1; // 0 would be considered 'no value' |
3869fb89 JG |
736 | |
737 | BOOST_FOREACH(uint256 hash, wss.vWalletUpgrade) | |
eed1785f | 738 | WriteTx(hash, pwallet->mapWallet[hash]); |
9eace6b1 JG |
739 | |
740 | // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: | |
3869fb89 | 741 | if (wss.fIsEncrypted && (wss.nFileVersion == 40000 || wss.nFileVersion == 50000)) |
9eace6b1 JG |
742 | return DB_NEED_REWRITE; |
743 | ||
3869fb89 | 744 | if (wss.nFileVersion < CLIENT_VERSION) // Update |
9eace6b1 JG |
745 | WriteVersion(CLIENT_VERSION); |
746 | ||
3869fb89 | 747 | if (wss.fAnyUnordered) |
eed1785f | 748 | result = ReorderTransactions(pwallet); |
9c7722b7 | 749 | |
eed1785f | 750 | return result; |
9eace6b1 JG |
751 | } |
752 | ||
77cbd462 | 753 | DBErrors CWalletDB::FindWalletTx(CWallet* pwallet, vector<uint256>& vTxHash, vector<CWalletTx>& vWtx) |
518f3bda JG |
754 | { |
755 | pwallet->vchDefaultKey = CPubKey(); | |
518f3bda JG |
756 | bool fNoncriticalErrors = false; |
757 | DBErrors result = DB_LOAD_OK; | |
758 | ||
759 | try { | |
760 | LOCK(pwallet->cs_wallet); | |
761 | int nMinVersion = 0; | |
762 | if (Read((string)"minversion", nMinVersion)) | |
763 | { | |
764 | if (nMinVersion > CLIENT_VERSION) | |
765 | return DB_TOO_NEW; | |
766 | pwallet->LoadMinVersion(nMinVersion); | |
767 | } | |
768 | ||
769 | // Get cursor | |
770 | Dbc* pcursor = GetCursor(); | |
771 | if (!pcursor) | |
772 | { | |
773 | LogPrintf("Error getting wallet database cursor\n"); | |
774 | return DB_CORRUPT; | |
775 | } | |
776 | ||
777 | while (true) | |
778 | { | |
779 | // Read next record | |
780 | CDataStream ssKey(SER_DISK, CLIENT_VERSION); | |
781 | CDataStream ssValue(SER_DISK, CLIENT_VERSION); | |
782 | int ret = ReadAtCursor(pcursor, ssKey, ssValue); | |
783 | if (ret == DB_NOTFOUND) | |
784 | break; | |
785 | else if (ret != 0) | |
786 | { | |
787 | LogPrintf("Error reading next record from wallet database\n"); | |
788 | return DB_CORRUPT; | |
789 | } | |
790 | ||
791 | string strType; | |
792 | ssKey >> strType; | |
793 | if (strType == "tx") { | |
794 | uint256 hash; | |
795 | ssKey >> hash; | |
796 | ||
77cbd462 CL |
797 | CWalletTx wtx; |
798 | ssValue >> wtx; | |
799 | ||
518f3bda | 800 | vTxHash.push_back(hash); |
77cbd462 | 801 | vWtx.push_back(wtx); |
518f3bda JG |
802 | } |
803 | } | |
804 | pcursor->close(); | |
805 | } | |
27df4123 | 806 | catch (const boost::thread_interrupted&) { |
518f3bda JG |
807 | throw; |
808 | } | |
809 | catch (...) { | |
810 | result = DB_CORRUPT; | |
811 | } | |
812 | ||
813 | if (fNoncriticalErrors && result == DB_LOAD_OK) | |
814 | result = DB_NONCRITICAL_ERROR; | |
815 | ||
816 | return result; | |
817 | } | |
818 | ||
77cbd462 | 819 | DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx) |
518f3bda JG |
820 | { |
821 | // build list of wallet TXs | |
822 | vector<uint256> vTxHash; | |
77cbd462 | 823 | DBErrors err = FindWalletTx(pwallet, vTxHash, vWtx); |
518f3bda JG |
824 | if (err != DB_LOAD_OK) |
825 | return err; | |
826 | ||
827 | // erase each wallet TX | |
828 | BOOST_FOREACH (uint256& hash, vTxHash) { | |
829 | if (!EraseTx(hash)) | |
830 | return DB_CORRUPT; | |
831 | } | |
832 | ||
833 | return DB_LOAD_OK; | |
834 | } | |
835 | ||
b31499ec | 836 | void ThreadFlushWalletDB(const string& strFile) |
9eace6b1 | 837 | { |
96931d6f GS |
838 | // Make this thread recognisable as the wallet flushing thread |
839 | RenameThread("bitcoin-wallet"); | |
840 | ||
9eace6b1 JG |
841 | static bool fOneThread; |
842 | if (fOneThread) | |
843 | return; | |
844 | fOneThread = true; | |
845 | if (!GetBoolArg("-flushwallet", true)) | |
846 | return; | |
847 | ||
848 | unsigned int nLastSeen = nWalletDBUpdated; | |
849 | unsigned int nLastFlushed = nWalletDBUpdated; | |
51ed9ec9 | 850 | int64_t nLastWalletUpdate = GetTime(); |
b31499ec | 851 | while (true) |
9eace6b1 | 852 | { |
1b43bf0d | 853 | MilliSleep(500); |
9eace6b1 JG |
854 | |
855 | if (nLastSeen != nWalletDBUpdated) | |
856 | { | |
857 | nLastSeen = nWalletDBUpdated; | |
858 | nLastWalletUpdate = GetTime(); | |
859 | } | |
860 | ||
861 | if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) | |
862 | { | |
cd9696fc | 863 | TRY_LOCK(bitdb.cs_db,lockDb); |
9eace6b1 JG |
864 | if (lockDb) |
865 | { | |
866 | // Don't do this if any databases are in use | |
867 | int nRefCount = 0; | |
ffe8b77a JG |
868 | map<string, int>::iterator mi = bitdb.mapFileUseCount.begin(); |
869 | while (mi != bitdb.mapFileUseCount.end()) | |
9eace6b1 JG |
870 | { |
871 | nRefCount += (*mi).second; | |
872 | mi++; | |
873 | } | |
874 | ||
b31499ec | 875 | if (nRefCount == 0) |
9eace6b1 | 876 | { |
b31499ec | 877 | boost::this_thread::interruption_point(); |
ffe8b77a JG |
878 | map<string, int>::iterator mi = bitdb.mapFileUseCount.find(strFile); |
879 | if (mi != bitdb.mapFileUseCount.end()) | |
9eace6b1 | 880 | { |
881a85a2 | 881 | LogPrint("db", "Flushing wallet.dat\n"); |
9eace6b1 | 882 | nLastFlushed = nWalletDBUpdated; |
51ed9ec9 | 883 | int64_t nStart = GetTimeMillis(); |
9eace6b1 JG |
884 | |
885 | // Flush wallet.dat so it's self contained | |
ffe8b77a | 886 | bitdb.CloseDb(strFile); |
cd9696fc | 887 | bitdb.CheckpointLSN(strFile); |
9eace6b1 | 888 | |
ffe8b77a | 889 | bitdb.mapFileUseCount.erase(mi++); |
f48742c2 | 890 | LogPrint("db", "Flushed wallet.dat %dms\n", GetTimeMillis() - nStart); |
9eace6b1 JG |
891 | } |
892 | } | |
893 | } | |
894 | } | |
895 | } | |
896 | } | |
897 | ||
898 | bool BackupWallet(const CWallet& wallet, const string& strDest) | |
899 | { | |
900 | if (!wallet.fFileBacked) | |
901 | return false; | |
b31499ec | 902 | while (true) |
9eace6b1 JG |
903 | { |
904 | { | |
cd9696fc | 905 | LOCK(bitdb.cs_db); |
ffe8b77a | 906 | if (!bitdb.mapFileUseCount.count(wallet.strWalletFile) || bitdb.mapFileUseCount[wallet.strWalletFile] == 0) |
9eace6b1 JG |
907 | { |
908 | // Flush log data to the dat file | |
ffe8b77a | 909 | bitdb.CloseDb(wallet.strWalletFile); |
cd9696fc | 910 | bitdb.CheckpointLSN(wallet.strWalletFile); |
ffe8b77a | 911 | bitdb.mapFileUseCount.erase(wallet.strWalletFile); |
9eace6b1 JG |
912 | |
913 | // Copy wallet.dat | |
a3241998 CF |
914 | boost::filesystem::path pathSrc = GetDataDir() / wallet.strWalletFile; |
915 | boost::filesystem::path pathDest(strDest); | |
916 | if (boost::filesystem::is_directory(pathDest)) | |
9eace6b1 JG |
917 | pathDest /= wallet.strWalletFile; |
918 | ||
919 | try { | |
920 | #if BOOST_VERSION >= 104000 | |
a3241998 | 921 | boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists); |
9eace6b1 | 922 | #else |
a3241998 | 923 | boost::filesystem::copy_file(pathSrc, pathDest); |
9eace6b1 | 924 | #endif |
7d9d134b | 925 | LogPrintf("copied wallet.dat to %s\n", pathDest.string()); |
9eace6b1 | 926 | return true; |
a3241998 | 927 | } catch (const boost::filesystem::filesystem_error& e) { |
7d9d134b | 928 | LogPrintf("error copying wallet.dat to %s - %s\n", pathDest.string(), e.what()); |
9eace6b1 JG |
929 | return false; |
930 | } | |
931 | } | |
932 | } | |
1b43bf0d | 933 | MilliSleep(100); |
9eace6b1 JG |
934 | } |
935 | return false; | |
936 | } | |
eed1785f GA |
937 | |
938 | // | |
939 | // Try to (very carefully!) recover wallet.dat if there is a problem. | |
940 | // | |
341e2385 | 941 | bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename, bool fOnlyKeys) |
eed1785f GA |
942 | { |
943 | // Recovery procedure: | |
944 | // move wallet.dat to wallet.timestamp.bak | |
945 | // Call Salvage with fAggressive=true to | |
946 | // get as much data as possible. | |
947 | // Rewrite salvaged data to wallet.dat | |
948 | // Set -rescan so any missing transactions will be | |
949 | // found. | |
51ed9ec9 | 950 | int64_t now = GetTime(); |
f48742c2 | 951 | std::string newFilename = strprintf("wallet.%d.bak", now); |
eed1785f | 952 | |
51598b26 PW |
953 | int result = dbenv.dbenv->dbrename(NULL, filename.c_str(), NULL, |
954 | newFilename.c_str(), DB_AUTO_COMMIT); | |
eed1785f | 955 | if (result == 0) |
7d9d134b | 956 | LogPrintf("Renamed %s to %s\n", filename, newFilename); |
eed1785f GA |
957 | else |
958 | { | |
7d9d134b | 959 | LogPrintf("Failed to rename %s to %s\n", filename, newFilename); |
eed1785f GA |
960 | return false; |
961 | } | |
962 | ||
963 | std::vector<CDBEnv::KeyValPair> salvagedData; | |
a5baba52 | 964 | bool fSuccess = dbenv.Salvage(newFilename, true, salvagedData); |
eed1785f GA |
965 | if (salvagedData.empty()) |
966 | { | |
7d9d134b | 967 | LogPrintf("Salvage(aggressive) found no records in %s.\n", newFilename); |
eed1785f GA |
968 | return false; |
969 | } | |
783b182c | 970 | LogPrintf("Salvage(aggressive) found %u records\n", salvagedData.size()); |
eed1785f | 971 | |
51598b26 | 972 | boost::scoped_ptr<Db> pdbCopy(new Db(dbenv.dbenv, 0)); |
b001c871 | 973 | int ret = pdbCopy->open(NULL, // Txn pointer |
eed1785f | 974 | filename.c_str(), // Filename |
b001c871 PK |
975 | "main", // Logical db name |
976 | DB_BTREE, // Database type | |
977 | DB_CREATE, // Flags | |
eed1785f GA |
978 | 0); |
979 | if (ret > 0) | |
980 | { | |
7d9d134b | 981 | LogPrintf("Cannot create database file %s\n", filename); |
eed1785f GA |
982 | return false; |
983 | } | |
984 | CWallet dummyWallet; | |
3869fb89 | 985 | CWalletScanState wss; |
eed1785f GA |
986 | |
987 | DbTxn* ptxn = dbenv.TxnBegin(); | |
988 | BOOST_FOREACH(CDBEnv::KeyValPair& row, salvagedData) | |
989 | { | |
990 | if (fOnlyKeys) | |
991 | { | |
992 | CDataStream ssKey(row.first, SER_DISK, CLIENT_VERSION); | |
993 | CDataStream ssValue(row.second, SER_DISK, CLIENT_VERSION); | |
994 | string strType, strErr; | |
995 | bool fReadOK = ReadKeyValue(&dummyWallet, ssKey, ssValue, | |
3869fb89 | 996 | wss, strType, strErr); |
eed1785f GA |
997 | if (!IsKeyType(strType)) |
998 | continue; | |
999 | if (!fReadOK) | |
1000 | { | |
7d9d134b | 1001 | LogPrintf("WARNING: CWalletDB::Recover skipping %s: %s\n", strType, strErr); |
eed1785f GA |
1002 | continue; |
1003 | } | |
1004 | } | |
1005 | Dbt datKey(&row.first[0], row.first.size()); | |
1006 | Dbt datValue(&row.second[0], row.second.size()); | |
1007 | int ret2 = pdbCopy->put(ptxn, &datKey, &datValue, DB_NOOVERWRITE); | |
1008 | if (ret2 > 0) | |
1009 | fSuccess = false; | |
1010 | } | |
1011 | ptxn->commit(0); | |
1012 | pdbCopy->close(0); | |
eed1785f GA |
1013 | |
1014 | return fSuccess; | |
1015 | } | |
1016 | ||
341e2385 | 1017 | bool CWalletDB::Recover(CDBEnv& dbenv, const std::string& filename) |
eed1785f GA |
1018 | { |
1019 | return CWalletDB::Recover(dbenv, filename, false); | |
1020 | } | |
b10e1470 WL |
1021 | |
1022 | bool CWalletDB::WriteDestData(const std::string &address, const std::string &key, const std::string &value) | |
1023 | { | |
1024 | nWalletDBUpdated++; | |
52955068 | 1025 | return Write(std::make_pair(std::string("destdata"), std::make_pair(address, key)), value); |
b10e1470 WL |
1026 | } |
1027 | ||
1028 | bool CWalletDB::EraseDestData(const std::string &address, const std::string &key) | |
1029 | { | |
1030 | nWalletDBUpdated++; | |
52955068 | 1031 | return Erase(std::make_pair(std::string("destdata"), std::make_pair(address, key))); |
b10e1470 | 1032 | } |