diff options
Diffstat (limited to 'src/wallet/walletdb.cpp')
-rw-r--r-- | src/wallet/walletdb.cpp | 108 |
1 files changed, 92 insertions, 16 deletions
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 79e0a330b7..6a8f0d2481 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -59,6 +59,7 @@ const std::string WALLETDESCRIPTORCKEY{"walletdescriptorckey"}; const std::string WALLETDESCRIPTORKEY{"walletdescriptorkey"}; const std::string WATCHMETA{"watchmeta"}; const std::string WATCHS{"watchs"}; +const std::unordered_set<std::string> LEGACY_TYPES{CRYPTED_KEY, CSCRIPT, DEFAULTKEY, HDCHAIN, KEYMETA, KEY, OLD_KEY, POOL, WATCHMETA, WATCHS}; } // namespace DBKeys // @@ -313,6 +314,7 @@ public: std::map<std::pair<uint256, CKeyID>, std::pair<CPubKey, std::vector<unsigned char>>> m_descriptor_crypt_keys; std::map<uint160, CHDChain> m_hd_chains; bool tx_corrupt{false}; + bool descriptor_unknown{false}; CWalletScanState() = default; }; @@ -626,7 +628,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, uint256 id; ssKey >> id; WalletDescriptor desc; - ssValue >> desc; + try { + ssValue >> desc; + } catch (const std::ios_base::failure& e) { + strErr = e.what(); + wss.descriptor_unknown = true; + return false; + } if (wss.m_descriptor_caches.count(id) == 0) { wss.m_descriptor_caches[id] = DescriptorCache(); } @@ -766,6 +774,12 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) DBErrors result = DBErrors::LOAD_OK; LOCK(pwallet->cs_wallet); + + // Last client version to open this wallet + int last_client = CLIENT_VERSION; + bool has_last_client = m_batch->Read(DBKeys::VERSION, last_client); + pwallet->WalletLogPrintf("Wallet file version = %d, last client version = %d\n", pwallet->GetVersion(), last_client); + try { int nMinVersion = 0; if (m_batch->Read(DBKeys::MINVERSION, nMinVersion)) { @@ -831,6 +845,13 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) // Set tx_corrupt back to false so that the error is only printed once (per corrupt tx) wss.tx_corrupt = false; result = DBErrors::CORRUPT; + } else if (wss.descriptor_unknown) { + strErr = strprintf("Error: Unrecognized descriptor found in wallet %s. ", pwallet->GetName()); + strErr += (last_client > CLIENT_VERSION) ? "The wallet might had been created on a newer version. " : + "The database might be corrupted or the software version is not compatible with one of your wallet descriptors. "; + strErr += "Please try running the latest software version"; + pwallet->WalletLogPrintf("%s\n", strErr); + return DBErrors::UNKNOWN_DESCRIPTOR; } else { // Leave other errors alone, if we try to fix them we might make things worse. fNoncriticalErrors = true; // ... but do warn the user there is something wrong. @@ -856,18 +877,18 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) } // Set the descriptor caches - for (auto desc_cache_pair : wss.m_descriptor_caches) { + for (const auto& desc_cache_pair : wss.m_descriptor_caches) { auto spk_man = pwallet->GetScriptPubKeyMan(desc_cache_pair.first); assert(spk_man); ((DescriptorScriptPubKeyMan*)spk_man)->SetCache(desc_cache_pair.second); } // Set the descriptor keys - for (auto desc_key_pair : wss.m_descriptor_keys) { + for (const auto& desc_key_pair : wss.m_descriptor_keys) { auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first); ((DescriptorScriptPubKeyMan*)spk_man)->AddKey(desc_key_pair.first.second, desc_key_pair.second); } - for (auto desc_key_pair : wss.m_descriptor_crypt_keys) { + for (const auto& desc_key_pair : wss.m_descriptor_crypt_keys) { auto spk_man = pwallet->GetScriptPubKeyMan(desc_key_pair.first.first); ((DescriptorScriptPubKeyMan*)spk_man)->AddCryptedKey(desc_key_pair.first.second, desc_key_pair.second.first, desc_key_pair.second.second); } @@ -883,13 +904,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) if (result != DBErrors::LOAD_OK) return result; - // Last client version to open this wallet, was previously the file version number - int last_client = CLIENT_VERSION; - m_batch->Read(DBKeys::VERSION, last_client); - - int wallet_version = pwallet->GetVersion(); - pwallet->WalletLogPrintf("Wallet File Version = %d\n", wallet_version > 0 ? wallet_version : last_client); - pwallet->WalletLogPrintf("Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total. Unknown wallet records: %u\n", wss.nKeys, wss.nCKeys, wss.nKeyMeta, wss.nKeys + wss.nCKeys, wss.m_unknown_records); @@ -909,7 +923,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) if (wss.fIsEncrypted && (last_client == 40000 || last_client == 50000)) return DBErrors::NEED_REWRITE; - if (last_client < CLIENT_VERSION) // Update + if (!has_last_client || last_client != CLIENT_VERSION) // Update m_batch->Write(DBKeys::VERSION, CLIENT_VERSION); if (wss.fAnyUnordered) @@ -1085,6 +1099,45 @@ bool WalletBatch::WriteWalletFlags(const uint64_t flags) return WriteIC(DBKeys::FLAGS, flags); } +bool WalletBatch::EraseRecords(const std::unordered_set<std::string>& types) +{ + // Get cursor + if (!m_batch->StartCursor()) + { + return false; + } + + // Iterate the DB and look for any records that have the type prefixes + while (true) + { + // Read next record + CDataStream key(SER_DISK, CLIENT_VERSION); + CDataStream value(SER_DISK, CLIENT_VERSION); + bool complete; + bool ret = m_batch->ReadAtCursor(key, value, complete); + if (complete) { + break; + } + else if (!ret) + { + m_batch->CloseCursor(); + return false; + } + + // Make a copy of key to avoid data being deleted by the following read of the type + Span<const unsigned char> key_data = MakeUCharSpan(key); + + std::string type; + key >> type; + + if (types.count(type) > 0) { + m_batch->Erase(key_data); + } + } + m_batch->CloseCursor(); + return true; +} + bool WalletBatch::TxnBegin() { return m_batch->TxnBegin(); @@ -1186,13 +1239,36 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase() } /** Return object for accessing temporary in-memory database. */ -std::unique_ptr<WalletDatabase> CreateMockWalletDatabase() +std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(DatabaseOptions& options) { - DatabaseOptions options; + + std::optional<DatabaseFormat> format; + if (options.require_format) format = options.require_format; + if (!format) { +#ifdef USE_BDB + format = DatabaseFormat::BERKELEY; +#endif +#ifdef USE_SQLITE + format = DatabaseFormat::SQLITE; +#endif + } + + if (format == DatabaseFormat::SQLITE) { #ifdef USE_SQLITE - return std::make_unique<SQLiteDatabase>("", "", options, true); -#elif USE_BDB + return std::make_unique<SQLiteDatabase>(":memory:", "", options, true); +#endif + assert(false); + } + +#ifdef USE_BDB return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), "", options); #endif + assert(false); +} + +std::unique_ptr<WalletDatabase> CreateMockWalletDatabase() +{ + DatabaseOptions options; + return CreateMockWalletDatabase(options); } } // namespace wallet |