From 09e9e689846b37489ac47b194a3e82dc716fac54 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Fri, 5 Jan 2024 18:42:18 -0500 Subject: [PATCH] wallet: Disallow legacy wallets 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. --- src/wallet/db.h | 1 - src/wallet/init.cpp | 5 --- src/wallet/test/util.h | 5 --- src/wallet/wallet.cpp | 6 +++- src/wallet/walletdb.cpp | 31 ++++++++-------- src/wallet/walletdb.h | 3 +- .../wallet_backwards_compatibility.py | 36 +++++++++++++++++++ 7 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/wallet/db.h b/src/wallet/db.h index e8790006a4d8b1..a16bb7735967fa 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -182,7 +182,6 @@ class WalletDatabase }; enum class DatabaseFormat { - BERKELEY, SQLITE, BERKELEY_RO, BERKELEY_SWAP, diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index a03743105523a7..c036164bae886e 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -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); diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h index 801dbacaf19224..27934f9bffdf13 100644 --- a/src/wallet/test/util.h +++ b/src/wallet/test/util.h @@ -5,8 +5,6 @@ #ifndef BITCOIN_WALLET_TEST_UTIL_H #define BITCOIN_WALLET_TEST_UTIL_H -#include // IWYU pragma: keep - #include #include #include @@ -28,9 +26,6 @@ struct WalletContext; static const DatabaseFormat DATABASE_FORMATS[] = { DatabaseFormat::SQLITE, -#ifdef USE_BDB - DatabaseFormat::BERKELEY, -#endif }; const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj"; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index dd9dfcd516a0a9..1a0374d56c888e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3038,7 +3038,11 @@ std::shared_ptr 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; } diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 39c5b6f8505042..3acdd72db1637c 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -485,6 +485,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; } @@ -1409,7 +1414,7 @@ std::unique_ptr MakeDatabase(const fs::path& path, const Databas std::optional format; if (exists) { if (IsBDBFile(BDBDataFile(path))) { - format = DatabaseFormat::BERKELEY; + format = DatabaseFormat::BERKELEY_RO; } if (IsSQLiteFile(SQLiteDataFile(path))) { if (format) { @@ -1437,9 +1442,11 @@ std::unique_ptr 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 @@ -1455,9 +1462,6 @@ std::unique_ptr 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) { @@ -1468,15 +1472,8 @@ std::unique_ptr 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 diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 70d69870126686..d750929585a37d 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -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 { diff --git a/test/functional/wallet_backwards_compatibility.py b/test/functional/wallet_backwards_compatibility.py index 4769c77dd34f07..ff5880f1570091 100755 --- a/test/functional/wallet_backwards_compatibility.py +++ b/test/functional/wallet_backwards_compatibility.py @@ -305,5 +305,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()