From 17bb63f9f9b08e6af60c089234fe878657dbc88e Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Fri, 5 Jan 2024 18:42:18 -0500 Subject: [PATCH] wallet: Disallow loading 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 | 3 ++ src/wallet/walletdb.cpp | 32 ++++++++----------- src/wallet/walletdb.h | 3 +- .../wallet_backwards_compatibility.py | 23 +++++++++++++ 7 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/wallet/db.h b/src/wallet/db.h index 570e9dd8c2..b632e2df0e 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -182,7 +182,6 @@ public: }; enum class DatabaseFormat { - BERKELEY, SQLITE, BERKELEY_RO, BERKELEY_SWAP, diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 34b7caa7e5..403342f1cf 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 3acaba0202..a0f9831f06 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 efd1898394..af975ae8b6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3052,6 +3052,9 @@ 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 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 3ae081735c..5538d81052 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; } @@ -1432,7 +1437,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) { @@ -1460,9 +1465,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 @@ -1475,12 +1482,8 @@ std::unique_ptr MakeDatabase(const fs::path& path, const Databas // Format is not set when a db doesn't already exist, so use the format specified by the options if it is set. if (!format && options.require_format) format = options.require_format; - // 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) { @@ -1491,15 +1494,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 5cdd449a6f..081b70e4cc 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 a5c5de85d0..2dbe9489dc 100755 --- a/test/functional/wallet_backwards_compatibility.py +++ b/test/functional/wallet_backwards_compatibility.py @@ -285,5 +285,28 @@ class BackwardsCompatibilityTest(BitcoinTestFramework): 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_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) + + # 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 + wallet_prev.unloadwallet() + + # 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()