diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 87f8c35a14c..48d68c4e6b6 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -107,9 +107,6 @@ public: //! Return whether wallet has private key. virtual bool isSpendable(const CTxDestination& dest) = 0; - //! Return whether wallet has watch only keys. - virtual bool haveWatchOnly() = 0; - //! Add or update address. virtual bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::optional& purpose) = 0; @@ -282,9 +279,6 @@ public: // Remove wallet. virtual void remove() = 0; - //! Return whether is a legacy wallet - virtual bool isLegacy() = 0; - //! Register handler for unload message. using UnloadFn = std::function; virtual std::unique_ptr handleUnload(UnloadFn fn) = 0; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 7d584920822..8536da0142d 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -195,28 +195,10 @@ OverviewPage::~OverviewPage() void OverviewPage::setBalance(const interfaces::WalletBalances& balances) { BitcoinUnit unit = walletModel->getOptionsModel()->getDisplayUnit(); - if (walletModel->wallet().isLegacy()) { - if (walletModel->wallet().privateKeysDisabled()) { - ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - } else { - ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelWatchAvailable->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelWatchPending->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelWatchImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelWatchTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.watch_only_balance + balances.unconfirmed_watch_only_balance + balances.immature_watch_only_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - } - } else { - ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); - } + ui->labelBalance->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelUnconfirmed->setText(BitcoinUnits::formatWithPrivacy(unit, balances.unconfirmed_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelImmature->setText(BitcoinUnits::formatWithPrivacy(unit, balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); + ui->labelTotal->setText(BitcoinUnits::formatWithPrivacy(unit, balances.balance + balances.unconfirmed_balance + balances.immature_balance, BitcoinUnits::SeparatorStyle::ALWAYS, m_privacy)); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users bool showImmature = balances.immature_balance != 0; @@ -281,11 +263,7 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit); - interfaces::Wallet& wallet = model->wallet(); - updateWatchOnlyLabels(wallet.haveWatchOnly() && !wallet.privateKeysDisabled()); - connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) { - updateWatchOnlyLabels(showWatchOnly && !walletModel->wallet().privateKeysDisabled()); - }); + updateWatchOnlyLabels(false); } // update the display unit, to not use the default ("BTC") diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b04e249d0b8..b8a9854f0e0 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -711,9 +711,6 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances) CAmount balance = balances.balance; if (model->wallet().hasExternalSigner()) { ui->labelBalanceName->setText(tr("External balance:")); - } else if (model->wallet().isLegacy() && model->wallet().privateKeysDisabled()) { - balance = balances.watch_only_balance; - ui->labelBalanceName->setText(tr("Watch-only balance:")); } ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance)); } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 574824b4190..b4a60b427e1 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -241,8 +241,8 @@ void TransactionView::setModel(WalletModel *_model) } } - // show/hide column Watch-only - updateWatchOnlyColumn(_model->wallet().haveWatchOnly()); + // hide column Watch-only + updateWatchOnlyColumn(false); // Watch-only signal connect(_model, &WalletModel::notifyWatchonlyChanged, this, &TransactionView::updateWatchOnlyColumn); @@ -368,8 +368,6 @@ void TransactionView::exportClicked() // name, column, role writer.setModel(transactionProxyModel); writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); - if (model->wallet().haveWatchOnly()) - writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly); writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); writer.addColumn(tr("Label"), 0, TransactionTableModel::LabelRole); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 0a01c0a45b1..455f9f469ed 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -45,7 +45,7 @@ WalletModel::WalletModel(std::unique_ptr wallet, ClientModel optionsModel(client_model.getOptionsModel()), timer(new QTimer(this)) { - fHaveWatchOnly = m_wallet->haveWatchOnly(); + fHaveWatchOnly = false; addressTableModel = new AddressTableModel(this); transactionTableModel = new TransactionTableModel(platformStyle, this); recentRequestsTableModel = new RecentRequestsTableModel(this); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index f17d100e750..0a58285a969 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -48,7 +48,7 @@ static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWallet if (require_mine) { // check that original tx consists entirely of our inputs // if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee) - isminefilter filter = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; + isminefilter filter = ISMINE_SPENDABLE; if (!AllInputsMine(wallet, *wtx.tx, filter)) { errors.emplace_back(Untranslated("Transaction contains inputs that don't belong to this wallet")); return feebumper::Result::WALLET_ERROR; diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 21e8a0b3bd2..b1b99630f6e 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -175,14 +175,6 @@ public: LOCK(m_wallet->cs_wallet); return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; } - bool haveWatchOnly() override - { - auto spk_man = m_wallet->GetLegacyScriptPubKeyMan(); - if (spk_man) { - return spk_man->HaveWatchOnly(); - } - return false; - }; bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::optional& purpose) override { return m_wallet->SetAddressBook(dest, name, purpose); @@ -407,12 +399,7 @@ public: result.balance = bal.m_mine_trusted; result.unconfirmed_balance = bal.m_mine_untrusted_pending; result.immature_balance = bal.m_mine_immature; - result.have_watch_only = haveWatchOnly(); - if (result.have_watch_only) { - result.watch_only_balance = bal.m_watchonly_trusted; - result.unconfirmed_watch_only_balance = bal.m_watchonly_untrusted_pending; - result.immature_watch_only_balance = bal.m_watchonly_immature; - } + result.have_watch_only = false; return result; } bool tryGetBalances(WalletBalances& balances, uint256& block_hash) override @@ -516,7 +503,6 @@ public: bool hasExternalSigner() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER); } bool privateKeysDisabled() override { return m_wallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); } bool taprootEnabled() override { - if (m_wallet->IsLegacy()) return false; auto spk_man = m_wallet->GetScriptPubKeyMan(OutputType::BECH32M, /*internal=*/false); return spk_man != nullptr; } @@ -526,7 +512,6 @@ public: { RemoveWallet(m_context, m_wallet, /*load_on_start=*/false); } - bool isLegacy() override { return m_wallet->IsLegacy(); } std::unique_ptr handleUnload(UnloadFn fn) override { return MakeSignalHandler(m_wallet->NotifyUnload.connect(fn)); diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index 5937f4dfa95..0ea1cf9e1c3 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -54,8 +54,6 @@ RPCHelpMan getnewaddress() std::optional parsed = ParseOutputType(request.params[1].get_str()); if (!parsed) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str())); - } else if (parsed.value() == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses"); } output_type = parsed.value(); } @@ -101,8 +99,6 @@ RPCHelpMan getrawchangeaddress() std::optional parsed = ParseOutputType(request.params[0].get_str()); if (!parsed) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str())); - } else if (parsed.value() == OutputType::BECH32M && pwallet->GetLegacyScriptPubKeyMan()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Legacy wallets cannot provide bech32m addresses"); } output_type = parsed.value(); } @@ -233,10 +229,6 @@ RPCHelpMan keypoolrefill() std::shared_ptr const pwallet = GetWalletForJSONRPCRequest(request); if (!pwallet) return UniValue::VNULL; - if (pwallet->IsLegacy() && pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); - } - LOCK(pwallet->cs_wallet); // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index f1430a3c601..da777c9c211 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -440,12 +440,6 @@ RPCHelpMan getbalances() {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"}, {RPCResult::Type::STR_AMOUNT, "used", /*optional=*/true, "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"}, }}, - {RPCResult::Type::OBJ, "watchonly", /*optional=*/true, "watchonly balances (not present if wallet does not watch anything)", - { - {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"}, - {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"}, - {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"}, - }}, RESULT_LAST_PROCESSED_BLOCK, } }, @@ -479,15 +473,6 @@ RPCHelpMan getbalances() } balances.pushKV("mine", std::move(balances_mine)); } - auto spk_man = wallet.GetLegacyScriptPubKeyMan(); - if (spk_man && spk_man->HaveWatchOnly()) { - UniValue balances_watchonly{UniValue::VOBJ}; - balances_watchonly.pushKV("trusted", ValueFromAmount(bal.m_watchonly_trusted)); - balances_watchonly.pushKV("untrusted_pending", ValueFromAmount(bal.m_watchonly_untrusted_pending)); - balances_watchonly.pushKV("immature", ValueFromAmount(bal.m_watchonly_immature)); - balances.pushKV("watchonly", std::move(balances_watchonly)); - } - AppendLastProcessedBlock(balances, wallet); return balances; }, diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index 9466ef414c7..66b438865ce 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -60,7 +60,6 @@ static RPCHelpMan getwalletinfo() {RPCResult::Type::NUM, "keypoolsize_hd_internal", /*optional=*/true, "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"}, {RPCResult::Type::NUM_TIME, "unlocked_until", /*optional=*/true, "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked (only present for passphrase-encrypted wallets)"}, {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kvB"}, - {RPCResult::Type::STR_HEX, "hdseedid", /*optional=*/true, "the Hash160 of the HD seed (only present when HD is enabled)"}, {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"}, {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"}, {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress", @@ -107,14 +106,6 @@ static RPCHelpMan getwalletinfo() } obj.pushKV("keypoolsize", (int64_t)kpExternalSize); - LegacyScriptPubKeyMan* spk_man = pwallet->GetLegacyScriptPubKeyMan(); - if (spk_man) { - CKeyID seed_id = spk_man->GetHDChain().seed_id; - if (!seed_id.IsNull()) { - obj.pushKV("hdseedid", seed_id.GetHex()); - } - } - if (pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) { obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize)); } diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 80f25c62c2b..171482043f7 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -25,29 +25,6 @@ using common::PSBTError; using util::ToString; namespace wallet { -//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details. -const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; - -util::Result LegacyScriptPubKeyMan::GetNewDestination(const OutputType type) -{ - if (LEGACY_OUTPUT_TYPES.count(type) == 0) { - return util::Error{_("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types")}; - } - assert(type != OutputType::BECH32M); - - // Fill-up keypool if needed - TopUp(); - - LOCK(cs_KeyStore); - - // Generate a new key that is added to wallet - CPubKey new_key; - if (!GetKeyFromPool(new_key, type)) { - return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; - } - LearnRelatedScripts(new_key, type); - return GetDestinationForKey(new_key, type); -} typedef std::vector valtype; @@ -272,326 +249,6 @@ bool LegacyDataSPKM::CheckDecryptionKey(const CKeyingMaterial& master_key) return true; } -bool LegacyScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) -{ - LOCK(cs_KeyStore); - encrypted_batch = batch; - if (!mapCryptedKeys.empty()) { - encrypted_batch = nullptr; - return false; - } - - KeyMap keys_to_encrypt; - keys_to_encrypt.swap(mapKeys); // Clear mapKeys so AddCryptedKeyInner will succeed. - for (const KeyMap::value_type& mKey : keys_to_encrypt) - { - const CKey &key = mKey.second; - CPubKey vchPubKey = key.GetPubKey(); - CKeyingMaterial vchSecret{UCharCast(key.begin()), UCharCast(key.end())}; - std::vector vchCryptedSecret; - if (!EncryptSecret(master_key, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) { - encrypted_batch = nullptr; - return false; - } - if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) { - encrypted_batch = nullptr; - return false; - } - } - encrypted_batch = nullptr; - return true; -} - -util::Result LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) -{ - if (LEGACY_OUTPUT_TYPES.count(type) == 0) { - return util::Error{_("Error: Legacy wallets only support the \"legacy\", \"p2sh-segwit\", and \"bech32\" address types")}; - } - assert(type != OutputType::BECH32M); - - LOCK(cs_KeyStore); - if (!CanGetAddresses(internal)) { - return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; - } - - // Fill-up keypool if needed - TopUp(); - - if (!ReserveKeyFromKeyPool(index, keypool, internal)) { - return util::Error{_("Error: Keypool ran out, please call keypoolrefill first")}; - } - return GetDestinationForKey(keypool.vchPubKey, type); -} - -bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal) -{ - LOCK(cs_KeyStore); - - auto it = m_inactive_hd_chains.find(seed_id); - if (it == m_inactive_hd_chains.end()) { - return false; - } - - CHDChain& chain = it->second; - - if (internal) { - chain.m_next_internal_index = std::max(chain.m_next_internal_index, index + 1); - } else { - chain.m_next_external_index = std::max(chain.m_next_external_index, index + 1); - } - - WalletBatch batch(m_storage.GetDatabase()); - TopUpChain(batch, chain, 0); - - return true; -} - -std::vector LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) -{ - LOCK(cs_KeyStore); - std::vector result; - // extract addresses and check if they match with an unused keypool key - for (const auto& keyid : GetAffectedKeys(script, *this)) { - std::map::const_iterator mi = m_pool_key_to_index.find(keyid); - if (mi != m_pool_key_to_index.end()) { - WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__); - for (const auto& keypool : MarkReserveKeysAsUsed(mi->second)) { - // derive all possible destinations as any of them could have been used - for (const auto& type : LEGACY_OUTPUT_TYPES) { - const auto& dest = GetDestinationForKey(keypool.vchPubKey, type); - result.push_back({dest, keypool.fInternal}); - } - } - - if (!TopUp()) { - WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); - } - } - - // Find the key's metadata and check if it's seed id (if it has one) is inactive, i.e. it is not the current m_hd_chain seed id. - // If so, TopUp the inactive hd chain - auto it = mapKeyMetadata.find(keyid); - if (it != mapKeyMetadata.end()){ - CKeyMetadata meta = it->second; - if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) { - std::vector path; - if (meta.has_key_origin) { - path = meta.key_origin.path; - } else if (!ParseHDKeypath(meta.hdKeypath, path)) { - WalletLogPrintf("%s: Adding inactive seed keys failed, invalid hdKeypath: %s\n", - __func__, - meta.hdKeypath); - } - if (path.size() != 3) { - WalletLogPrintf("%s: Adding inactive seed keys failed, invalid path size: %d, has_key_origin: %s\n", - __func__, - path.size(), - meta.has_key_origin); - } else { - bool internal = (path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0; - int64_t index = path[2] & ~BIP32_HARDENED_KEY_LIMIT; - - if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) { - WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__); - } - } - } - } - } - - return result; -} - -void LegacyScriptPubKeyMan::UpgradeKeyMetadata() -{ - LOCK(cs_KeyStore); - if (m_storage.IsLocked() || m_storage.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { - return; - } - - std::unique_ptr batch = std::make_unique(m_storage.GetDatabase()); - for (auto& meta_pair : mapKeyMetadata) { - CKeyMetadata& meta = meta_pair.second; - if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin - CKey key; - GetKey(meta.hd_seed_id, key); - CExtKey masterKey; - masterKey.SetSeed(key); - // Add to map - CKeyID master_id = masterKey.key.GetPubKey().GetID(); - std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint); - if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) { - throw std::runtime_error("Invalid stored hdKeypath"); - } - meta.has_key_origin = true; - if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) { - meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN; - } - - // Write meta to wallet - CPubKey pubkey; - if (GetPubKey(meta_pair.first, pubkey)) { - batch->WriteKeyMetadata(meta, pubkey, true); - } - } - } -} - -bool LegacyScriptPubKeyMan::SetupGeneration(bool force) -{ - if ((CanGenerateKeys() && !force) || m_storage.IsLocked()) { - return false; - } - - SetHDSeed(GenerateNewSeed()); - if (!NewKeyPool()) { - return false; - } - return true; -} - -bool LegacyScriptPubKeyMan::IsHDEnabled() const -{ - return !m_hd_chain.seed_id.IsNull(); -} - -bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const -{ - LOCK(cs_KeyStore); - // Check if the keypool has keys - bool keypool_has_keys; - if (internal && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { - keypool_has_keys = setInternalKeyPool.size() > 0; - } else { - keypool_has_keys = KeypoolCountExternalKeys() > 0; - } - // If the keypool doesn't have keys, check if we can generate them - if (!keypool_has_keys) { - return CanGenerateKeys(); - } - return keypool_has_keys; -} - -bool LegacyScriptPubKeyMan::Upgrade(int prev_version, int new_version, bilingual_str& error) -{ - LOCK(cs_KeyStore); - - if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - // Nothing to do here if private keys are not enabled - return true; - } - - bool hd_upgrade = false; - bool split_upgrade = false; - if (IsFeatureSupported(new_version, FEATURE_HD) && !IsHDEnabled()) { - WalletLogPrintf("Upgrading wallet to HD\n"); - m_storage.SetMinVersion(FEATURE_HD); - - // generate a new master key - CPubKey masterPubKey = GenerateNewSeed(); - SetHDSeed(masterPubKey); - hd_upgrade = true; - } - // Upgrade to HD chain split if necessary - if (!IsFeatureSupported(prev_version, FEATURE_HD_SPLIT) && IsFeatureSupported(new_version, FEATURE_HD_SPLIT)) { - WalletLogPrintf("Upgrading wallet to use HD chain split\n"); - m_storage.SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); - split_upgrade = FEATURE_HD_SPLIT > prev_version; - // Upgrade the HDChain - if (m_hd_chain.nVersion < CHDChain::VERSION_HD_CHAIN_SPLIT) { - m_hd_chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; - if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(m_hd_chain)) { - throw std::runtime_error(std::string(__func__) + ": writing chain failed"); - } - } - } - // Mark all keys currently in the keypool as pre-split - if (split_upgrade) { - MarkPreSplitKeys(); - } - // Regenerate the keypool if upgraded to HD - if (hd_upgrade) { - if (!NewKeyPool()) { - error = _("Unable to generate keys"); - return false; - } - } - return true; -} - -bool LegacyScriptPubKeyMan::HavePrivateKeys() const -{ - LOCK(cs_KeyStore); - return !mapKeys.empty() || !mapCryptedKeys.empty(); -} - -bool LegacyScriptPubKeyMan::HaveCryptedKeys() const -{ - LOCK(cs_KeyStore); - return !mapCryptedKeys.empty(); -} - -void LegacyScriptPubKeyMan::RewriteDB() -{ - LOCK(cs_KeyStore); - setInternalKeyPool.clear(); - setExternalKeyPool.clear(); - m_pool_key_to_index.clear(); - // Note: can't top-up keypool here, because wallet is locked. - // User will be prompted to unlock wallet the next operation - // that requires a new key. -} - -static int64_t GetOldestKeyTimeInPool(const std::set& setKeyPool, WalletBatch& batch) { - if (setKeyPool.empty()) { - return GetTime(); - } - - CKeyPool keypool; - int64_t nIndex = *(setKeyPool.begin()); - if (!batch.ReadPool(nIndex, keypool)) { - throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); - } - assert(keypool.vchPubKey.IsValid()); - return keypool.nTime; -} - -std::optional LegacyScriptPubKeyMan::GetOldestKeyPoolTime() const -{ - LOCK(cs_KeyStore); - - WalletBatch batch(m_storage.GetDatabase()); - - // load oldest key from keypool, get time and return - int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); - if (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { - oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey); - if (!set_pre_split_keypool.empty()) { - oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey); - } - } - - return oldestKey; -} - -size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() const -{ - LOCK(cs_KeyStore); - return setExternalKeyPool.size() + set_pre_split_keypool.size(); -} - -unsigned int LegacyScriptPubKeyMan::GetKeyPoolSize() const -{ - LOCK(cs_KeyStore); - return setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(); -} - -int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const -{ - LOCK(cs_KeyStore); - return nTimeFirstKey; -} - std::unique_ptr LegacyDataSPKM::GetSolvingProvider(const CScript& script) const { return std::make_unique(*this); @@ -620,166 +277,11 @@ bool LegacyDataSPKM::CanProvide(const CScript& script, SignatureData& sigdata) } } -bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map& coins, int sighash, std::map& input_errors) const -{ - return ::SignTransaction(tx, this, coins, sighash, input_errors); -} - -SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const -{ - CKey key; - if (!GetKey(ToKeyID(pkhash), key)) { - return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; - } - - if (MessageSign(key, message, str_sig)) { - return SigningResult::OK; - } - return SigningResult::SIGNING_FAILED; -} - -std::optional LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const -{ - if (n_signed) { - *n_signed = 0; - } - for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { - const CTxIn& txin = psbtx.tx->vin[i]; - PSBTInput& input = psbtx.inputs.at(i); - - if (PSBTInputSigned(input)) { - continue; - } - - // Get the Sighash type - if (sign && input.sighash_type != std::nullopt && *input.sighash_type != sighash_type) { - return PSBTError::SIGHASH_MISMATCH; - } - - // Check non_witness_utxo has specified prevout - if (input.non_witness_utxo) { - if (txin.prevout.n >= input.non_witness_utxo->vout.size()) { - return PSBTError::MISSING_INPUTS; - } - } else if (input.witness_utxo.IsNull()) { - // There's no UTXO so we can just skip this now - continue; - } - SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, &txdata, sighash_type, nullptr, finalize); - - bool signed_one = PSBTInputSigned(input); - if (n_signed && (signed_one || !sign)) { - // If sign is false, we assume that we _could_ sign if we get here. This - // will never have false negatives; it is hard to tell under what i - // circumstances it could have false positives. - (*n_signed)++; - } - } - - // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change - for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { - UpdatePSBTOutput(HidingSigningProvider(this, true, !bip32derivs), psbtx, i); - } - - return {}; -} - -std::unique_ptr LegacyScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const -{ - LOCK(cs_KeyStore); - - CKeyID key_id = GetKeyForDestination(*this, dest); - if (!key_id.IsNull()) { - auto it = mapKeyMetadata.find(key_id); - if (it != mapKeyMetadata.end()) { - return std::make_unique(it->second); - } - } - - CScript scriptPubKey = GetScriptForDestination(dest); - auto it = m_script_metadata.find(CScriptID(scriptPubKey)); - if (it != m_script_metadata.end()) { - return std::make_unique(it->second); - } - - return nullptr; -} - -uint256 LegacyScriptPubKeyMan::GetID() const -{ - return uint256::ONE; -} - -/** - * Update wallet first key creation time. This should be called whenever keys - * are added to the wallet, with the oldest key creation time. - */ -void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime) -{ - AssertLockHeld(cs_KeyStore); - if (nCreateTime <= 1) { - // Cannot determine birthday information, so set the wallet birthday to - // the beginning of time. - nTimeFirstKey = 1; - } else if (nTimeFirstKey == UNKNOWN_TIME || nCreateTime < nTimeFirstKey) { - nTimeFirstKey = nCreateTime; - } - - NotifyFirstKeyTimeChanged(this, nTimeFirstKey); -} - bool LegacyDataSPKM::LoadKey(const CKey& key, const CPubKey &pubkey) { return AddKeyPubKeyInner(key, pubkey); } -bool LegacyScriptPubKeyMan::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) -{ - LOCK(cs_KeyStore); - WalletBatch batch(m_storage.GetDatabase()); - return LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(batch, secret, pubkey); -} - -bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey) -{ - AssertLockHeld(cs_KeyStore); - - // Make sure we aren't adding private keys to private key disabled wallets - assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); - - // FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey - // which is overridden below. To avoid flushes, the database handle is - // tunneled through to it. - bool needsDB = !encrypted_batch; - if (needsDB) { - encrypted_batch = &batch; - } - if (!AddKeyPubKeyInner(secret, pubkey)) { - if (needsDB) encrypted_batch = nullptr; - return false; - } - if (needsDB) encrypted_batch = nullptr; - - // check if we need to remove from watch-only - CScript script; - script = GetScriptForDestination(PKHash(pubkey)); - if (HaveWatchOnly(script)) { - RemoveWatchOnly(script); - } - script = GetScriptForRawPubKey(pubkey); - if (HaveWatchOnly(script)) { - RemoveWatchOnly(script); - } - - m_storage.UnsetBlankWalletFlag(batch); - if (!m_storage.HasEncryptionKeys()) { - return batch.WriteKey(pubkey, - secret.GetPrivKey(), - mapKeyMetadata[pubkey.GetID()]); - } - return true; -} - bool LegacyDataSPKM::LoadCScript(const CScript& redeemScript) { /* A sanity check was added in pull #3843 to avoid adding redeemScripts @@ -801,57 +303,18 @@ void LegacyDataSPKM::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& me mapKeyMetadata[keyID] = meta; } -void LegacyScriptPubKeyMan::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta) -{ - LOCK(cs_KeyStore); - LegacyDataSPKM::LoadKeyMetadata(keyID, meta); - UpdateTimeFirstKey(meta.nCreateTime); -} - void LegacyDataSPKM::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta) { LOCK(cs_KeyStore); m_script_metadata[script_id] = meta; } -void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta) -{ - LOCK(cs_KeyStore); - LegacyDataSPKM::LoadScriptMetadata(script_id, meta); - UpdateTimeFirstKey(meta.nCreateTime); -} - bool LegacyDataSPKM::AddKeyPubKeyInner(const CKey& key, const CPubKey& pubkey) { LOCK(cs_KeyStore); return FillableSigningProvider::AddKeyPubKey(key, pubkey); } -bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) -{ - LOCK(cs_KeyStore); - if (!m_storage.HasEncryptionKeys()) { - return FillableSigningProvider::AddKeyPubKey(key, pubkey); - } - - if (m_storage.IsLocked()) { - return false; - } - - std::vector vchCryptedSecret; - CKeyingMaterial vchSecret{UCharCast(key.begin()), UCharCast(key.end())}; - if (!m_storage.WithEncryptionKey([&](const CKeyingMaterial& encryption_key) { - return EncryptSecret(encryption_key, vchSecret, pubkey.GetHash(), vchCryptedSecret); - })) { - return false; - } - - if (!AddCryptedKey(pubkey, vchCryptedSecret)) { - return false; - } - return true; -} - bool LegacyDataSPKM::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret, bool checksum_valid) { // Set fDecryptionThoroughlyChecked to false when the checksum is invalid @@ -872,24 +335,6 @@ bool LegacyDataSPKM::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vec return true; } -bool LegacyScriptPubKeyMan::AddCryptedKey(const CPubKey &vchPubKey, - const std::vector &vchCryptedSecret) -{ - if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret)) - return false; - { - LOCK(cs_KeyStore); - if (encrypted_batch) - return encrypted_batch->WriteCryptedKey(vchPubKey, - vchCryptedSecret, - mapKeyMetadata[vchPubKey.GetID()]); - else - return WalletBatch(m_storage.GetDatabase()).WriteCryptedKey(vchPubKey, - vchCryptedSecret, - mapKeyMetadata[vchPubKey.GetID()]); - } -} - bool LegacyDataSPKM::HaveWatchOnly(const CScript &dest) const { LOCK(cs_KeyStore); @@ -902,6 +347,11 @@ bool LegacyDataSPKM::HaveWatchOnly() const return (!setWatchOnly.empty()); } +bool LegacyDataSPKM::LoadWatchOnly(const CScript &dest) +{ + return AddWatchOnlyInMem(dest); +} + static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) { std::vector> solutions; @@ -909,32 +359,6 @@ static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) (pubKeyOut = CPubKey(solutions[0])).IsFullyValid(); } -bool LegacyScriptPubKeyMan::RemoveWatchOnly(const CScript &dest) -{ - { - LOCK(cs_KeyStore); - setWatchOnly.erase(dest); - CPubKey pubKey; - if (ExtractPubKey(dest, pubKey)) { - mapWatchKeys.erase(pubKey.GetID()); - } - // Related CScripts are not removed; having superfluous scripts around is - // harmless (see comment in ImplicitlyLearnRelatedKeyScripts). - } - - if (!HaveWatchOnly()) - NotifyWatchonlyChanged(false); - if (!WalletBatch(m_storage.GetDatabase()).EraseWatchOnly(dest)) - return false; - - return true; -} - -bool LegacyDataSPKM::LoadWatchOnly(const CScript &dest) -{ - return AddWatchOnlyInMem(dest); -} - bool LegacyDataSPKM::AddWatchOnlyInMem(const CScript &dest) { LOCK(cs_KeyStore); @@ -947,59 +371,12 @@ bool LegacyDataSPKM::AddWatchOnlyInMem(const CScript &dest) return true; } -bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) -{ - if (!AddWatchOnlyInMem(dest)) - return false; - const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)]; - UpdateTimeFirstKey(meta.nCreateTime); - NotifyWatchonlyChanged(true); - if (batch.WriteWatchOnly(dest, meta)) { - m_storage.UnsetBlankWalletFlag(batch); - return true; - } - return false; -} - -bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) -{ - m_script_metadata[CScriptID(dest)].nCreateTime = create_time; - return AddWatchOnlyWithDB(batch, dest); -} - -bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest) -{ - WalletBatch batch(m_storage.GetDatabase()); - return AddWatchOnlyWithDB(batch, dest); -} - -bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest, int64_t nCreateTime) -{ - m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; - return AddWatchOnly(dest); -} - void LegacyDataSPKM::LoadHDChain(const CHDChain& chain) { LOCK(cs_KeyStore); m_hd_chain = chain; } -void LegacyScriptPubKeyMan::AddHDChain(const CHDChain& chain) -{ - LOCK(cs_KeyStore); - // Store the new chain - if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) { - throw std::runtime_error(std::string(__func__) + ": writing chain failed"); - } - // When there's an old chain, add it as an inactive chain as we are now rotating hd chains - if (!m_hd_chain.seed_id.IsNull()) { - AddInactiveHDChain(m_hd_chain); - } - - m_hd_chain = chain; -} - void LegacyDataSPKM::AddInactiveHDChain(const CHDChain& chain) { LOCK(cs_KeyStore); @@ -1086,626 +463,6 @@ bool LegacyDataSPKM::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) con return GetWatchPubKey(address, vchPubKeyOut); } -CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, CHDChain& hd_chain, bool internal) -{ - assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); - assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); - AssertLockHeld(cs_KeyStore); - bool fCompressed = m_storage.CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets - - CKey secret; - - // Create new metadata - int64_t nCreationTime = GetTime(); - CKeyMetadata metadata(nCreationTime); - - // use HD key derivation if HD was enabled during wallet creation and a seed is present - if (IsHDEnabled()) { - DeriveNewChildKey(batch, metadata, secret, hd_chain, (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); - } else { - secret.MakeNewKey(fCompressed); - } - - // Compressed public keys were introduced in version 0.6.0 - if (fCompressed) { - m_storage.SetMinVersion(FEATURE_COMPRPUBKEY); - } - - CPubKey pubkey = secret.GetPubKey(); - assert(secret.VerifyPubKey(pubkey)); - - mapKeyMetadata[pubkey.GetID()] = metadata; - UpdateTimeFirstKey(nCreationTime); - - if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { - throw std::runtime_error(std::string(__func__) + ": AddKey failed"); - } - return pubkey; -} - -//! Try to derive an extended key, throw if it fails. -static void DeriveExtKey(CExtKey& key_in, unsigned int index, CExtKey& key_out) { - if (!key_in.Derive(key_out, index)) { - throw std::runtime_error("Could not derive extended key"); - } -} - -void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal) -{ - // for now we use a fixed keypath scheme of m/0'/0'/k - CKey seed; //seed (256bit) - CExtKey masterKey; //hd master key - CExtKey accountKey; //key at m/0' - CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal) - CExtKey childKey; //key at m/0'/0'/' - - // try to get the seed - if (!GetKey(hd_chain.seed_id, seed)) - throw std::runtime_error(std::string(__func__) + ": seed not found"); - - masterKey.SetSeed(seed); - - // derive m/0' - // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) - DeriveExtKey(masterKey, BIP32_HARDENED_KEY_LIMIT, accountKey); - - // derive m/0'/0' (external chain) OR m/0'/1' (internal chain) - assert(internal ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true); - DeriveExtKey(accountKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0), chainChildKey); - - // derive child key at next index, skip keys already known to the wallet - do { - // always derive hardened keys - // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range - // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 - if (internal) { - DeriveExtKey(chainChildKey, hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT, childKey); - metadata.hdKeypath = "m/0'/1'/" + ToString(hd_chain.nInternalChainCounter) + "'"; - metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); - metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT); - metadata.key_origin.path.push_back(hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT); - hd_chain.nInternalChainCounter++; - } - else { - DeriveExtKey(chainChildKey, hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT, childKey); - metadata.hdKeypath = "m/0'/0'/" + ToString(hd_chain.nExternalChainCounter) + "'"; - metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); - metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); - metadata.key_origin.path.push_back(hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); - hd_chain.nExternalChainCounter++; - } - } while (HaveKey(childKey.key.GetPubKey().GetID())); - secret = childKey.key; - metadata.hd_seed_id = hd_chain.seed_id; - CKeyID master_id = masterKey.key.GetPubKey().GetID(); - std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint); - metadata.has_key_origin = true; - // update the chain model in the database - if (hd_chain.seed_id == m_hd_chain.seed_id && !batch.WriteHDChain(hd_chain)) - throw std::runtime_error(std::string(__func__) + ": writing HD chain model failed"); -} - -void LegacyDataSPKM::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) -{ - LOCK(cs_KeyStore); - if (keypool.m_pre_split) { - set_pre_split_keypool.insert(nIndex); - } else if (keypool.fInternal) { - setInternalKeyPool.insert(nIndex); - } else { - setExternalKeyPool.insert(nIndex); - } - m_max_keypool_index = std::max(m_max_keypool_index, nIndex); - m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex; - - // If no metadata exists yet, create a default with the pool key's - // creation time. Note that this may be overwritten by actually - // stored metadata for that key later, which is fine. - CKeyID keyid = keypool.vchPubKey.GetID(); - if (mapKeyMetadata.count(keyid) == 0) - mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); -} - -bool LegacyScriptPubKeyMan::CanGenerateKeys() const -{ - // A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD) - LOCK(cs_KeyStore); - return IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD); -} - -CPubKey LegacyScriptPubKeyMan::GenerateNewSeed() -{ - assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); - CKey key = GenerateRandomKey(); - return DeriveNewSeed(key); -} - -CPubKey LegacyScriptPubKeyMan::DeriveNewSeed(const CKey& key) -{ - int64_t nCreationTime = GetTime(); - CKeyMetadata metadata(nCreationTime); - - // calculate the seed - CPubKey seed = key.GetPubKey(); - assert(key.VerifyPubKey(seed)); - - // set the hd keypath to "s" -> Seed, refers the seed to itself - metadata.hdKeypath = "s"; - metadata.has_key_origin = false; - metadata.hd_seed_id = seed.GetID(); - - { - LOCK(cs_KeyStore); - - // mem store the metadata - mapKeyMetadata[seed.GetID()] = metadata; - - // write the key&metadata to the database - if (!AddKeyPubKey(key, seed)) - throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); - } - - return seed; -} - -void LegacyScriptPubKeyMan::SetHDSeed(const CPubKey& seed) -{ - LOCK(cs_KeyStore); - // store the keyid (hash160) together with - // the child index counter in the database - // as a hdchain object - CHDChain newHdChain; - newHdChain.nVersion = m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; - newHdChain.seed_id = seed.GetID(); - AddHDChain(newHdChain); - NotifyCanGetAddressesChanged(); - WalletBatch batch(m_storage.GetDatabase()); - m_storage.UnsetBlankWalletFlag(batch); -} - -/** - * Mark old keypool keys as used, - * and generate all new keys - */ -bool LegacyScriptPubKeyMan::NewKeyPool() -{ - if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - return false; - } - { - LOCK(cs_KeyStore); - WalletBatch batch(m_storage.GetDatabase()); - - for (const int64_t nIndex : setInternalKeyPool) { - batch.ErasePool(nIndex); - } - setInternalKeyPool.clear(); - - for (const int64_t nIndex : setExternalKeyPool) { - batch.ErasePool(nIndex); - } - setExternalKeyPool.clear(); - - for (const int64_t nIndex : set_pre_split_keypool) { - batch.ErasePool(nIndex); - } - set_pre_split_keypool.clear(); - - m_pool_key_to_index.clear(); - - if (!TopUp()) { - return false; - } - WalletLogPrintf("LegacyScriptPubKeyMan::NewKeyPool rewrote keypool\n"); - } - return true; -} - -bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize) -{ - if (!CanGenerateKeys()) { - return false; - } - - WalletBatch batch(m_storage.GetDatabase()); - if (!batch.TxnBegin()) return false; - if (!TopUpChain(batch, m_hd_chain, kpSize)) { - return false; - } - for (auto& [chain_id, chain] : m_inactive_hd_chains) { - if (!TopUpChain(batch, chain, kpSize)) { - return false; - } - } - if (!batch.TxnCommit()) throw std::runtime_error(strprintf("Error during keypool top up. Cannot commit changes for wallet %s", m_storage.GetDisplayName())); - NotifyCanGetAddressesChanged(); - // Note: Unlike with DescriptorSPKM, LegacySPKM does not need to call - // m_storage.TopUpCallback() as we do not know what new scripts the LegacySPKM is - // watching for. CWallet's scriptPubKey cache is not used for LegacySPKMs. - return true; -} - -bool LegacyScriptPubKeyMan::TopUpChain(WalletBatch& batch, CHDChain& chain, unsigned int kpSize) -{ - LOCK(cs_KeyStore); - - if (m_storage.IsLocked()) return false; - - // Top up key pool - unsigned int nTargetSize; - if (kpSize > 0) { - nTargetSize = kpSize; - } else { - nTargetSize = m_keypool_size; - } - int64_t target = std::max((int64_t) nTargetSize, int64_t{1}); - - // count amount of available keys (internal, external) - // make sure the keypool of external and internal keys fits the user selected target (-keypool) - int64_t missingExternal; - int64_t missingInternal; - if (chain == m_hd_chain) { - missingExternal = std::max(target - (int64_t)setExternalKeyPool.size(), int64_t{0}); - missingInternal = std::max(target - (int64_t)setInternalKeyPool.size(), int64_t{0}); - } else { - missingExternal = std::max(target - (chain.nExternalChainCounter - chain.m_next_external_index), int64_t{0}); - missingInternal = std::max(target - (chain.nInternalChainCounter - chain.m_next_internal_index), int64_t{0}); - } - - if (!IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { - // don't create extra internal keys - missingInternal = 0; - } - bool internal = false; - for (int64_t i = missingInternal + missingExternal; i--;) { - if (i < missingInternal) { - internal = true; - } - - CPubKey pubkey(GenerateNewKey(batch, chain, internal)); - if (chain == m_hd_chain) { - AddKeypoolPubkeyWithDB(pubkey, internal, batch); - } - } - if (missingInternal + missingExternal > 0) { - if (chain == m_hd_chain) { - WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size()); - } else { - WalletLogPrintf("inactive seed with id %s added %d external keys, %d internal keys\n", HexStr(chain.seed_id), missingExternal, missingInternal); - } - } - return true; -} - -void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch) -{ - LOCK(cs_KeyStore); - assert(m_max_keypool_index < std::numeric_limits::max()); // How in the hell did you use so many keys? - int64_t index = ++m_max_keypool_index; - if (!batch.WritePool(index, CKeyPool(pubkey, internal))) { - throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed"); - } - if (internal) { - setInternalKeyPool.insert(index); - } else { - setExternalKeyPool.insert(index); - } - m_pool_key_to_index[pubkey.GetID()] = index; -} - -void LegacyScriptPubKeyMan::KeepDestination(int64_t nIndex, const OutputType& type) -{ - assert(type != OutputType::BECH32M); - // Remove from key pool - WalletBatch batch(m_storage.GetDatabase()); - batch.ErasePool(nIndex); - CPubKey pubkey; - bool have_pk = GetPubKey(m_index_to_reserved_key.at(nIndex), pubkey); - assert(have_pk); - LearnRelatedScripts(pubkey, type); - m_index_to_reserved_key.erase(nIndex); - WalletLogPrintf("keypool keep %d\n", nIndex); -} - -void LegacyScriptPubKeyMan::ReturnDestination(int64_t nIndex, bool fInternal, const CTxDestination&) -{ - // Return to key pool - { - LOCK(cs_KeyStore); - if (fInternal) { - setInternalKeyPool.insert(nIndex); - } else if (!set_pre_split_keypool.empty()) { - set_pre_split_keypool.insert(nIndex); - } else { - setExternalKeyPool.insert(nIndex); - } - CKeyID& pubkey_id = m_index_to_reserved_key.at(nIndex); - m_pool_key_to_index[pubkey_id] = nIndex; - m_index_to_reserved_key.erase(nIndex); - NotifyCanGetAddressesChanged(); - } - WalletLogPrintf("keypool return %d\n", nIndex); -} - -bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const OutputType type) -{ - assert(type != OutputType::BECH32M); - if (!CanGetAddresses(/*internal=*/ false)) { - return false; - } - - CKeyPool keypool; - { - LOCK(cs_KeyStore); - int64_t nIndex; - if (!ReserveKeyFromKeyPool(nIndex, keypool, /*fRequestedInternal=*/ false) && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { - if (m_storage.IsLocked()) return false; - WalletBatch batch(m_storage.GetDatabase()); - result = GenerateNewKey(batch, m_hd_chain, /*internal=*/ false); - return true; - } - KeepDestination(nIndex, type); - result = keypool.vchPubKey; - } - return true; -} - -bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) -{ - nIndex = -1; - keypool.vchPubKey = CPubKey(); - { - LOCK(cs_KeyStore); - - bool fReturningInternal = fRequestedInternal; - fReturningInternal &= (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); - bool use_split_keypool = set_pre_split_keypool.empty(); - std::set& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; - - // Get the oldest key - if (setKeyPool.empty()) { - return false; - } - - WalletBatch batch(m_storage.GetDatabase()); - - auto it = setKeyPool.begin(); - nIndex = *it; - setKeyPool.erase(it); - if (!batch.ReadPool(nIndex, keypool)) { - throw std::runtime_error(std::string(__func__) + ": read failed"); - } - CPubKey pk; - if (!GetPubKey(keypool.vchPubKey.GetID(), pk)) { - throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); - } - // If the key was pre-split keypool, we don't care about what type it is - if (use_split_keypool && keypool.fInternal != fReturningInternal) { - throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); - } - if (!keypool.vchPubKey.IsValid()) { - throw std::runtime_error(std::string(__func__) + ": keypool entry invalid"); - } - - assert(m_index_to_reserved_key.count(nIndex) == 0); - m_index_to_reserved_key[nIndex] = keypool.vchPubKey.GetID(); - m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); - WalletLogPrintf("keypool reserve %d\n", nIndex); - } - NotifyCanGetAddressesChanged(); - return true; -} - -void LegacyScriptPubKeyMan::LearnRelatedScripts(const CPubKey& key, OutputType type) -{ - assert(type != OutputType::BECH32M); - if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) { - CTxDestination witdest = WitnessV0KeyHash(key.GetID()); - CScript witprog = GetScriptForDestination(witdest); - // Make sure the resulting program is solvable. - const auto desc = InferDescriptor(witprog, *this); - assert(desc && desc->IsSolvable()); - AddCScript(witprog); - } -} - -void LegacyScriptPubKeyMan::LearnAllRelatedScripts(const CPubKey& key) -{ - // OutputType::P2SH_SEGWIT always adds all necessary scripts for all types. - LearnRelatedScripts(key, OutputType::P2SH_SEGWIT); -} - -std::vector LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) -{ - AssertLockHeld(cs_KeyStore); - bool internal = setInternalKeyPool.count(keypool_id); - if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id)); - std::set *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool); - auto it = setKeyPool->begin(); - - std::vector result; - WalletBatch batch(m_storage.GetDatabase()); - while (it != std::end(*setKeyPool)) { - const int64_t& index = *(it); - if (index > keypool_id) break; // set*KeyPool is ordered - - CKeyPool keypool; - if (batch.ReadPool(index, keypool)) { //TODO: This should be unnecessary - m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); - } - LearnAllRelatedScripts(keypool.vchPubKey); - batch.ErasePool(index); - WalletLogPrintf("keypool index %d removed\n", index); - it = setKeyPool->erase(it); - result.push_back(std::move(keypool)); - } - - return result; -} - -std::vector GetAffectedKeys(const CScript& spk, const SigningProvider& provider) -{ - std::vector dummy; - FlatSigningProvider out; - InferDescriptor(spk, provider)->Expand(0, DUMMY_SIGNING_PROVIDER, dummy, out); - std::vector ret; - ret.reserve(out.pubkeys.size()); - for (const auto& entry : out.pubkeys) { - ret.push_back(entry.first); - } - return ret; -} - -void LegacyScriptPubKeyMan::MarkPreSplitKeys() -{ - WalletBatch batch(m_storage.GetDatabase()); - for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) { - int64_t index = *it; - CKeyPool keypool; - if (!batch.ReadPool(index, keypool)) { - throw std::runtime_error(std::string(__func__) + ": read keypool entry failed"); - } - keypool.m_pre_split = true; - if (!batch.WritePool(index, keypool)) { - throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed"); - } - set_pre_split_keypool.insert(index); - it = setExternalKeyPool.erase(it); - } -} - -bool LegacyScriptPubKeyMan::AddCScript(const CScript& redeemScript) -{ - WalletBatch batch(m_storage.GetDatabase()); - return AddCScriptWithDB(batch, redeemScript); -} - -bool LegacyScriptPubKeyMan::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript) -{ - if (!FillableSigningProvider::AddCScript(redeemScript)) - return false; - if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) { - m_storage.UnsetBlankWalletFlag(batch); - return true; - } - return false; -} - -bool LegacyScriptPubKeyMan::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info) -{ - LOCK(cs_KeyStore); - std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint); - mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path; - mapKeyMetadata[pubkey.GetID()].has_key_origin = true; - mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path, /*apostrophe=*/true); - return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); -} - -bool LegacyScriptPubKeyMan::ImportScripts(const std::set scripts, int64_t timestamp) -{ - WalletBatch batch(m_storage.GetDatabase()); - for (const auto& entry : scripts) { - CScriptID id(entry); - if (HaveCScript(id)) { - WalletLogPrintf("Already have script %s, skipping\n", HexStr(entry)); - continue; - } - if (!AddCScriptWithDB(batch, entry)) { - return false; - } - - if (timestamp > 0) { - m_script_metadata[CScriptID(entry)].nCreateTime = timestamp; - } - } - if (timestamp > 0) { - UpdateTimeFirstKey(timestamp); - } - - return true; -} - -bool LegacyScriptPubKeyMan::ImportPrivKeys(const std::map& privkey_map, const int64_t timestamp) -{ - WalletBatch batch(m_storage.GetDatabase()); - for (const auto& entry : privkey_map) { - const CKey& key = entry.second; - CPubKey pubkey = key.GetPubKey(); - const CKeyID& id = entry.first; - assert(key.VerifyPubKey(pubkey)); - // Skip if we already have the key - if (HaveKey(id)) { - WalletLogPrintf("Already have key with pubkey %s, skipping\n", HexStr(pubkey)); - continue; - } - mapKeyMetadata[id].nCreateTime = timestamp; - // If the private key is not present in the wallet, insert it. - if (!AddKeyPubKeyWithDB(batch, key, pubkey)) { - return false; - } - UpdateTimeFirstKey(timestamp); - } - return true; -} - -bool LegacyScriptPubKeyMan::ImportPubKeys(const std::vector>& ordered_pubkeys, const std::map& pubkey_map, const std::map>& key_origins, const bool add_keypool, const int64_t timestamp) -{ - WalletBatch batch(m_storage.GetDatabase()); - for (const auto& entry : key_origins) { - AddKeyOriginWithDB(batch, entry.second.first, entry.second.second); - } - for (const auto& [id, internal] : ordered_pubkeys) { - auto entry = pubkey_map.find(id); - if (entry == pubkey_map.end()) { - continue; - } - const CPubKey& pubkey = entry->second; - CPubKey temp; - if (GetPubKey(id, temp)) { - // Already have pubkey, skipping - WalletLogPrintf("Already have pubkey %s, skipping\n", HexStr(temp)); - continue; - } - if (!AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) { - return false; - } - mapKeyMetadata[id].nCreateTime = timestamp; - - // Add to keypool only works with pubkeys - if (add_keypool) { - AddKeypoolPubkeyWithDB(pubkey, internal, batch); - NotifyCanGetAddressesChanged(); - } - } - return true; -} - -bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::set& script_pub_keys, const bool have_solving_data, const int64_t timestamp) -{ - WalletBatch batch(m_storage.GetDatabase()); - for (const CScript& script : script_pub_keys) { - if (!have_solving_data || !IsMine(script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated - if (!AddWatchOnlyWithDB(batch, script, timestamp)) { - return false; - } - } - } - return true; -} - -std::set LegacyScriptPubKeyMan::GetKeys() const -{ - LOCK(cs_KeyStore); - if (!m_storage.HasEncryptionKeys()) { - return FillableSigningProvider::GetKeys(); - } - std::set set_address; - for (const auto& mi : mapCryptedKeys) { - set_address.insert(mi.first); - } - return set_address; -} - std::unordered_set LegacyDataSPKM::GetCandidateScriptPubKeys() const { LOCK(cs_KeyStore); @@ -2177,7 +934,7 @@ bool DescriptorScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, Walle return true; } -util::Result DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) +util::Result DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index) { LOCK(cs_desc_man); auto op_dest = GetNewDestination(type); diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 8af16319c1f..0e776d38a02 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -65,99 +65,6 @@ static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; std::vector GetAffectedKeys(const CScript& spk, const SigningProvider& provider); -/** A key from a CWallet's keypool - * - * The wallet holds one (for pre HD-split wallets) or several keypools. These - * are sets of keys that have not yet been used to provide addresses or receive - * change. - * - * The Bitcoin Core wallet was originally a collection of unrelated private - * keys with their associated addresses. If a non-HD wallet generated a - * key/address, gave that address out and then restored a backup from before - * that key's generation, then any funds sent to that address would be - * lost definitively. - * - * The keypool was implemented to avoid this scenario (commit: 10384941). The - * wallet would generate a set of keys (100 by default). When a new public key - * was required, either to give out as an address or to use in a change output, - * it would be drawn from the keypool. The keypool would then be topped up to - * maintain 100 keys. This ensured that as long as the wallet hadn't used more - * than 100 keys since the previous backup, all funds would be safe, since a - * restored wallet would be able to scan for all owned addresses. - * - * A keypool also allowed encrypted wallets to give out addresses without - * having to be decrypted to generate a new private key. - * - * With the introduction of HD wallets (commit: f1902510), the keypool - * essentially became an address look-ahead pool. Restoring old backups can no - * longer definitively lose funds as long as the addresses used were from the - * wallet's HD seed (since all private keys can be rederived from the seed). - * However, if many addresses were used since the backup, then the wallet may - * not know how far ahead in the HD chain to look for its addresses. The - * keypool is used to implement a 'gap limit'. The keypool maintains a set of - * keys (by default 1000) ahead of the last used key and scans for the - * addresses of those keys. This avoids the risk of not seeing transactions - * involving the wallet's addresses, or of re-using the same address. - * In the unlikely case where none of the addresses in the `gap limit` are - * used on-chain, the look-ahead will not be incremented to keep - * a constant size and addresses beyond this range will not be detected by an - * old backup. For this reason, it is not recommended to decrease keypool size - * lower than default value. - * - * The HD-split wallet feature added a second keypool (commit: 02592f4c). There - * is an external keypool (for addresses to hand out) and an internal keypool - * (for change addresses). - * - * Keypool keys are stored in the wallet/keystore's keymap. The keypool data is - * stored as sets of indexes in the wallet (setInternalKeyPool, - * setExternalKeyPool and set_pre_split_keypool), and a map from the key to the - * index (m_pool_key_to_index). The CKeyPool object is used to - * serialize/deserialize the pool data to/from the database. - */ -class CKeyPool -{ -public: - //! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB - int64_t nTime; - //! The public key - CPubKey vchPubKey; - //! Whether this keypool entry is in the internal keypool (for change outputs) - bool fInternal; - //! Whether this key was generated for a keypool before the wallet was upgraded to HD-split - bool m_pre_split; - - CKeyPool(); - CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn); - - template - void Serialize(Stream& s) const - { - s << int{259900}; // Unused field, writes the highest client version ever written - s << nTime << vchPubKey << fInternal << m_pre_split; - } - - template - void Unserialize(Stream& s) - { - s >> int{}; // Discard unused field - s >> nTime >> vchPubKey; - try { - s >> fInternal; - } catch (std::ios_base::failure&) { - /* flag as external address if we can't read the internal boolean - (this will be the case for any wallet before the HD chain split version) */ - fInternal = false; - } - try { - s >> m_pre_split; - } catch (std::ios_base::failure&) { - /* flag as postsplit address if we can't read the m_pre_split boolean - (this will be the case for any wallet that upgrades to HD chain split) */ - m_pre_split = false; - } - } -}; - struct WalletDestination { CTxDestination dest; @@ -186,7 +93,7 @@ public: virtual bool CheckDecryptionKey(const CKeyingMaterial& master_key) { return false; } virtual bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { return false; } - virtual util::Result GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) { return util::Error{Untranslated("Not supported")}; } + virtual util::Result GetReservedDestination(const OutputType type, bool internal, int64_t& index) { return util::Error{Untranslated("Not supported")}; } virtual void KeepDestination(int64_t index, const OutputType& type) {} virtual void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) {} @@ -277,8 +184,6 @@ static const std::unordered_set LEGACY_OUTPUT_TYPES { OutputType::BECH32, }; -class DescriptorScriptPubKeyMan; - // Manages the data for a LegacyScriptPubKeyMan. // This is the minimum necessary to load a legacy wallet so that it can be migrated. class LegacyDataSPKM : public ScriptPubKeyMan, public FillableSigningProvider @@ -331,12 +236,6 @@ public: bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; - std::set setInternalKeyPool GUARDED_BY(cs_KeyStore); - std::set setExternalKeyPool GUARDED_BY(cs_KeyStore); - std::set set_pre_split_keypool GUARDED_BY(cs_KeyStore); - int64_t m_max_keypool_index GUARDED_BY(cs_KeyStore) = 0; - std::map m_pool_key_to_index; - //! Load metadata (used by LoadWallet) virtual void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata); virtual void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata); @@ -357,8 +256,6 @@ public: void LoadHDChain(const CHDChain& chain); void AddInactiveHDChain(const CHDChain& chain); const CHDChain& GetHDChain() const { return m_hd_chain; } - //! Load a keypool entry - void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool); //! Fetches a pubkey from mapWatchKeys if it exists there bool GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const; @@ -377,199 +274,6 @@ public: bool DeleteRecordsWithDB(WalletBatch& batch); }; -// Implements the full legacy wallet behavior -class LegacyScriptPubKeyMan : public LegacyDataSPKM -{ -private: - WalletBatch *encrypted_batch GUARDED_BY(cs_KeyStore) = nullptr; - - // By default, do not scan any block until keys/scripts are generated/imported - int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = UNKNOWN_TIME; - - //! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments) - int64_t m_keypool_size GUARDED_BY(cs_KeyStore){DEFAULT_KEYPOOL_SIZE}; - - bool AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) override; - - /** - * Private version of AddWatchOnly method which does not accept a - * timestamp, and which will reset the wallet's nTimeFirstKey value to 1 if - * the watch key did not previously have a timestamp associated with it. - * Because this is an inherited virtual method, it is accessible despite - * being marked private, but it is marked private anyway to encourage use - * of the other AddWatchOnly which accepts a timestamp and sets - * nTimeFirstKey more intelligently for more efficient rescans. - */ - bool AddWatchOnly(const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - //! Adds a watch-only address to the store, and saves it to disk. - bool AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - - //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - - void AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch); - - //! Adds a script to the store and saves it to disk - bool AddCScriptWithDB(WalletBatch& batch, const CScript& script); - - /** Add a KeyOriginInfo to the wallet */ - bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info); - - /* HD derive new child key (on internal or external chain) */ - void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - - // Tracks keypool indexes to CKeyIDs of keys that have been taken out of the keypool but may be returned to it - std::map m_index_to_reserved_key; - - //! Fetches a key from the keypool - bool GetKeyFromPool(CPubKey &key, const OutputType type); - - /** - * Reserves a key from the keypool and sets nIndex to its index - * - * @param[out] nIndex the index of the key in keypool - * @param[out] keypool the keypool the key was drawn from, which could be the - * the pre-split pool if present, or the internal or external pool - * @param fRequestedInternal true if the caller would like the key drawn - * from the internal keypool, false if external is preferred - * - * @return true if succeeded, false if failed due to empty keypool - * @throws std::runtime_error if keypool read failed, key was invalid, - * was not found in the wallet, or was misclassified in the internal - * or external keypool - */ - bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); - - /** - * Like TopUp() but adds keys for inactive HD chains. - * Ensures that there are at least -keypool number of keys derived after the given index. - * - * @param seed_id the CKeyID for the HD seed. - * @param index the index to start generating keys from - * @param internal whether the internal chain should be used. true for internal chain, false for external chain. - * - * @return true if seed was found and keys were derived. false if unable to derive seeds - */ - bool TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal); - - bool TopUpChain(WalletBatch& batch, CHDChain& chain, unsigned int size); -public: - LegacyScriptPubKeyMan(WalletStorage& storage, int64_t keypool_size) : LegacyDataSPKM(storage), m_keypool_size(keypool_size) {} - - util::Result GetNewDestination(const OutputType type) override; - - bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override; - - util::Result GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override; - void KeepDestination(int64_t index, const OutputType& type) override; - void ReturnDestination(int64_t index, bool internal, const CTxDestination&) override; - - bool TopUp(unsigned int size = 0) override; - - std::vector MarkUnusedAddresses(const CScript& script) override; - - //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo - void UpgradeKeyMetadata(); - - bool IsHDEnabled() const override; - - bool SetupGeneration(bool force = false) override; - - bool Upgrade(int prev_version, int new_version, bilingual_str& error) override; - - bool HavePrivateKeys() const override; - bool HaveCryptedKeys() const override; - - void RewriteDB() override; - - std::optional GetOldestKeyPoolTime() const override; - size_t KeypoolCountExternalKeys() const; - unsigned int GetKeyPoolSize() const override; - - int64_t GetTimeFirstKey() const override; - - std::unique_ptr GetMetadata(const CTxDestination& dest) const override; - - bool CanGetAddresses(bool internal = false) const override; - - bool SignTransaction(CMutableTransaction& tx, const std::map& coins, int sighash, std::map& input_errors) const override; - SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override; - std::optional FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override; - - uint256 GetID() const override; - - //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; - //! Adds an encrypted key to the store, and saves it to disk. - bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector &vchCryptedSecret); - void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - //! Load metadata (used by LoadWallet) - void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) override; - void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) override; - //! Generate a new key - CPubKey GenerateNewKey(WalletBatch& batch, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - - /* Set the HD chain model (chain child index counters) and writes it to the database */ - void AddHDChain(const CHDChain& chain); - - //! Remove a watch only script from the keystore - bool RemoveWatchOnly(const CScript &dest); - bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - - /* SigningProvider overrides */ - bool AddCScript(const CScript& redeemScript) override; - - bool NewKeyPool(); - void MarkPreSplitKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - - bool ImportScripts(const std::set scripts, int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - bool ImportPrivKeys(const std::map& privkey_map, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - bool ImportPubKeys(const std::vector>& ordered_pubkeys, const std::map& pubkey_map, const std::map>& key_origins, const bool add_keypool, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - bool ImportScriptPubKeys(const std::set& script_pub_keys, const bool have_solving_data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - - /* Returns true if the wallet can generate new keys */ - bool CanGenerateKeys() const; - - /* Generates a new HD seed (will not be activated) */ - CPubKey GenerateNewSeed(); - - /* Derives a new HD seed (will not be activated) */ - CPubKey DeriveNewSeed(const CKey& key); - - /* Set the current HD seed (will reset the chain child index counters) - Sets the seed's version based on the current wallet version (so the - caller must ensure the current wallet version is correct before calling - this function). */ - void SetHDSeed(const CPubKey& key); - - /** - * Explicitly make the wallet learn the related scripts for outputs to the - * given key. This is purely to make the wallet file compatible with older - * software, as FillableSigningProvider automatically does this implicitly for all - * keys now. - */ - void LearnRelatedScripts(const CPubKey& key, OutputType); - - /** - * Same as LearnRelatedScripts, but when the OutputType is not known (and could - * be anything). - */ - void LearnAllRelatedScripts(const CPubKey& key); - - /** - * Marks all keys in the keypool up to and including the provided key as used. - * - * @param keypool_id determines the last key to mark as used - * - * @return All affected keys - */ - std::vector MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore); - const std::map& GetAllReserveKeys() const { return m_pool_key_to_index; } - - std::set GetKeys() const override; -}; - /** Wraps a LegacyScriptPubKeyMan so that it can be returned in a new unique_ptr. Does not provide privkeys */ class LegacySigningProvider : public SigningProvider { @@ -644,7 +348,7 @@ public: bool CheckDecryptionKey(const CKeyingMaterial& master_key) override; bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override; - util::Result GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override; + util::Result GetReservedDestination(const OutputType type, bool internal, int64_t& index) override; void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) override; // Tops up the descriptor cache and m_map_script_pub_keys. The cache is stored in the wallet file diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 7b26cf15bd3..57a60d22920 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -508,8 +508,6 @@ std::map> ListCoins(const CWallet& wallet) std::map> result; CCoinControl coin_control; - // Include watch-only for LegacyScriptPubKeyMan wallets without private keys - coin_control.fAllowWatchOnly = wallet.GetLegacyScriptPubKeyMan() && wallet.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); CoinFilterParams coins_params; coins_params.only_spendable = false; coins_params.skip_locked = false; diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp index b152f98a292..9901590af33 100644 --- a/src/wallet/test/ismine_tests.cpp +++ b/src/wallet/test/ismine_tests.cpp @@ -36,25 +36,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) CScript scriptPubKey; isminetype result; - // P2PK compressed - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - scriptPubKey = GetScriptForRawPubKey(pubkeys[0]); - - // Keystore does not have key - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has key - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2PK compressed - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -67,25 +48,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2PK uncompressed - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - scriptPubKey = GetScriptForRawPubKey(uncompressedPubkey); - - // Keystore does not have key - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has key - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2PK uncompressed - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -98,25 +60,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2PKH compressed - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - scriptPubKey = GetScriptForDestination(PKHash(pubkeys[0])); - - // Keystore does not have key - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has key - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2PKH compressed - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -129,25 +72,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2PKH uncompressed - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - scriptPubKey = GetScriptForDestination(PKHash(uncompressedPubkey)); - - // Keystore does not have key - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has key - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2PKH uncompressed - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -160,33 +84,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2SH - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - - CScript redeemScript = GetScriptForDestination(PKHash(pubkeys[0])); - scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); - - // Keystore does not have redeemScript or key - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has redeemScript but no key - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has redeemScript and key - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2SH - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -200,25 +97,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // (P2PKH inside) P2SH inside P2SH (invalid) - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - - CScript redeemscript_inner = GetScriptForDestination(PKHash(pubkeys[0])); - CScript redeemscript = GetScriptForDestination(ScriptHash(redeemscript_inner)); - scriptPubKey = GetScriptForDestination(ScriptHash(redeemscript)); - - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript_inner)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - // (P2PKH inside) P2SH inside P2SH (invalid) - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -228,25 +106,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(spk_manager, nullptr); } - // (P2PKH inside) P2SH inside P2WSH (invalid) - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - - CScript redeemscript = GetScriptForDestination(PKHash(pubkeys[0])); - CScript witnessscript = GetScriptForDestination(ScriptHash(redeemscript)); - scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); - - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemscript)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - // (P2PKH inside) P2SH inside P2WSH (invalid) - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -256,23 +115,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(spk_manager, nullptr); } - // P2WPKH inside P2WSH (invalid) - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - - CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0])); - scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); - - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - // P2WPKH inside P2WSH (invalid) - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -282,25 +124,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(spk_manager, nullptr); } - // (P2PKH inside) P2WSH inside P2WSH (invalid) - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - - CScript witnessscript_inner = GetScriptForDestination(PKHash(pubkeys[0])); - CScript witnessscript = GetScriptForDestination(WitnessV0ScriptHash(witnessscript_inner)); - scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript)); - - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript_inner)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - // (P2PKH inside) P2WSH inside P2WSH (invalid) - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -310,22 +133,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(spk_manager, nullptr); } - // P2WPKH compressed - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - - scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0])); - - // Keystore implicitly has key and P2SH redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2WPKH compressed - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -338,27 +145,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2WPKH uncompressed - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); - - scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey)); - - // Keystore has key, but no P2SH redeemScript - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has key and P2SH redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - // P2WPKH uncompressed (invalid) - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -368,41 +154,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(spk_manager, nullptr); } - // scriptPubKey multisig - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - - scriptPubKey = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); - - // Keystore does not have any keys - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has 1/2 keys - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); - - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has 2/2 keys - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); - - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has 2/2 keys and the script - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - // scriptPubKey multisig - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -415,29 +166,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2SH multisig - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); - - CScript redeemScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); - scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); - - // Keystore has no redeemScript - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2SH multisig - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -452,35 +180,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2WSH multisig with compressed keys - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); - - CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]}); - scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); - - // Keystore has keys, but no witnessScript or P2SH redeemScript - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has keys and witnessScript, but no P2SH redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has keys, witnessScript, P2SH redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2WSH multisig with compressed keys - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -495,35 +194,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - // P2WSH multisig with uncompressed key - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); - - CScript witnessScript = GetScriptForMultisig(2, {uncompressedPubkey, pubkeys[1]}); - scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); - - // Keystore has keys, but no witnessScript or P2SH redeemScript - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has keys and witnessScript, but no P2SH redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has keys, witnessScript, P2SH redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - // P2WSH multisig with uncompressed key (invalid) - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -534,36 +204,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) BOOST_CHECK_EQUAL(spk_manager, nullptr); } - // P2WSH multisig wrapped in P2SH - Legacy - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - - CScript witnessScript = GetScriptForMultisig(2, {pubkeys[0], pubkeys[1]}); - CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript)); - scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript)); - - // Keystore has no witnessScript, P2SH redeemScript, or keys - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has witnessScript and P2SH redeemScript, but no keys - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(redeemScript)); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessScript)); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - - // Keystore has keys, witnessScript, P2SH redeemScript - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[1])); - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 1); - } - // P2WSH multisig wrapped in P2SH - Descriptor { CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); @@ -640,66 +280,6 @@ BOOST_AUTO_TEST_CASE(ismine_standard) result = spk_manager->IsMine(scriptPubKey); BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); } - - // OP_RETURN - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - - scriptPubKey.clear(); - scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]); - - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - - // witness unspendable - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - - scriptPubKey.clear(); - scriptPubKey << OP_0 << "aabb"_hex; - - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - - // witness unknown - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - - scriptPubKey.clear(); - scriptPubKey << OP_16 << "aabb"_hex; - - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } - - // Nonstandard - { - CWallet keystore(chain.get(), "", CreateMockableWalletDatabase()); - keystore.SetupLegacyScriptPubKeyMan(); - LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0])); - - scriptPubKey.clear(); - scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL; - - result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey); - BOOST_CHECK_EQUAL(result, ISMINE_NO); - BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->GetScriptPubKeys().count(scriptPubKey) == 0); - } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp index f27865865db..420c4866b09 100644 --- a/src/wallet/test/scriptpubkeyman_tests.cpp +++ b/src/wallet/test/scriptpubkeyman_tests.cpp @@ -15,32 +15,6 @@ namespace wallet { BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup) -// Test LegacyScriptPubKeyMan::CanProvide behavior, making sure it returns true -// for recognized scripts even when keys may not be available for signing. -BOOST_AUTO_TEST_CASE(CanProvide) -{ - // Set up wallet and keyman variables. - CWallet wallet(m_node.chain.get(), "", CreateMockableWalletDatabase()); - LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan(); - - // Make a 1 of 2 multisig script - std::vector keys(2); - std::vector pubkeys; - for (CKey& key : keys) { - key.MakeNewKey(true); - pubkeys.emplace_back(key.GetPubKey()); - } - CScript multisig_script = GetScriptForMultisig(1, pubkeys); - CScript p2sh_script = GetScriptForDestination(ScriptHash(multisig_script)); - SignatureData data; - - // Verify the p2sh(multisig) script is not recognized until the multisig - // script is added to the keystore to make it solvable - BOOST_CHECK(!keyman.CanProvide(p2sh_script, data)); - keyman.AddCScript(multisig_script); - BOOST_CHECK(keyman.CanProvide(p2sh_script, data)); -} - BOOST_AUTO_TEST_CASE(DescriptorScriptPubKeyManTests) { std::unique_ptr& chain = m_node.chain; diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp index 91952245d70..b12dc19ab9f 100644 --- a/src/wallet/test/walletdb_tests.cpp +++ b/src/wallet/test/walletdb_tests.cpp @@ -29,34 +29,5 @@ BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue) BOOST_CHECK_THROW(ssValue >> dummy, std::ios_base::failure); } -BOOST_AUTO_TEST_CASE(walletdb_read_write_deadlock) -{ - // Exercises a db read write operation that shouldn't deadlock. - for (const DatabaseFormat& db_format : DATABASE_FORMATS) { - // Context setup - DatabaseOptions options; - options.require_format = db_format; - DatabaseStatus status; - bilingual_str error_string; - std::unique_ptr db = MakeDatabase(m_path_root / strprintf("wallet_%d_.dat", db_format).c_str(), options, status, error_string); - BOOST_CHECK_EQUAL(status, DatabaseStatus::SUCCESS); - - std::shared_ptr wallet(new CWallet(m_node.chain.get(), "", std::move(db))); - wallet->m_keypool_size = 4; - - // 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)); - } -} - BOOST_AUTO_TEST_SUITE_END() } // namespace wallet diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a01a00586cb..ec38e55491f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -313,9 +313,6 @@ class FastWalletRescanFilter public: FastWalletRescanFilter(const CWallet& wallet) : m_wallet(wallet) { - // fast rescanning via block filters is only supported by descriptor wallets right now - assert(!m_wallet.IsLegacy()); - // create initial filter with scripts from all ScriptPubKeyMans for (auto spkm : m_wallet.GetAllScriptPubKeyMans()) { auto desc_spkm{dynamic_cast(spkm)}; @@ -548,21 +545,6 @@ const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const return &(it->second); } -void CWallet::UpgradeKeyMetadata() -{ - if (IsLocked() || IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { - return; - } - - auto spk_man = GetLegacyScriptPubKeyMan(); - if (!spk_man) { - return; - } - - spk_man->UpgradeKeyMetadata(); - SetWalletFlag(WALLET_FLAG_KEY_ORIGIN_METADATA); -} - void CWallet::UpgradeDescriptorCache() { if (!IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) || IsLocked() || IsWalletFlagSet(WALLET_FLAG_LAST_HARDENED_XPUB_CACHED)) { @@ -630,8 +612,6 @@ bool CWallet::Unlock(const SecureString& strWalletPassphrase) continue; // try another master key } if (Unlock(plain_master_key)) { - // Now that we've unlocked, upgrade the key metadata - UpgradeKeyMetadata(); // Now that we've unlocked, upgrade the descriptor cache UpgradeDescriptorCache(); return true; @@ -895,13 +875,6 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) // If we are using descriptors, make new descriptors with a new seed if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) && !IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) { SetupDescriptorScriptPubKeyMans(); - } else if (auto spk_man = GetLegacyScriptPubKeyMan()) { - // if we are using HD, replace the HD seed with a new one - if (spk_man->IsHDEnabled()) { - if (!spk_man->SetupGeneration(true)) { - return false; - } - } } Lock(); @@ -1059,23 +1032,6 @@ bool CWallet::IsSpentKey(const CScript& scriptPubKey) const if (IsAddressPreviouslySpent(dest)) { return true; } - - if (LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan()) { - for (const auto& keyid : GetAffectedKeys(scriptPubKey, *spk_man)) { - WitnessV0KeyHash wpkh_dest(keyid); - if (IsAddressPreviouslySpent(wpkh_dest)) { - return true; - } - ScriptHash sh_wpkh_dest(GetScriptForDestination(wpkh_dest)); - if (IsAddressPreviouslySpent(sh_wpkh_dest)) { - return true; - } - PKHash pkh_dest(keyid); - if (IsAddressPreviouslySpent(pkh_dest)) { - return true; - } - } - } return false; } @@ -1651,11 +1607,6 @@ isminetype CWallet::IsMine(const CScript& script) const return res; } - // Legacy wallet - if (LegacyScriptPubKeyMan* spkm = GetLegacyScriptPubKeyMan()) { - return spkm->IsMine(script); - } - return ISMINE_NO; } @@ -1788,59 +1739,6 @@ void CWallet::InitWalletFlags(uint64_t flags) if (!LoadWalletFlags(flags)) assert(false); } -bool CWallet::ImportScripts(const std::set scripts, int64_t timestamp) -{ - auto spk_man = GetLegacyScriptPubKeyMan(); - if (!spk_man) { - return false; - } - LOCK(spk_man->cs_KeyStore); - return spk_man->ImportScripts(scripts, timestamp); -} - -bool CWallet::ImportPrivKeys(const std::map& privkey_map, const int64_t timestamp) -{ - auto spk_man = GetLegacyScriptPubKeyMan(); - if (!spk_man) { - return false; - } - LOCK(spk_man->cs_KeyStore); - return spk_man->ImportPrivKeys(privkey_map, timestamp); -} - -bool CWallet::ImportPubKeys(const std::vector>& ordered_pubkeys, const std::map& pubkey_map, const std::map>& key_origins, const bool add_keypool, const int64_t timestamp) -{ - auto spk_man = GetLegacyScriptPubKeyMan(); - if (!spk_man) { - return false; - } - LOCK(spk_man->cs_KeyStore); - return spk_man->ImportPubKeys(ordered_pubkeys, pubkey_map, key_origins, add_keypool, timestamp); -} - -bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) -{ - auto spk_man = GetLegacyScriptPubKeyMan(); - if (!spk_man) { - return false; - } - LOCK(spk_man->cs_KeyStore); - if (!spk_man->ImportScriptPubKeys(script_pub_keys, have_solving_data, timestamp)) { - return false; - } - if (apply_label) { - WalletBatch batch(GetDatabase()); - for (const CScript& script : script_pub_keys) { - CTxDestination dest; - ExtractDestination(script, dest); - if (IsValidDestination(dest)) { - SetAddressBookWithDB(batch, dest, label, AddressPurpose::RECEIVE); - } - } - } - return true; -} - void CWallet::MaybeUpdateBirthTime(int64_t time) { int64_t birthtime = m_birth_time.load(); @@ -1913,7 +1811,7 @@ CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_bloc ScanResult result; std::unique_ptr fast_rescan_filter; - if (!IsLegacy() && chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique(*this); + if (chain().hasBlockFilterIndex(BlockFilterType::BASIC)) fast_rescan_filter = std::make_unique(*this); WalletLogPrintf("Rescan started from block %s... (%s)\n", start_block.ToString(), fast_rescan_filter ? "fast variant using block filters" : "slow variant inspecting all blocks"); @@ -2182,11 +2080,6 @@ void MaybeResendWalletTxs(WalletContext& context) } -/** @defgroup Actions - * - * @{ - */ - bool CWallet::SignTransaction(CMutableTransaction& tx) const { AssertLockHeld(cs_wallet); @@ -2559,11 +2452,6 @@ size_t CWallet::KeypoolCountExternalKeys() const { AssertLockHeld(cs_wallet); - auto legacy_spk_man = GetLegacyScriptPubKeyMan(); - if (legacy_spk_man) { - return legacy_spk_man->KeypoolCountExternalKeys(); - } - unsigned int count = 0; for (auto spk_man : m_external_spk_managers) { count += spk_man.second->GetKeyPoolSize(); @@ -2695,13 +2583,11 @@ util::Result ReserveDestination::GetReservedDestination(bool int } if (nIndex == -1) { - CKeyPool keypool; int64_t index; - auto op_address = m_spk_man->GetReservedDestination(type, internal, index, keypool); + auto op_address = m_spk_man->GetReservedDestination(type, internal, index); if (!op_address) return op_address; nIndex = index; address = *op_address; - fInternal = keypool.fInternal; } return address; } @@ -2786,68 +2672,6 @@ void CWallet::ListLockedCoins(std::vector& vOutpts) const } } -/** @} */ // end of Actions - -void CWallet::GetKeyBirthTimes(std::map& mapKeyBirth) const { - AssertLockHeld(cs_wallet); - mapKeyBirth.clear(); - - // map in which we'll infer heights of other keys - std::map mapKeyFirstBlock; - TxStateConfirmed max_confirm{uint256{}, /*height=*/-1, /*index=*/-1}; - max_confirm.confirmed_block_height = GetLastBlockHeight() > 144 ? GetLastBlockHeight() - 144 : 0; // the tip can be reorganized; use a 144-block safety margin - CHECK_NONFATAL(chain().findAncestorByHeight(GetLastBlockHash(), max_confirm.confirmed_block_height, FoundBlock().hash(max_confirm.confirmed_block_hash))); - - { - LegacyScriptPubKeyMan* spk_man = GetLegacyScriptPubKeyMan(); - assert(spk_man != nullptr); - LOCK(spk_man->cs_KeyStore); - - // get birth times for keys with metadata - for (const auto& entry : spk_man->mapKeyMetadata) { - if (entry.second.nCreateTime) { - mapKeyBirth[entry.first] = entry.second.nCreateTime; - } - } - - // Prepare to infer birth heights for keys without metadata - for (const CKeyID &keyid : spk_man->GetKeys()) { - if (mapKeyBirth.count(keyid) == 0) - mapKeyFirstBlock[keyid] = &max_confirm; - } - - // if there are no such keys, we're done - if (mapKeyFirstBlock.empty()) - return; - - // find first block that affects those keys, if there are any left - for (const auto& entry : mapWallet) { - // iterate over all wallet transactions... - const CWalletTx &wtx = entry.second; - if (auto* conf = wtx.state()) { - // ... which are already in a block - for (const CTxOut &txout : wtx.tx->vout) { - // iterate over all their outputs - for (const auto &keyid : GetAffectedKeys(txout.scriptPubKey, *spk_man)) { - // ... and all their affected keys - auto rit = mapKeyFirstBlock.find(keyid); - if (rit != mapKeyFirstBlock.end() && conf->confirmed_block_height < rit->second->confirmed_block_height) { - rit->second = conf; - } - } - } - } - } - } - - // Extract block timestamps for those keys - for (const auto& entry : mapKeyFirstBlock) { - int64_t block_time; - CHECK_NONFATAL(chain().findBlock(entry.second->confirmed_block_hash, FoundBlock().time(block_time))); - mapKeyBirth[entry.first] = block_time - TIMESTAMP_WINDOW; // block times can be 2h off - } -} - /** * Compute smart timestamp for a transaction being added to the wallet. * @@ -3465,21 +3289,6 @@ bool CWallet::BackupWallet(const std::string& strDest) const return GetDatabase().Backup(strDest); } -CKeyPool::CKeyPool() -{ - nTime = GetTime(); - fInternal = false; - m_pre_split = false; -} - -CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) -{ - nTime = GetTime(); - vchPubKey = vchPubKeyIn; - fInternal = internalIn; - m_pre_split = false; -} - int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const { AssertLockHeld(cs_wallet); @@ -3616,10 +3425,6 @@ std::set CWallet::GetScriptPubKeyMans(const CScript& script) c SignatureData sigdata; Assume(std::all_of(spk_mans.begin(), spk_mans.end(), [&script, &sigdata](ScriptPubKeyMan* spkm) { return spkm->CanProvide(script, sigdata); })); - // Legacy wallet - LegacyScriptPubKeyMan* spkm = GetLegacyScriptPubKeyMan(); - if (spkm && spkm->CanProvide(script, sigdata)) spk_mans.insert(spkm); - return spk_mans; } @@ -3647,10 +3452,6 @@ std::unique_ptr CWallet::GetSolvingProvider(const CScript& scri return it->second.at(0)->GetSolvingProvider(script); } - // Legacy wallet - LegacyScriptPubKeyMan* spkm = GetLegacyScriptPubKeyMan(); - if (spkm && spkm->CanProvide(script, sigdata)) return spkm->GetSolvingProvider(script); - return nullptr; } @@ -3666,18 +3467,6 @@ std::vector CWallet::GetWalletDescriptors(const CScript& scrip return descs; } -LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const -{ - if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { - return nullptr; - } - // Legacy wallets only have one ScriptPubKeyMan which is a LegacyScriptPubKeyMan. - // Everything in m_internal_spk_managers and m_external_spk_managers point to the same legacyScriptPubKeyMan. - auto it = m_internal_spk_managers.find(OutputType::LEGACY); - if (it == m_internal_spk_managers.end()) return nullptr; - return dynamic_cast(it->second); -} - LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const { if (IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { @@ -3688,12 +3477,6 @@ LegacyDataSPKM* CWallet::GetLegacyDataSPKM() const return dynamic_cast(it->second); } -LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan() -{ - SetupLegacyScriptPubKeyMan(); - return GetLegacyScriptPubKeyMan(); -} - void CWallet::AddScriptPubKeyMan(const uint256& id, std::unique_ptr spkm_man) { // Add spkm_man to m_spk_managers before calling any method @@ -3716,9 +3499,8 @@ void CWallet::SetupLegacyScriptPubKeyMan() return; } - std::unique_ptr spk_manager = m_database->Format() == "bdb_ro" ? - std::make_unique(*this) : - std::make_unique(*this, m_keypool_size); + Assert(m_database->Format() == "bdb_ro" || m_database->Format() == "mock"); + std::unique_ptr spk_manager = std::make_unique(*this); for (const auto& type : LEGACY_OUTPUT_TYPES) { m_internal_spk_managers[type] = spk_manager.get(); @@ -3915,11 +3697,6 @@ void CWallet::DeactivateScriptPubKeyMan(uint256 id, OutputType type, bool intern NotifyCanGetAddressesChanged(); } -bool CWallet::IsLegacy() const -{ - return !IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS); -} - DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDescriptor& desc) const { auto spk_man_pair = m_spk_managers.find(desc.id); @@ -3937,11 +3714,6 @@ DescriptorScriptPubKeyMan* CWallet::GetDescriptorScriptPubKeyMan(const WalletDes std::optional CWallet::IsInternalScriptPubKeyMan(ScriptPubKeyMan* spk_man) const { - // Legacy script pubkey man can't be either external or internal - if (IsLegacy()) { - return std::nullopt; - } - // only active ScriptPubKeyMan can be internal if (!GetActiveScriptPubKeyMans().count(spk_man)) { return std::nullopt; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 659e3854e71..7802b155669 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -558,9 +558,6 @@ public: SteadyClock::duration ScanningDuration() const { return fScanningWallet ? SteadyClock::now() - m_scanning_start.load() : SteadyClock::duration{}; } double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; } - //! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo - void UpgradeKeyMetadata() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - //! Upgrade DescriptorCaches void UpgradeDescriptorCache() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -583,7 +580,6 @@ public: bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); - void GetKeyBirthTimes(std::map &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); unsigned int ComputeTimeSmart(const CWalletTx& wtx, bool rescanning_old_block) const; /** @@ -920,9 +916,6 @@ public: /** Loads the flags into the wallet. (used by LoadWallet) */ bool LoadWalletFlags(uint64_t flags); - /** Determine if we are a legacy wallet */ - bool IsLegacy() const; - /** Returns a bracketed wallet name for displaying in logs, will return [default wallet] if the wallet has no name */ std::string GetDisplayName() const override { @@ -963,8 +956,6 @@ public: std::vector GetWalletDescriptors(const CScript& script) const; //! Get the LegacyScriptPubKeyMan which is used for all types, internal, and external. - LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const; - LegacyScriptPubKeyMan* GetOrCreateLegacyScriptPubKeyMan(); LegacyDataSPKM* GetLegacyDataSPKM() const; LegacyDataSPKM* GetOrCreateLegacyDataSPKM(); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index cf43716efd1..23e1c8be99a 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -154,11 +154,6 @@ bool WalletBatch::EraseMasterKey(unsigned int id) return EraseIC(std::make_pair(DBKeys::MASTER_KEY, id)); } -bool WalletBatch::WriteCScript(const uint160& hash, const CScript& redeemScript) -{ - return WriteIC(std::make_pair(DBKeys::CSCRIPT, hash), redeemScript, false); -} - bool WalletBatch::WriteWatchOnly(const CScript &dest, const CKeyMetadata& keyMeta) { if (!WriteIC(std::make_pair(DBKeys::WATCHMETA, dest), keyMeta)) { @@ -203,21 +198,6 @@ bool WalletBatch::WriteOrderPosNext(int64_t nOrderPosNext) return WriteIC(DBKeys::ORDERPOSNEXT, nOrderPosNext); } -bool WalletBatch::ReadPool(int64_t nPool, CKeyPool& keypool) -{ - return m_batch->Read(std::make_pair(DBKeys::POOL, nPool), keypool); -} - -bool WalletBatch::WritePool(int64_t nPool, const CKeyPool& keypool) -{ - return WriteIC(std::make_pair(DBKeys::POOL, nPool), keypool); -} - -bool WalletBatch::ErasePool(int64_t nPool) -{ - return EraseIC(std::make_pair(DBKeys::POOL, nPool)); -} - bool WalletBatch::WriteMinVersion(int nVersion) { return WriteIC(DBKeys::MINVERSION, nVersion); @@ -744,18 +724,6 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, }); result = std::max(result, watch_meta_res.m_result); - // Load keypool - LoadResult pool_res = LoadRecords(pwallet, batch, DBKeys::POOL, - [] (CWallet* pwallet, DataStream& key, DataStream& value, std::string& err) { - int64_t nIndex; - key >> nIndex; - CKeyPool keypool; - value >> keypool; - pwallet->GetOrCreateLegacyDataSPKM()->LoadKeyPool(nIndex, keypool); - return DBErrors::LOAD_OK; - }); - result = std::max(result, pool_res.m_result); - // Deal with old "wkey" and "defaultkey" records. // These are not actually loaded, but we need to check for them @@ -791,15 +759,6 @@ static DBErrors LoadLegacyWalletRecords(CWallet* pwallet, DatabaseBatch& batch, // Only do logging and time first key update if there were no critical errors pwallet->WalletLogPrintf("Legacy Wallet Keys: %u plaintext, %u encrypted, %u w/ metadata, %u total.\n", key_res.m_records, ckey_res.m_records, keymeta_res.m_records, key_res.m_records + ckey_res.m_records); - - // nTimeFirstKey is only reliable if all keys have metadata - if (pwallet->IsLegacy() && (key_res.m_records + ckey_res.m_records + watch_script_res.m_records) != (keymeta_res.m_records + watch_meta_res.m_records)) { - auto spk_man = pwallet->GetLegacyScriptPubKeyMan(); - if (spk_man) { - LOCK(spk_man->cs_KeyStore); - spk_man->UpdateTimeFirstKey(1); - } - } } return result; @@ -1253,14 +1212,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) if (any_unordered) result = pwallet->ReorderTransactions(); - // Upgrade all of the wallet keymetadata to have the hd master key id - // This operation is not atomic, but if it fails, updated entries are still backwards compatible with older software - try { - pwallet->UpgradeKeyMetadata(); - } catch (...) { - result = DBErrors::CORRUPT; - } - // Upgrade all of the descriptor caches to cache the last hardened xpub // This operation is not atomic, but if it fails, only new entries are added so it is backwards compatible try { @@ -1366,11 +1317,6 @@ bool WalletBatch::EraseAddressData(const CTxDestination& dest) return m_batch->ErasePrefix(prefix); } -bool WalletBatch::WriteHDChain(const CHDChain& chain) -{ - return WriteIC(DBKeys::HDCHAIN, chain); -} - bool WalletBatch::WriteWalletFlags(const uint64_t flags) { return WriteIC(DBKeys::FLAGS, flags); diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index ee2ae3eede5..e0bfc56a454 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -21,7 +21,6 @@ class uint256; struct CBlockLocator; namespace wallet { -class CKeyPool; class CMasterKey; class CWallet; class CWalletTx; @@ -239,8 +238,6 @@ public: bool WriteMasterKey(unsigned int nID, const CMasterKey& kMasterKey); bool EraseMasterKey(unsigned int id); - bool WriteCScript(const uint160& hash, const CScript& redeemScript); - bool WriteWatchOnly(const CScript &script, const CKeyMetadata &keymeta); bool EraseWatchOnly(const CScript &script); @@ -252,10 +249,6 @@ public: bool WriteOrderPosNext(int64_t nOrderPosNext); - bool ReadPool(int64_t nPool, CKeyPool& keypool); - bool WritePool(int64_t nPool, const CKeyPool& keypool); - bool ErasePool(int64_t nPool); - bool WriteMinVersion(int nVersion); bool WriteDescriptorKey(const uint256& desc_id, const CPubKey& pubkey, const CPrivKey& privkey); @@ -279,9 +272,6 @@ public: DBErrors LoadWallet(CWallet* pwallet); - //! write the hdchain model (external chain child index counter) - bool WriteHDChain(const CHDChain& chain); - //! Delete records of the given types bool EraseRecords(const std::unordered_set& types); diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 61c5fffe365..4ab1dd5e459 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -33,12 +34,8 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag wallet_instance->SetMinVersion(FEATURE_LATEST); wallet_instance->InitWalletFlags(wallet_creation_flags); - if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) { - auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan(); - spk_man->SetupGeneration(false); - } else { - wallet_instance->SetupDescriptorScriptPubKeyMans(); - } + Assert(wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); + wallet_instance->SetupDescriptorScriptPubKeyMans(); tfm::format(std::cout, "Topping up keypool...\n"); wallet_instance->TopUpKeyPool(); diff --git a/test/sanitizer_suppressions/tsan b/test/sanitizer_suppressions/tsan index 7c675644ac0..298ad175da0 100644 --- a/test/sanitizer_suppressions/tsan +++ b/test/sanitizer_suppressions/tsan @@ -5,8 +5,6 @@ # race (TODO fix) race:LoadWallet -race:WalletBatch::WriteHDChain -race:BerkeleyBatch race:DatabaseBatch race:zmq::* race:bitcoin-qt