diff options
Diffstat (limited to 'src')
34 files changed, 164 insertions, 114 deletions
diff --git a/src/core_read.cpp b/src/core_read.cpp index 484f41f262..1f9227bf0b 100644 --- a/src/core_read.cpp +++ b/src/core_read.cpp @@ -21,13 +21,15 @@ #include <string> namespace { - -opcodetype ParseOpCode(const std::string& s) +class OpCodeParser { - static std::map<std::string, opcodetype> mapOpNames; +private: + std::map<std::string, opcodetype> mapOpNames; - if (mapOpNames.empty()) { - for (unsigned int op = 0; op <= MAX_OPCODE; op++) { +public: + OpCodeParser() + { + for (unsigned int op = 0; op <= MAX_OPCODE; ++op) { // Allow OP_RESERVED to get into mapOpNames if (op < OP_NOP && op != OP_RESERVED) { continue; @@ -44,10 +46,18 @@ opcodetype ParseOpCode(const std::string& s) } } } + opcodetype Parse(const std::string& s) const + { + auto it = mapOpNames.find(s); + if (it == mapOpNames.end()) throw std::runtime_error("script parse error: unknown opcode"); + return it->second; + } +}; - auto it = mapOpNames.find(s); - if (it == mapOpNames.end()) throw std::runtime_error("script parse error: unknown opcode"); - return it->second; +opcodetype ParseOpCode(const std::string& s) +{ + static const OpCodeParser ocp; + return ocp.Parse(s); } } // namespace diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp index 7f6471740f..6aa89a2402 100644 --- a/src/dummywallet.cpp +++ b/src/dummywallet.cpp @@ -12,7 +12,7 @@ namespace interfaces { class Chain; class Handler; class Wallet; -class WalletClient; +class WalletLoader; } class DummyWalletInit : public WalletInitInterface { @@ -64,7 +64,7 @@ std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) throw std::logic_error("Wallet function called in non-wallet build."); } -std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args) +std::unique_ptr<WalletLoader> MakeWalletLoader(Chain& chain, ArgsManager& args) { throw std::logic_error("Wallet function called in non-wallet build."); } diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp index c549ed3cc0..f13bd4a05d 100644 --- a/src/init/bitcoin-gui.cpp +++ b/src/init/bitcoin-gui.cpp @@ -27,9 +27,9 @@ public: } std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); } std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); } - std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override + std::unique_ptr<interfaces::WalletLoader> makeWalletLoader(interfaces::Chain& chain) override { - return MakeWalletClient(chain, *Assert(m_node.args)); + return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); } interfaces::Ipc* ipc() override { return m_ipc.get(); } diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp index fa56153745..be6dfefe7d 100644 --- a/src/init/bitcoin-node.cpp +++ b/src/init/bitcoin-node.cpp @@ -29,9 +29,9 @@ public: } std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); } std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); } - std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override + std::unique_ptr<interfaces::WalletLoader> makeWalletLoader(interfaces::Chain& chain) override { - return MakeWalletClient(chain, *Assert(m_node.args)); + return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); } interfaces::Ipc* ipc() override { return m_ipc.get(); } diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp index d71177e885..17a074cb8d 100644 --- a/src/init/bitcoin-qt.cpp +++ b/src/init/bitcoin-qt.cpp @@ -24,9 +24,9 @@ public: } std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); } std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); } - std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override + std::unique_ptr<interfaces::WalletLoader> makeWalletLoader(interfaces::Chain& chain) override { - return MakeWalletClient(chain, *Assert(m_node.args)); + return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); } NodeContext m_node; diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp index 9c8d5bd9bb..9c2e44569a 100644 --- a/src/init/bitcoind.cpp +++ b/src/init/bitcoind.cpp @@ -24,9 +24,9 @@ public: } std::unique_ptr<interfaces::Node> makeNode() override { return interfaces::MakeNode(m_node); } std::unique_ptr<interfaces::Chain> makeChain() override { return interfaces::MakeChain(m_node); } - std::unique_ptr<interfaces::WalletClient> makeWalletClient(interfaces::Chain& chain) override + std::unique_ptr<interfaces::WalletLoader> makeWalletLoader(interfaces::Chain& chain) override { - return MakeWalletClient(chain, *Assert(m_node.args)); + return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr<interfaces::Echo> makeEcho() override { return interfaces::MakeEcho(); } NodeContext& m_node; diff --git a/src/interfaces/init.cpp b/src/interfaces/init.cpp index a3c949e616..f0f8aa5fed 100644 --- a/src/interfaces/init.cpp +++ b/src/interfaces/init.cpp @@ -11,7 +11,7 @@ namespace interfaces { std::unique_ptr<Node> Init::makeNode() { return {}; } std::unique_ptr<Chain> Init::makeChain() { return {}; } -std::unique_ptr<WalletClient> Init::makeWalletClient(Chain& chain) { return {}; } +std::unique_ptr<WalletLoader> Init::makeWalletLoader(Chain& chain) { return {}; } std::unique_ptr<Echo> Init::makeEcho() { return {}; } Ipc* Init::ipc() { return nullptr; } } // namespace interfaces diff --git a/src/interfaces/init.h b/src/interfaces/init.h index 2a38054a17..a4ecf4b5d1 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -14,7 +14,7 @@ class Chain; class Echo; class Ipc; class Node; -class WalletClient; +class WalletLoader; //! Initial interface created when a process is first started, and used to give //! and get access to other interfaces (Node, Chain, Wallet, etc). @@ -29,7 +29,7 @@ public: virtual ~Init() = default; virtual std::unique_ptr<Node> makeNode(); virtual std::unique_ptr<Chain> makeChain(); - virtual std::unique_ptr<WalletClient> makeWalletClient(Chain& chain); + virtual std::unique_ptr<WalletLoader> makeWalletLoader(Chain& chain); virtual std::unique_ptr<Echo> makeEcho(); virtual Ipc* ipc(); }; diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 974156e6e1..cf45b0b392 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -37,7 +37,7 @@ struct bilingual_str; namespace interfaces { class Handler; -class WalletClient; +class WalletLoader; struct BlockTip; //! Block and header tip information @@ -187,8 +187,8 @@ public: //! Broadcast transaction. virtual TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, std::string& err_string) = 0; - //! Get wallet client. - virtual WalletClient& walletClient() = 0; + //! Get wallet loader. + virtual WalletLoader& walletLoader() = 0; //! Register handler for init messages. using InitMessageFn = std::function<void(const std::string& message)>; @@ -210,7 +210,7 @@ public: using ShowProgressFn = std::function<void(const std::string& title, int progress, bool resume_possible)>; virtual std::unique_ptr<Handler> handleShowProgress(ShowProgressFn fn) = 0; - //! Register handler for wallet client constructed messages. + //! Register handler for wallet loader constructed messages. using InitWalletFn = std::function<void()>; virtual std::unique_ptr<Handler> handleInitWallet(InitWalletFn fn) = 0; diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 4213a22749..a02134f2b2 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -256,6 +256,9 @@ public: // Return whether private keys enabled. virtual bool privateKeysDisabled() = 0; + // Return whether the wallet contains a Taproot scriptPubKeyMan + virtual bool taprootEnabled() = 0; + // Return whether wallet uses an external signer. virtual bool hasExternalSigner() = 0; @@ -310,7 +313,7 @@ public: //! Wallet chain client that in addition to having chain client methods for //! starting up, shutting down, and registering RPCs, also has additional //! methods (called by the GUI) to load and create wallets. -class WalletClient : public ChainClient +class WalletLoader : public ChainClient { public: //! Create new wallet. @@ -419,9 +422,9 @@ struct WalletTxOut //! dummywallet.cpp and throws if the wallet component is not compiled. std::unique_ptr<Wallet> MakeWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet); -//! Return implementation of ChainClient interface for a wallet client. This +//! Return implementation of ChainClient interface for a wallet loader. This //! function will be undefined in builds where ENABLE_WALLET is false. -std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args); +std::unique_ptr<WalletLoader> MakeWalletLoader(Chain& chain, ArgsManager& args); } // namespace interfaces diff --git a/src/node/context.h b/src/node/context.h index 26873345b4..e0f85ec6fc 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -23,7 +23,7 @@ namespace interfaces { class Chain; class ChainClient; class Init; -class WalletClient; +class WalletLoader; } // namespace interfaces //! NodeContext struct containing references to chain state and connection @@ -52,7 +52,7 @@ struct NodeContext { std::vector<std::unique_ptr<interfaces::ChainClient>> chain_clients; //! Reference to chain client that should used to load or create wallets //! opened by the gui. - interfaces::WalletClient* wallet_client{nullptr}; + interfaces::WalletLoader* wallet_loader{nullptr}; std::unique_ptr<CScheduler> scheduler; std::function<void()> rpc_interruption_point = [] {}; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 8109dce2c0..21bfdff5da 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -63,7 +63,7 @@ using interfaces::FoundBlock; using interfaces::Handler; using interfaces::MakeHandler; using interfaces::Node; -using interfaces::WalletClient; +using interfaces::WalletLoader; namespace node { namespace { @@ -280,9 +280,9 @@ public: { return BroadcastTransaction(*m_context, std::move(tx), err_string, max_tx_fee, /*relay=*/ true, /*wait_callback=*/ false); } - WalletClient& walletClient() override + WalletLoader& walletLoader() override { - return *Assert(m_context->wallet_client); + return *Assert(m_context->wallet_loader); } std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override { diff --git a/src/node/ui_interface.h b/src/node/ui_interface.h index f969bcde21..5e11b08976 100644 --- a/src/node/ui_interface.h +++ b/src/node/ui_interface.h @@ -82,7 +82,7 @@ public: /** Progress message during initialization. */ ADD_SIGNALS_DECL_WRAPPER(InitMessage, void, const std::string& message); - /** Wallet client created. */ + /** Wallet loader created. */ ADD_SIGNALS_DECL_WRAPPER(InitWallet, void, ); /** Number of network connections changed. */ diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 81a1d88d20..e7d4006a70 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -1242,7 +1242,7 @@ void BitcoinGUI::incomingTransaction(const QString& date, int unit, const CAmoun // On new transaction, make an info balloon QString msg = tr("Date: %1\n").arg(date) + tr("Amount: %1\n").arg(BitcoinUnits::formatWithUnit(unit, amount, true)); - if (m_node.walletClient().getWallets().size() > 1 && !walletName.isEmpty()) { + if (m_node.walletLoader().getWallets().size() > 1 && !walletName.isEmpty()) { msg += tr("Wallet: %1\n").arg(walletName); } msg += tr("Type: %1\n").arg(type); diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 06d39426c9..4b2c3ed434 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -195,7 +195,7 @@ </widget> </item> <item> - <widget class="QCheckBox" name="useBech32"> + <widget class="QComboBox" name="addressType"> <property name="sizePolicy"> <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> <horstretch>0</horstretch> @@ -211,12 +211,6 @@ <property name="focusPolicy"> <enum>Qt::StrongFocus</enum> </property> - <property name="toolTip"> - <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</string> - </property> - <property name="text"> - <string>Generate native segwit (Bech32) address</string> - </property> </widget> </item> <item> @@ -366,7 +360,7 @@ <tabstops> <tabstop>reqLabel</tabstop> <tabstop>reqAmount</tabstop> - <tabstop>useBech32</tabstop> + <tabstop>addressType</tabstop> <tabstop>reqMessage</tabstop> <tabstop>receiveButton</tabstop> <tabstop>clearButton</tabstop> diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index d47ee95826..0d859a8253 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -87,10 +87,18 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) &QItemSelectionModel::selectionChanged, this, &ReceiveCoinsDialog::recentRequestsView_selectionChanged); - if (model->wallet().getDefaultAddressType() == OutputType::BECH32) { - ui->useBech32->setCheckState(Qt::Checked); - } else { - ui->useBech32->setCheckState(Qt::Unchecked); + // Populate address type dropdown and select default + auto add_address_type = [&](OutputType type, const QString& text, const QString& tooltip) { + const auto index = ui->addressType->count(); + ui->addressType->addItem(text, (int) type); + ui->addressType->setItemData(index, tooltip, Qt::ToolTipRole); + if (model->wallet().getDefaultAddressType() == type) ui->addressType->setCurrentIndex(index); + }; + add_address_type(OutputType::LEGACY, "Base58 (Legacy)", "Not recommended due to higher fees and less protection against typos."); + add_address_type(OutputType::P2SH_SEGWIT, "Base58 (P2SH-SegWit)", "Generates an address compatible with older wallets."); + add_address_type(OutputType::BECH32, "Bech32 (SegWit)", "Generates a native segwit address (BIP-173). Some old wallets don't support it."); + if (model->wallet().taprootEnabled()) { + add_address_type(OutputType::BECH32M, "Bech32m (Taproot)", "Bech32m (BIP-350) is an upgrade to Bech32, wallet support is still limited."); } // Set the button to be enabled or disabled based on whether the wallet can give out new addresses. @@ -144,15 +152,7 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() QString address; QString label = ui->reqLabel->text(); /* Generate new receiving address */ - OutputType address_type; - if (ui->useBech32->isChecked()) { - address_type = OutputType::BECH32; - } else { - address_type = model->wallet().getDefaultAddressType(); - if (address_type == OutputType::BECH32) { - address_type = OutputType::P2SH_SEGWIT; - } - } + const OutputType address_type = (OutputType)ui->addressType->currentData().toInt(); address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "", address_type); switch(model->getAddressTableModel()->getEditStatus()) diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 85703b3350..558ec41bd8 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -201,7 +201,7 @@ void SplashScreen::handleLoadWallet() { #ifdef ENABLE_WALLET if (!WalletModel::isWalletEnabled()) return; - m_handler_load_wallet = m_node->walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { + m_handler_load_wallet = m_node->walletLoader().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(std::bind(ShowProgress, this, std::placeholders::_1, std::placeholders::_2, false))); m_connected_wallets.emplace_back(std::move(wallet)); }); diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index 39cc1c1e1d..e814452907 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -60,8 +60,8 @@ void EditAddressAndSubmit( void TestAddAddressesToSendBook(interfaces::Node& node) { TestChain100Setup test; - auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args)); - test.m_node.wallet_client = wallet_client.get(); + auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args)); + test.m_node.wallet_loader = wallet_loader.get(); node.setContext(&test.m_node); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", gArgs, CreateMockWalletDatabase()); wallet->LoadWallet(); @@ -115,7 +115,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node) std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); OptionsModel optionsModel; ClientModel clientModel(node, &optionsModel); - WalletContext& context = *node.walletClient().context(); + WalletContext& context = *node.walletLoader().context(); AddWallet(context, wallet); WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get()); RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 93d6aa805f..dff69058e5 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -138,8 +138,8 @@ void TestGUI(interfaces::Node& node) for (int i = 0; i < 5; ++i) { test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } - auto wallet_client = interfaces::MakeWalletClient(*test.m_node.chain, *Assert(test.m_node.args)); - test.m_node.wallet_client = wallet_client.get(); + auto wallet_loader = interfaces::MakeWalletLoader(*test.m_node.chain, *Assert(test.m_node.args)); + test.m_node.wallet_loader = wallet_loader.get(); node.setContext(&test.m_node); const std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), "", gArgs, CreateMockWalletDatabase()); wallet->LoadWallet(); @@ -175,7 +175,7 @@ void TestGUI(interfaces::Node& node) TransactionView transactionView(platformStyle.get()); OptionsModel optionsModel; ClientModel clientModel(node, &optionsModel); - WalletContext& context = *node.walletClient().context(); + WalletContext& context = *node.walletLoader().context(); AddWallet(context, wallet); WalletModel walletModel(interfaces::MakeWallet(context, wallet), clientModel, platformStyle.get()); RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt); diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index b9a9fcf3d1..6cf7ec36c2 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -37,7 +37,7 @@ WalletController::WalletController(ClientModel& client_model, const PlatformStyl , m_platform_style(platform_style) , m_options_model(client_model.getOptionsModel()) { - m_handler_load_wallet = m_node.walletClient().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { + m_handler_load_wallet = m_node.walletLoader().handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { getOrCreateWallet(std::move(wallet)); }); @@ -61,7 +61,7 @@ std::map<std::string, bool> WalletController::listWalletDir() const { QMutexLocker locker(&m_mutex); std::map<std::string, bool> wallets; - for (const std::string& name : m_node.walletClient().listWalletDir()) { + for (const std::string& name : m_node.walletLoader().listWalletDir()) { wallets[name] = false; } for (WalletModel* wallet_model : m_wallets) { @@ -255,7 +255,7 @@ void CreateWalletActivity::createWallet() } QTimer::singleShot(500, worker(), [this, name, flags] { - std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message); + std::unique_ptr<interfaces::Wallet> wallet = node().walletLoader().createWallet(name, m_passphrase, flags, m_error_message, m_warning_message); if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet)); @@ -336,7 +336,7 @@ void OpenWalletActivity::open(const std::string& path) tr("Opening Wallet <b>%1</b>…").arg(name.toHtmlEscaped())); QTimer::singleShot(0, worker(), [this, path] { - std::unique_ptr<interfaces::Wallet> wallet = node().walletClient().loadWallet(path, m_error_message, m_warning_message); + std::unique_ptr<interfaces::Wallet> wallet = node().walletLoader().loadWallet(path, m_error_message, m_warning_message); if (wallet) m_wallet_model = m_wallet_controller->getOrCreateWallet(std::move(wallet)); @@ -359,7 +359,7 @@ void LoadWalletsActivity::load() tr("Loading wallets…")); QTimer::singleShot(0, worker(), [this] { - for (auto& wallet : node().walletClient().getWallets()) { + for (auto& wallet : node().walletLoader().getWallets()) { m_wallet_controller->getOrCreateWallet(std::move(wallet)); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 052453cf65..5f9c0d6a12 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -583,7 +583,7 @@ QString WalletModel::getDisplayName() const bool WalletModel::isMultiwallet() { - return m_node.walletClient().getWallets().size() > 1; + return m_node.walletLoader().getWallets().size() > 1; } void WalletModel::refresh(bool pk_hash_only) diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp index 65a33de4b4..c750f7ee3a 100644 --- a/src/test/fuzz/block.cpp +++ b/src/test/fuzz/block.cpp @@ -58,8 +58,6 @@ FUZZ_TARGET_INIT(block, initialize_block) (void)block.ToString(); (void)BlockMerkleRoot(block); if (!block.vtx.empty()) { - // TODO: Avoid array index out of bounds error in BlockWitnessMerkleRoot - // when block.vtx.empty(). (void)BlockWitnessMerkleRoot(block); } (void)GetBlockWeight(block); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index d0be9fe73a..eb23e8725f 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -195,6 +195,16 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector<const true); assert(!rv.has_value()); + auto maybe_verify_failure = VerifyLoadedChainstate( + *Assert(m_node.chainman), + fReindex.load(), + m_args.GetBoolArg("-reindex-chainstate", false), + chainparams.GetConsensus(), + m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS), + m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL), + static_cast<int64_t(*)()>(GetTime)); + assert(!maybe_verify_failure.has_value()); + BlockValidationState state; if (!m_node.chainman->ActiveChainstate().ActivateBestChain(state)) { throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString())); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index f2de68295e..57edba4c7d 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -276,10 +276,6 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti // mark the original tx as bumped bumped_txid = tx->GetHash(); if (!wallet.MarkReplaced(oldWtx.GetHash(), bumped_txid)) { - // TODO: see if JSON-RPC has a standard way of returning a response - // along with an exception. It would be good to return information about - // wtxBumped to the caller even if marking the original transaction - // replaced does not succeed for some reason. errors.push_back(Untranslated("Created new bumpfee transaction but could not mark the original transaction as replaced")); } return Result::OK; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 8e7fc95f44..7368f87574 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -133,7 +133,7 @@ void WalletInit::Construct(NodeContext& node) const LogPrintf("Wallet disabled!\n"); return; } - auto wallet_client = node.init->makeWalletClient(*node.chain); - node.wallet_client = wallet_client.get(); - node.chain_clients.emplace_back(std::move(wallet_client)); + auto wallet_loader = node.init->makeWalletLoader(*node.chain); + node.wallet_loader = wallet_loader.get(); + node.chain_clients.emplace_back(std::move(wallet_loader)); } diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index bba909b807..54986413d6 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -40,7 +40,7 @@ using interfaces::MakeHandler; using interfaces::Wallet; using interfaces::WalletAddress; using interfaces::WalletBalances; -using interfaces::WalletClient; +using interfaces::WalletLoader; using interfaces::WalletOrderForm; using interfaces::WalletTx; using interfaces::WalletTxOut; @@ -461,6 +461,11 @@ public: bool canGetAddresses() override { return m_wallet->CanGetAddresses(); } 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; + } OutputType getDefaultAddressType() override { return m_wallet->m_default_address_type; } CAmount getDefaultMaxTxFee() override { return m_wallet->m_default_max_tx_fee; } void remove() override @@ -505,15 +510,15 @@ public: std::shared_ptr<CWallet> m_wallet; }; -class WalletClientImpl : public WalletClient +class WalletLoaderImpl : public WalletLoader { public: - WalletClientImpl(Chain& chain, ArgsManager& args) + WalletLoaderImpl(Chain& chain, ArgsManager& args) { m_context.chain = &chain; m_context.args = &args; } - ~WalletClientImpl() override { UnloadWallets(m_context); } + ~WalletLoaderImpl() override { UnloadWallets(m_context); } //! ChainClient methods void registerRpcs() override @@ -534,7 +539,7 @@ public: void stop() override { return StopWallets(m_context); } void setMockTime(int64_t time) override { return SetMockTime(time); } - //! WalletClient methods + //! WalletLoader methods std::unique_ptr<Wallet> createWallet(const std::string& name, const SecureString& passphrase, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings) override { std::shared_ptr<CWallet> wallet; @@ -595,8 +600,8 @@ public: namespace interfaces { std::unique_ptr<Wallet> MakeWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(context, wallet) : nullptr; } -std::unique_ptr<WalletClient> MakeWalletClient(Chain& chain, ArgsManager& args) +std::unique_ptr<WalletLoader> MakeWalletLoader(Chain& chain, ArgsManager& args) { - return std::make_unique<wallet::WalletClientImpl>(chain, args); + return std::make_unique<wallet::WalletLoaderImpl>(chain, args); } } // namespace interfaces diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index c7400cafe7..2db47bc855 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -359,7 +359,12 @@ RPCHelpMan keypoolrefill() RPCHelpMan newkeypool() { return RPCHelpMan{"newkeypool", - "\nEntirely clears and refills the keypool."+ + "\nEntirely clears and refills the keypool.\n" + "WARNING: On non-HD wallets, this will require a new backup immediately, to include the new keys.\n" + "When restoring a backup of an HD wallet created before the newkeypool command is run, funds received to\n" + "new addresses may not appear automatically. They have not been lost, but the wallet may not find them.\n" + "This can be fixed by running the newkeypool command on the backup and then rescanning, so the wallet\n" + "re-generates the required keys." + HELP_REQUIRING_PASSPHRASE, {}, RPCResult{RPCResult::Type::NONE, "", ""}, diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp index 170675c035..ade2813e0a 100644 --- a/src/wallet/test/init_test_fixture.cpp +++ b/src/wallet/test/init_test_fixture.cpp @@ -11,7 +11,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) { - m_wallet_client = MakeWalletClient(*m_node.chain, *Assert(m_node.args)); + m_wallet_loader = MakeWalletLoader(*m_node.chain, *Assert(m_node.args)); std::string sep; sep += fs::path::preferred_separator; diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h index 37ae907de5..485aa50658 100644 --- a/src/wallet/test/init_test_fixture.h +++ b/src/wallet/test/init_test_fixture.h @@ -19,7 +19,7 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup { fs::path m_datadir; fs::path m_cwd; std::map<std::string, fs::path> m_walletdir_path_cases; - std::unique_ptr<interfaces::WalletClient> m_wallet_client; + std::unique_ptr<interfaces::WalletLoader> m_wallet_loader; }; #endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp index 222c2bf4b7..3bbf18043d 100644 --- a/src/wallet/test/init_tests.cpp +++ b/src/wallet/test/init_tests.cpp @@ -15,7 +15,7 @@ BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) { SetWalletDir(m_walletdir_path_cases["default"]); - bool result = m_wallet_client->verify(); + bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); @@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom) { SetWalletDir(m_walletdir_path_cases["custom"]); - bool result = m_wallet_client->verify(); + bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]); @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist) SetWalletDir(m_walletdir_path_cases["nonexistent"]); { ASSERT_DEBUG_LOG("does not exist"); - bool result = m_wallet_client->verify(); + bool result = m_wallet_loader->verify(); BOOST_CHECK(result == false); } } @@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory) SetWalletDir(m_walletdir_path_cases["file"]); { ASSERT_DEBUG_LOG("is not a directory"); - bool result = m_wallet_client->verify(); + bool result = m_wallet_loader->verify(); BOOST_CHECK(result == false); } } @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative) SetWalletDir(m_walletdir_path_cases["relative"]); { ASSERT_DEBUG_LOG("is a relative path"); - bool result = m_wallet_client->verify(); + bool result = m_wallet_loader->verify(); BOOST_CHECK(result == false); } } @@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) { SetWalletDir(m_walletdir_path_cases["trailing"]); - bool result = m_wallet_client->verify(); + bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) { SetWalletDir(m_walletdir_path_cases["trailing2"]); - bool result = m_wallet_client->verify(); + bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index 762702522e..83bc75528d 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -12,7 +12,7 @@ WalletTestingSetup::WalletTestingSetup(const std::string& chainName) { m_wallet.LoadWallet(); m_chain_notifications_handler = m_node.chain->handleNotifications({ &m_wallet, [](CWallet*) {} }); - m_wallet_client->registerRpcs(); + m_wallet_loader->registerRpcs(); } WalletTestingSetup::~WalletTestingSetup() diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index 8bf2d36227..02d5707e36 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -21,7 +21,7 @@ struct WalletTestingSetup : public TestingSetup { explicit WalletTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~WalletTestingSetup(); - std::unique_ptr<interfaces::WalletClient> m_wallet_client = interfaces::MakeWalletClient(*m_node.chain, *Assert(m_node.args)); + std::unique_ptr<interfaces::WalletLoader> m_wallet_loader = interfaces::MakeWalletLoader(*m_node.chain, *Assert(m_node.args)); CWallet m_wallet; std::unique_ptr<interfaces::Handler> m_chain_notifications_handler; }; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index decdbc7090..6e1552abd7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1967,29 +1967,58 @@ OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& chang return *change_type; } - // if m_default_address_type is legacy, use legacy address as change (even - // if some of the outputs are P2WPKH or P2WSH). + // if m_default_address_type is legacy, use legacy address as change. if (m_default_address_type == OutputType::LEGACY) { return OutputType::LEGACY; } - // if any destination is P2WPKH or P2WSH, use P2WPKH for the change - // output. + bool any_tr{false}; + bool any_wpkh{false}; + bool any_sh{false}; + bool any_pkh{false}; + for (const auto& recipient : vecSend) { - // Check if any destination contains a witness program: - int witnessversion = 0; - std::vector<unsigned char> witnessprogram; - if (recipient.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { - if (GetScriptPubKeyMan(OutputType::BECH32M, true)) { - return OutputType::BECH32M; - } else if (GetScriptPubKeyMan(OutputType::BECH32, true)) { - return OutputType::BECH32; - } else { - return m_default_address_type; - } - } + std::vector<std::vector<uint8_t>> dummy; + const TxoutType type{Solver(recipient.scriptPubKey, dummy)}; + if (type == TxoutType::WITNESS_V1_TAPROOT) { + any_tr = true; + } else if (type == TxoutType::WITNESS_V0_KEYHASH) { + any_wpkh = true; + } else if (type == TxoutType::SCRIPTHASH) { + any_sh = true; + } else if (type == TxoutType::PUBKEYHASH) { + any_pkh = true; + } + } + + const bool has_bech32m_spkman(GetScriptPubKeyMan(OutputType::BECH32M, /*internal=*/true)); + if (has_bech32m_spkman && any_tr) { + // Currently tr is the only type supported by the BECH32M spkman + return OutputType::BECH32M; + } + const bool has_bech32_spkman(GetScriptPubKeyMan(OutputType::BECH32, /*internal=*/true)); + if (has_bech32_spkman && any_wpkh) { + // Currently wpkh is the only type supported by the BECH32 spkman + return OutputType::BECH32; + } + const bool has_p2sh_segwit_spkman(GetScriptPubKeyMan(OutputType::P2SH_SEGWIT, /*internal=*/true)); + if (has_p2sh_segwit_spkman && any_sh) { + // Currently sh_wpkh is the only type supported by the P2SH_SEGWIT spkman + // As of 2021 about 80% of all SH are wrapping WPKH, so use that + return OutputType::P2SH_SEGWIT; + } + const bool has_legacy_spkman(GetScriptPubKeyMan(OutputType::LEGACY, /*internal=*/true)); + if (has_legacy_spkman && any_pkh) { + // Currently pkh is the only type supported by the LEGACY spkman + return OutputType::LEGACY; } + if (has_bech32m_spkman) { + return OutputType::BECH32M; + } + if (has_bech32_spkman) { + return OutputType::BECH32; + } // else use m_default_address_type for change return m_default_address_type; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e2a2aebeb5..30777dbe2c 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -48,7 +48,7 @@ struct bilingual_str; //! Explicitly unload and delete the wallet. //! Blocks the current thread after signaling the unload intent so that all -//! wallet clients release the wallet. +//! wallet pointer owners release the wallet. //! Note that, when blocking is not required, the wallet is implicitly unloaded //! by the shared pointer deleter. void UnloadWallet(std::shared_ptr<CWallet>&& wallet); |