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 4610a89637e5d4..32a4afe12fee79 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 @@ -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"; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ba43cb48f8c399..fe5aef9feaac87 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3034,7 +3034,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 c006ee3bed8ec2..4df61982a26653 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -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; } @@ -1389,7 +1394,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) { @@ -1417,9 +1422,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 @@ -1435,9 +1442,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) { @@ -1448,15 +1452,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 32c3c29b5e3654..f2cc527fb08a42 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 62c7a2d9c0167f..9bf86d5c167766 100755 --- a/test/functional/wallet_backwards_compatibility.py +++ b/test/functional/wallet_backwards_compatibility.py @@ -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()