wallet, refactor: Decouple into HasLegacyRecords()

The new helper will be used to fix a crash in the
wallet migration process (watch-only, non-blank,
private keys disabled, empty wallet - no scripts
or addresses imported).

Co-authored-by: Matias Furszyfer <mfurszy@protonmail.com>
This commit is contained in:
pablomartin4btc 2025-03-26 15:44:32 -03:00
parent 639279e86a
commit 42c13141b5
3 changed files with 39 additions and 17 deletions

View file

@ -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));
}
}

View file

@ -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<DatabaseCursor> 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<DatabaseCursor> 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;

View file

@ -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