Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-2022 The Bitcoin Core developers
3 : : // Distributed under the MIT software license, see the accompanying
4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 : :
6 : : #include <bitcoin-build-config.h> // IWYU pragma: keep
7 : :
8 : : #include <wallet/walletdb.h>
9 : :
10 : : #include <common/system.h>
11 : : #include <key_io.h>
12 : : #include <protocol.h>
13 : : #include <script/script.h>
14 : : #include <serialize.h>
15 : : #include <sync.h>
16 : : #include <util/bip32.h>
17 : : #include <util/check.h>
18 : : #include <util/fs.h>
19 : : #include <util/time.h>
20 : : #include <util/translation.h>
21 : : #include <wallet/migrate.h>
22 : : #include <wallet/sqlite.h>
23 : : #include <wallet/wallet.h>
24 : :
25 : : #include <atomic>
26 : : #include <optional>
27 : : #include <string>
28 : :
29 : : namespace wallet {
30 : : namespace DBKeys {
31 : : const std::string ACENTRY{"acentry"};
32 : : const std::string ACTIVEEXTERNALSPK{"activeexternalspk"};
33 : : const std::string ACTIVEINTERNALSPK{"activeinternalspk"};
34 : : const std::string BESTBLOCK_NOMERKLE{"bestblock_nomerkle"};
35 : : const std::string BESTBLOCK{"bestblock"};
36 : : const std::string CRYPTED_KEY{"ckey"};
37 : : const std::string CSCRIPT{"cscript"};
38 : : const std::string DEFAULTKEY{"defaultkey"};
39 : : const std::string DESTDATA{"destdata"};
40 : : const std::string FLAGS{"flags"};
41 : : const std::string HDCHAIN{"hdchain"};
42 : : const std::string KEYMETA{"keymeta"};
43 : : const std::string KEY{"key"};
44 : : const std::string LOCKED_UTXO{"lockedutxo"};
45 : : const std::string MASTER_KEY{"mkey"};
46 : : const std::string MINVERSION{"minversion"};
47 : : const std::string NAME{"name"};
48 : : const std::string OLD_KEY{"wkey"};
49 : : const std::string ORDERPOSNEXT{"orderposnext"};
50 : : const std::string POOL{"pool"};
51 : : const std::string PURPOSE{"purpose"};
52 : : const std::string SETTINGS{"settings"};
53 : : const std::string TX{"tx"};
54 : : const std::string VERSION{"version"};
55 : : const std::string WALLETDESCRIPTOR{"walletdescriptor"};
56 : : const std::string WALLETDESCRIPTORCACHE{"walletdescriptorcache"};
57 : : const std::string WALLETDESCRIPTORLHCACHE{"walletdescriptorlhcache"};
58 : : const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"};
59 : : const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"};
60 : : const std::string WATCHMETA{"watchmeta"};
61 : : const std::string WATCHS{"watchs"};
62 : : const std::unordered_set<std::string> LEGACY_TYPES{CRYPTED_KEY, CSCRIPT, DEFAULTKEY, HDCHAIN, KEYMETA, KEY, OLD_KEY, POOL, WATCHMETA, WATCHS};
63 : : } // namespace DBKeys
64 : :
65 : : //
66 : : // WalletBatch
67 : : //
68 : :
69 : 28334 : bool WalletBatch::WriteName(const std::string& strAddress, const std::string& strName)
70 : : {
71 [ + - ]: 85002 : return WriteIC(std::make_pair(DBKeys::NAME, strAddress), strName);
72 : : }
73 : :
74 : 26 : bool WalletBatch::EraseName(const std::string& strAddress)
75 : : {
76 : : // This should only be used for sending addresses, never for receiving addresses,
77 : : // receiving addresses must always have an address book entry if they're not change return.
78 [ + - ]: 78 : return EraseIC(std::make_pair(DBKeys::NAME, strAddress));
79 : : }
80 : :
81 : 28334 : bool WalletBatch::WritePurpose(const std::string& strAddress, const std::string& strPurpose)
82 : : {
83 [ + - ]: 85002 : return WriteIC(std::make_pair(DBKeys::PURPOSE, strAddress), strPurpose);
84 : : }
85 : :
86 : 26 : bool WalletBatch::ErasePurpose(const std::string& strAddress)
87 : : {
88 [ + - ]: 78 : return EraseIC(std::make_pair(DBKeys::PURPOSE, strAddress));
89 : : }
90 : :
91 : 23442 : bool WalletBatch::WriteTx(const CWalletTx& wtx)
92 : : {
93 [ + - ]: 46884 : return WriteIC(std::make_pair(DBKeys::TX, wtx.GetHash()), wtx);
94 : : }
95 : :
96 : 14 : bool WalletBatch::EraseTx(uint256 hash)
97 : : {
98 [ + - ]: 28 : return EraseIC(std::make_pair(DBKeys::TX, hash));
99 : : }
100 : :
101 : 0 : bool WalletBatch::WriteKeyMetadata(const CKeyMetadata& meta, const CPubKey& pubkey, const bool overwrite)
102 : : {
103 [ # # ]: 0 : return WriteIC(std::make_pair(DBKeys::KEYMETA, pubkey), meta, overwrite);
104 : : }
105 : :
106 : 0 : bool WalletBatch::WriteKey(const CPubKey& vchPubKey, const CPrivKey& vchPrivKey, const CKeyMetadata& keyMeta)
107 : : {
108 [ # # ]: 0 : if (!WriteKeyMetadata(keyMeta, vchPubKey, false)) {
109 : : return false;
110 : : }
111 : :
112 : : // hash pubkey/privkey to accelerate wallet load
113 : 0 : std::vector<unsigned char> vchKey;
114 [ # # ]: 0 : vchKey.reserve(vchPubKey.size() + vchPrivKey.size());
115 [ # # ]: 0 : vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
116 [ # # ]: 0 : vchKey.insert(vchKey.end(), vchPrivKey.begin(), vchPrivKey.end());
117 : :
118 [ # # # # : 0 : return WriteIC(std::make_pair(DBKeys::KEY, vchPubKey), std::make_pair(vchPrivKey, Hash(vchKey)), false);
# # ]
119 : 0 : }
120 : :
121 : 0 : bool WalletBatch::WriteCryptedKey(const CPubKey& vchPubKey,
122 : : const std::vector<unsigned char>& vchCryptedSecret,
123 : : const CKeyMetadata &keyMeta)
124 : : {
125 [ # # ]: 0 : if (!WriteKeyMetadata(keyMeta, vchPubKey, true)) {
126 : : return false;
127 : : }
128 : :
129 : : // Compute a checksum of the encrypted key
130 : 0 : uint256 checksum = Hash(vchCryptedSecret);
131 : :
132 : 0 : const auto key = std::make_pair(DBKeys::CRYPTED_KEY, vchPubKey);
133 [ # # # # : 0 : if (!WriteIC(key, std::make_pair(vchCryptedSecret, checksum), false)) {
# # ]
134 : : // It may already exist, so try writing just the checksum
135 : 0 : std::vector<unsigned char> val;
136 [ # # # # ]: 0 : if (!m_batch->Read(key, val)) {
137 : : return false;
138 : : }
139 [ # # # # : 0 : if (!WriteIC(key, std::make_pair(val, checksum), true)) {
# # ]
140 : : return false;
141 : : }
142 : 0 : }
143 [ # # # # ]: 0 : EraseIC(std::make_pair(DBKeys::KEY, vchPubKey));
144 : 0 : return true;
145 : 0 : }
146 : :
147 : 26 : bool WalletBatch::WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey)
148 : : {
149 [ + - ]: 52 : return WriteIC(std::make_pair(DBKeys::MASTER_KEY, nID), kMasterKey, true);
150 : : }
151 : :
152 : 1 : bool WalletBatch::EraseMasterKey(unsigned int id)
153 : : {
154 [ + - ]: 2 : return EraseIC(std::make_pair(DBKeys::MASTER_KEY, id));
155 : : }
156 : :
157 : 0 : bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta)
158 : : {
159 [ # # # # ]: 0 : if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) {
160 : : return false;
161 : : }
162 [ # # ]: 0 : return WriteIC(std::make_pair(DBKeys::WATCHS, dest), uint8_t{'1'});
163 : : }
164 : :
165 : 0 : bool WalletBatch::EraseWatchOnly(const CScript &dest)
166 : : {
167 [ # # # # ]: 0 : if (!EraseIC(std::make_pair(DBKeys::WATCHMETA, dest))) {
168 : : return false;
169 : : }
170 [ # # ]: 0 : return EraseIC(std::make_pair(DBKeys::WATCHS, dest));
171 : : }
172 : :
173 : 1165 : bool WalletBatch::WriteBestBlock(const CBlockLocator& locator)
174 : : {
175 [ + - ]: 1165 : WriteIC(DBKeys::BESTBLOCK, CBlockLocator()); // Write empty block locator so versions that require a merkle branch automatically rescan
176 : 2330 : return WriteIC(DBKeys::BESTBLOCK_NOMERKLE, locator);
177 : : }
178 : :
179 : 1582 : bool WalletBatch::ReadBestBlock(CBlockLocator& locator)
180 : : {
181 [ + + + - ]: 1582 : if (m_batch->Read(DBKeys::BESTBLOCK, locator) && !locator.vHave.empty()) return true;
182 : 1582 : return m_batch->Read(DBKeys::BESTBLOCK_NOMERKLE, locator);
183 : : }
184 : :
185 : 0 : bool WalletBatch::IsEncrypted()
186 : : {
187 : 0 : DataStream prefix;
188 [ # # ]: 0 : prefix << DBKeys::MASTER_KEY;
189 [ # # # # ]: 0 : if (auto cursor = m_batch->GetNewPrefixCursor(prefix)) {
190 : 0 : DataStream k, v;
191 [ # # # # ]: 0 : if (cursor->Next(k, v) == DatabaseCursor::Status::MORE) return true;
192 : 0 : }
193 : 0 : return false;
194 : 0 : }
195 : :
196 : 17949 : bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext)
197 : : {
198 : 17949 : return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext);
199 : : }
200 : :
201 : 511 : bool WalletBatch::WriteMinVersion(int nVersion)
202 : : {
203 : 511 : return WriteIC(DBKeys::MINVERSION, nVersion);
204 : : }
205 : :
206 : 3193 : bool WalletBatch::WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal)
207 : : {
208 [ + + ]: 4821 : std::string key = internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK;
209 [ + - + - ]: 6386 : return WriteIC(make_pair(key, type), id);
210 : 3193 : }
211 : :
212 : 1 : bool WalletBatch::EraseActiveScriptPubKeyMan(uint8_t type, bool internal)
213 : : {
214 [ - + ]: 1 : const std::string key{internal ? DBKeys::ACTIVEINTERNALSPK : DBKeys::ACTIVEEXTERNALSPK};
215 [ + - + - ]: 2 : return EraseIC(make_pair(key, type));
216 : 1 : }
217 : :
218 : 3391 : bool WalletBatch::WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey)
219 : : {
220 : : // hash pubkey/privkey to accelerate wallet load
221 : 3391 : std::vector<unsigned char> key;
222 [ + - ]: 3391 : key.reserve(pubkey.size() + privkey.size());
223 [ + - ]: 3391 : key.insert(key.end(), pubkey.begin(), pubkey.end());
224 [ + - ]: 3391 : key.insert(key.end(), privkey.begin(), privkey.end());
225 : :
226 [ + - + - : 10173 : return WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)), std::make_pair(privkey, Hash(key)), false);
+ - ]
227 : 3391 : }
228 : :
229 : 265 : bool WalletBatch::WriteCryptedDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const std::vector<unsigned char>& secret)
230 : : {
231 [ + - + - ]: 530 : if (!WriteIC(std::make_pair(DBKeys::WALLETDESCRIPTORCKEY, std::make_pair(desc_id, pubkey)), secret, false)) {
232 : : return false;
233 : : }
234 [ + - ]: 265 : EraseIC(std::make_pair(DBKeys::WALLETDESCRIPTORKEY, std::make_pair(desc_id, pubkey)));
235 : 265 : return true;
236 : : }
237 : :
238 : 95875 : bool WalletBatch::WriteDescriptor(const uint256& desc_id, const WalletDescriptor& descriptor)
239 : : {
240 [ + - ]: 191750 : return WriteIC(make_pair(DBKeys::WALLETDESCRIPTOR, desc_id), descriptor);
241 : : }
242 : :
243 : 10992 : bool WalletBatch::WriteDescriptorDerivedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index, uint32_t der_index)
244 : : {
245 : 10992 : std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
246 [ + - ]: 10992 : xpub.Encode(ser_xpub.data());
247 [ + - + - ]: 21984 : return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), std::make_pair(key_exp_index, der_index)), ser_xpub);
248 : 10992 : }
249 : :
250 : 3713 : bool WalletBatch::WriteDescriptorParentCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
251 : : {
252 : 3713 : std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
253 [ + - ]: 3713 : xpub.Encode(ser_xpub.data());
254 [ + - + - ]: 7426 : return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORCACHE, desc_id), key_exp_index), ser_xpub);
255 : 3713 : }
256 : :
257 : 2994 : bool WalletBatch::WriteDescriptorLastHardenedCache(const CExtPubKey& xpub, const uint256& desc_id, uint32_t key_exp_index)
258 : : {
259 : 2994 : std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
260 [ + - ]: 2994 : xpub.Encode(ser_xpub.data());
261 [ + - + - ]: 5988 : return WriteIC(std::make_pair(std::make_pair(DBKeys::WALLETDESCRIPTORLHCACHE, desc_id), key_exp_index), ser_xpub);
262 : 2994 : }
263 : :
264 : 437992 : bool WalletBatch::WriteDescriptorCacheItems(const uint256& desc_id, const DescriptorCache& cache)
265 : : {
266 [ + + + - ]: 441705 : for (const auto& parent_xpub_pair : cache.GetCachedParentExtPubKeys()) {
267 [ - + + - ]: 3713 : if (!WriteDescriptorParentCache(parent_xpub_pair.second, desc_id, parent_xpub_pair.first)) {
268 : 0 : return false;
269 : : }
270 : 0 : }
271 [ + + ]: 448984 : for (const auto& derived_xpub_map_pair : cache.GetCachedDerivedExtPubKeys()) {
272 [ + + + - ]: 21984 : for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) {
273 [ - + + - ]: 10992 : if (!WriteDescriptorDerivedCache(derived_xpub_pair.second, desc_id, derived_xpub_map_pair.first, derived_xpub_pair.first)) {
274 : 0 : return false;
275 : : }
276 : : }
277 : 0 : }
278 [ + + + - ]: 440986 : for (const auto& lh_xpub_pair : cache.GetCachedLastHardenedExtPubKeys()) {
279 [ - + + - ]: 2994 : if (!WriteDescriptorLastHardenedCache(lh_xpub_pair.second, desc_id, lh_xpub_pair.first)) {
280 : 0 : return false;
281 : : }
282 : 0 : }
283 : 437992 : return true;
284 : : }
285 : :
286 : 1 : bool WalletBatch::WriteLockedUTXO(const COutPoint& output)
287 : : {
288 [ + - ]: 2 : return WriteIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)), uint8_t{'1'});
289 : : }
290 : :
291 : 58 : bool WalletBatch::EraseLockedUTXO(const COutPoint& output)
292 : : {
293 [ + - ]: 116 : return EraseIC(std::make_pair(DBKeys::LOCKED_UTXO, std::make_pair(output.hash, output.n)));
294 : : }
295 : :
296 : 171 : bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
297 : : {
298 : 171 : LOCK(pwallet->cs_wallet);
299 : 171 : try {
300 [ + - ]: 171 : CPubKey vchPubKey;
301 [ + - ]: 171 : ssKey >> vchPubKey;
302 [ - + ]: 171 : if (!vchPubKey.IsValid())
303 : : {
304 [ - - + - ]: 171 : strErr = "Error reading wallet database: CPubKey corrupt";
305 : : return false;
306 : : }
307 : 171 : CKey key;
308 : 171 : CPrivKey pkey;
309 : 171 : uint256 hash;
310 : :
311 [ + - ]: 171 : ssValue >> pkey;
312 : :
313 : : // Old wallets store keys as DBKeys::KEY [pubkey] => [privkey]
314 : : // ... which was slow for wallets with lots of keys, because the public key is re-derived from the private key
315 : : // using EC operations as a checksum.
316 : : // Newer wallets store keys as DBKeys::KEY [pubkey] => [privkey][hash(pubkey,privkey)], which is much faster while
317 : : // remaining backwards-compatible.
318 : 171 : try
319 : : {
320 [ + - ]: 171 : ssValue >> hash;
321 : : }
322 [ - - ]: 0 : catch (const std::ios_base::failure&) {}
323 : :
324 : 171 : bool fSkipCheck = false;
325 : :
326 [ + - ]: 171 : if (!hash.IsNull())
327 : : {
328 : : // hash pubkey/privkey to accelerate wallet load
329 : 171 : std::vector<unsigned char> vchKey;
330 [ + - ]: 171 : vchKey.reserve(vchPubKey.size() + pkey.size());
331 [ + - ]: 171 : vchKey.insert(vchKey.end(), vchPubKey.begin(), vchPubKey.end());
332 [ + - ]: 171 : vchKey.insert(vchKey.end(), pkey.begin(), pkey.end());
333 : :
334 [ + - - + ]: 171 : if (Hash(vchKey) != hash)
335 : : {
336 [ # # ]: 0 : strErr = "Error reading wallet database: CPubKey/CPrivKey corrupt";
337 : 0 : return false;
338 : : }
339 : :
340 : 171 : fSkipCheck = true;
341 : 171 : }
342 : :
343 [ + - - + ]: 171 : if (!key.Load(pkey, vchPubKey, fSkipCheck))
344 : : {
345 [ # # ]: 0 : strErr = "Error reading wallet database: CPrivKey corrupt";
346 : : return false;
347 : : }
348 [ + - + - : 171 : if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadKey(key, vchPubKey))
- + ]
349 : : {
350 [ # # ]: 0 : strErr = "Error reading wallet database: LegacyDataSPKM::LoadKey failed";
351 : : return false;
352 : : }
353 [ - - ]: 171 : } catch (const std::exception& e) {
354 [ - - ]: 0 : if (strErr.empty()) {
355 [ - - ]: 0 : strErr = e.what();
356 : : }
357 : 0 : return false;
358 : 0 : }
359 : 171 : return true;
360 : 171 : }
361 : :
362 : 24 : bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
363 : : {
364 : 24 : LOCK(pwallet->cs_wallet);
365 : 24 : try {
366 [ + - ]: 24 : CPubKey vchPubKey;
367 [ + - ]: 24 : ssKey >> vchPubKey;
368 [ - + ]: 24 : if (!vchPubKey.IsValid())
369 : : {
370 [ - - + - ]: 24 : strErr = "Error reading wallet database: CPubKey corrupt";
371 : : return false;
372 : : }
373 : 24 : std::vector<unsigned char> vchPrivKey;
374 [ + - ]: 24 : ssValue >> vchPrivKey;
375 : :
376 : : // Get the checksum and check it
377 : 24 : bool checksum_valid = false;
378 [ + - ]: 24 : if (!ssValue.eof()) {
379 : 24 : uint256 checksum;
380 [ + - ]: 24 : ssValue >> checksum;
381 [ + - - + ]: 24 : if (!(checksum_valid = Hash(vchPrivKey) == checksum)) {
382 [ # # ]: 0 : strErr = "Error reading wallet database: Encrypted key corrupt";
383 : : return false;
384 : : }
385 : : }
386 : :
387 [ + - + - : 24 : if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCryptedKey(vchPubKey, vchPrivKey, checksum_valid))
- + ]
388 : : {
389 [ # # ]: 0 : strErr = "Error reading wallet database: LegacyDataSPKM::LoadCryptedKey failed";
390 : : return false;
391 : : }
392 [ # # ]: 0 : } catch (const std::exception& e) {
393 [ - - ]: 0 : if (strErr.empty()) {
394 [ - - ]: 0 : strErr = e.what();
395 : : }
396 : 0 : return false;
397 : 0 : }
398 : 24 : return true;
399 : 24 : }
400 : :
401 : 19 : bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr)
402 : : {
403 : 19 : LOCK(pwallet->cs_wallet);
404 : 19 : try {
405 : : // Master encryption key is loaded into only the wallet and not any of the ScriptPubKeyMans.
406 : 19 : unsigned int nID;
407 [ + - ]: 19 : ssKey >> nID;
408 [ + - ]: 19 : CMasterKey kMasterKey;
409 [ + - ]: 19 : ssValue >> kMasterKey;
410 [ - + ]: 19 : if(pwallet->mapMasterKeys.count(nID) != 0)
411 : : {
412 [ # # ]: 0 : strErr = strprintf("Error reading wallet database: duplicate CMasterKey id %u", nID);
413 : 0 : return false;
414 : : }
415 [ + - + - ]: 19 : pwallet->mapMasterKeys[nID] = kMasterKey;
416 [ + - ]: 19 : if (pwallet->nMasterKeyMaxID < nID)
417 : 19 : pwallet->nMasterKeyMaxID = nID;
418 : :
419 [ # # ]: 0 : } catch (const std::exception& e) {
420 [ - - ]: 0 : if (strErr.empty()) {
421 [ - - ]: 0 : strErr = e.what();
422 : : }
423 : 0 : return false;
424 : 0 : }
425 : 19 : return true;
426 : 19 : }
427 : :
428 : 27 : bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr)
429 : : {
430 : 27 : LOCK(pwallet->cs_wallet);
431 : 27 : try {
432 : 27 : CHDChain chain;
433 [ + - ]: 27 : ssValue >> chain;
434 [ + - + - ]: 27 : pwallet->GetOrCreateLegacyDataSPKM()->LoadHDChain(chain);
435 [ - - ]: 0 : } catch (const std::exception& e) {
436 [ - - ]: 0 : if (strErr.empty()) {
437 [ - - ]: 0 : strErr = e.what();
438 : : }
439 : 0 : return false;
440 : 0 : }
441 : : return true;
442 : 27 : }
443 : :
444 : 888 : static DBErrors LoadMinVersion(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
445 : : {
446 : 888 : AssertLockHeld(pwallet->cs_wallet);
447 : 888 : int nMinVersion = 0;
448 [ + + ]: 888 : if (batch.Read(DBKeys::MINVERSION, nMinVersion)) {
449 [ + - ]: 325 : if (nMinVersion > FEATURE_LATEST)
450 : : return DBErrors::TOO_NEW;
451 : 325 : pwallet->LoadMinVersion(nMinVersion);
452 : : }
453 : : return DBErrors::LOAD_OK;
454 : : }
455 : :
456 : 888 : static DBErrors LoadWalletFlags(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
457 : : {
458 : 888 : AssertLockHeld(pwallet->cs_wallet);
459 : 888 : uint64_t flags;
460 [ + + ]: 888 : if (batch.Read(DBKeys::FLAGS, flags)) {
461 [ - + ]: 325 : if (!pwallet->LoadWalletFlags(flags)) {
462 : 0 : pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n");
463 : 0 : return DBErrors::TOO_NEW;
464 : : }
465 : : // All wallets must be descriptor wallets unless opened with a bdb_ro db
466 : : // bdb_ro is only used for legacy to descriptor migration.
467 [ + + + - : 650 : if (pwallet->GetDatabase().Format() != "bdb_ro" && !pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
+ - - + ]
468 : 0 : return DBErrors::LEGACY_WALLET;
469 : : }
470 : : }
471 : : return DBErrors::LOAD_OK;
472 : : }
473 : :
474 : : struct LoadResult
475 : : {
476 : : DBErrors m_result{DBErrors::LOAD_OK};
477 : : int m_records{0};
478 : : };
479 : :
480 : : using LoadFunc = std::function<DBErrors(CWallet* pwallet, DataStream& key, DataStream& value, std::string& err)>;
481 : 23737 : static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, DataStream& prefix, LoadFunc load_func)
482 : : {
483 : 23737 : LoadResult result;
484 : 23737 : DataStream ssKey;
485 : 23737 : DataStream ssValue{};
486 : :
487 [ + - ]: 23737 : Assume(!prefix.empty());
488 [ + - ]: 23737 : std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
489 [ - + ]: 23737 : if (!cursor) {
490 [ # # ]: 0 : pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", key);
491 : 0 : result.m_result = DBErrors::CORRUPT;
492 : 0 : return result;
493 : : }
494 : :
495 : 74035 : while (true) {
496 [ + - ]: 48886 : DatabaseCursor::Status status = cursor->Next(ssKey, ssValue);
497 [ + + ]: 48886 : if (status == DatabaseCursor::Status::DONE) {
498 : : break;
499 [ - + ]: 25149 : } else if (status == DatabaseCursor::Status::FAIL) {
500 [ # # ]: 0 : pwallet->WalletLogPrintf("Error reading next '%s' record for wallet database\n", key);
501 : 0 : result.m_result = DBErrors::CORRUPT;
502 : 0 : return result;
503 : : }
504 [ + - ]: 25149 : std::string type;
505 [ + - ]: 25149 : ssKey >> type;
506 [ - + ]: 25149 : assert(type == key);
507 [ + - ]: 25149 : std::string error;
508 [ + - ]: 25149 : DBErrors record_res = load_func(pwallet, ssKey, ssValue, error);
509 [ + + ]: 25149 : if (record_res != DBErrors::LOAD_OK) {
510 [ + - ]: 3 : pwallet->WalletLogPrintf("%s\n", error);
511 : : }
512 [ + + ]: 25149 : result.m_result = std::max(result.m_result, record_res);
513 : 25149 : ++result.m_records;
514 : 25149 : }
515 : 23737 : return result;
516 : 23737 : }
517 : :
518 : 14253 : static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std::string& key, LoadFunc load_func)
519 : : {
520 : 14253 : DataStream prefix;
521 [ + - ]: 14253 : prefix << key;
522 [ + - + - ]: 28506 : return LoadRecords(pwallet, batch, key, prefix, load_func);
523 : 14253 : }
524 : :
525 : 32 : bool HasLegacyRecords(CWallet& wallet)
526 : : {
527 : 32 : const auto& batch = wallet.GetDatabase().MakeBatch();
528 [ + - ]: 64 : return HasLegacyRecords(wallet, *batch);
529 : 32 : }
530 : :
531 : 322 : bool HasLegacyRecords(CWallet& wallet, DatabaseBatch& batch)
532 : : {
533 [ + + + - ]: 3309 : for (const auto& type : DBKeys::LEGACY_TYPES) {
534 : 2987 : DataStream key;
535 : 2987 : DataStream value{};
536 : 2987 : DataStream prefix;
537 : :
538 [ + - ]: 2987 : prefix << type;
539 [ + - ]: 2987 : std::unique_ptr<DatabaseCursor> cursor = batch.GetNewPrefixCursor(prefix);
540 [ - + ]: 2987 : if (!cursor) {
541 : : // Could only happen on a closed db, which means there is an error in the code flow.
542 [ # # ]: 0 : wallet.WalletLogPrintf("Error getting database cursor for '%s' records", type);
543 [ # # # # ]: 0 : throw std::runtime_error(strprintf("Error getting database cursor for '%s' records", type));
544 : : }
545 : :
546 [ + - ]: 2987 : DatabaseCursor::Status status = cursor->Next(key, value);
547 [ + + ]: 2987 : if (status != DatabaseCursor::Status::DONE) {
548 : 30 : return true;
549 : : }
550 : 2987 : }
551 : : return false;
552 : : }
553 : :
554 : 888 : static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
555 : : {
556 : 888 : AssertLockHeld(pwallet->cs_wallet);
557 : 888 : DBErrors result = DBErrors::LOAD_OK;
558 : :
559 : : // Make sure descriptor wallets don't have any legacy records
560 [ + + ]: 888 : if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
561 [ + + ]: 290 : if (HasLegacyRecords(*pwallet, batch)) {
562 : 1 : pwallet->WalletLogPrintf("Error: Unexpected legacy entry found in descriptor wallet %s. The wallet might have been tampered with or created with malicious intent.\n", pwallet->GetName());
563 : 1 : return DBErrors::UNEXPECTED_LEGACY_ENTRY;
564 : : }
565 : :
566 : : return DBErrors::LOAD_OK;
567 : : }
568 : :
569 : : // Load HD Chain
570 : : // Note: There should only be one HDCHAIN record with no data following the type
571 [ + - ]: 598 : LoadResult hd_chain_res = LoadRecords(pwallet, batch, DBKeys::HDCHAIN,
572 : 27 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
573 [ - + ]: 27 : return LoadHDChain(pwallet, value, err) ? DBErrors:: LOAD_OK : DBErrors::CORRUPT;
574 : : });
575 [ + - ]: 598 : result = std::max(result, hd_chain_res.m_result);
576 : :
577 : : // Load unencrypted keys
578 [ + - ]: 598 : LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::KEY,
579 : 171 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
580 [ - + ]: 171 : return LoadKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT;
581 : : });
582 [ + - ]: 598 : result = std::max(result, key_res.m_result);
583 : :
584 : : // Load encrypted keys
585 [ + - ]: 598 : LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::CRYPTED_KEY,
586 : 24 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
587 [ - + ]: 24 : return LoadCryptedKey(pwallet, key, value, err) ? DBErrors::LOAD_OK : DBErrors::CORRUPT;
588 : : });
589 [ + - ]: 598 : result = std::max(result, ckey_res.m_result);
590 : :
591 : : // Load scripts
592 [ + - ]: 598 : LoadResult script_res = LoadRecords(pwallet, batch, DBKeys::CSCRIPT,
593 : 81 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
594 : 81 : uint160 hash;
595 : 81 : key >> hash;
596 : 81 : CScript script;
597 [ + - ]: 81 : value >> script;
598 [ + - + - : 81 : if (!pwallet->GetOrCreateLegacyDataSPKM()->LoadCScript(script))
- + ]
599 : : {
600 [ - - ]: 81 : strErr = "Error reading wallet database: LegacyDataSPKM::LoadCScript failed";
601 : : return DBErrors::NONCRITICAL_ERROR;
602 : : }
603 : : return DBErrors::LOAD_OK;
604 : 81 : });
605 [ + - ]: 598 : result = std::max(result, script_res.m_result);
606 : :
607 : : // Check whether rewrite is needed
608 [ + + ]: 598 : if (ckey_res.m_records > 0) {
609 : : // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
610 [ - + - - ]: 4 : if (last_client == 40000 || last_client == 50000) result = std::max(result, DBErrors::NEED_REWRITE);
611 : : }
612 : :
613 : : // Load keymeta
614 [ + - ]: 598 : std::map<uint160, CHDChain> hd_chains;
615 : 0 : LoadResult keymeta_res = LoadRecords(pwallet, batch, DBKeys::KEYMETA,
616 [ + - ]: 598 : [&hd_chains] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
617 : 199 : CPubKey vchPubKey;
618 : 199 : key >> vchPubKey;
619 : 199 : CKeyMetadata keyMeta;
620 [ + - ]: 199 : value >> keyMeta;
621 [ + - + - : 199 : pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyMetadata(vchPubKey.GetID(), keyMeta);
+ - ]
622 : :
623 : : // Extract some CHDChain info from this metadata if it has any
624 [ + - + + : 199 : if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
+ - ]
625 : : // Get the path from the key origin or from the path string
626 : : // Not applicable when path is "s" or "m" as those indicate a seed
627 : : // See https://github.com/bitcoin/bitcoin/pull/12924
628 : 190 : bool internal = false;
629 : 190 : uint32_t index = 0;
630 [ + + + - ]: 190 : if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") {
631 : 157 : std::vector<uint32_t> path;
632 [ + - ]: 157 : if (keyMeta.has_key_origin) {
633 : : // We have a key origin, so pull it from its path vector
634 [ + - ]: 157 : path = keyMeta.key_origin.path;
635 : : } else {
636 : : // No key origin, have to parse the string
637 [ # # # # ]: 0 : if (!ParseHDKeypath(keyMeta.hdKeypath, path)) {
638 [ # # ]: 0 : strErr = "Error reading wallet database: keymeta with invalid HD keypath";
639 : : return DBErrors::NONCRITICAL_ERROR;
640 : : }
641 : : }
642 : :
643 : : // Extract the index and internal from the path
644 : : // Path string is m/0'/k'/i'
645 : : // Path vector is [0', k', i'] (but as ints OR'd with the hardened bit
646 : : // k == 0 for external, 1 for internal. i is the index
647 [ + + ]: 157 : if (path.size() != 3) {
648 [ + - ]: 1 : strErr = "Error reading wallet database: keymeta found with unexpected path";
649 : : return DBErrors::NONCRITICAL_ERROR;
650 : : }
651 [ - + ]: 156 : if (path[0] != 0x80000000) {
652 [ # # ]: 0 : strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000) for the element at index 0", path[0]);
653 : 0 : return DBErrors::NONCRITICAL_ERROR;
654 : : }
655 [ + + + - ]: 156 : if (path[1] != 0x80000000 && path[1] != (1 | 0x80000000)) {
656 [ # # ]: 0 : strErr = strprintf("Unexpected path index of 0x%08x (expected 0x80000000 or 0x80000001) for the element at index 1", path[1]);
657 : 0 : return DBErrors::NONCRITICAL_ERROR;
658 : : }
659 [ - + ]: 156 : if ((path[2] & 0x80000000) == 0) {
660 [ # # ]: 0 : strErr = strprintf("Unexpected path index of 0x%08x (expected to be greater than or equal to 0x80000000)", path[2]);
661 : 0 : return DBErrors::NONCRITICAL_ERROR;
662 : : }
663 : 156 : internal = path[1] == (1 | 0x80000000);
664 : 156 : index = path[2] & ~0x80000000;
665 : 157 : }
666 : :
667 : : // Insert a new CHDChain, or get the one that already exists
668 [ + - + + ]: 189 : auto [ins, inserted] = hd_chains.emplace(keyMeta.hd_seed_id, CHDChain());
669 [ + + ]: 189 : CHDChain& chain = ins->second;
670 [ + + ]: 189 : if (inserted) {
671 : : // For new chains, we want to default to VERSION_HD_BASE until we see an internal
672 : 33 : chain.nVersion = CHDChain::VERSION_HD_BASE;
673 : 33 : chain.seed_id = keyMeta.hd_seed_id;
674 : : }
675 [ + + ]: 189 : if (internal) {
676 : 62 : chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT;
677 [ + + ]: 86 : chain.nInternalChainCounter = std::max(chain.nInternalChainCounter, index + 1);
678 : : } else {
679 [ + + ]: 202 : chain.nExternalChainCounter = std::max(chain.nExternalChainCounter, index + 1);
680 : : }
681 : : }
682 : : return DBErrors::LOAD_OK;
683 : 199 : });
684 [ + + ]: 598 : result = std::max(result, keymeta_res.m_result);
685 : :
686 : : // Set inactive chains
687 [ + + ]: 598 : if (!hd_chains.empty()) {
688 [ + - ]: 27 : LegacyDataSPKM* legacy_spkm = pwallet->GetLegacyDataSPKM();
689 [ + - ]: 27 : if (legacy_spkm) {
690 [ + + + + ]: 60 : for (const auto& [hd_seed_id, chain] : hd_chains) {
691 [ + + ]: 33 : if (hd_seed_id != legacy_spkm->GetHDChain().seed_id) {
692 [ + - ]: 6 : legacy_spkm->AddInactiveHDChain(chain);
693 : : }
694 : : }
695 : : } else {
696 [ # # ]: 0 : pwallet->WalletLogPrintf("Inactive HD Chains found but no Legacy ScriptPubKeyMan\n");
697 : 0 : result = DBErrors::CORRUPT;
698 : : }
699 : : }
700 : :
701 : : // Load watchonly scripts
702 [ + - ]: 598 : LoadResult watch_script_res = LoadRecords(pwallet, batch, DBKeys::WATCHS,
703 : 44 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
704 : 44 : CScript script;
705 [ + - ]: 44 : key >> script;
706 : 44 : uint8_t fYes;
707 [ + - ]: 44 : value >> fYes;
708 [ + - ]: 44 : if (fYes == '1') {
709 [ + - + - ]: 44 : pwallet->GetOrCreateLegacyDataSPKM()->LoadWatchOnly(script);
710 : : }
711 : 44 : return DBErrors::LOAD_OK;
712 : 44 : });
713 [ + - ]: 598 : result = std::max(result, watch_script_res.m_result);
714 : :
715 : : // Load watchonly meta
716 [ + - ]: 598 : LoadResult watch_meta_res = LoadRecords(pwallet, batch, DBKeys::WATCHMETA,
717 : 44 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
718 : 44 : CScript script;
719 [ + - ]: 44 : key >> script;
720 : 44 : CKeyMetadata keyMeta;
721 [ + - ]: 44 : value >> keyMeta;
722 [ + - + - : 44 : pwallet->GetOrCreateLegacyDataSPKM()->LoadScriptMetadata(CScriptID(script), keyMeta);
+ - ]
723 : 44 : return DBErrors::LOAD_OK;
724 : 44 : });
725 [ + - ]: 598 : result = std::max(result, watch_meta_res.m_result);
726 : :
727 : : // Deal with old "wkey" and "defaultkey" records.
728 : : // These are not actually loaded, but we need to check for them
729 : :
730 : : // We don't want or need the default key, but if there is one set,
731 : : // we want to make sure that it is valid so that we can detect corruption
732 : : // Note: There should only be one DEFAULTKEY with nothing trailing the type
733 [ + - ]: 598 : LoadResult default_key_res = LoadRecords(pwallet, batch, DBKeys::DEFAULTKEY,
734 : 0 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
735 [ # # ]: 0 : CPubKey default_pubkey;
736 : 0 : try {
737 [ # # ]: 0 : value >> default_pubkey;
738 [ - - ]: 0 : } catch (const std::exception& e) {
739 [ - - ]: 0 : err = e.what();
740 : 0 : return DBErrors::CORRUPT;
741 : 0 : }
742 [ # # ]: 0 : if (!default_pubkey.IsValid()) {
743 : 0 : err = "Error reading wallet database: Default Key corrupt";
744 : 0 : return DBErrors::CORRUPT;
745 : : }
746 : : return DBErrors::LOAD_OK;
747 : : });
748 [ + - ]: 598 : result = std::max(result, default_key_res.m_result);
749 : :
750 : : // "wkey" records are unsupported, if we see any, throw an error
751 [ + - ]: 598 : LoadResult wkey_res = LoadRecords(pwallet, batch, DBKeys::OLD_KEY,
752 : 0 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
753 : 0 : err = "Found unsupported 'wkey' record, try loading with version 0.18";
754 : 0 : return DBErrors::LOAD_FAIL;
755 : : });
756 [ + - ]: 598 : result = std::max(result, wkey_res.m_result);
757 : :
758 [ + - ]: 598 : if (result <= DBErrors::NONCRITICAL_ERROR) {
759 : : // Only do logging and time first key update if there were no critical errors
760 : 598 : pwallet->WalletLogPrintf("Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n",
761 [ + - ]: 598 : key_res.m_records, ckey_res.m_records, keymeta_res.m_records, key_res.m_records + ckey_res.m_records);
762 : : }
763 : :
764 : 598 : return result;
765 : 598 : }
766 : :
767 : : template<typename... Args>
768 : 9484 : static DataStream PrefixStream(const Args&... args)
769 : : {
770 : 9484 : DataStream prefix;
771 [ + - ]: 9484 : SerializeMany(prefix, args...);
772 : 9484 : return prefix;
773 : 0 : }
774 : :
775 : 888 : static DBErrors LoadDescriptorWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
776 : : {
777 : 888 : AssertLockHeld(pwallet->cs_wallet);
778 : :
779 : : // Load descriptor record
780 : 888 : int num_keys = 0;
781 : 888 : int num_ckeys= 0;
782 [ + - ]: 888 : LoadResult desc_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTOR,
783 : 888 : [&batch, &num_keys, &num_ckeys, &last_client] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
784 : 2373 : DBErrors result = DBErrors::LOAD_OK;
785 : :
786 : 2373 : uint256 id;
787 : 2373 : key >> id;
788 : 2373 : WalletDescriptor desc;
789 : 2373 : try {
790 [ + + ]: 2373 : value >> desc;
791 [ - + ]: 1 : } catch (const std::ios_base::failure& e) {
792 [ + - ]: 1 : strErr = strprintf("Error: Unrecognized descriptor found in wallet %s. ", pwallet->GetName());
793 : 1 : strErr += (last_client > CLIENT_VERSION) ? "The wallet might had been created on a newer version. " :
794 [ + - ]: 1 : "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. ";
795 [ + - ]: 1 : strErr += "Please try running the latest software version";
796 : : // Also include error details
797 [ + - ]: 1 : strErr = strprintf("%s\nDetails: %s", strErr, e.what());
798 : 1 : return DBErrors::UNKNOWN_DESCRIPTOR;
799 : 1 : }
800 [ + - ]: 2372 : DescriptorScriptPubKeyMan& spkm = pwallet->LoadDescriptorScriptPubKeyMan(id, desc);
801 : :
802 : : // Prior to doing anything with this spkm, verify ID compatibility
803 [ + - + + ]: 2372 : if (id != spkm.GetID()) {
804 [ + - ]: 1 : strErr = "The descriptor ID calculated by the wallet differs from the one in DB";
805 : : return DBErrors::CORRUPT;
806 : : }
807 : :
808 : 2371 : DescriptorCache cache;
809 : :
810 : : // Get key cache for this descriptor
811 [ + - ]: 2371 : DataStream prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCACHE, id);
812 : 0 : LoadResult key_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCACHE, prefix,
813 [ + - ]: 2371 : [&id, &cache] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
814 : 2258 : bool parent = true;
815 : 2258 : uint256 desc_id;
816 : 2258 : uint32_t key_exp_index;
817 : 2258 : uint32_t der_index;
818 : 2258 : key >> desc_id;
819 [ - + ]: 2258 : assert(desc_id == id);
820 : 2258 : key >> key_exp_index;
821 : :
822 : : // if the der_index exists, it's a derived xpub
823 : 2258 : try
824 : : {
825 [ + + ]: 2258 : key >> der_index;
826 : : parent = false;
827 : : }
828 : 2089 : catch (...) {}
829 : :
830 : 2258 : std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
831 [ + - ]: 2258 : value >> ser_xpub;
832 [ + - ]: 2258 : CExtPubKey xpub;
833 [ + - ]: 2258 : xpub.Decode(ser_xpub.data());
834 [ + + ]: 2258 : if (parent) {
835 [ + - ]: 2089 : cache.CacheParentExtPubKey(key_exp_index, xpub);
836 : : } else {
837 [ + - ]: 169 : cache.CacheDerivedExtPubKey(key_exp_index, der_index, xpub);
838 : : }
839 : 2258 : return DBErrors::LOAD_OK;
840 : 2258 : });
841 [ + - ]: 2371 : result = std::max(result, key_cache_res.m_result);
842 : :
843 : : // Get last hardened cache for this descriptor
844 [ + - ]: 4742 : prefix = PrefixStream(DBKeys::WALLETDESCRIPTORLHCACHE, id);
845 : 0 : LoadResult lh_cache_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORLHCACHE, prefix,
846 [ + - ]: 2371 : [&id, &cache] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
847 : 2070 : uint256 desc_id;
848 : 2070 : uint32_t key_exp_index;
849 : 2070 : key >> desc_id;
850 [ - + ]: 2070 : assert(desc_id == id);
851 : 2070 : key >> key_exp_index;
852 : :
853 : 2070 : std::vector<unsigned char> ser_xpub(BIP32_EXTKEY_SIZE);
854 [ + - ]: 2070 : value >> ser_xpub;
855 [ + - ]: 2070 : CExtPubKey xpub;
856 [ + - ]: 2070 : xpub.Decode(ser_xpub.data());
857 [ + - ]: 2070 : cache.CacheLastHardenedExtPubKey(key_exp_index, xpub);
858 : 2070 : return DBErrors::LOAD_OK;
859 : 2070 : });
860 [ + - ]: 2371 : result = std::max(result, lh_cache_res.m_result);
861 : :
862 : : // Set the cache for this descriptor
863 [ + - ]: 2371 : auto spk_man = (DescriptorScriptPubKeyMan*)pwallet->GetScriptPubKeyMan(id);
864 [ - + ]: 2371 : assert(spk_man);
865 [ + - ]: 2371 : spk_man->SetCache(cache);
866 : :
867 : : // Get unencrypted keys
868 [ + - ]: 4742 : prefix = PrefixStream(DBKeys::WALLETDESCRIPTORKEY, id);
869 : 0 : LoadResult key_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORKEY, prefix,
870 [ + - ]: 2371 : [&id, &spk_man] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
871 : 2055 : uint256 desc_id;
872 : 2055 : CPubKey pubkey;
873 : 2055 : key >> desc_id;
874 [ - + ]: 2055 : assert(desc_id == id);
875 : 2055 : key >> pubkey;
876 [ - + ]: 2055 : if (!pubkey.IsValid())
877 : : {
878 : 0 : strErr = "Error reading wallet database: descriptor unencrypted key CPubKey corrupt";
879 : 0 : return DBErrors::CORRUPT;
880 : : }
881 : 2055 : CKey privkey;
882 : 2055 : CPrivKey pkey;
883 : 2055 : uint256 hash;
884 : :
885 [ + - ]: 2055 : value >> pkey;
886 [ + - ]: 2055 : value >> hash;
887 : :
888 : : // hash pubkey/privkey to accelerate wallet load
889 : 2055 : std::vector<unsigned char> to_hash;
890 [ + - ]: 2055 : to_hash.reserve(pubkey.size() + pkey.size());
891 [ + - ]: 2055 : to_hash.insert(to_hash.end(), pubkey.begin(), pubkey.end());
892 [ + - ]: 2055 : to_hash.insert(to_hash.end(), pkey.begin(), pkey.end());
893 : :
894 [ + - - + ]: 2055 : if (Hash(to_hash) != hash)
895 : : {
896 [ # # ]: 0 : strErr = "Error reading wallet database: descriptor unencrypted key CPubKey/CPrivKey corrupt";
897 : : return DBErrors::CORRUPT;
898 : : }
899 : :
900 [ + - - + ]: 2055 : if (!privkey.Load(pkey, pubkey, true))
901 : : {
902 [ - - ]: 2055 : strErr = "Error reading wallet database: descriptor unencrypted key CPrivKey corrupt";
903 : : return DBErrors::CORRUPT;
904 : : }
905 [ + - + - ]: 2055 : spk_man->AddKey(pubkey.GetID(), privkey);
906 : : return DBErrors::LOAD_OK;
907 : 2055 : });
908 [ + - ]: 2371 : result = std::max(result, key_res.m_result);
909 : 2371 : num_keys = key_res.m_records;
910 : :
911 : : // Get encrypted keys
912 [ + - ]: 4742 : prefix = PrefixStream(DBKeys::WALLETDESCRIPTORCKEY, id);
913 : 0 : LoadResult ckey_res = LoadRecords(pwallet, batch, DBKeys::WALLETDESCRIPTORCKEY, prefix,
914 [ + - ]: 2371 : [&id, &spk_man] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
915 : 231 : uint256 desc_id;
916 : 231 : CPubKey pubkey;
917 : 231 : key >> desc_id;
918 [ - + ]: 231 : assert(desc_id == id);
919 : 231 : key >> pubkey;
920 [ - + ]: 231 : if (!pubkey.IsValid())
921 : : {
922 : 0 : err = "Error reading wallet database: descriptor encrypted key CPubKey corrupt";
923 : 0 : return DBErrors::CORRUPT;
924 : : }
925 : 231 : std::vector<unsigned char> privkey;
926 [ + - ]: 231 : value >> privkey;
927 : :
928 [ + - + - ]: 231 : spk_man->AddCryptedKey(pubkey.GetID(), pubkey, privkey);
929 : 231 : return DBErrors::LOAD_OK;
930 : 231 : });
931 [ + - ]: 2371 : result = std::max(result, ckey_res.m_result);
932 : 2371 : num_ckeys = ckey_res.m_records;
933 : :
934 : 2371 : return result;
935 : 2373 : });
936 : :
937 [ + + ]: 888 : if (desc_res.m_result <= DBErrors::NONCRITICAL_ERROR) {
938 : : // Only log if there are no critical errors
939 : 886 : pwallet->WalletLogPrintf("Descriptors: %u, Descriptor Keys: %u plaintext, %u encrypted, %u total.\n",
940 : 886 : desc_res.m_records, num_keys, num_ckeys, num_keys + num_ckeys);
941 : : }
942 : :
943 : 888 : return desc_res.m_result;
944 : : }
945 : :
946 : 887 : static DBErrors LoadAddressBookRecords(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
947 : : {
948 : 887 : AssertLockHeld(pwallet->cs_wallet);
949 : 887 : DBErrors result = DBErrors::LOAD_OK;
950 : :
951 : : // Load name record
952 [ + - ]: 887 : LoadResult name_res = LoadRecords(pwallet, batch, DBKeys::NAME,
953 : 2892 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
954 [ + - ]: 2892 : std::string strAddress;
955 [ + - ]: 2892 : key >> strAddress;
956 [ + - ]: 2892 : std::string label;
957 [ + - ]: 2892 : value >> label;
958 [ + - + - : 5784 : pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
+ - ]
959 : 2892 : return DBErrors::LOAD_OK;
960 : 2892 : });
961 [ + - ]: 887 : result = std::max(result, name_res.m_result);
962 : :
963 : : // Load purpose record
964 [ + - ]: 887 : LoadResult purpose_res = LoadRecords(pwallet, batch, DBKeys::PURPOSE,
965 : 2892 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
966 [ + - ]: 2892 : std::string strAddress;
967 [ + - ]: 2892 : key >> strAddress;
968 [ + - ]: 2892 : std::string purpose_str;
969 [ + - ]: 2892 : value >> purpose_str;
970 [ - + ]: 2892 : std::optional<AddressPurpose> purpose{PurposeFromString(purpose_str)};
971 [ - + ]: 2892 : if (!purpose) {
972 [ # # ]: 0 : pwallet->WalletLogPrintf("Warning: nonstandard purpose string '%s' for address '%s'\n", purpose_str, strAddress);
973 : : }
974 [ + - + - ]: 2892 : pwallet->m_address_book[DecodeDestination(strAddress)].purpose = purpose;
975 : 2892 : return DBErrors::LOAD_OK;
976 : 2892 : });
977 [ + - ]: 887 : result = std::max(result, purpose_res.m_result);
978 : :
979 : : // Load destination data record
980 [ + - ]: 887 : LoadResult dest_res = LoadRecords(pwallet, batch, DBKeys::DESTDATA,
981 : 10 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
982 [ + - ]: 10 : std::string strAddress, strKey, strValue;
983 [ + - ]: 10 : key >> strAddress;
984 [ + - ]: 10 : key >> strKey;
985 [ + - ]: 10 : value >> strValue;
986 [ + - ]: 10 : const CTxDestination& dest{DecodeDestination(strAddress)};
987 [ + + ]: 10 : if (strKey.compare("used") == 0) {
988 : : // Load "used" key indicating if an IsMine address has
989 : : // previously been spent from with avoid_reuse option enabled.
990 : : // The strValue is not used for anything currently, but could
991 : : // hold more information in the future. Current values are just
992 : : // "1" or "p" for present (which was written prior to
993 : : // f5ba424cd44619d9b9be88b8593d69a7ba96db26).
994 [ + - ]: 7 : pwallet->LoadAddressPreviouslySpent(dest);
995 [ + - ]: 3 : } else if (strKey.starts_with("rr")) {
996 : : // Load "rr##" keys where ## is a decimal number, and strValue
997 : : // is a serialized RecentRequestEntry object.
998 [ + - + - ]: 6 : pwallet->LoadAddressReceiveRequest(dest, strKey.substr(2), strValue);
999 : : }
1000 : 10 : return DBErrors::LOAD_OK;
1001 : 10 : });
1002 [ + - ]: 887 : result = std::max(result, dest_res.m_result);
1003 : :
1004 : 887 : return result;
1005 : : }
1006 : :
1007 : 887 : static DBErrors LoadTxRecords(CWallet* pwallet, DatabaseBatch& batch, std::vector<uint256>& upgraded_txs, bool& any_unordered) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1008 : : {
1009 : 887 : AssertLockHeld(pwallet->cs_wallet);
1010 : 887 : DBErrors result = DBErrors::LOAD_OK;
1011 : :
1012 : : // Load tx record
1013 : 887 : any_unordered = false;
1014 : 0 : LoadResult tx_res = LoadRecords(pwallet, batch, DBKeys::TX,
1015 [ + - ]: 887 : [&any_unordered, &upgraded_txs] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1016 : 7607 : DBErrors result = DBErrors::LOAD_OK;
1017 : 7607 : uint256 hash;
1018 : 7607 : key >> hash;
1019 : : // LoadToWallet call below creates a new CWalletTx that fill_wtx
1020 : : // callback fills with transaction metadata.
1021 : 15214 : auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) {
1022 [ - + ]: 7607 : if(!new_tx) {
1023 : : // There's some corruption here since the tx we just tried to load was already in the wallet.
1024 : 0 : err = "Error: Corrupt transaction found. This can be fixed by removing transactions from wallet and rescanning.";
1025 : 0 : result = DBErrors::CORRUPT;
1026 : 0 : return false;
1027 : : }
1028 : 7607 : value >> wtx;
1029 [ + - ]: 7607 : if (wtx.GetHash() != hash)
1030 : : return false;
1031 : :
1032 : : // Undo serialize changes in 31600
1033 [ - + ]: 7607 : if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
1034 : : {
1035 [ # # ]: 0 : if (!value.empty())
1036 : : {
1037 : 0 : uint8_t fTmp;
1038 : 0 : uint8_t fUnused;
1039 [ # # ]: 0 : std::string unused_string;
1040 [ # # # # : 0 : value >> fTmp >> fUnused >> unused_string;
# # ]
1041 : 0 : pwallet->WalletLogPrintf("LoadWallet() upgrading tx ver=%d %d %s\n",
1042 [ # # # # ]: 0 : wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString());
1043 : 0 : wtx.fTimeReceivedIsTxTime = fTmp;
1044 : 0 : }
1045 : : else
1046 : : {
1047 [ # # ]: 0 : pwallet->WalletLogPrintf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString());
1048 : 0 : wtx.fTimeReceivedIsTxTime = 0;
1049 : : }
1050 : 0 : upgraded_txs.push_back(hash);
1051 : : }
1052 : :
1053 [ - + ]: 7607 : if (wtx.nOrderPos == -1)
1054 : 0 : any_unordered = true;
1055 : :
1056 : : return true;
1057 : 7607 : };
1058 [ + - - + ]: 7607 : if (!pwallet->LoadToWallet(hash, fill_wtx)) {
1059 : : // Use std::max as fill_wtx may have already set result to CORRUPT
1060 [ # # ]: 0 : result = std::max(result, DBErrors::NEED_RESCAN);
1061 : : }
1062 : 7607 : return result;
1063 : : });
1064 [ + - ]: 887 : result = std::max(result, tx_res.m_result);
1065 : :
1066 : : // Load locked utxo record
1067 [ + - ]: 887 : LoadResult locked_utxo_res = LoadRecords(pwallet, batch, DBKeys::LOCKED_UTXO,
1068 : 2 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1069 : 2 : Txid hash;
1070 : 2 : uint32_t n;
1071 : 2 : key >> hash;
1072 : 2 : key >> n;
1073 : 2 : pwallet->LockCoin(COutPoint(hash, n));
1074 : 2 : return DBErrors::LOAD_OK;
1075 : : });
1076 [ + - ]: 887 : result = std::max(result, locked_utxo_res.m_result);
1077 : :
1078 : : // Load orderposnext record
1079 : : // Note: There should only be one ORDERPOSNEXT record with nothing trailing the type
1080 [ + - ]: 887 : LoadResult order_pos_res = LoadRecords(pwallet, batch, DBKeys::ORDERPOSNEXT,
1081 : 172 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) {
1082 : 172 : try {
1083 [ + - ]: 172 : value >> pwallet->nOrderPosNext;
1084 [ - - ]: 0 : } catch (const std::exception& e) {
1085 [ - - ]: 0 : err = e.what();
1086 : 0 : return DBErrors::NONCRITICAL_ERROR;
1087 : 0 : }
1088 : 172 : return DBErrors::LOAD_OK;
1089 : : });
1090 [ + - ]: 887 : result = std::max(result, order_pos_res.m_result);
1091 : :
1092 : : // After loading all tx records, abandon any coinbase that is no longer in the active chain.
1093 : : // This could happen during an external wallet load, or if the user replaced the chain data.
1094 [ + + + + ]: 8494 : for (auto& [id, wtx] : pwallet->mapWallet) {
1095 [ + + ]: 14180 : if (wtx.IsCoinBase() && wtx.isInactive()) {
1096 : 592 : pwallet->AbandonTransaction(wtx);
1097 : : }
1098 : : }
1099 : :
1100 : 887 : return result;
1101 : : }
1102 : :
1103 : 887 : static DBErrors LoadActiveSPKMs(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1104 : : {
1105 : 887 : AssertLockHeld(pwallet->cs_wallet);
1106 : 887 : DBErrors result = DBErrors::LOAD_OK;
1107 : :
1108 : : // Load spk records
1109 [ + - ]: 887 : std::set<std::pair<OutputType, bool>> seen_spks;
1110 [ + - + - : 4435 : for (const auto& spk_key : {DBKeys::ACTIVEEXTERNALSPK, DBKeys::ACTIVEINTERNALSPK}) {
+ + - - ]
1111 : 0 : LoadResult spkm_res = LoadRecords(pwallet, batch, spk_key,
1112 [ + - ]: 1774 : [&seen_spks, &spk_key] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& strErr) {
1113 : 1978 : uint8_t output_type;
1114 : 1978 : key >> output_type;
1115 : 1978 : uint256 id;
1116 : 1978 : value >> id;
1117 : :
1118 : 1978 : bool internal = spk_key == DBKeys::ACTIVEINTERNALSPK;
1119 [ - + ]: 1978 : auto [it, insert] = seen_spks.emplace(static_cast<OutputType>(output_type), internal);
1120 [ - + ]: 1978 : if (!insert) {
1121 : 0 : strErr = "Multiple ScriptpubKeyMans specified for a single type";
1122 : 0 : return DBErrors::CORRUPT;
1123 : : }
1124 : 1978 : pwallet->LoadActiveScriptPubKeyMan(id, static_cast<OutputType>(output_type), /*internal=*/internal);
1125 : 1978 : return DBErrors::LOAD_OK;
1126 : : });
1127 [ + - ]: 3548 : result = std::max(result, spkm_res.m_result);
1128 [ + + - - ]: 2661 : }
1129 : 887 : return result;
1130 : 887 : }
1131 : :
1132 : 887 : static DBErrors LoadDecryptionKeys(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
1133 : : {
1134 : 887 : AssertLockHeld(pwallet->cs_wallet);
1135 : :
1136 : : // Load decryption key (mkey) records
1137 [ + - ]: 887 : LoadResult mkey_res = LoadRecords(pwallet, batch, DBKeys::MASTER_KEY,
1138 : 19 : [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) {
1139 [ - + ]: 19 : if (!LoadEncryptionKey(pwallet, key, value, err)) {
1140 : 0 : return DBErrors::CORRUPT;
1141 : : }
1142 : : return DBErrors::LOAD_OK;
1143 : : });
1144 : 887 : return mkey_res.m_result;
1145 : : }
1146 : :
1147 : 888 : DBErrors WalletBatch::LoadWallet(CWallet* pwallet)
1148 : : {
1149 : 888 : DBErrors result = DBErrors::LOAD_OK;
1150 : 888 : bool any_unordered = false;
1151 : 888 : std::vector<uint256> upgraded_txs;
1152 : :
1153 [ + - ]: 888 : LOCK(pwallet->cs_wallet);
1154 : :
1155 : : // Last client version to open this wallet
1156 : 888 : int last_client = CLIENT_VERSION;
1157 [ + - ]: 888 : bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client);
1158 [ + - + - ]: 888 : pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client);
1159 : :
1160 : 888 : try {
1161 [ + - + - ]: 888 : if ((result = LoadMinVersion(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result;
1162 : :
1163 : : // Load wallet flags, so they are known when processing other records.
1164 : : // The FLAGS key is absent during wallet creation.
1165 [ + - + - ]: 888 : if ((result = LoadWalletFlags(pwallet, *m_batch)) != DBErrors::LOAD_OK) return result;
1166 : :
1167 : : #ifndef ENABLE_EXTERNAL_SIGNER
1168 : : if (pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER)) {
1169 : : pwallet->WalletLogPrintf("Error: External signer wallet being loaded without external signer support compiled\n");
1170 : : return DBErrors::EXTERNAL_SIGNER_SUPPORT_REQUIRED;
1171 : : }
1172 : : #endif
1173 : :
1174 : : // Load legacy wallet keys
1175 [ + - + - ]: 888 : result = std::max(LoadLegacyWalletRecords(pwallet, *m_batch, last_client), result);
1176 : :
1177 : : // Load descriptors
1178 [ + - + + ]: 888 : result = std::max(LoadDescriptorWalletRecords(pwallet, *m_batch, last_client), result);
1179 : : // Early return if there are unknown descriptors. Later loading of ACTIVEINTERNALSPK and ACTIVEEXTERNALEXPK
1180 : : // may reference the unknown descriptor's ID which can result in a misleading corruption error
1181 : : // when in reality the wallet is simply too new.
1182 [ + + ]: 888 : if (result == DBErrors::UNKNOWN_DESCRIPTOR) return result;
1183 : :
1184 : : // Load address book
1185 [ + - + + ]: 887 : result = std::max(LoadAddressBookRecords(pwallet, *m_batch), result);
1186 : :
1187 : : // Load tx records
1188 [ + - + + ]: 887 : result = std::max(LoadTxRecords(pwallet, *m_batch, upgraded_txs, any_unordered), result);
1189 : :
1190 : : // Load SPKMs
1191 [ + - + + ]: 887 : result = std::max(LoadActiveSPKMs(pwallet, *m_batch), result);
1192 : :
1193 : : // Load decryption keys
1194 [ + - + + ]: 1771 : result = std::max(LoadDecryptionKeys(pwallet, *m_batch), result);
1195 : 0 : } catch (...) {
1196 : : // Exceptions that can be ignored or treated as non-critical are handled by the individual loading functions.
1197 : : // Any uncaught exceptions will be caught here and treated as critical.
1198 : 0 : result = DBErrors::CORRUPT;
1199 [ - - ]: 0 : }
1200 : :
1201 : : // Any wallet corruption at all: skip any rewriting or
1202 : : // upgrading, we don't want to make it worse.
1203 [ + + ]: 887 : if (result != DBErrors::LOAD_OK)
1204 : : return result;
1205 : :
1206 [ - + ]: 884 : for (const uint256& hash : upgraded_txs)
1207 [ # # # # ]: 0 : WriteTx(pwallet->mapWallet.at(hash));
1208 : :
1209 [ + + + + ]: 884 : if (!has_last_client || last_client != CLIENT_VERSION) // Update
1210 [ + - ]: 635 : m_batch->Write(DBKeys::VERSION, CLIENT_VERSION);
1211 : :
1212 [ - + ]: 884 : if (any_unordered)
1213 [ # # ]: 0 : result = pwallet->ReorderTransactions();
1214 : :
1215 : : // Upgrade all of the descriptor caches to cache the last hardened xpub
1216 : : // This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible
1217 : 884 : try {
1218 [ + - ]: 884 : pwallet->UpgradeDescriptorCache();
1219 : 0 : } catch (...) {
1220 : 0 : result = DBErrors::CORRUPT;
1221 [ - - ]: 0 : }
1222 : :
1223 : : // Since it was accidentally possible to "encrypt" a wallet with private keys disabled, we should check if this is
1224 : : // such a wallet and remove the encryption key records to avoid any future issues.
1225 : : // Although wallets without private keys should not have *ckey records, we should double check that.
1226 : : // Removing the mkey records is only safe if there are no *ckey records.
1227 [ + - + + : 884 : if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && pwallet->HasEncryptionKeys() && !pwallet->HaveCryptedKeys()) {
+ - + + +
- + - ]
1228 [ + - ]: 1 : pwallet->WalletLogPrintf("Detected extraneous encryption keys in this wallet without private keys. Removing extraneous encryption keys.\n");
1229 [ + - + + ]: 2 : for (const auto& [id, _] : pwallet->mapMasterKeys) {
1230 [ + - - + ]: 1 : if (!EraseMasterKey(id)) {
1231 [ # # ]: 0 : pwallet->WalletLogPrintf("Error: Unable to remove extraneous encryption key '%u'. Wallet corrupt.\n", id);
1232 : : return DBErrors::CORRUPT;
1233 : : }
1234 : : }
1235 : 1 : pwallet->mapMasterKeys.clear();
1236 : : }
1237 : :
1238 : 884 : return result;
1239 : 888 : }
1240 : :
1241 : 375 : static bool RunWithinTxn(WalletBatch& batch, std::string_view process_desc, const std::function<bool(WalletBatch&)>& func)
1242 : : {
1243 [ - + ]: 375 : if (!batch.TxnBegin()) {
1244 [ # # ]: 0 : LogDebug(BCLog::WALLETDB, "Error: cannot create db txn for %s\n", process_desc);
1245 : 0 : return false;
1246 : : }
1247 : :
1248 : : // Run procedure
1249 [ + + ]: 375 : if (!func(batch)) {
1250 [ + - ]: 1 : LogDebug(BCLog::WALLETDB, "Error: %s failed\n", process_desc);
1251 : 1 : batch.TxnAbort();
1252 : 1 : return false;
1253 : : }
1254 : :
1255 [ - + ]: 374 : if (!batch.TxnCommit()) {
1256 [ # # ]: 0 : LogDebug(BCLog::WALLETDB, "Error: cannot commit db txn for %s\n", process_desc);
1257 : 0 : return false;
1258 : : }
1259 : :
1260 : : // All good
1261 : : return true;
1262 : : }
1263 : :
1264 : 375 : bool RunWithinTxn(WalletDatabase& database, std::string_view process_desc, const std::function<bool(WalletBatch&)>& func)
1265 : : {
1266 : 375 : WalletBatch batch(database);
1267 [ + - ]: 375 : return RunWithinTxn(batch, process_desc, func);
1268 : 375 : }
1269 : :
1270 : 24 : bool WalletBatch::WriteAddressPreviouslySpent(const CTxDestination& dest, bool previously_spent)
1271 : : {
1272 [ + - + - ]: 96 : auto key{std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), std::string("used")))};
1273 [ + + + - : 48 : return previously_spent ? WriteIC(key, std::string("1")) : EraseIC(key);
+ - ]
1274 : 24 : }
1275 : :
1276 : 4 : bool WalletBatch::WriteAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& receive_request)
1277 : : {
1278 [ + - + - : 20 : return WriteIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)), receive_request);
+ - ]
1279 : : }
1280 : :
1281 : 1 : bool WalletBatch::EraseAddressReceiveRequest(const CTxDestination& dest, const std::string& id)
1282 : : {
1283 [ + - + - : 5 : return EraseIC(std::make_pair(DBKeys::DESTDATA, std::make_pair(EncodeDestination(dest), "rr" + id)));
+ - ]
1284 : : }
1285 : :
1286 : 27 : bool WalletBatch::EraseAddressData(const CTxDestination& dest)
1287 : : {
1288 : 27 : DataStream prefix;
1289 [ + - + - ]: 54 : prefix << DBKeys::DESTDATA << EncodeDestination(dest);
1290 [ + - ]: 27 : return m_batch->ErasePrefix(prefix);
1291 : 27 : }
1292 : :
1293 : 3739 : bool WalletBatch::WriteWalletFlags(const uint64_t flags)
1294 : : {
1295 : 3739 : return WriteIC(DBKeys::FLAGS, flags);
1296 : : }
1297 : :
1298 : 28 : bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types)
1299 : : {
1300 : 28 : return std::all_of(types.begin(), types.end(), [&](const std::string& type) {
1301 [ + - + - ]: 280 : return m_batch->ErasePrefix(DataStream() << type);
1302 : 28 : });
1303 : : }
1304 : :
1305 : 71483 : bool WalletBatch::TxnBegin()
1306 : : {
1307 : 71483 : return m_batch->TxnBegin();
1308 : : }
1309 : :
1310 : 71480 : bool WalletBatch::TxnCommit()
1311 : : {
1312 : 71480 : bool res = m_batch->TxnCommit();
1313 [ + - ]: 71480 : if (res) {
1314 [ + + ]: 71489 : for (const auto& listener : m_txn_listeners) {
1315 : 9 : listener.on_commit();
1316 : : }
1317 : : // txn finished, clear listeners
1318 : 71480 : m_txn_listeners.clear();
1319 : : }
1320 : 71480 : return res;
1321 : : }
1322 : :
1323 : 1 : bool WalletBatch::TxnAbort()
1324 : : {
1325 : 1 : bool res = m_batch->TxnAbort();
1326 [ + - ]: 1 : if (res) {
1327 [ - + ]: 1 : for (const auto& listener : m_txn_listeners) {
1328 : 0 : listener.on_abort();
1329 : : }
1330 : : // txn finished, clear listeners
1331 : 1 : m_txn_listeners.clear();
1332 : : }
1333 : 1 : return res;
1334 : : }
1335 : :
1336 : 9 : void WalletBatch::RegisterTxnListener(const DbTxnListener& l)
1337 : : {
1338 [ - + ]: 9 : assert(m_batch->HasActiveTxn());
1339 : 9 : m_txn_listeners.emplace_back(l);
1340 : 9 : }
1341 : :
1342 : 1266 : std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error)
1343 : : {
1344 : 1266 : bool exists;
1345 : 1266 : try {
1346 [ + - ]: 1266 : exists = fs::symlink_status(path).type() != fs::file_type::not_found;
1347 [ - - ]: 0 : } catch (const fs::filesystem_error& e) {
1348 [ - - - - : 0 : error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), e.code().message()));
- - - - ]
1349 : 0 : status = DatabaseStatus::FAILED_BAD_PATH;
1350 : 0 : return nullptr;
1351 : 0 : }
1352 : :
1353 : 1266 : std::optional<DatabaseFormat> format;
1354 [ + + ]: 1266 : if (exists) {
1355 [ + - + + ]: 1498 : if (IsBDBFile(BDBDataFile(path))) {
1356 : 42 : format = DatabaseFormat::BERKELEY_RO;
1357 : : }
1358 [ + - + + ]: 1498 : if (IsSQLiteFile(SQLiteDataFile(path))) {
1359 [ - + ]: 400 : if (format) {
1360 [ # # # # ]: 0 : error = Untranslated(strprintf("Failed to load database path '%s'. Data is in ambiguous format.", fs::PathToString(path)));
1361 : 0 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1362 : 0 : return nullptr;
1363 : : }
1364 : 400 : format = DatabaseFormat::SQLITE;
1365 : : }
1366 [ + + ]: 517 : } else if (options.require_existing) {
1367 [ + - + - ]: 6 : error = Untranslated(strprintf("Failed to load database path '%s'. Path does not exist.", fs::PathToString(path)));
1368 : 3 : status = DatabaseStatus::FAILED_NOT_FOUND;
1369 : 3 : return nullptr;
1370 : : }
1371 : :
1372 [ + + + + ]: 1263 : if (!format && options.require_existing) {
1373 [ + - + - ]: 540 : error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in recognized format.", fs::PathToString(path)));
1374 : 270 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1375 : 270 : return nullptr;
1376 : : }
1377 : :
1378 [ + + + + ]: 993 : if (format && options.require_create) {
1379 [ + - + - ]: 8 : error = Untranslated(strprintf("Failed to create database path '%s'. Database already exists.", fs::PathToString(path)));
1380 : 4 : status = DatabaseStatus::FAILED_ALREADY_EXISTS;
1381 : 4 : return nullptr;
1382 : : }
1383 : :
1384 : : // BERKELEY_RO can only be opened if require_format was set, which only occurs in migration.
1385 [ + + + + : 989 : if (format && format == DatabaseFormat::BERKELEY_RO && (!options.require_format || options.require_format != DatabaseFormat::BERKELEY_RO)) {
+ + - + ]
1386 [ + - + - ]: 12 : error = Untranslated(strprintf("Failed to open database path '%s'. The wallet appears to be a Legacy wallet, please use the wallet migration tool (migratewallet RPC).", fs::PathToString(path)));
1387 : 6 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1388 : 6 : return nullptr;
1389 : : }
1390 : :
1391 : : // A db already exists so format is set, but options also specifies the format, so make sure they agree
1392 [ + + + + : 983 : if (format && options.require_format && format != options.require_format) {
+ - ]
1393 [ # # # # ]: 0 : error = Untranslated(strprintf("Failed to load database path '%s'. Data is not in required format.", fs::PathToString(path)));
1394 : 0 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1395 : 0 : return nullptr;
1396 : : }
1397 : :
1398 : : // Format is not set when a db doesn't already exist, so use the format specified by the options if it is set.
1399 [ + + + + ]: 983 : if (!format && options.require_format) format = options.require_format;
1400 : :
1401 [ + + ]: 983 : if (!format) {
1402 : 3 : format = DatabaseFormat::SQLITE;
1403 : : }
1404 : :
1405 [ + - ]: 983 : if (format == DatabaseFormat::SQLITE) {
1406 : 948 : return MakeSQLiteDatabase(path, options, status, error);
1407 : : }
1408 : :
1409 [ + - ]: 35 : if (format == DatabaseFormat::BERKELEY_RO) {
1410 : 35 : return MakeBerkeleyRODatabase(path, options, status, error);
1411 : : }
1412 : :
1413 [ # # ]: 0 : error = Untranslated(STR_INTERNAL_BUG("Could not determine wallet format"));
1414 : 0 : status = DatabaseStatus::FAILED_BAD_FORMAT;
1415 : 0 : return nullptr;
1416 : : }
1417 : : } // namespace wallet
|