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