Skip to content

Commit

Permalink
wallet: Disallow legacy wallets
Browse files Browse the repository at this point in the history
Legacy wallets do not have the descriptors flag set. Don't load wallets
without the descriptors flag.

At the same time, we will no longer load BDB databases since they are
only used for legacy wallets.
  • Loading branch information
achow101 committed Nov 7, 2024
1 parent 56b0246 commit 4fcb431
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 30 deletions.
1 change: 0 additions & 1 deletion src/wallet/db.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ class WalletDatabase
};

enum class DatabaseFormat {
BERKELEY,
SQLITE,
BERKELEY_RO,
BERKELEY_SWAP,
Expand Down
5 changes: 0 additions & 5 deletions src/wallet/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,6 @@ void WalletInit::AddWalletOptions(ArgsManager& argsman) const

bool WalletInit::ParameterInteraction() const
{
#ifdef USE_BDB
if (!BerkeleyDatabaseSanityCheck()) {
return InitError(Untranslated("A version conflict was detected between the run-time BerkeleyDB library and the one used during compilation."));
}
#endif
if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
for (const std::string& wallet : gArgs.GetArgs("-wallet")) {
LogPrintf("%s: parameter interaction: -disablewallet -> ignoring -wallet=%s\n", __func__, wallet);
Expand Down
5 changes: 0 additions & 5 deletions src/wallet/test/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#ifndef BITCOIN_WALLET_TEST_UTIL_H
#define BITCOIN_WALLET_TEST_UTIL_H

#include <bitcoin-build-config.h> // IWYU pragma: keep

#include <addresstype.h>
#include <wallet/db.h>

Expand All @@ -27,9 +25,6 @@ struct WalletContext;

static const DatabaseFormat DATABASE_FORMATS[] = {
DatabaseFormat::SQLITE,
#ifdef USE_BDB
DatabaseFormat::BERKELEY,
#endif
};

const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj";
Expand Down
6 changes: 5 additions & 1 deletion src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3034,7 +3034,11 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
error = strprintf(_("Unexpected legacy entry in descriptor wallet found. Loading wallet %s\n\n"
"The wallet might have been tampered with or created with malicious intent.\n"), walletFile);
return nullptr;
} else {
} else if (nLoadWalletRet == DBErrors::LEGACY_WALLET) {
error = strprintf(_("Error loading %s: Wallet is a legacy wallet. Please migrate to a descriptor wallet using the migration tool (migratewallet RPC)."), walletFile);
return nullptr;
}
else {
error = strprintf(_("Error loading %s"), walletFile);
return nullptr;
}
Expand Down
31 changes: 14 additions & 17 deletions src/wallet/walletdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,11 @@ static DBErrors LoadWalletFlags(CWallet* pwallet, DatabaseBatch& batch) EXCLUSIV
pwallet->WalletLogPrintf("Error reading wallet database: Unknown non-tolerable wallet flags found\n");
return DBErrors::TOO_NEW;
}
// All wallets must be descriptor wallets unless opened with a bdb_ro db
// bdb_ro is only used for legacy to descriptor migration.
if (pwallet->GetDatabase().Format() != "bdb_ro" && !pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
return DBErrors::LEGACY_WALLET;
}
}
return DBErrors::LOAD_OK;
}
Expand Down Expand Up @@ -1389,7 +1394,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
std::optional<DatabaseFormat> format;
if (exists) {
if (IsBDBFile(BDBDataFile(path))) {
format = DatabaseFormat::BERKELEY;
format = DatabaseFormat::BERKELEY_RO;
}
if (IsSQLiteFile(SQLiteDataFile(path))) {
if (format) {
Expand Down Expand Up @@ -1417,9 +1422,11 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
return nullptr;
}

// If BERKELEY was the format, then change the format from BERKELEY to BERKELEY_RO
if (format && options.require_format && format == DatabaseFormat::BERKELEY && options.require_format == DatabaseFormat::BERKELEY_RO) {
format = DatabaseFormat::BERKELEY_RO;
// BERKELEY_RO can only be opened if require_format was set, which only occurs in migration.
if (format && format == DatabaseFormat::BERKELEY_RO && (!options.require_format || options.require_format != DatabaseFormat::BERKELEY_RO)) {
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)));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}

// A db already exists so format is set, but options also specifies the format, so make sure they agree
Expand All @@ -1435,9 +1442,6 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
// If the format is not specified or detected, choose the default format based on what is available. We prefer BDB over SQLite for now.
if (!format) {
format = DatabaseFormat::SQLITE;
#ifdef USE_BDB
format = DatabaseFormat::BERKELEY;
#endif
}

if (format == DatabaseFormat::SQLITE) {
Expand All @@ -1448,15 +1452,8 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas
return MakeBerkeleyRODatabase(path, options, status, error);
}

#ifdef USE_BDB
if constexpr (true) {
return MakeBerkeleyDatabase(path, options, status, error);
} else
#endif
{
error = Untranslated(strprintf("Failed to open database path '%s'. Build does not support Berkeley DB database format.", fs::PathToString(path)));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
error = Untranslated(STR_INTERNAL_BUG("Could not determine wallet format"));
status = DatabaseStatus::FAILED_BAD_FORMAT;
return nullptr;
}
} // namespace wallet
3 changes: 2 additions & 1 deletion src/wallet/walletdb.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ enum class DBErrors : int
UNKNOWN_DESCRIPTOR = 6,
LOAD_FAIL = 7,
UNEXPECTED_LEGACY_ENTRY = 8,
CORRUPT = 9,
LEGACY_WALLET = 9,
CORRUPT = 10,
};

namespace DBKeys {
Expand Down
36 changes: 36 additions & 0 deletions test/functional/wallet_backwards_compatibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,5 +304,41 @@ def run_test(self):
info = wallet_res.getaddressinfo(address)
assert_equal(info, addr_info)

self.log.info("Test that a wallet from a legacy only node must be migrated, from:")
for node in legacy_nodes:
self.log.info(f"- {node.version}")
wallet_name = f"legacy_up_{node.version}"
if self.major_version_less_than(node, 17):
# createwallet is only available in 0.17+
self.restart_node(node.index, extra_args=[f"-wallet={wallet_name}"])
wallet_prev = node.get_wallet_rpc(wallet_name)
address = wallet_prev.getnewaddress('', "bech32")
addr_info = wallet_prev.validateaddress(address)
else:
if self.major_version_at_least(node, 21):
node.rpc.createwallet(wallet_name=wallet_name, descriptors=False)
else:
node.rpc.createwallet(wallet_name=wallet_name)
wallet_prev = node.get_wallet_rpc(wallet_name)
address = wallet_prev.getnewaddress('', "bech32")
addr_info = wallet_prev.getaddressinfo(address)

hdkeypath = addr_info["hdkeypath"].replace("'", "h")
pubkey = addr_info["pubkey"]

# Make a backup of the wallet file
backup_path = os.path.join(self.options.tmpdir, f"{wallet_name}.dat")
wallet_prev.backupwallet(backup_path)

# Remove the wallet from old node
if self.major_version_at_least(node, 17):
wallet_prev.unloadwallet()
else:
self.stop_node(node.index)

# Restore the wallet to master
# Legacy wallets are no longer supported. Trying to load these should result in an error
assert_raises_rpc_error(-18, "The wallet appears to be a Legacy wallet, please use the wallet migration tool (migratewallet RPC)", node_master.restorewallet, wallet_name, backup_path)

if __name__ == '__main__':
BackwardsCompatibilityTest(__file__).main()

0 comments on commit 4fcb431

Please sign in to comment.