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