diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp index fee8c85873e..91952245d70 100644 --- a/src/wallet/test/walletdb_tests.cpp +++ b/src/wallet/test/walletdb_tests.cpp @@ -47,11 +47,14 @@ BOOST_AUTO_TEST_CASE(walletdb_read_write_deadlock) // Create legacy spkm LOCK(wallet->cs_wallet); auto legacy_spkm = wallet->GetOrCreateLegacyScriptPubKeyMan(); + BOOST_CHECK(!HasLegacyRecords(*wallet)); BOOST_CHECK(legacy_spkm->SetupGeneration(true)); + BOOST_CHECK(HasLegacyRecords(*wallet)); wallet->Flush(); // Now delete all records, which performs a read write operation. BOOST_CHECK(wallet->GetLegacyScriptPubKeyMan()->DeleteRecords()); + BOOST_CHECK(!HasLegacyRecords(*wallet)); } } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index a19c03d70ef..3ae081735cc 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -540,6 +540,35 @@ static LoadResult LoadRecords(CWallet* pwallet, DatabaseBatch& batch, const std: return LoadRecords(pwallet, batch, key, prefix, load_func); } +bool HasLegacyRecords(CWallet& wallet) +{ + const auto& batch = wallet.GetDatabase().MakeBatch(); + return HasLegacyRecords(wallet, *batch); +} + +bool HasLegacyRecords(CWallet& wallet, DatabaseBatch& batch) +{ + for (const auto& type : DBKeys::LEGACY_TYPES) { + DataStream key; + DataStream value{}; + DataStream prefix; + + prefix << type; + std::unique_ptr cursor = batch.GetNewPrefixCursor(prefix); + if (!cursor) { + // Could only happen on a closed db, which means there is an error in the code flow. + wallet.WalletLogPrintf("Error getting database cursor for '%s' records", type); + throw std::runtime_error(strprintf("Error getting database cursor for '%s' records", type)); + } + + DatabaseCursor::Status status = cursor->Next(key, value); + if (status != DatabaseCursor::Status::DONE) { + return true; + } + } + return false; +} + static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, int last_client) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { AssertLockHeld(pwallet->cs_wallet); @@ -547,23 +576,9 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, // Make sure descriptor wallets don't have any legacy records if (pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { - for (const auto& type : DBKeys::LEGACY_TYPES) { - DataStream key; - DataStream value{}; - - DataStream prefix; - prefix << type; - std::unique_ptr cursor = batch.GetNewPrefixCursor(prefix); - if (!cursor) { - pwallet->WalletLogPrintf("Error getting database cursor for '%s' records\n", type); - return DBErrors::CORRUPT; - } - - DatabaseCursor::Status status = cursor->Next(key, value); - if (status != DatabaseCursor::Status::DONE) { - 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()); - return DBErrors::UNEXPECTED_LEGACY_ENTRY; - } + if (HasLegacyRecords(*pwallet, batch)) { + 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()); + return DBErrors::UNEXPECTED_LEGACY_ENTRY; } return DBErrors::LOAD_OK; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 70d69870126..5cdd449a6fd 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -333,6 +333,10 @@ bool LoadKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::stri bool LoadCryptedKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); bool LoadEncryptionKey(CWallet* pwallet, DataStream& ssKey, DataStream& ssValue, std::string& strErr); bool LoadHDChain(CWallet* pwallet, DataStream& ssValue, std::string& strErr); + +//! Returns true if there are any DBKeys::LEGACY_TYPES record in the wallet db +bool HasLegacyRecords(CWallet& wallet); +bool HasLegacyRecords(CWallet& wallet, DatabaseBatch& batch); } // namespace wallet #endif // BITCOIN_WALLET_WALLETDB_H