From b9374946405c831560227daa3bbe6a8e6235221f Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 15 Jun 2017 10:34:17 -0400 Subject: [PATCH 01/20] Make feebumper class stateless Make feebumper methods static and remove stored state in the class. Having the results of feebumper calls persist in an object makes process separation between Qt and wallet awkward, because it means the feebumper object either has to be serialized back and forth between Qt and wallet processes between fee bump calls, or that the feebumper object needs to stay alive in the wallet process with an object reference passed back to Qt. It's simpler just to have fee bumper calls return their results immediately instead of storing them in an object with an extended lifetime. In addition to making feebumper methods static, also: - Move LOCK calls from Qt code to feebumper - Move TransactionCanBeBumped implementation from Qt code to feebumper - Rename CFeeBumper class to FeeBumper (every CFeeBumper reference had to be updated in this PR anyway so this doesn't increase the size of the diff) This change was originally part of https://github.com/bitcoin/bitcoin/pull/10244 --- src/qt/walletmodel.cpp | 40 ++++++----------- src/wallet/feebumper.cpp | 112 ++++++++++++++++++++++++----------------------- src/wallet/feebumper.h | 39 ++++++++--------- src/wallet/rpcwallet.cpp | 39 ++++++++++------- 4 files changed, 111 insertions(+), 119 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index ba0e1da0c78..e2c91f288d6 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -659,32 +659,26 @@ bool WalletModel::abandonTransaction(uint256 hash) const bool WalletModel::transactionCanBeBumped(uint256 hash) const { - LOCK2(cs_main, wallet->cs_wallet); - const CWalletTx *wtx = wallet->GetWalletTx(hash); - return wtx && SignalsOptInRBF(*wtx) && !wtx->mapValue.count("replaced_by_txid"); + return FeeBumper::TransactionCanBeBumped(wallet, hash); } bool WalletModel::bumpFee(uint256 hash) { - std::unique_ptr feeBump; - { - CCoinControl coin_control; - coin_control.signalRbf = true; - LOCK2(cs_main, wallet->cs_wallet); - feeBump.reset(new CFeeBumper(wallet, hash, coin_control, 0)); - } - if (feeBump->getResult() != BumpFeeResult::OK) - { + CCoinControl coin_control; + coin_control.signalRbf = true; + std::vector errors; + CAmount oldFee; + CAmount newFee; + CMutableTransaction mtx; + if (FeeBumper::CreateTransaction(wallet, hash, coin_control, 0 /* totalFee */, errors, oldFee, newFee, mtx) != BumpFeeResult::OK) { QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "
(" + - (feeBump->getErrors().size() ? QString::fromStdString(feeBump->getErrors()[0]) : "") +")"); + (errors.size() ? QString::fromStdString(errors[0]) : "") +")"); return false; } // allow a user based fee verification QString questionString = tr("Do you want to increase the fee?"); questionString.append("
"); - CAmount oldFee = feeBump->getOldFee(); - CAmount newFee = feeBump->getNewFee(); questionString.append(""); questionString.append("
"); questionString.append(tr("Current fee:")); @@ -715,23 +709,15 @@ bool WalletModel::bumpFee(uint256 hash) } // sign bumped transaction - bool res = false; - { - LOCK2(cs_main, wallet->cs_wallet); - res = feeBump->signTransaction(wallet); - } - if (!res) { + if (!FeeBumper::SignTransaction(wallet, mtx)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction.")); return false; } // commit the bumped transaction - { - LOCK2(cs_main, wallet->cs_wallet); - res = feeBump->commit(wallet); - } - if(!res) { + uint256 txid; + if(FeeBumper::CommitTransaction(wallet, hash, std::move(mtx), errors, txid) != BumpFeeResult::OK) { QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "
(" + - QString::fromStdString(feeBump->getErrors()[0])+")"); + QString::fromStdString(errors[0])+")"); return false; } return true; diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 4bfd8726a54..de1b610e822 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -15,6 +15,7 @@ #include "util.h" #include "net.h" +namespace { // Calculate the size of the transaction assuming all signatures are max size // Use DummySignatureCreator, which inserts 72 byte signatures everywhere. // TODO: re-use this in CWallet::CreateTransaction (right now @@ -42,11 +43,11 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *pWal return GetVirtualTransactionSize(txNew); } -bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx) { +BumpFeeResult PreconditionChecks(const CWallet* pWallet, const CWalletTx& wtx, std::vector& vErrors) +{ if (pWallet->HasWalletSpend(wtx.GetHash())) { vErrors.push_back("Transaction has descendants in the wallet"); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return false; + return BumpFeeResult::INVALID_PARAMETER; } { @@ -54,58 +55,63 @@ bool CFeeBumper::preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx auto it_mp = mempool.mapTx.find(wtx.GetHash()); if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) { vErrors.push_back("Transaction has descendants in the mempool"); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return false; + return BumpFeeResult::INVALID_PARAMETER; } } if (wtx.GetDepthInMainChain() != 0) { vErrors.push_back("Transaction has been mined, or is conflicted with a mined transaction"); - currentResult = BumpFeeResult::WALLET_ERROR; - return false; + return BumpFeeResult::WALLET_ERROR; } - return true; + return BumpFeeResult::OK; } +} // namespace -CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee) - : - txid(std::move(txidIn)), - nOldFee(0), - nNewFee(0) +bool FeeBumper::TransactionCanBeBumped(CWallet* pWallet, const uint256& txid) { + LOCK2(cs_main, pWallet->cs_wallet); + const CWalletTx* wtx = pWallet->GetWalletTx(txid); + return wtx && SignalsOptInRBF(*wtx) && !wtx->mapValue.count("replaced_by_txid"); +} + +BumpFeeResult FeeBumper::CreateTransaction(const CWallet* pWallet, + const uint256& txid, + const CCoinControl& coin_control, + CAmount totalFee, + std::vector& vErrors, + CAmount& nOldFee, + CAmount& nNewFee, + CMutableTransaction& mtx) +{ + LOCK2(cs_main, pWallet->cs_wallet); vErrors.clear(); - bumpedTxid.SetNull(); - AssertLockHeld(pWallet->cs_wallet); if (!pWallet->mapWallet.count(txid)) { vErrors.push_back("Invalid or non-wallet transaction id"); - currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY; - return; + return BumpFeeResult::INVALID_ADDRESS_OR_KEY; } auto it = pWallet->mapWallet.find(txid); const CWalletTx& wtx = it->second; - if (!preconditionChecks(pWallet, wtx)) { - return; + BumpFeeResult result = PreconditionChecks(pWallet, wtx, vErrors); + if (result != BumpFeeResult::OK) { + return result; } if (!SignalsOptInRBF(wtx)) { vErrors.push_back("Transaction is not BIP 125 replaceable"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } if (wtx.mapValue.count("replaced_by_txid")) { vErrors.push_back(strprintf("Cannot bump transaction %s which was already bumped by transaction %s", txid.ToString(), wtx.mapValue.at("replaced_by_txid"))); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } // 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) if (!pWallet->IsAllFromMe(wtx, ISMINE_SPENDABLE)) { vErrors.push_back("Transaction contains inputs that don't belong to this wallet"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } // figure out which output was change @@ -115,16 +121,14 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin if (pWallet->IsChange(wtx.tx->vout[i])) { if (nOutput != -1) { vErrors.push_back("Transaction has multiple change outputs"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } nOutput = i; } } if (nOutput == -1) { vErrors.push_back("Transaction does not have a change output"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } // Calculate the expected size of the new transaction. @@ -132,8 +136,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx, pWallet); if (maxNewTxSize < 0) { vErrors.push_back("Transaction contains inputs that cannot be signed"); - currentResult = BumpFeeResult::INVALID_ADDRESS_OR_KEY; - return; + return BumpFeeResult::INVALID_ADDRESS_OR_KEY; } // calculate the old fee and fee-rate @@ -153,15 +156,13 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin if (totalFee < minTotalFee) { vErrors.push_back(strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)", FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize)))); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return; + return BumpFeeResult::INVALID_PARAMETER; } CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize); if (totalFee < requiredFee) { vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)", FormatMoney(requiredFee))); - currentResult = BumpFeeResult::INVALID_PARAMETER; - return; + return BumpFeeResult::INVALID_PARAMETER; } nNewFee = totalFee; nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); @@ -184,8 +185,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin if (nNewFee > maxTxFee) { vErrors.push_back(strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)", FormatMoney(nNewFee), FormatMoney(maxTxFee))); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } // check that fee rate is higher than mempool's minimum fee @@ -196,8 +196,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000); if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) { vErrors.push_back(strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK()))); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } // Now modify the output to increase the fee. @@ -208,8 +207,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin CTxOut* poutput = &(mtx.vout[nOutput]); if (poutput->nValue < nDelta) { vErrors.push_back("Change output is too small to bump the fee"); - currentResult = BumpFeeResult::WALLET_ERROR; - return; + return BumpFeeResult::WALLET_ERROR; } // If the output would become dust, discard it (converting the dust to fee) @@ -227,30 +225,34 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin } } - currentResult = BumpFeeResult::OK; + return BumpFeeResult::OK; } -bool CFeeBumper::signTransaction(CWallet *pWallet) -{ - return pWallet->SignTransaction(mtx); +bool FeeBumper::SignTransaction(CWallet* pWallet, CMutableTransaction& mtx) { + LOCK2(cs_main, pWallet->cs_wallet); + return pWallet->SignTransaction(mtx); } -bool CFeeBumper::commit(CWallet *pWallet) +BumpFeeResult FeeBumper::CommitTransaction(CWallet* pWallet, + const uint256& txid, + CMutableTransaction&& mtx, + std::vector& vErrors, + uint256& bumpedTxid) { - AssertLockHeld(pWallet->cs_wallet); - if (!vErrors.empty() || currentResult != BumpFeeResult::OK) { - return false; + LOCK2(cs_main, pWallet->cs_wallet); + if (!vErrors.empty()) { + return BumpFeeResult::MISC_ERROR; } if (txid.IsNull() || !pWallet->mapWallet.count(txid)) { vErrors.push_back("Invalid or non-wallet transaction id"); - currentResult = BumpFeeResult::MISC_ERROR; - return false; + return BumpFeeResult::MISC_ERROR; } CWalletTx& oldWtx = pWallet->mapWallet[txid]; // make sure the transaction still has no descendants and hasn't been mined in the meantime - if (!preconditionChecks(pWallet, oldWtx)) { - return false; + BumpFeeResult result = PreconditionChecks(pWallet, oldWtx, vErrors); + if (result != BumpFeeResult::OK) { + return result; } CWalletTx wtxBumped(pWallet, MakeTransactionRef(std::move(mtx))); @@ -266,7 +268,7 @@ bool CFeeBumper::commit(CWallet *pWallet) if (!pWallet->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. vErrors.push_back(strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason())); - return false; + return BumpFeeResult::WALLET_ERROR; } bumpedTxid = wtxBumped.GetHash(); @@ -284,6 +286,6 @@ bool CFeeBumper::commit(CWallet *pWallet) // replaced does not succeed for some reason. vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced."); } - return true; + return BumpFeeResult::OK; } diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index 3d64e53c15c..aa5b2dea60d 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -23,39 +23,38 @@ enum class BumpFeeResult MISC_ERROR, }; -class CFeeBumper +class FeeBumper { public: - CFeeBumper(const CWallet *pWalletIn, const uint256 txidIn, const CCoinControl& coin_control, CAmount totalFee); - BumpFeeResult getResult() const { return currentResult; } - const std::vector& getErrors() const { return vErrors; } - CAmount getOldFee() const { return nOldFee; } - CAmount getNewFee() const { return nNewFee; } - uint256 getBumpedTxId() const { return bumpedTxid; } + /* return whether transaction can be bumped */ + static bool TransactionCanBeBumped(CWallet* pWallet, const uint256& txid); + + /* create bumpfee transaction */ + static BumpFeeResult CreateTransaction(const CWallet* pWallet, + const uint256& txid, + const CCoinControl& coin_control, + CAmount totalFee, + std::vector& vErrors, + CAmount& nOldFee, + CAmount& nNewFee, + CMutableTransaction& mtx); /* signs the new transaction, * returns false if the tx couldn't be found or if it was * impossible to create the signature(s) */ - bool signTransaction(CWallet *pWallet); + static bool SignTransaction(CWallet* pWallet, CMutableTransaction& mtx); /* commits the fee bump, * returns true, in case of CWallet::CommitTransaction was successful * but, eventually sets vErrors if the tx could not be added to the mempool (will try later) * or if the old transaction could not be marked as replaced */ - bool commit(CWallet *pWalletNonConst); - -private: - bool preconditionChecks(const CWallet *pWallet, const CWalletTx& wtx); - - const uint256 txid; - uint256 bumpedTxid; - CMutableTransaction mtx; - std::vector vErrors; - BumpFeeResult currentResult; - CAmount nOldFee; - CAmount nNewFee; + static BumpFeeResult CommitTransaction(CWallet* pWallet, + const uint256& txid, + CMutableTransaction&& mtx, + std::vector& vErrors, + uint256& bumpedTxid); }; #endif // BITCOIN_WALLET_FEEBUMPER_H diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index f63ce1bb48c..37e70994b47 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2946,45 +2946,50 @@ UniValue bumpfee(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); EnsureWalletIsUnlocked(pwallet); - CFeeBumper feeBump(pwallet, hash, coin_control, totalFee); - BumpFeeResult res = feeBump.getResult(); + std::vector errors; + CAmount oldFee; + CAmount newFee; + CMutableTransaction mtx; + BumpFeeResult res = FeeBumper::CreateTransaction(pwallet, hash, coin_control, totalFee, errors, oldFee, newFee, mtx); if (res != BumpFeeResult::OK) { switch(res) { case BumpFeeResult::INVALID_ADDRESS_OR_KEY: - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, feeBump.getErrors()[0]); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errors[0]); break; case BumpFeeResult::INVALID_REQUEST: - throw JSONRPCError(RPC_INVALID_REQUEST, feeBump.getErrors()[0]); + throw JSONRPCError(RPC_INVALID_REQUEST, errors[0]); break; case BumpFeeResult::INVALID_PARAMETER: - throw JSONRPCError(RPC_INVALID_PARAMETER, feeBump.getErrors()[0]); + throw JSONRPCError(RPC_INVALID_PARAMETER, errors[0]); break; case BumpFeeResult::WALLET_ERROR: - throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]); + throw JSONRPCError(RPC_WALLET_ERROR, errors[0]); break; default: - throw JSONRPCError(RPC_MISC_ERROR, feeBump.getErrors()[0]); + throw JSONRPCError(RPC_MISC_ERROR, errors[0]); break; } } // sign bumped transaction - if (!feeBump.signTransaction(pwallet)) { + if (!FeeBumper::SignTransaction(pwallet, mtx)) { throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction."); } // commit the bumped transaction - if(!feeBump.commit(pwallet)) { - throw JSONRPCError(RPC_WALLET_ERROR, feeBump.getErrors()[0]); + uint256 txid; + if (FeeBumper::CommitTransaction(pwallet, hash, std::move(mtx), errors, txid) != BumpFeeResult::OK) { + throw JSONRPCError(RPC_WALLET_ERROR, errors[0]); } UniValue result(UniValue::VOBJ); - result.push_back(Pair("txid", feeBump.getBumpedTxId().GetHex())); - result.push_back(Pair("origfee", ValueFromAmount(feeBump.getOldFee()))); - result.push_back(Pair("fee", ValueFromAmount(feeBump.getNewFee()))); - UniValue errors(UniValue::VARR); - for (const std::string& err: feeBump.getErrors()) - errors.push_back(err); - result.push_back(Pair("errors", errors)); + result.push_back(Pair("txid", txid.GetHex())); + result.push_back(Pair("origfee", ValueFromAmount(oldFee))); + result.push_back(Pair("fee", ValueFromAmount(newFee))); + UniValue result_errors(UniValue::VARR); + for (const std::string& error : errors) { + result_errors.push_back(error); + } + result.push_back(Pair("errors", result_errors)); return result; } From a89a060e3468919b0898e4b19f8e0c86a7706d7f Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 13:55:43 -0400 Subject: [PATCH 02/20] Remove direct bitcoin calls from qt/bitcoin.cpp --- src/Makefile.am | 5 +++ src/ipc/interfaces.cpp | 15 +++++++++ src/ipc/interfaces.h | 74 +++++++++++++++++++++++++++++++++++++++++ src/ipc/local/interfaces.cpp | 64 ++++++++++++++++++++++++++++++++++++ src/ipc/local/interfaces.h | 19 +++++++++++ src/ipc/util.h | 17 ++++++++++ src/qt/bitcoin.cpp | 78 ++++++++++++++++---------------------------- 7 files changed, 222 insertions(+), 50 deletions(-) create mode 100644 src/ipc/interfaces.cpp create mode 100644 src/ipc/interfaces.h create mode 100644 src/ipc/local/interfaces.cpp create mode 100644 src/ipc/local/interfaces.h create mode 100644 src/ipc/util.h diff --git a/src/Makefile.am b/src/Makefile.am index f7abab482e9..ba6f80d496b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -104,6 +104,9 @@ BITCOIN_CORE_H = \ httpserver.h \ indirectmap.h \ init.h \ + ipc/interfaces.h \ + ipc/local/interfaces.h \ + ipc/util.h \ key.h \ keystore.h \ dbwrapper.h \ @@ -341,6 +344,8 @@ libbitcoin_util_a_SOURCES = \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ + ipc/interfaces.cpp \ + ipc/local/interfaces.cpp \ random.cpp \ rpc/protocol.cpp \ support/cleanse.cpp \ diff --git a/src/ipc/interfaces.cpp b/src/ipc/interfaces.cpp new file mode 100644 index 00000000000..88f18c32e86 --- /dev/null +++ b/src/ipc/interfaces.cpp @@ -0,0 +1,15 @@ +#include "ipc/interfaces.h" + +#include "ipc/local/interfaces.h" + +namespace ipc { + +std::unique_ptr MakeNode(Protocol protocol) +{ + if (protocol == LOCAL) { + return local::MakeNode(); + } + return {}; +} + +} // namespace ipc diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h new file mode 100644 index 00000000000..d103e494bc0 --- /dev/null +++ b/src/ipc/interfaces.h @@ -0,0 +1,74 @@ +#ifndef BITCOIN_IPC_INTERFACES_H +#define BITCOIN_IPC_INTERFACES_H + +#include +#include + +namespace ipc { + +class Handler; + +//! Top-level interface for a bitcoin node (bitcoind process). +class Node +{ +public: + virtual ~Node() {} + + //! Set command line arguments. + virtual void parseParameters(int argc, const char* const argv[]) = 0; + + //! Load settings from configuration file. + virtual void readConfigFile(const std::string& conf_path) = 0; + + //! Choose network parameters. + virtual void selectParams(const std::string& network) = 0; + + //! Init logging. + virtual void initLogging() = 0; + + //! Init parameter interaction. + virtual void initParameterInteraction() = 0; + + //! Get warnings. + virtual std::string getWarnings(const std::string& type) = 0; + + //! Initialize app dependencies. + virtual bool baseInitialize() = 0; + + //! Start node. + virtual bool appInitMain() = 0; + + //! Stop node. + virtual void appShutdown() = 0; + + //! Start shutdown. + virtual void startShutdown() = 0; + + //! Register handler for init messages. + using InitMessageFn = std::function; + virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; +}; + +//! Interface for managing a registered handler. +class Handler +{ +public: + virtual ~Handler() {} + + //! Disconnect the handler. + virtual void disconnect() = 0; +}; + +//! Protocol IPC interface should use to communicate with implementation. +enum Protocol { + LOCAL, //!< Call functions linked into current executable. +}; + +//! Create IPC node interface, communicating with requested protocol. Returns +//! null if protocol isn't implemented or is not available in the current build +//! configuation. +std::unique_ptr MakeNode(Protocol protocol); + +} // namespace ipc + +#endif // BITCOIN_IPC_INTERFACES_H diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp new file mode 100644 index 00000000000..cdc4d5f6219 --- /dev/null +++ b/src/ipc/local/interfaces.cpp @@ -0,0 +1,64 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace ipc { +namespace local { +namespace { + +class HandlerImpl : public Handler +{ +public: + HandlerImpl(boost::signals2::connection connection) : m_connection(std::move(connection)) {} + + void disconnect() override { m_connection.disconnect(); } + + boost::signals2::scoped_connection m_connection; +}; + +class NodeImpl : public Node +{ +public: + void parseParameters(int argc, const char* const argv[]) override { gArgs.ParseParameters(argc, argv); } + void readConfigFile(const std::string& conf_path) override { gArgs.ReadConfigFile(conf_path); } + void selectParams(const std::string& network) override { ::SelectParams(network); } + void initLogging() override { ::InitLogging(); } + void initParameterInteraction() override { ::InitParameterInteraction(); } + std::string getWarnings(const std::string& type) override { return ::GetWarnings(type); } + bool baseInitialize() override + { + return ::AppInitBasicSetup() && ::AppInitParameterInteraction() && ::AppInitSanityChecks() && + ::AppInitLockDataDirectory(); + } + bool appInitMain() override { return ::AppInitMain(m_thread_group, m_scheduler); } + void appShutdown() override + { + ::Interrupt(m_thread_group); + m_thread_group.join_all(); + ::Shutdown(); + } + void startShutdown() override { ::StartShutdown(); } + std::unique_ptr handleInitMessage(InitMessageFn fn) override + { + return MakeUnique(::uiInterface.InitMessage.connect(fn)); + } + + boost::thread_group m_thread_group; + ::CScheduler m_scheduler; +}; + +} // namespace + +std::unique_ptr MakeNode() { return MakeUnique(); } + +} // namespace local +} // namespace ipc diff --git a/src/ipc/local/interfaces.h b/src/ipc/local/interfaces.h new file mode 100644 index 00000000000..fe2def29cc4 --- /dev/null +++ b/src/ipc/local/interfaces.h @@ -0,0 +1,19 @@ +#ifndef BITCOIN_IPC_LOCAL_INTERFACES_H +#define BITCOIN_IPC_LOCAL_INTERFACES_H + +#include + +namespace ipc { + +class Node; + +namespace local { + +//! Return local, in-process implementation of ipc::Node interface. +std::unique_ptr MakeNode(); + +} // namespace local + +} // namespace ipc + +#endif // BITCOIN_IPC_LOCAL_INTERFACES_H diff --git a/src/ipc/util.h b/src/ipc/util.h new file mode 100644 index 00000000000..d4e32d95332 --- /dev/null +++ b/src/ipc/util.h @@ -0,0 +1,17 @@ +#ifndef BITCOIN_IPC_UTIL_H +#define BITCOIN_IPC_UTIL_H + +#include + +namespace ipc { + +//! Substitute for for C++14 std::make_unique. +template +std::unique_ptr MakeUnique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +} // namespace ipc + +#endif // BITCOIN_IPC_UTIL_H diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 2b6bd354a09..e2aa06606a4 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -27,6 +27,7 @@ #endif #include "init.h" +#include "ipc/interfaces.h" #include "rpc/server.h" #include "scheduler.h" #include "ui_interface.h" @@ -177,11 +178,7 @@ class BitcoinCore: public QObject { Q_OBJECT public: - explicit BitcoinCore(); - /** Basic initialization, before starting initialization/shutdown thread. - * Return true on success. - */ - static bool baseInitialize(); + explicit BitcoinCore(ipc::Node& ipc_node); public Q_SLOTS: void initialize(); @@ -193,11 +190,10 @@ public Q_SLOTS: void runawayException(const QString &message); private: - boost::thread_group threadGroup; - CScheduler scheduler; - /// Pass fatal exception message to UI thread void handleRunawayException(const std::exception *e); + + ipc::Node& m_ipc_node; }; /** Main Bitcoin application object */ @@ -205,7 +201,7 @@ class BitcoinApplication: public QApplication { Q_OBJECT public: - explicit BitcoinApplication(int &argc, char **argv); + explicit BitcoinApplication(ipc::Node& ipc_node, int &argc, char **argv); ~BitcoinApplication(); #ifdef ENABLE_WALLET @@ -246,6 +242,7 @@ public Q_SLOTS: private: QThread *coreThread; + ipc::Node& m_ipc_node; OptionsModel *optionsModel; ClientModel *clientModel; BitcoinGUI *window; @@ -263,36 +260,15 @@ public Q_SLOTS: #include "bitcoin.moc" -BitcoinCore::BitcoinCore(): - QObject() +BitcoinCore::BitcoinCore(ipc::Node& ipc_node) : + QObject(), m_ipc_node(ipc_node) { } void BitcoinCore::handleRunawayException(const std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); - Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui"))); -} - -bool BitcoinCore::baseInitialize() -{ - if (!AppInitBasicSetup()) - { - return false; - } - if (!AppInitParameterInteraction()) - { - return false; - } - if (!AppInitSanityChecks()) - { - return false; - } - if (!AppInitLockDataDirectory()) - { - return false; - } - return true; + Q_EMIT runawayException(QString::fromStdString(m_ipc_node.getWarnings("gui"))); } void BitcoinCore::initialize() @@ -300,7 +276,7 @@ void BitcoinCore::initialize() try { qDebug() << __func__ << ": Running initialization in thread"; - bool rv = AppInitMain(threadGroup, scheduler); + bool rv = m_ipc_node.appInitMain(); Q_EMIT initializeResult(rv); } catch (const std::exception& e) { handleRunawayException(&e); @@ -314,9 +290,7 @@ void BitcoinCore::shutdown() try { qDebug() << __func__ << ": Running Shutdown in thread"; - Interrupt(threadGroup); - threadGroup.join_all(); - Shutdown(); + m_ipc_node.appShutdown(); qDebug() << __func__ << ": Shutdown finished"; Q_EMIT shutdownResult(); } catch (const std::exception& e) { @@ -326,9 +300,10 @@ void BitcoinCore::shutdown() } } -BitcoinApplication::BitcoinApplication(int &argc, char **argv): +BitcoinApplication::BitcoinApplication(ipc::Node& ipc_node, int &argc, char **argv): QApplication(argc, argv), coreThread(0), + m_ipc_node(ipc_node), optionsModel(0), clientModel(0), window(0), @@ -410,7 +385,7 @@ void BitcoinApplication::startThread() if(coreThread) return; coreThread = new QThread(this); - BitcoinCore *executor = new BitcoinCore(); + BitcoinCore *executor = new BitcoinCore(m_ipc_node); executor->moveToThread(coreThread); /* communication to and from thread */ @@ -428,8 +403,8 @@ void BitcoinApplication::startThread() void BitcoinApplication::parameterSetup() { - InitLogging(); - InitParameterInteraction(); + m_ipc_node.initLogging(); + m_ipc_node.initParameterInteraction(); } void BitcoinApplication::requestInitialize() @@ -460,7 +435,7 @@ void BitcoinApplication::requestShutdown() delete clientModel; clientModel = 0; - StartShutdown(); + m_ipc_node.startShutdown(); // Request shutdown from core thread Q_EMIT requestedShutdown(); @@ -548,9 +523,12 @@ int main(int argc, char *argv[]) { SetupEnvironment(); + std::unique_ptr ipc_node; + ipc_node = ipc::MakeNode(ipc::LOCAL); + /// 1. Parse command-line options. These take precedence over anything else. // Command-line options take precedence: - gArgs.ParseParameters(argc, argv); + ipc_node->parseParameters(argc, argv); // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory @@ -564,7 +542,7 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin_locale); - BitcoinApplication app(argc, argv); + BitcoinApplication app(*ipc_node, argc, argv); #if QT_VERSION > 0x050100 // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); @@ -627,7 +605,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } try { - gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + ipc_node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception& e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); @@ -642,7 +620,7 @@ int main(int argc, char *argv[]) // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { - SelectParams(ChainNameFromCommandLine()); + ipc_node->selectParams(ChainNameFromCommandLine()); } catch(std::exception &e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what())); return EXIT_FAILURE; @@ -694,7 +672,7 @@ int main(int argc, char *argv[]) app.createOptionsModel(gArgs.IsArgSet("-resetguisettings")); // Subscribe to global signals from core - uiInterface.InitMessage.connect(InitMessage); + std::unique_ptr handlerInitMessage = ipc_node->handleInitMessage(InitMessage); if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); @@ -706,7 +684,7 @@ int main(int argc, char *argv[]) // Perform base initialization before spinning up initialization/shutdown thread // This is acceptable because this function only contains steps that are quick to execute, // so the GUI thread won't be held up. - if (BitcoinCore::baseInitialize()) { + if (ipc_node->baseInitialize()) { app.requestInitialize(); #if defined(Q_OS_WIN) && QT_VERSION >= 0x050000 WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("%1 didn't yet exit safely...").arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); @@ -721,10 +699,10 @@ int main(int argc, char *argv[]) } } catch (const std::exception& e) { PrintExceptionContinue(&e, "Runaway exception"); - app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); + app.handleRunawayException(QString::fromStdString(ipc_node->getWarnings("gui"))); } catch (...) { PrintExceptionContinue(nullptr, "Runaway exception"); - app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); + app.handleRunawayException(QString::fromStdString(ipc_node->getWarnings("gui"))); } return rv; } From 2a2a28b0fcc2a17eb850b6721a7472283097e9cf Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 14:23:14 -0400 Subject: [PATCH 03/20] Remove direct bitcoin calls from qt/optionsmodel.cpp --- src/ipc/interfaces.h | 16 ++++++++++++++++ src/ipc/local/interfaces.cpp | 6 ++++++ src/qt/bitcoin.cpp | 2 +- src/qt/optionsmodel.cpp | 30 +++++++++++++----------------- src/qt/optionsmodel.h | 7 ++++++- src/qt/test/paymentservertests.cpp | 4 +++- src/qt/test/wallettests.cpp | 4 +++- 7 files changed, 48 insertions(+), 21 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index d103e494bc0..682dc3fe5ce 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -1,9 +1,13 @@ #ifndef BITCOIN_IPC_INTERFACES_H #define BITCOIN_IPC_INTERFACES_H +#include "netaddress.h" // For Network + #include #include +class proxyType; + namespace ipc { class Handler; @@ -17,6 +21,12 @@ class Node //! Set command line arguments. virtual void parseParameters(int argc, const char* const argv[]) = 0; + //! Set a command line argument if it doesn't already have a value + virtual bool softSetArg(const std::string& arg, const std::string& value) = 0; + + //! Set a command line boolean argument if it doesn't already have a value + virtual bool softSetBoolArg(const std::string& arg, bool value) = 0; + //! Load settings from configuration file. virtual void readConfigFile(const std::string& conf_path) = 0; @@ -44,6 +54,12 @@ class Node //! Start shutdown. virtual void startShutdown() = 0; + //! Map port. + virtual void mapPort(bool use_upnp) = 0; + + //! Get proxy. + virtual bool getProxy(Network net, proxyType& proxy_info) = 0; + //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index cdc4d5f6219..5212c2c9518 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,6 +32,8 @@ class NodeImpl : public Node public: void parseParameters(int argc, const char* const argv[]) override { gArgs.ParseParameters(argc, argv); } void readConfigFile(const std::string& conf_path) override { gArgs.ReadConfigFile(conf_path); } + bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } + bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string& network) override { ::SelectParams(network); } void initLogging() override { ::InitLogging(); } void initParameterInteraction() override { ::InitParameterInteraction(); } @@ -47,6 +51,8 @@ class NodeImpl : public Node ::Shutdown(); } void startShutdown() override { ::StartShutdown(); } + void mapPort(bool use_upnp) override { ::MapPort(use_upnp); } + bool getProxy(Network net, proxyType& proxy_info) override { return ::GetProxy(net, proxy_info); } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeUnique(::uiInterface.InitMessage.connect(fn)); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index e2aa06606a4..267513df9ad 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -358,7 +358,7 @@ void BitcoinApplication::createPaymentServer() void BitcoinApplication::createOptionsModel(bool resetSettings) { - optionsModel = new OptionsModel(nullptr, resetSettings); + optionsModel = new OptionsModel(m_ipc_node, nullptr, resetSettings); } void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 77efef3e3ca..b7ebd816dd7 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -13,23 +13,19 @@ #include "amount.h" #include "init.h" +#include "ipc/interfaces.h" #include "validation.h" // For DEFAULT_SCRIPTCHECK_THREADS #include "net.h" #include "netbase.h" #include "txdb.h" // for -dbcache defaults #include "intro.h" -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#include "wallet/walletdb.h" -#endif - #include #include #include -OptionsModel::OptionsModel(QObject *parent, bool resetSettings) : - QAbstractListModel(parent) +OptionsModel::OptionsModel(ipc::Node& ipc_node, QObject *parent, bool resetSettings) : + QAbstractListModel(parent), m_ipc_node(ipc_node) { Init(resetSettings); } @@ -92,12 +88,12 @@ void OptionsModel::Init(bool resetSettings) // Main if (!settings.contains("nDatabaseCache")) settings.setValue("nDatabaseCache", (qint64)nDefaultDbCache); - if (!gArgs.SoftSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString())) + if (!m_ipc_node.softSetArg("-dbcache", settings.value("nDatabaseCache").toString().toStdString())) addOverriddenOption("-dbcache"); if (!settings.contains("nThreadsScriptVerif")) settings.setValue("nThreadsScriptVerif", DEFAULT_SCRIPTCHECK_THREADS); - if (!gArgs.SoftSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString())) + if (!m_ipc_node.softSetArg("-par", settings.value("nThreadsScriptVerif").toString().toStdString())) addOverriddenOption("-par"); if (!settings.contains("strDataDir")) @@ -107,19 +103,19 @@ void OptionsModel::Init(bool resetSettings) #ifdef ENABLE_WALLET if (!settings.contains("bSpendZeroConfChange")) settings.setValue("bSpendZeroConfChange", true); - if (!gArgs.SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) + if (!m_ipc_node.softSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) addOverriddenOption("-spendzeroconfchange"); #endif // Network if (!settings.contains("fUseUPnP")) settings.setValue("fUseUPnP", DEFAULT_UPNP); - if (!gArgs.SoftSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) + if (!m_ipc_node.softSetBoolArg("-upnp", settings.value("fUseUPnP").toBool())) addOverriddenOption("-upnp"); if (!settings.contains("fListen")) settings.setValue("fListen", DEFAULT_LISTEN); - if (!gArgs.SoftSetBoolArg("-listen", settings.value("fListen").toBool())) + if (!m_ipc_node.softSetBoolArg("-listen", settings.value("fListen").toBool())) addOverriddenOption("-listen"); if (!settings.contains("fUseProxy")) @@ -127,7 +123,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("addrProxy")) settings.setValue("addrProxy", "127.0.0.1:9050"); // Only try to set -proxy, if user has enabled fUseProxy - if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) + if (settings.value("fUseProxy").toBool() && !m_ipc_node.softSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) addOverriddenOption("-proxy"); else if(!settings.value("fUseProxy").toBool() && !gArgs.GetArg("-proxy", "").empty()) addOverriddenOption("-proxy"); @@ -137,7 +133,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("addrSeparateProxyTor")) settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050"); // Only try to set -onion, if user has enabled fUseSeparateProxyTor - if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) + if (settings.value("fUseSeparateProxyTor").toBool() && !m_ipc_node.softSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) addOverriddenOption("-onion"); else if(!settings.value("fUseSeparateProxyTor").toBool() && !gArgs.GetArg("-onion", "").empty()) addOverriddenOption("-onion"); @@ -145,7 +141,7 @@ void OptionsModel::Init(bool resetSettings) // Display if (!settings.contains("language")) settings.setValue("language", ""); - if (!gArgs.SoftSetArg("-lang", settings.value("language").toString().toStdString())) + if (!m_ipc_node.softSetArg("-lang", settings.value("language").toString().toStdString())) addOverriddenOption("-lang"); language = settings.value("language").toString(); @@ -277,7 +273,7 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; case MapPortUPnP: // core option - can be changed on-the-fly settings.setValue("fUseUPnP", value.toBool()); - MapPort(value.toBool()); + m_ipc_node.mapPort(value.toBool()); break; case MinimizeOnClose: fMinimizeOnClose = value.toBool(); @@ -422,7 +418,7 @@ bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const // Directly query current base proxy, because // GUI settings can be overridden with -proxy. proxyType curProxy; - if (GetProxy(NET_IPV4, curProxy)) { + if (m_ipc_node.getProxy(NET_IPV4, curProxy)) { proxy.setType(QNetworkProxy::Socks5Proxy); proxy.setHostName(QString::fromStdString(curProxy.proxy.ToStringIP())); proxy.setPort(curProxy.proxy.GetPort()); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 78529fbdcc9..4c83c66dd58 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -9,6 +9,10 @@ #include +namespace ipc { +class Node; +} + QT_BEGIN_NAMESPACE class QNetworkProxy; QT_END_NAMESPACE @@ -24,7 +28,7 @@ class OptionsModel : public QAbstractListModel Q_OBJECT public: - explicit OptionsModel(QObject *parent = 0, bool resetSettings = false); + explicit OptionsModel(ipc::Node& ipc_node, QObject *parent = 0, bool resetSettings = false); enum OptionID { StartAtStartup, // bool @@ -73,6 +77,7 @@ class OptionsModel : public QAbstractListModel bool isRestartRequired(); private: + ipc::Node& m_ipc_node; /* Qt-only settings */ bool fHideTrayIcon; bool fMinimizeToTray; diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index d9fb8698212..27aeb5f1174 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -8,6 +8,7 @@ #include "paymentrequestdata.h" #include "amount.h" +#include "ipc/interfaces.h" #include "random.h" #include "script/script.h" #include "script/standard.h" @@ -65,7 +66,8 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector platformStyle(PlatformStyle::instantiate("other")); SendCoinsDialog sendCoinsDialog(platformStyle.get()); TransactionView transactionView(platformStyle.get()); - OptionsModel optionsModel; + auto ipc_node = ipc::MakeNode(ipc::LOCAL); + OptionsModel optionsModel(*ipc_node); WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); From 36f0ef517a58ca1dbb1f974fec400750bffc54ad Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 14:33:47 -0400 Subject: [PATCH 04/20] Remove direct bitcoin calls from qt/bitcoingui.cpp --- src/ipc/interfaces.h | 15 +++++++++++++++ src/ipc/local/interfaces.cpp | 9 +++++++++ src/qt/bitcoin.cpp | 2 +- src/qt/bitcoingui.cpp | 14 ++++++++------ src/qt/bitcoingui.h | 12 +++++++++++- 5 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 682dc3fe5ce..a623969d7dc 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -54,6 +54,9 @@ class Node //! Start shutdown. virtual void startShutdown() = 0; + //! Return whether shutdown was requested. + virtual bool shutdownRequested() = 0; + //! Map port. virtual void mapPort(bool use_upnp) = 0; @@ -63,6 +66,18 @@ class Node //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; + + //! Register handler for message box messages. + using MessageBoxFn = + std::function; + virtual std::unique_ptr handleMessageBox(MessageBoxFn fn) = 0; + + //! Register handler for question messages. + using QuestionFn = std::function; + virtual std::unique_ptr handleQuestion(QuestionFn fn) = 0; }; //! Interface for managing a registered handler. diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 5212c2c9518..dc2828b01eb 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -51,12 +51,21 @@ class NodeImpl : public Node ::Shutdown(); } void startShutdown() override { ::StartShutdown(); } + bool shutdownRequested() override { return ::ShutdownRequested(); } void mapPort(bool use_upnp) override { ::MapPort(use_upnp); } bool getProxy(Network net, proxyType& proxy_info) override { return ::GetProxy(net, proxy_info); } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeUnique(::uiInterface.InitMessage.connect(fn)); } + std::unique_ptr handleMessageBox(MessageBoxFn fn) override + { + return MakeUnique(::uiInterface.ThreadSafeMessageBox.connect(fn)); + } + std::unique_ptr handleQuestion(QuestionFn fn) override + { + return MakeUnique(::uiInterface.ThreadSafeQuestion.connect(fn)); + } boost::thread_group m_thread_group; ::CScheduler m_scheduler; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 267513df9ad..4b20b27eb84 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -363,7 +363,7 @@ void BitcoinApplication::createOptionsModel(bool resetSettings) void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) { - window = new BitcoinGUI(platformStyle, networkStyle, 0); + window = new BitcoinGUI(m_ipc_node, platformStyle, networkStyle, 0); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index e3970298e61..239eec197f0 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -33,6 +33,7 @@ #include "chainparams.h" #include "init.h" +#include "ipc/interfaces.h" #include "ui_interface.h" #include "util.h" @@ -78,9 +79,10 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM = * collisions in the future with additional wallets */ const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; -BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : +BitcoinGUI::BitcoinGUI(ipc::Node& ipc_node, const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), enableWallet(false), + m_ipc_node(ipc_node), clientModel(0), walletFrame(0), unitDisplayControl(0), @@ -1110,7 +1112,7 @@ void BitcoinGUI::toggleHidden() void BitcoinGUI::detectShutdown() { - if (ShutdownRequested()) + if (m_ipc_node.shutdownRequested()) { if(rpcConsole) rpcConsole->hide(); @@ -1175,15 +1177,15 @@ static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, co void BitcoinGUI::subscribeToCoreSignals() { // Connect signals to client - uiInterface.ThreadSafeMessageBox.connect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); - uiInterface.ThreadSafeQuestion.connect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); + m_handler_message_box = m_ipc_node.handleMessageBox(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); + m_handler_question = m_ipc_node.handleQuestion(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); } void BitcoinGUI::unsubscribeFromCoreSignals() { // Disconnect signals from client - uiInterface.ThreadSafeMessageBox.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _2, _3)); - uiInterface.ThreadSafeQuestion.disconnect(boost::bind(ThreadSafeMessageBox, this, _1, _3, _4)); + m_handler_message_box->disconnect(); + m_handler_question->disconnect(); } void BitcoinGUI::toggleNetworkActive() diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index aa45ea1f0a0..6da67f18a35 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -18,6 +18,8 @@ #include #include +#include + class ClientModel; class NetworkStyle; class Notificator; @@ -31,6 +33,11 @@ class WalletModel; class HelpMessageDialog; class ModalOverlay; +namespace ipc { +class Handler; +class Node; +} + QT_BEGIN_NAMESPACE class QAction; class QProgressBar; @@ -49,7 +56,7 @@ class BitcoinGUI : public QMainWindow static const QString DEFAULT_WALLET; static const std::string DEFAULT_UIPLATFORM; - explicit BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); + explicit BitcoinGUI(ipc::Node& ipc_node, const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); ~BitcoinGUI(); /** Set the client model. @@ -77,6 +84,9 @@ class BitcoinGUI : public QMainWindow bool eventFilter(QObject *object, QEvent *event); private: + ipc::Node& m_ipc_node; + std::unique_ptr m_handler_message_box; + std::unique_ptr m_handler_question; ClientModel *clientModel; WalletFrame *walletFrame; From cfccc1ef8abe496e10eef5b80b83ac25d1bf8c61 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 14:40:41 -0400 Subject: [PATCH 05/20] Remove direct bitcoin calls from qt/utilitydialog.cpp --- src/ipc/interfaces.h | 4 ++++ src/ipc/local/interfaces.cpp | 1 + src/qt/bitcoin.cpp | 2 +- src/qt/bitcoingui.cpp | 4 ++-- src/qt/utilitydialog.cpp | 5 +++-- src/qt/utilitydialog.h | 6 +++++- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index a623969d7dc..106fadc675d 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -1,6 +1,7 @@ #ifndef BITCOIN_IPC_INTERFACES_H #define BITCOIN_IPC_INTERFACES_H +#include "init.h" // For HelpMessageMode #include "netaddress.h" // For Network #include @@ -57,6 +58,9 @@ class Node //! Return whether shutdown was requested. virtual bool shutdownRequested() = 0; + //! Get help message string. + virtual std::string helpMessage(HelpMessageMode mode) = 0; + //! Map port. virtual void mapPort(bool use_upnp) = 0; diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index dc2828b01eb..199d28fab42 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -52,6 +52,7 @@ class NodeImpl : public Node } void startShutdown() override { ::StartShutdown(); } bool shutdownRequested() override { return ::ShutdownRequested(); } + std::string helpMessage(HelpMessageMode mode) override { return ::HelpMessage(mode); } void mapPort(bool use_upnp) override { ::MapPort(use_upnp); } bool getProxy(Network net, proxyType& proxy_info) override { return ::GetProxy(net, proxy_info); } std::unique_ptr handleInitMessage(InitMessageFn fn) override diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 4b20b27eb84..3c3faf6527d 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -586,7 +586,7 @@ int main(int argc, char *argv[]) // but before showing splash screen. if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { - HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version")); + HelpMessageDialog help(*ipc_node, nullptr, gArgs.IsArgSet("-version")); help.showOrPrint(); return EXIT_SUCCESS; } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 239eec197f0..80434cff592 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -153,7 +153,7 @@ BitcoinGUI::BitcoinGUI(ipc::Node& ipc_node, const PlatformStyle *_platformStyle, #endif rpcConsole = new RPCConsole(_platformStyle, 0); - helpMessageDialog = new HelpMessageDialog(this, false); + helpMessageDialog = new HelpMessageDialog(ipc_node, this, false); #ifdef ENABLE_WALLET if(enableWallet) { @@ -646,7 +646,7 @@ void BitcoinGUI::aboutClicked() if(!clientModel) return; - HelpMessageDialog dlg(this, true); + HelpMessageDialog dlg(m_ipc_node, this, true); dlg.exec(); } diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 5d8c23d13c1..a00a271fcc1 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -19,6 +19,7 @@ #include "clientversion.h" #include "init.h" +#include "ipc/interfaces.h" #include "util.h" #include @@ -31,7 +32,7 @@ #include /** "Help message" or "About" dialog box */ -HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : +HelpMessageDialog::HelpMessageDialog(ipc::Node& ipc_node, QWidget *parent, bool about) : QDialog(parent), ui(new Ui::HelpMessageDialog) { @@ -77,7 +78,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, bool about) : cursor.insertText(header); cursor.insertBlock(); - std::string strUsage = HelpMessage(HMM_BITCOIN_QT); + std::string strUsage = ipc_node.helpMessage(HMM_BITCOIN_QT); const bool showDebug = gArgs.GetBoolArg("-help-debug", false); strUsage += HelpMessageGroup(tr("UI Options:").toStdString()); if (showDebug) { diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h index acaa864148c..5a4f1672472 100644 --- a/src/qt/utilitydialog.h +++ b/src/qt/utilitydialog.h @@ -10,6 +10,10 @@ class BitcoinGUI; +namespace ipc { + class Node; +} + namespace Ui { class HelpMessageDialog; } @@ -20,7 +24,7 @@ class HelpMessageDialog : public QDialog Q_OBJECT public: - explicit HelpMessageDialog(QWidget *parent, bool about); + explicit HelpMessageDialog(ipc::Node& ipc_node, QWidget *parent, bool about); ~HelpMessageDialog(); void printToConsole(); From a273cd0a7d1b08417ad41a079c028b326e724f75 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 15:10:47 -0400 Subject: [PATCH 06/20] Remove direct bitcoin calls from qt/splashscreen.cpp --- src/ipc/interfaces.h | 23 ++++++++++++++++++ src/ipc/local/interfaces.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++ src/qt/bitcoin.cpp | 5 ++-- src/qt/splashscreen.cpp | 52 ++++++++++++++-------------------------- src/qt/splashscreen.h | 26 ++++++++++++-------- 5 files changed, 116 insertions(+), 47 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 106fadc675d..841f0a39565 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -12,6 +12,7 @@ class proxyType; namespace ipc { class Handler; +class Wallet; //! Top-level interface for a bitcoin node (bitcoind process). class Node @@ -58,6 +59,9 @@ class Node //! Return whether shutdown was requested. virtual bool shutdownRequested() = 0; + //! Interrupt initialization. + virtual bool interruptInit() = 0; + //! Get help message string. virtual std::string helpMessage(HelpMessageMode mode) = 0; @@ -82,6 +86,25 @@ class Node const std::string& caption, unsigned int style)>; virtual std::unique_ptr handleQuestion(QuestionFn fn) = 0; + + //! Register handler for progress messages. + using ShowProgressFn = std::function; + virtual std::unique_ptr handleShowProgress(ShowProgressFn fn) = 0; + + //! Register handler for load wallet messages. + using LoadWalletFn = std::function wallet)>; + virtual std::unique_ptr handleLoadWallet(LoadWalletFn fn) = 0; +}; + +//! Interface for accessing a wallet. +class Wallet +{ +public: + virtual ~Wallet() {} + + //! Register handler for show progress messages. + using ShowProgressFn = std::function; + virtual std::unique_ptr handleShowProgress(ShowProgressFn fn) = 0; }; //! Interface for managing a registered handler. diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 199d28fab42..8ed087d269c 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -11,12 +11,25 @@ #include #include +#if defined(HAVE_CONFIG_H) +#include "config/bitcoin-config.h" +#endif +#ifdef ENABLE_WALLET +#include "wallet/wallet.h" +#endif + #include namespace ipc { namespace local { namespace { +#ifdef ENABLE_WALLET +#define CHECK_WALLET(x) x +#else +#define CHECK_WALLET(x) throw std::logic_error("Wallet function called in non-wallet build.") +#endif + class HandlerImpl : public Handler { public: @@ -27,6 +40,21 @@ class HandlerImpl : public Handler boost::signals2::scoped_connection m_connection; }; +#ifdef ENABLE_WALLET +class WalletImpl : public Wallet +{ +public: + WalletImpl(CWallet& wallet) : m_wallet(wallet) {} + + std::unique_ptr handleShowProgress(ShowProgressFn fn) override + { + return MakeUnique(m_wallet.ShowProgress.connect(fn)); + } + + CWallet& m_wallet; +}; +#endif + class NodeImpl : public Node { public: @@ -52,6 +80,19 @@ class NodeImpl : public Node } void startShutdown() override { ::StartShutdown(); } bool shutdownRequested() override { return ::ShutdownRequested(); } + bool interruptInit() override + { + std::function action; + { + LOCK(m_break_action_lock); + action = m_break_action; + } + if (action) { + action(); + return true; + } + return false; + } std::string helpMessage(HelpMessageMode mode) override { return ::HelpMessage(mode); } void mapPort(bool use_upnp) override { ::MapPort(use_upnp); } bool getProxy(Network net, proxyType& proxy_info) override { return ::GetProxy(net, proxy_info); } @@ -67,9 +108,25 @@ class NodeImpl : public Node { return MakeUnique(::uiInterface.ThreadSafeQuestion.connect(fn)); } + std::unique_ptr handleShowProgress(ShowProgressFn fn) override + { + return MakeUnique(::uiInterface.ShowProgress.connect(fn)); + } + std::unique_ptr handleLoadWallet(LoadWalletFn fn) override + { + CHECK_WALLET(return MakeUnique( + ::uiInterface.LoadWallet.connect([fn](CWallet* wallet) { fn(MakeUnique(*wallet)); }))); + } boost::thread_group m_thread_group; ::CScheduler m_scheduler; + std::function m_break_action; + CCriticalSection m_break_action_lock; + boost::signals2::scoped_connection m_break_action_connection{ + ::uiInterface.SetProgressBreakAction.connect([this](std::function action) { + LOCK(m_break_action_lock); + m_break_action = std::move(action); + })}; }; } // namespace diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 3c3faf6527d..ed45354dcaf 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -372,7 +372,7 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { - SplashScreen *splash = new SplashScreen(0, networkStyle); + SplashScreen *splash = new SplashScreen(m_ipc_node, 0, networkStyle); // We don't hold a direct pointer to the splash screen after creation, but the splash // screen will take care of deleting itself when slotFinish happens. splash->show(); @@ -566,7 +566,6 @@ int main(int argc, char *argv[]) // Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType< CAmount >("CAmount"); - qRegisterMetaType< std::function >("std::function"); /// 3. Application identification // must be set before OptionsModel is initialized or translations are loaded, @@ -672,7 +671,7 @@ int main(int argc, char *argv[]) app.createOptionsModel(gArgs.IsArgSet("-resetguisettings")); // Subscribe to global signals from core - std::unique_ptr handlerInitMessage = ipc_node->handleInitMessage(InitMessage); + std::unique_ptr m_handler_init_message = ipc_node->handleInitMessage(InitMessage); if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 1b7cc69231c..44b609b6354 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -12,22 +12,19 @@ #include "clientversion.h" #include "init.h" +#include "ipc/interfaces.h" #include "util.h" #include "ui_interface.h" #include "version.h" -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#endif - #include #include #include #include #include -SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) : - QWidget(0, f), curAlignment(0) +SplashScreen::SplashScreen(ipc::Node& ipc_node, Qt::WindowFlags f, const NetworkStyle *networkStyle) : + QWidget(0, f), curAlignment(0), m_ipc_node(ipc_node) { // set reference point, paddings int paddingRight = 50; @@ -142,8 +139,8 @@ SplashScreen::~SplashScreen() bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) { if (ev->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(ev); - if(keyEvent->text()[0] == 'q' && breakAction != nullptr) { - breakAction(); + if(keyEvent->text()[0] == 'q') { + m_ipc_node.interruptInit(); } } return QObject::eventFilter(obj, ev); @@ -175,47 +172,34 @@ static void ShowProgress(SplashScreen *splash, const std::string &title, int nPr InitMessage(splash, title + strprintf("%d", nProgress) + "%"); } -void SplashScreen::setBreakAction(const std::function &action) -{ - breakAction = action; -} - -static void SetProgressBreakAction(SplashScreen *splash, const std::function &action) -{ - QMetaObject::invokeMethod(splash, "setBreakAction", - Qt::QueuedConnection, - Q_ARG(std::function, action)); -} - #ifdef ENABLE_WALLET -void SplashScreen::ConnectWallet(CWallet* wallet) +void SplashScreen::ConnectWallet(std::unique_ptr wallet) { - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - connectedWallets.push_back(wallet); + m_connected_wallet_handlers.emplace_back(wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2))); + m_connected_wallets.emplace_back(std::move(wallet)); } #endif void SplashScreen::subscribeToCoreSignals() { // Connect signals to client - uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - uiInterface.SetProgressBreakAction.connect(boost::bind(SetProgressBreakAction, this, _1)); + m_handler_init_message = m_ipc_node.handleInitMessage(boost::bind(InitMessage, this, _1)); + m_handler_show_progress = m_ipc_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); #ifdef ENABLE_WALLET - uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1)); + m_handler_load_wallet = m_ipc_node.handleLoadWallet([this](std::unique_ptr wallet) { ConnectWallet(std::move(wallet)); }); #endif } void SplashScreen::unsubscribeFromCoreSignals() { // Disconnect signals from client - uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); -#ifdef ENABLE_WALLET - for (CWallet* const & pwallet : connectedWallets) { - pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + m_handler_init_message->disconnect(); + m_handler_show_progress->disconnect(); + for (auto& handler : m_connected_wallet_handlers) { + handler->disconnect(); } -#endif + m_connected_wallet_handlers.clear(); + m_connected_wallets.clear(); } void SplashScreen::showMessage(const QString &message, int alignment, const QColor &color) @@ -237,6 +221,6 @@ void SplashScreen::paintEvent(QPaintEvent *event) void SplashScreen::closeEvent(QCloseEvent *event) { - StartShutdown(); // allows an "emergency" shutdown during startup + m_ipc_node.startShutdown(); // allows an "emergency" shutdown during startup event->ignore(); } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index a88ebb98a87..4cc5c766fe8 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -5,12 +5,18 @@ #ifndef BITCOIN_QT_SPLASHSCREEN_H #define BITCOIN_QT_SPLASHSCREEN_H -#include #include -class CWallet; +#include + class NetworkStyle; +namespace ipc { +class Handler; +class Node; +class Wallet; +}; + /** Class for the splashscreen with information of the running client. * * @note this is intentionally not a QSplashScreen. Bitcoin Core initialization @@ -22,7 +28,7 @@ class SplashScreen : public QWidget Q_OBJECT public: - explicit SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle); + explicit SplashScreen(ipc::Node& ipc_node, Qt::WindowFlags f, const NetworkStyle *networkStyle); ~SplashScreen(); protected: @@ -35,9 +41,6 @@ public Q_SLOTS: /** Show message and progress */ void showMessage(const QString &message, int alignment, const QColor &color); - - /** Sets the break action */ - void setBreakAction(const std::function &action); protected: bool eventFilter(QObject * obj, QEvent * ev); @@ -47,16 +50,19 @@ public Q_SLOTS: /** Disconnect core signals to splash screen */ void unsubscribeFromCoreSignals(); /** Connect wallet signals to splash screen */ - void ConnectWallet(CWallet*); + void ConnectWallet(std::unique_ptr wallet); QPixmap pixmap; QString curMessage; QColor curColor; int curAlignment; - QList connectedWallets; - - std::function breakAction; + ipc::Node& m_ipc_node; + std::unique_ptr m_handler_init_message; + std::unique_ptr m_handler_show_progress; + std::unique_ptr m_handler_load_wallet; + std::list> m_connected_wallets; + std::list> m_connected_wallet_handlers; }; #endif // BITCOIN_QT_SPLASHSCREEN_H From d74039de11efa940269c997dbf7f7d9b0d547697 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 15:37:36 -0400 Subject: [PATCH 07/20] Remove direct bitcoin calls from qt/clientmodel.cpp --- src/ipc/interfaces.h | 69 ++++++++++++++++++++ src/ipc/local/interfaces.cpp | 83 ++++++++++++++++++++++++ src/qt/bitcoin.cpp | 2 +- src/qt/bitcoingui.cpp | 8 +-- src/qt/clientmodel.cpp | 147 +++++++++++------------------------------- src/qt/clientmodel.h | 36 +++++------ src/qt/rpcconsole.cpp | 8 ++- src/qt/trafficgraphwidget.cpp | 13 ++-- src/qt/walletview.cpp | 3 +- src/validation.cpp | 2 +- src/validation.h | 2 +- 11 files changed, 228 insertions(+), 145 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 841f0a39565..102f2b08c5d 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -2,6 +2,7 @@ #define BITCOIN_IPC_INTERFACES_H #include "init.h" // For HelpMessageMode +#include "net.h" // For CConnman::NumConnections #include "netaddress.h" // For Network #include @@ -71,6 +72,48 @@ class Node //! Get proxy. virtual bool getProxy(Network net, proxyType& proxy_info) = 0; + //! Get number of connections. + virtual size_t getNodeCount(CConnman::NumConnections flags) = 0; + + //! Get total bytes recv. + virtual int64_t getTotalBytesRecv() = 0; + + //! Get total bytes sent. + virtual int64_t getTotalBytesSent() = 0; + + //! Get mempool size. + virtual size_t getMempoolSize() = 0; + + //! Get mempool dynamic usage. + virtual size_t getMempoolDynamicUsage() = 0; + + //! Get header tip height and time. + virtual bool getHeaderTip(int& height, int64_t& block_time) = 0; + + //! Get num blocks. + virtual int getNumBlocks() = 0; + + //! Get last block time. + virtual int64_t getLastBlockTime() = 0; + + //! Get verification progress. + virtual double getVerificationProgress() = 0; + + //! Is initial block download. + virtual bool isInitialBlockDownload() = 0; + + //! Get reindex. + virtual bool getReindex() = 0; + + //! Get importing. + virtual bool getImporting() = 0; + + //! Set network active. + virtual void setNetworkActive(bool active) = 0; + + //! Get network active. + virtual bool getNetworkActive() = 0; + //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; @@ -94,6 +137,32 @@ class Node //! Register handler for load wallet messages. using LoadWalletFn = std::function wallet)>; virtual std::unique_ptr handleLoadWallet(LoadWalletFn fn) = 0; + + //! Register handler for number of connections changed messages. + using NotifyNumConnectionsChangedFn = std::function; + virtual std::unique_ptr handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) = 0; + + //! Register handler for network active messages. + using NotifyNetworkActiveChangedFn = std::function; + virtual std::unique_ptr handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) = 0; + + //! Register handler for notify alert messages. + using NotifyAlertChangedFn = std::function; + virtual std::unique_ptr handleNotifyAlertChanged(NotifyAlertChangedFn fn) = 0; + + //! Register handler for ban list messages. + using BannedListChangedFn = std::function; + virtual std::unique_ptr handleBannedListChanged(BannedListChangedFn fn) = 0; + + //! Register handler for block tip messages. + using NotifyBlockTipFn = + std::function; + virtual std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) = 0; + + //! Register handler for header tip messages. + using NotifyHeaderTipFn = + std::function; + virtual std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) = 0; }; //! Interface for accessing a wallet. diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 8ed087d269c..15b93236c64 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -96,6 +97,56 @@ class NodeImpl : public Node std::string helpMessage(HelpMessageMode mode) override { return ::HelpMessage(mode); } void mapPort(bool use_upnp) override { ::MapPort(use_upnp); } bool getProxy(Network net, proxyType& proxy_info) override { return ::GetProxy(net, proxy_info); } + size_t getNodeCount(CConnman::NumConnections flags) override + { + return ::g_connman ? ::g_connman->GetNodeCount(flags) : 0; + } + int64_t getTotalBytesRecv() override { return ::g_connman ? ::g_connman->GetTotalBytesRecv() : 0; } + int64_t getTotalBytesSent() override { return ::g_connman ? ::g_connman->GetTotalBytesSent() : 0; } + size_t getMempoolSize() override { return ::mempool.size(); } + size_t getMempoolDynamicUsage() override { return ::mempool.DynamicMemoryUsage(); } + bool getHeaderTip(int& height, int64_t& block_time) override + { + LOCK(::cs_main); + if (::pindexBestHeader) { + height = ::pindexBestHeader->nHeight; + block_time = ::pindexBestHeader->GetBlockTime(); + return true; + } + return false; + } + int getNumBlocks() override + { + LOCK(::cs_main); + return ::chainActive.Height(); + } + int64_t getLastBlockTime() override + { + LOCK(::cs_main); + if (::chainActive.Tip()) { + return ::chainActive.Tip()->GetBlockTime(); + } + return ::Params().GenesisBlock().GetBlockTime(); // Genesis block's time of current network + } + double getVerificationProgress() override + { + const CBlockIndex* tip; + { + LOCK(::cs_main); + tip = ::chainActive.Tip(); + } + return ::GuessVerificationProgress(::Params().TxData(), tip); + } + bool isInitialBlockDownload() override { return ::IsInitialBlockDownload(); } + bool getReindex() override { return ::fReindex; } + bool getImporting() override { return ::fImporting; } + void setNetworkActive(bool active) override + { + if (::g_connman) { + ::g_connman->SetNetworkActive(active); + } + } + bool getNetworkActive() override { return ::g_connman && ::g_connman->GetNetworkActive(); } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeUnique(::uiInterface.InitMessage.connect(fn)); @@ -117,6 +168,38 @@ class NodeImpl : public Node CHECK_WALLET(return MakeUnique( ::uiInterface.LoadWallet.connect([fn](CWallet* wallet) { fn(MakeUnique(*wallet)); }))); } + std::unique_ptr handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override + { + return MakeUnique(::uiInterface.NotifyNumConnectionsChanged.connect(fn)); + } + std::unique_ptr handleNotifyNetworkActiveChanged(NotifyNetworkActiveChangedFn fn) override + { + return MakeUnique(::uiInterface.NotifyNetworkActiveChanged.connect(fn)); + } + std::unique_ptr handleNotifyAlertChanged(NotifyAlertChangedFn fn) override + { + return MakeUnique(::uiInterface.NotifyAlertChanged.connect(fn)); + } + std::unique_ptr handleBannedListChanged(BannedListChangedFn fn) override + { + return MakeUnique(::uiInterface.BannedListChanged.connect(fn)); + } + std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) override + { + return MakeUnique( + ::uiInterface.NotifyBlockTip.connect([fn](bool initial_download, const CBlockIndex* block) { + fn(initial_download, block->nHeight, block->GetBlockTime(), + ::GuessVerificationProgress(::Params().TxData(), block)); + })); + } + std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) override + { + return MakeUnique( + ::uiInterface.NotifyHeaderTip.connect([fn](bool initial_download, const CBlockIndex* block) { + fn(initial_download, block->nHeight, block->GetBlockTime(), + ::GuessVerificationProgress(::Params().TxData(), block)); + })); + } boost::thread_group m_thread_group; ::CScheduler m_scheduler; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ed45354dcaf..2fbd4fa66d3 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -455,7 +455,7 @@ void BitcoinApplication::initializeResult(bool success) paymentServer->setOptionsModel(optionsModel); #endif - clientModel = new ClientModel(optionsModel); + clientModel = new ClientModel(m_ipc_node, optionsModel); window->setClientModel(clientModel); #ifdef ENABLE_WALLET diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 80434cff592..975ce84a1f0 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -481,7 +481,7 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) connect(_clientModel, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool))); modalOverlay->setKnownBestHeight(_clientModel->getHeaderTipHeight(), QDateTime::fromTime_t(_clientModel->getHeaderTipTime())); - setNumBlocks(_clientModel->getNumBlocks(), _clientModel->getLastBlockDate(), _clientModel->getVerificationProgress(nullptr), false); + setNumBlocks(m_ipc_node.getNumBlocks(), QDateTime::fromTime_t(m_ipc_node.getLastBlockTime()), m_ipc_node.getVerificationProgress(), false); connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); // Receive and report messages from client model @@ -729,7 +729,7 @@ void BitcoinGUI::updateNetworkState() QString tooltip; - if (clientModel->getNetworkActive()) { + if (m_ipc_node.getNetworkActive()) { tooltip = tr("%n active connection(s) to Bitcoin network", "", count) + QString(".
") + tr("Click to disable network activity."); } else { tooltip = tr("Network activity disabled.") + QString("
") + tr("Click to enable network activity again."); @@ -1190,9 +1190,7 @@ void BitcoinGUI::unsubscribeFromCoreSignals() void BitcoinGUI::toggleNetworkActive() { - if (clientModel) { - clientModel->setNetworkActive(!clientModel->getNetworkActive()); - } + m_ipc_node.setNetworkActive(!m_ipc_node.getNetworkActive()); } UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) : diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 52ce11cefd2..fae872aebf9 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -13,6 +13,7 @@ #include "chainparams.h" #include "checkpoints.h" #include "clientversion.h" +#include "ipc/interfaces.h" #include "validation.h" #include "net.h" #include "txmempool.h" @@ -30,8 +31,9 @@ class CBlockIndex; static int64_t nLastHeaderTipUpdateNotification = 0; static int64_t nLastBlockTipUpdateNotification = 0; -ClientModel::ClientModel(OptionsModel *_optionsModel, QObject *parent) : +ClientModel::ClientModel(ipc::Node& _ipcNode, OptionsModel *_optionsModel, QObject *parent) : QObject(parent), + m_ipc_node(_ipcNode), optionsModel(_optionsModel), peerTableModel(0), banTableModel(0), @@ -64,15 +66,7 @@ int ClientModel::getNumConnections(unsigned int flags) const else if (flags == CONNECTIONS_ALL) connections = CConnman::CONNECTIONS_ALL; - if(g_connman) - return g_connman->GetNodeCount(connections); - return 0; -} - -int ClientModel::getNumBlocks() const -{ - LOCK(cs_main); - return chainActive.Height(); + return m_ipc_node.getNodeCount(connections); } int ClientModel::getHeaderTipHeight() const @@ -80,10 +74,11 @@ int ClientModel::getHeaderTipHeight() const if (cachedBestHeaderHeight == -1) { // make sure we initially populate the cache via a cs_main lock // otherwise we need to wait for a tip update - LOCK(cs_main); - if (pindexBestHeader) { - cachedBestHeaderHeight = pindexBestHeader->nHeight; - cachedBestHeaderTime = pindexBestHeader->GetBlockTime(); + int height; + int64_t blockTime; + if (m_ipc_node.getHeaderTip(height, blockTime)) { + cachedBestHeaderHeight = height; + cachedBestHeaderTime = blockTime; } } return cachedBestHeaderHeight; @@ -92,66 +87,22 @@ int ClientModel::getHeaderTipHeight() const int64_t ClientModel::getHeaderTipTime() const { if (cachedBestHeaderTime == -1) { - LOCK(cs_main); - if (pindexBestHeader) { - cachedBestHeaderHeight = pindexBestHeader->nHeight; - cachedBestHeaderTime = pindexBestHeader->GetBlockTime(); + int height; + int64_t blockTime; + if (m_ipc_node.getHeaderTip(height, blockTime)) { + cachedBestHeaderHeight = height; + cachedBestHeaderTime = blockTime; } } return cachedBestHeaderTime; } -quint64 ClientModel::getTotalBytesRecv() const -{ - if(!g_connman) - return 0; - return g_connman->GetTotalBytesRecv(); -} - -quint64 ClientModel::getTotalBytesSent() const -{ - if(!g_connman) - return 0; - return g_connman->GetTotalBytesSent(); -} - -QDateTime ClientModel::getLastBlockDate() const -{ - LOCK(cs_main); - - if (chainActive.Tip()) - return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime()); - - return QDateTime::fromTime_t(Params().GenesisBlock().GetBlockTime()); // Genesis block's time of current network -} - -long ClientModel::getMempoolSize() const -{ - return mempool.size(); -} - -size_t ClientModel::getMempoolDynamicUsage() const -{ - return mempool.DynamicMemoryUsage(); -} - -double ClientModel::getVerificationProgress(const CBlockIndex *tipIn) const -{ - CBlockIndex *tip = const_cast(tipIn); - if (!tip) - { - LOCK(cs_main); - tip = chainActive.Tip(); - } - return GuessVerificationProgress(Params().TxData(), tip); -} - void ClientModel::updateTimer() { // no locking required at this point // the following calls will acquire the required lock - Q_EMIT mempoolSizeChanged(getMempoolSize(), getMempoolDynamicUsage()); - Q_EMIT bytesChanged(getTotalBytesRecv(), getTotalBytesSent()); + Q_EMIT mempoolSizeChanged(m_ipc_node.getMempoolSize(), m_ipc_node.getMempoolDynamicUsage()); + Q_EMIT bytesChanged(m_ipc_node.getTotalBytesRecv(), m_ipc_node.getTotalBytesSent()); } void ClientModel::updateNumConnections(int numConnections) @@ -169,16 +120,11 @@ void ClientModel::updateAlert() Q_EMIT alertsChanged(getStatusBarWarnings()); } -bool ClientModel::inInitialBlockDownload() const -{ - return IsInitialBlockDownload(); -} - enum BlockSource ClientModel::getBlockSource() const { - if (fReindex) + if (m_ipc_node.getReindex()) return BLOCK_SOURCE_REINDEX; - else if (fImporting) + else if (m_ipc_node.getImporting()) return BLOCK_SOURCE_DISK; else if (getNumConnections() > 0) return BLOCK_SOURCE_NETWORK; @@ -186,24 +132,9 @@ enum BlockSource ClientModel::getBlockSource() const return BLOCK_SOURCE_NONE; } -void ClientModel::setNetworkActive(bool active) -{ - if (g_connman) { - g_connman->SetNetworkActive(active); - } -} - -bool ClientModel::getNetworkActive() const -{ - if (g_connman) { - return g_connman->GetNetworkActive(); - } - return false; -} - QString ClientModel::getStatusBarWarnings() const { - return QString::fromStdString(GetWarnings("gui")); + return QString::fromStdString(m_ipc_node.getWarnings("gui")); } OptionsModel *ClientModel::getOptionsModel() @@ -285,7 +216,7 @@ static void BannedListChanged(ClientModel *clientmodel) QMetaObject::invokeMethod(clientmodel, "updateBanlist", Qt::QueuedConnection); } -static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CBlockIndex *pIndex, bool fHeader) +static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, int height, int64_t blockTime, double verificationProgress, bool fHeader) { // lock free async UI updates in case we have a new block tip // during initial sync, only update the UI if the last update @@ -298,16 +229,16 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB if (fHeader) { // cache best headers time and height to reduce future cs_main locks - clientmodel->cachedBestHeaderHeight = pIndex->nHeight; - clientmodel->cachedBestHeaderTime = pIndex->GetBlockTime(); + clientmodel->cachedBestHeaderHeight = height; + clientmodel->cachedBestHeaderTime = blockTime; } // if we are in-sync, update the UI regardless of last update time if (!initialSync || now - nLastUpdateNotification > MODEL_UPDATE_DELAY) { //pass an async signal to the UI thread QMetaObject::invokeMethod(clientmodel, "numBlocksChanged", Qt::QueuedConnection, - Q_ARG(int, pIndex->nHeight), - Q_ARG(QDateTime, QDateTime::fromTime_t(pIndex->GetBlockTime())), - Q_ARG(double, clientmodel->getVerificationProgress(pIndex)), + Q_ARG(int, height), + Q_ARG(QDateTime, QDateTime::fromTime_t(blockTime)), + Q_ARG(double, verificationProgress), Q_ARG(bool, fHeader)); nLastUpdateNotification = now; } @@ -316,23 +247,23 @@ static void BlockTipChanged(ClientModel *clientmodel, bool initialSync, const CB void ClientModel::subscribeToCoreSignals() { // Connect signals to client - uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - uiInterface.NotifyNumConnectionsChanged.connect(boost::bind(NotifyNumConnectionsChanged, this, _1)); - uiInterface.NotifyNetworkActiveChanged.connect(boost::bind(NotifyNetworkActiveChanged, this, _1)); - uiInterface.NotifyAlertChanged.connect(boost::bind(NotifyAlertChanged, this)); - uiInterface.BannedListChanged.connect(boost::bind(BannedListChanged, this)); - uiInterface.NotifyBlockTip.connect(boost::bind(BlockTipChanged, this, _1, _2, false)); - uiInterface.NotifyHeaderTip.connect(boost::bind(BlockTipChanged, this, _1, _2, true)); + m_handler_show_progress = m_ipc_node.handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); + m_handler_notify_num_connections_changed = m_ipc_node.handleNotifyNumConnectionsChanged(boost::bind(NotifyNumConnectionsChanged, this, _1)); + m_handler_notify_network_active_changed = m_ipc_node.handleNotifyNetworkActiveChanged(boost::bind(NotifyNetworkActiveChanged, this, _1)); + m_handler_notify_alert_changed = m_ipc_node.handleNotifyAlertChanged(boost::bind(NotifyAlertChanged, this)); + m_handler_banned_list_changed = m_ipc_node.handleBannedListChanged(boost::bind(BannedListChanged, this)); + m_handler_notify_block_tip = m_ipc_node.handleNotifyBlockTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, false)); + m_handler_notify_header_tip = m_ipc_node.handleNotifyHeaderTip(boost::bind(BlockTipChanged, this, _1, _2, _3, _4, true)); } void ClientModel::unsubscribeFromCoreSignals() { // Disconnect signals from client - uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); - uiInterface.NotifyNumConnectionsChanged.disconnect(boost::bind(NotifyNumConnectionsChanged, this, _1)); - uiInterface.NotifyNetworkActiveChanged.disconnect(boost::bind(NotifyNetworkActiveChanged, this, _1)); - uiInterface.NotifyAlertChanged.disconnect(boost::bind(NotifyAlertChanged, this)); - uiInterface.BannedListChanged.disconnect(boost::bind(BannedListChanged, this)); - uiInterface.NotifyBlockTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, false)); - uiInterface.NotifyHeaderTip.disconnect(boost::bind(BlockTipChanged, this, _1, _2, true)); + m_handler_show_progress->disconnect(); + m_handler_notify_num_connections_changed->disconnect(); + m_handler_notify_network_active_changed->disconnect(); + m_handler_notify_alert_changed->disconnect(); + m_handler_banned_list_changed->disconnect(); + m_handler_notify_block_tip->disconnect(); + m_handler_notify_header_tip->disconnect(); } diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index 6447cae1bb4..06794a7a345 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -9,6 +9,7 @@ #include #include +#include class BanTableModel; class OptionsModel; @@ -16,6 +17,11 @@ class PeerTableModel; class CBlockIndex; +namespace ipc { +class Handler; +class Node; +} + QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE @@ -40,37 +46,21 @@ class ClientModel : public QObject Q_OBJECT public: - explicit ClientModel(OptionsModel *optionsModel, QObject *parent = 0); + explicit ClientModel(ipc::Node& ipc_node, OptionsModel *optionsModel, QObject *parent = 0); ~ClientModel(); + ipc::Node& getIpcNode() const { return m_ipc_node; } OptionsModel *getOptionsModel(); PeerTableModel *getPeerTableModel(); BanTableModel *getBanTableModel(); //! Return number of connections, default is in- and outbound (total) int getNumConnections(unsigned int flags = CONNECTIONS_ALL) const; - int getNumBlocks() const; int getHeaderTipHeight() const; int64_t getHeaderTipTime() const; - //! Return number of transactions in the mempool - long getMempoolSize() const; - //! Return the dynamic memory usage of the mempool - size_t getMempoolDynamicUsage() const; - - quint64 getTotalBytesRecv() const; - quint64 getTotalBytesSent() const; - - double getVerificationProgress(const CBlockIndex *tip) const; - QDateTime getLastBlockDate() const; - - //! Return true if core is doing initial block download - bool inInitialBlockDownload() const; + //! Returns enum BlockSource of the current importing/syncing state enum BlockSource getBlockSource() const; - //! Return true if network activity in core is enabled - bool getNetworkActive() const; - //! Toggle network activity state in core - void setNetworkActive(bool active); //! Return warnings to be displayed in status bar QString getStatusBarWarnings() const; @@ -85,6 +75,14 @@ class ClientModel : public QObject mutable std::atomic cachedBestHeaderTime; private: + ipc::Node& m_ipc_node; + std::unique_ptr m_handler_show_progress; + std::unique_ptr m_handler_notify_num_connections_changed; + std::unique_ptr m_handler_notify_network_active_changed; + std::unique_ptr m_handler_notify_alert_changed; + std::unique_ptr m_handler_banned_list_changed; + std::unique_ptr m_handler_notify_block_tip; + std::unique_ptr m_handler_notify_header_tip; OptionsModel *optionsModel; PeerTableModel *peerTableModel; BanTableModel *banTableModel; diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 3590a98efac..68d9d970fa1 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -14,6 +14,7 @@ #include "guiutil.h" #include "platformstyle.h" #include "chainparams.h" +#include "ipc/interfaces.h" #include "netbase.h" #include "rpc/server.h" #include "rpc/client.h" @@ -532,13 +533,14 @@ void RPCConsole::setClientModel(ClientModel *model) setNumConnections(model->getNumConnections()); connect(model, SIGNAL(numConnectionsChanged(int)), this, SLOT(setNumConnections(int))); - setNumBlocks(model->getNumBlocks(), model->getLastBlockDate(), model->getVerificationProgress(nullptr), false); + ipc::Node& ipc_node = clientModel->getIpcNode(); + setNumBlocks(ipc_node.getNumBlocks(), QDateTime::fromTime_t(ipc_node.getLastBlockTime()), ipc_node.getVerificationProgress(), false); connect(model, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(setNumBlocks(int,QDateTime,double,bool))); updateNetworkState(); connect(model, SIGNAL(networkActiveChanged(bool)), this, SLOT(setNetworkActive(bool))); - updateTrafficStats(model->getTotalBytesRecv(), model->getTotalBytesSent()); + updateTrafficStats(ipc_node.getTotalBytesRecv(), ipc_node.getTotalBytesSent()); connect(model, SIGNAL(bytesChanged(quint64,quint64)), this, SLOT(updateTrafficStats(quint64, quint64))); connect(model, SIGNAL(mempoolSizeChanged(long,size_t)), this, SLOT(setMempoolSize(long,size_t))); @@ -782,7 +784,7 @@ void RPCConsole::updateNetworkState() connections += tr("In:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_IN)) + " / "; connections += tr("Out:") + " " + QString::number(clientModel->getNumConnections(CONNECTIONS_OUT)) + ")"; - if(!clientModel->getNetworkActive()) { + if(!clientModel->getIpcNode().getNetworkActive()) { connections += " (" + tr("Network activity disabled") + ")"; } diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index 5bb863451fd..580b26f7458 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -4,6 +4,7 @@ #include "trafficgraphwidget.h" #include "clientmodel.h" +#include "ipc/interfaces.h" #include #include @@ -35,8 +36,8 @@ void TrafficGraphWidget::setClientModel(ClientModel *model) { clientModel = model; if(model) { - nLastBytesIn = model->getTotalBytesRecv(); - nLastBytesOut = model->getTotalBytesSent(); + nLastBytesIn = model->getIpcNode().getTotalBytesRecv(); + nLastBytesOut = model->getIpcNode().getTotalBytesSent(); } } @@ -123,8 +124,8 @@ void TrafficGraphWidget::updateRates() { if(!clientModel) return; - quint64 bytesIn = clientModel->getTotalBytesRecv(), - bytesOut = clientModel->getTotalBytesSent(); + quint64 bytesIn = clientModel->getIpcNode().getTotalBytesRecv(), + bytesOut = clientModel->getIpcNode().getTotalBytesSent(); float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval(); float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval(); vSamplesIn.push_front(inRate); @@ -169,8 +170,8 @@ void TrafficGraphWidget::clear() fMax = 0.0f; if(clientModel) { - nLastBytesIn = clientModel->getTotalBytesRecv(); - nLastBytesOut = clientModel->getTotalBytesSent(); + nLastBytesIn = clientModel->getIpcNode().getTotalBytesRecv(); + nLastBytesOut = clientModel->getIpcNode().getTotalBytesSent(); } timer->start(); } diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 971f5e0e1a7..24c827fea6c 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -19,6 +19,7 @@ #include "transactionview.h" #include "walletmodel.h" +#include "ipc/interfaces.h" #include "ui_interface.h" #include @@ -152,7 +153,7 @@ void WalletView::setWalletModel(WalletModel *_walletModel) void WalletView::processNewTransaction(const QModelIndex& parent, int start, int /*end*/) { // Prevent balloon-spam when initial block download is in progress - if (!walletModel || !clientModel || clientModel->inInitialBlockDownload()) + if (!walletModel || !clientModel || clientModel->getIpcNode().isInitialBlockDownload()) return; TransactionTableModel *ttm = walletModel->getTransactionTableModel(); diff --git a/src/validation.cpp b/src/validation.cpp index 8bd23a0f1d7..af2ccaaeaac 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4374,7 +4374,7 @@ void DumpMempool(void) } //! Guess how far we are in the verification process at the given block index -double GuessVerificationProgress(const ChainTxData& data, CBlockIndex *pindex) { +double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pindex) { if (pindex == nullptr) return 0.0; diff --git a/src/validation.h b/src/validation.h index edb8eab894b..e43eaaf84a6 100644 --- a/src/validation.h +++ b/src/validation.h @@ -276,7 +276,7 @@ bool ActivateBestChain(CValidationState& state, const CChainParams& chainparams, CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ -double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex); +double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex); /** * Mark one block file as pruned. From 33b64684c9bd84639b51dfc52f6e80d6be4ff194 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 15:44:10 -0400 Subject: [PATCH 08/20] Remove direct bitcoin calls from qt/intro.cpp --- src/qt/bitcoin.cpp | 2 +- src/qt/intro.cpp | 8 +++++--- src/qt/intro.h | 6 +++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 2fbd4fa66d3..54b85031a83 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -592,7 +592,7 @@ int main(int argc, char *argv[]) /// 5. Now that settings and translations are available, ask user for data directory // User language is set up: pick a data directory - if (!Intro::pickDataDirectory()) + if (!Intro::pickDataDirectory(*ipc_node)) return EXIT_SUCCESS; /// 6. Determine availability of data directory and parse bitcoin.conf diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index 72809942f13..a891401475a 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -12,6 +12,7 @@ #include "guiutil.h" +#include "ipc/interfaces.h" #include "util.h" #include @@ -186,7 +187,7 @@ QString Intro::getDefaultDataDirectory() return GUIUtil::boostPathToQString(GetDefaultDataDir()); } -bool Intro::pickDataDirectory() +bool Intro::pickDataDirectory(ipc::Node& ipc_node) { QSettings settings; /* If data directory provided on command line, no need to look at settings @@ -230,8 +231,9 @@ bool Intro::pickDataDirectory() * override -datadir in the bitcoin.conf file in the default data directory * (to be consistent with bitcoind behavior) */ - if(dataDir != getDefaultDataDirectory()) - gArgs.SoftSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting + if(dataDir != getDefaultDataDirectory()) { + ipc_node.softSetArg("-datadir", GUIUtil::qstringToBoostPath(dataDir).string()); // use OS locale for path setting + } return true; } diff --git a/src/qt/intro.h b/src/qt/intro.h index 5b428b379c4..a43f67eaef1 100644 --- a/src/qt/intro.h +++ b/src/qt/intro.h @@ -13,6 +13,10 @@ static const bool DEFAULT_CHOOSE_DATADIR = false; class FreespaceChecker; +namespace ipc { + class Node; +} + namespace Ui { class Intro; } @@ -41,7 +45,7 @@ class Intro : public QDialog * @note do NOT call global GetDataDir() before calling this function, this * will cause the wrong path to be cached. */ - static bool pickDataDirectory(); + static bool pickDataDirectory(ipc::Node& ipc_node); /** * Determine default data directory for operating system. From 15a7acc309b228b24bd5d25dcfa437d16224ac83 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 15:57:19 -0400 Subject: [PATCH 09/20] Remove direct bitcoin calls from qt/peertablemodel.cpp --- src/ipc/interfaces.h | 8 ++++++++ src/ipc/local/interfaces.cpp | 26 ++++++++++++++++++++++++++ src/net_processing.h | 6 +++--- src/qt/clientmodel.cpp | 2 +- src/qt/peertablemodel.cpp | 36 +++++++++++++----------------------- src/qt/peertablemodel.h | 7 ++++++- 6 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 102f2b08c5d..1e82bdc6890 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -7,8 +7,12 @@ #include #include +#include +#include +class CNodeStats; class proxyType; +struct CNodeStateStats; namespace ipc { @@ -75,6 +79,10 @@ class Node //! Get number of connections. virtual size_t getNodeCount(CConnman::NumConnections flags) = 0; + //! Get stats for connected nodes. + using NodesStats = std::vector>; + virtual bool getNodesStats(NodesStats& stats) = 0; + //! Get total bytes recv. virtual int64_t getTotalBytesRecv() = 0; diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 15b93236c64..b9f838dd69c 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -101,6 +102,31 @@ class NodeImpl : public Node { return ::g_connman ? ::g_connman->GetNodeCount(flags) : 0; } + bool getNodesStats(NodesStats& stats) override + { + stats.clear(); + + if (::g_connman) { + std::vector stats_temp; + ::g_connman->GetNodeStats(stats_temp); + + stats.reserve(stats_temp.size()); + for (auto& node_stats_temp : stats_temp) { + stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); + } + + // Try to retrieve the CNodeStateStats for each node. + TRY_LOCK(::cs_main, lockMain); + if (lockMain) { + for (auto& node_stats : stats) { + std::get<1>(node_stats) = + GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); + } + } + return true; + } + return false; + } int64_t getTotalBytesRecv() override { return ::g_connman ? ::g_connman->GetTotalBytesRecv() : 0; } int64_t getTotalBytesSent() override { return ::g_connman ? ::g_connman->GetTotalBytesSent() : 0; } size_t getMempoolSize() override { return ::mempool.size(); } diff --git a/src/net_processing.h b/src/net_processing.h index db6d81e6b67..eb82aef6189 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -41,9 +41,9 @@ class PeerLogicValidation : public CValidationInterface { }; struct CNodeStateStats { - int nMisbehavior; - int nSyncHeight; - int nCommonHeight; + int nMisbehavior = 0; + int nSyncHeight = -1; + int nCommonHeight = -1; std::vector vHeightInFlight; }; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index fae872aebf9..4e427760789 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -41,7 +41,7 @@ ClientModel::ClientModel(ipc::Node& _ipcNode, OptionsModel *_optionsModel, QObje { cachedBestHeaderHeight = -1; cachedBestHeaderTime = -1; - peerTableModel = new PeerTableModel(this); + peerTableModel = new PeerTableModel(m_ipc_node, this); banTableModel = new BanTableModel(this); pollTimer = new QTimer(this); connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 42934f8055d..6df6c948e9e 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -8,6 +8,7 @@ #include "guiconstants.h" #include "guiutil.h" +#include "ipc/interfaces.h" #include "validation.h" // for cs_main #include "sync.h" @@ -52,38 +53,26 @@ class PeerTablePriv std::map mapNodeRows; /** Pull a full list of peers from vNodes into our cache */ - void refreshPeers() + void refreshPeers(ipc::Node& ipc_node) { { cachedNodeStats.clear(); - std::vector vstats; - if(g_connman) - g_connman->GetNodeStats(vstats); + + ipc::Node::NodesStats nodes; + ipc_node.getNodesStats(nodes); #if QT_VERSION >= 0x040700 - cachedNodeStats.reserve(vstats.size()); + cachedNodeStats.reserve(nodes.size()); #endif - for (const CNodeStats& nodestats : vstats) + for (auto& node : nodes) { CNodeCombinedStats stats; - stats.nodeStateStats.nMisbehavior = 0; - stats.nodeStateStats.nSyncHeight = -1; - stats.nodeStateStats.nCommonHeight = -1; - stats.fNodeStateStatsAvailable = false; - stats.nodeStats = nodestats; + stats.nodeStats = std::get<0>(node); + stats.fNodeStateStatsAvailable = std::get<1>(node); + stats.nodeStateStats = std::get<2>(node); cachedNodeStats.append(stats); } } - // Try to retrieve the CNodeStateStats for each node. - { - TRY_LOCK(cs_main, lockMain); - if (lockMain) - { - for (CNodeCombinedStats &stats : cachedNodeStats) - stats.fNodeStateStatsAvailable = GetNodeStateStats(stats.nodeStats.nodeid, stats.nodeStateStats); - } - } - if (sortColumn >= 0) // sort cacheNodeStats (use stable sort to prevent rows jumping around unnecessarily) qStableSort(cachedNodeStats.begin(), cachedNodeStats.end(), NodeLessThan(sortColumn, sortOrder)); @@ -109,8 +98,9 @@ class PeerTablePriv } }; -PeerTableModel::PeerTableModel(ClientModel *parent) : +PeerTableModel::PeerTableModel(ipc::Node& ipc_node, ClientModel *parent) : QAbstractTableModel(parent), + m_ipc_node(ipc_node), clientModel(parent), timer(0) { @@ -221,7 +211,7 @@ const CNodeCombinedStats *PeerTableModel::getNodeStats(int idx) void PeerTableModel::refresh() { Q_EMIT layoutAboutToBeChanged(); - priv->refreshPeers(); + priv->refreshPeers(m_ipc_node); Q_EMIT layoutChanged(); } diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index cc47b67ec9b..4a279de1a18 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -14,6 +14,10 @@ class ClientModel; class PeerTablePriv; +namespace ipc { +class Node; +} + QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE @@ -45,7 +49,7 @@ class PeerTableModel : public QAbstractTableModel Q_OBJECT public: - explicit PeerTableModel(ClientModel *parent = 0); + explicit PeerTableModel(ipc::Node& ipc_node, ClientModel *parent = 0); ~PeerTableModel(); const CNodeCombinedStats *getNodeStats(int idx); int getRowByNodeId(NodeId nodeid); @@ -74,6 +78,7 @@ public Q_SLOTS: void refresh(); private: + ipc::Node& m_ipc_node; ClientModel *clientModel; QStringList columns; std::unique_ptr priv; From 00776785bf30826146a9b91143e5deb1d5d7ad6f Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 16:02:44 -0400 Subject: [PATCH 10/20] Remove direct bitcoin calls from qt/bantablemodel.cpp --- src/ipc/interfaces.h | 4 ++++ src/ipc/local/interfaces.cpp | 8 ++++++++ src/qt/bantablemodel.cpp | 11 ++++++----- src/qt/bantablemodel.h | 7 ++++++- src/qt/clientmodel.cpp | 2 +- 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 1e82bdc6890..bd46b2d440f 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -1,6 +1,7 @@ #ifndef BITCOIN_IPC_INTERFACES_H #define BITCOIN_IPC_INTERFACES_H +#include "addrdb.h" // For banmap_t #include "init.h" // For HelpMessageMode #include "net.h" // For CConnman::NumConnections #include "netaddress.h" // For Network @@ -83,6 +84,9 @@ class Node using NodesStats = std::vector>; virtual bool getNodesStats(NodesStats& stats) = 0; + //! Get ban map entries. + virtual bool getBanned(banmap_t& banmap) = 0; + //! Get total bytes recv. virtual int64_t getTotalBytesRecv() = 0; diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index b9f838dd69c..06db83ca73c 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -127,6 +127,14 @@ class NodeImpl : public Node } return false; } + bool getBanned(banmap_t& banmap) + { + if (::g_connman) { + ::g_connman->GetBanned(banmap); + return true; + } + return false; + } int64_t getTotalBytesRecv() override { return ::g_connman ? ::g_connman->GetTotalBytesRecv() : 0; } int64_t getTotalBytesSent() override { return ::g_connman ? ::g_connman->GetTotalBytesSent() : 0; } size_t getMempoolSize() override { return ::mempool.size(); } diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index f8a99506c13..28fc9b2de37 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -8,6 +8,7 @@ #include "guiconstants.h" #include "guiutil.h" +#include "ipc/interfaces.h" #include "sync.h" #include "utiltime.h" @@ -45,11 +46,10 @@ class BanTablePriv Qt::SortOrder sortOrder; /** Pull a full list of banned nodes from CNode into our cache */ - void refreshBanlist() + void refreshBanlist(ipc::Node& ipc_node) { banmap_t banMap; - if(g_connman) - g_connman->GetBanned(banMap); + ipc_node.getBanned(banMap); cachedBanlist.clear(); #if QT_VERSION >= 0x040700 @@ -82,8 +82,9 @@ class BanTablePriv } }; -BanTableModel::BanTableModel(ClientModel *parent) : +BanTableModel::BanTableModel(ipc::Node& ipc_node, ClientModel *parent) : QAbstractTableModel(parent), + m_ipc_node(ipc_node), clientModel(parent) { columns << tr("IP/Netmask") << tr("Banned Until"); @@ -168,7 +169,7 @@ QModelIndex BanTableModel::index(int row, int column, const QModelIndex &parent) void BanTableModel::refresh() { Q_EMIT layoutAboutToBeChanged(); - priv->refreshBanlist(); + priv->refreshBanlist(m_ipc_node); Q_EMIT layoutChanged(); } diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h index 062cfdc9315..b443f3590e9 100644 --- a/src/qt/bantablemodel.h +++ b/src/qt/bantablemodel.h @@ -13,6 +13,10 @@ class ClientModel; class BanTablePriv; +namespace ipc { + class Node; +} + struct CCombinedBan { CSubNet subnet; CBanEntry banEntry; @@ -39,7 +43,7 @@ class BanTableModel : public QAbstractTableModel Q_OBJECT public: - explicit BanTableModel(ClientModel *parent = 0); + explicit BanTableModel(ipc::Node& ipc_node, ClientModel *parent = 0); ~BanTableModel(); void startAutoRefresh(); void stopAutoRefresh(); @@ -65,6 +69,7 @@ public Q_SLOTS: void refresh(); private: + ipc::Node& m_ipc_node; ClientModel *clientModel; QStringList columns; std::unique_ptr priv; diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 4e427760789..963c10bff89 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -42,7 +42,7 @@ ClientModel::ClientModel(ipc::Node& _ipcNode, OptionsModel *_optionsModel, QObje cachedBestHeaderHeight = -1; cachedBestHeaderTime = -1; peerTableModel = new PeerTableModel(m_ipc_node, this); - banTableModel = new BanTableModel(this); + banTableModel = new BanTableModel(m_ipc_node, this); pollTimer = new QTimer(this); connect(pollTimer, SIGNAL(timeout()), this, SLOT(updateTimer())); pollTimer->start(MODEL_UPDATE_DELAY); From 36f096a39edec628f2658dd20c0173dd8706ea3f Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 16:38:51 -0400 Subject: [PATCH 11/20] Remove direct bitcoin calls from qt/rpcconsole.cpp --- src/ipc/interfaces.h | 23 ++++++++++++ src/ipc/local/interfaces.cpp | 36 ++++++++++++++++++ src/qt/bitcoingui.cpp | 2 +- src/qt/rpcconsole.cpp | 44 +++++++++++----------- src/qt/rpcconsole.h | 13 +++++-- src/qt/test/rpcnestedtests.cpp | 84 +++++++++++++++++++++--------------------- 6 files changed, 135 insertions(+), 67 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index bd46b2d440f..0bdf2bd56a7 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -12,6 +12,8 @@ #include class CNodeStats; +class RPCTimerInterface; +class UniValue; class proxyType; struct CNodeStateStats; @@ -87,6 +89,15 @@ class Node //! Get ban map entries. virtual bool getBanned(banmap_t& banmap) = 0; + //! Ban node. + virtual bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) = 0; + + //! Unban node. + virtual bool unban(const CSubNet& ip) = 0; + + //! Disconnect node. + virtual bool disconnect(NodeId id) = 0; + //! Get total bytes recv. virtual int64_t getTotalBytesRecv() = 0; @@ -126,6 +137,18 @@ class Node //! Get network active. virtual bool getNetworkActive() = 0; + //! Execute rpc command. + virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; + + //! List rpc commands. + virtual std::vector listRpcCommands() = 0; + + //! Set RPC timer interface if unset. + virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) = 0; + + //! Unset RPC timer interface. + virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0; + //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 06db83ca73c..4777cbac536 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #endif #include +#include namespace ipc { namespace local { @@ -135,6 +137,29 @@ class NodeImpl : public Node } return false; } + bool ban(const CNetAddr& net_addr, BanReason reason, int64_t ban_time_offset) override + { + if (::g_connman) { + ::g_connman->Ban(net_addr, reason, ban_time_offset); + return true; + } + return false; + } + bool unban(const CSubNet& ip) override + { + if (::g_connman) { + ::g_connman->Unban(ip); + return true; + } + return false; + } + bool disconnect(NodeId id) override + { + if (::g_connman) { + return ::g_connman->DisconnectNode(id); + } + return false; + } int64_t getTotalBytesRecv() override { return ::g_connman ? ::g_connman->GetTotalBytesRecv() : 0; } int64_t getTotalBytesSent() override { return ::g_connman ? ::g_connman->GetTotalBytesSent() : 0; } size_t getMempoolSize() override { return ::mempool.size(); } @@ -181,6 +206,17 @@ class NodeImpl : public Node } } bool getNetworkActive() override { return ::g_connman && ::g_connman->GetNetworkActive(); } + UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override + { + JSONRPCRequest req; + req.params = params; + req.strMethod = command; + req.URI = uri; + return ::tableRPC.execute(req); + } + std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } + void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { ::RPCSetTimerInterfaceIfUnset(iface); } + void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { ::RPCUnsetTimerInterface(iface); } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeUnique(::uiInterface.InitMessage.connect(fn)); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 975ce84a1f0..e28a78a5238 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -152,7 +152,7 @@ BitcoinGUI::BitcoinGUI(ipc::Node& ipc_node, const PlatformStyle *_platformStyle, setUnifiedTitleAndToolBarOnMac(true); #endif - rpcConsole = new RPCConsole(_platformStyle, 0); + rpcConsole = new RPCConsole(ipc_node, _platformStyle, 0); helpMessageDialog = new HelpMessageDialog(ipc_node, this, false); #ifdef ENABLE_WALLET if(enableWallet) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 68d9d970fa1..abdff4ce5aa 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -16,6 +16,7 @@ #include "chainparams.h" #include "ipc/interfaces.h" #include "netbase.h" +#include "ipc/interfaces.h" #include "rpc/server.h" #include "rpc/client.h" #include "util.h" @@ -83,12 +84,17 @@ const QStringList historyFilter = QStringList() class RPCExecutor : public QObject { Q_OBJECT +public: + RPCExecutor(ipc::Node& ipc_node) : m_ipc_node(ipc_node) {} public Q_SLOTS: void request(const QString &command); Q_SIGNALS: void reply(int category, const QString &command); + +private: + ipc::Node& m_ipc_node; }; /** Class for handling RPC timers @@ -140,13 +146,14 @@ class QtRPCTimerInterface: public RPCTimerInterface * - Within double quotes, only escape \c " and backslashes before a \c " or another backslash * - Within single quotes, no escaping is possible and no special interpretation takes place * + * @param[in] ipc_node optional node to execute command on * @param[out] result stringified Result from the executed command(chain) * @param[in] strCommand Command line to split * @param[in] fExecute set true if you want the command to be executed * @param[out] pstrFilteredOut Command line, filtered to remove any sensitive data */ -bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut) +bool RPCConsole::RPCParseCommandLine(ipc::Node* ipc_node, std::string &strResult, const std::string &strCommand, const bool fExecute, std::string * const pstrFilteredOut) { std::vector< std::vector > stack; stack.push_back(std::vector()); @@ -300,18 +307,16 @@ bool RPCConsole::RPCParseCommandLine(std::string &strResult, const std::string & if (fExecute) { // Convert argument list to JSON objects in method-dependent way, // and pass it along with the method name to the dispatcher. - JSONRPCRequest req; - req.params = RPCConvertValues(stack.back()[0], std::vector(stack.back().begin() + 1, stack.back().end())); - req.strMethod = stack.back()[0]; + std::string uri; #ifdef ENABLE_WALLET // TODO: Move this logic to WalletModel if (!vpwallets.empty()) { // in Qt, use always the wallet with index 0 when running with multiple wallets QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(vpwallets[0]->GetName())); - req.URI = "/wallet/"+std::string(encodedName.constData(), encodedName.length()); + uri = "/wallet/"+std::string(encodedName.constData(), encodedName.length()); } #endif - lastResult = tableRPC.execute(req); + lastResult = ipc_node->executeRpc(stack.back()[0],RPCConvertValues(stack.back()[0], std::vector(stack.back().begin() + 1, stack.back().end())), uri); } state = STATE_COMMAND_EXECUTED; @@ -392,7 +397,7 @@ void RPCExecutor::request(const QString &command) { std::string result; std::string executableCommand = command.toStdString() + "\n"; - if(!RPCConsole::RPCExecuteCommandLine(result, executableCommand)) + if(!RPCConsole::RPCExecuteCommandLine(m_ipc_node, result, executableCommand)) { Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); return; @@ -418,8 +423,9 @@ void RPCExecutor::request(const QString &command) } } -RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : +RPCConsole::RPCConsole(ipc::Node& ipc_node, const PlatformStyle *_platformStyle, QWidget *parent) : QWidget(parent), + m_ipc_node(ipc_node), ui(new Ui::RPCConsole), clientModel(0), historyPtr(0), @@ -460,7 +466,7 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : rpcTimerInterface = new QtRPCTimerInterface(); // avoid accidentally overwriting an existing, non QTThread // based timer interface - RPCSetTimerInterfaceIfUnset(rpcTimerInterface); + m_ipc_node.rpcSetTimerInterfaceIfUnset(rpcTimerInterface); setTrafficGraphRange(INITIAL_TRAFFIC_GRAPH_MINS); @@ -475,7 +481,7 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : RPCConsole::~RPCConsole() { GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); - RPCUnsetTimerInterface(rpcTimerInterface); + m_ipc_node.rpcUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; } @@ -635,7 +641,7 @@ void RPCConsole::setClientModel(ClientModel *model) //Setup autocomplete and attach it QStringList wordList; - std::vector commandList = tableRPC.listCommands(); + std::vector commandList = m_ipc_node.listRpcCommands(); for (size_t i = 0; i < commandList.size(); ++i) { wordList << commandList[i].c_str(); @@ -831,7 +837,7 @@ void RPCConsole::on_lineEdit_returnPressed() std::string strFilteredCmd; try { std::string dummy; - if (!RPCParseCommandLine(dummy, cmd.toStdString(), false, &strFilteredCmd)) { + if (!RPCParseCommandLine(nullptr, dummy, cmd.toStdString(), false, &strFilteredCmd)) { // Failed to parse command, so we cannot even filter it for the history throw std::runtime_error("Invalid command line"); } @@ -887,7 +893,7 @@ void RPCConsole::browseHistory(int offset) void RPCConsole::startExecutor() { - RPCExecutor *executor = new RPCExecutor(); + RPCExecutor *executor = new RPCExecutor(m_ipc_node); executor->moveToThread(&thread); // Replies from executor object must go to this object @@ -1125,9 +1131,6 @@ void RPCConsole::showBanTableContextMenu(const QPoint& point) void RPCConsole::disconnectSelectedNode() { - if(!g_connman) - return; - // Get selected peer addresses QList nodes = GUIUtil::getEntryData(ui->peerWidget, PeerTableModel::NetNodeId); for(int i = 0; i < nodes.count(); i++) @@ -1135,14 +1138,14 @@ void RPCConsole::disconnectSelectedNode() // Get currently selected peer address NodeId id = nodes.at(i).data().toLongLong(); // Find the node, disconnect it and clear the selected node - if(g_connman->DisconnectNode(id)) + if(m_ipc_node.disconnect(id)) clearSelectedNode(); } } void RPCConsole::banSelectedNode(int bantime) { - if (!clientModel || !g_connman) + if (!clientModel) return; // Get selected peer addresses @@ -1160,7 +1163,7 @@ void RPCConsole::banSelectedNode(int bantime) // Find possible nodes, ban it and clear the selected node const CNodeCombinedStats *stats = clientModel->getPeerTableModel()->getNodeStats(detailNodeRow); if(stats) { - g_connman->Ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime); + m_ipc_node.ban(stats->nodeStats.addr, BanReasonManuallyAdded, bantime); } } clearSelectedNode(); @@ -1181,9 +1184,8 @@ void RPCConsole::unbanSelectedNode() CSubNet possibleSubnet; LookupSubNet(strNode.toStdString().c_str(), possibleSubnet); - if (possibleSubnet.IsValid() && g_connman) + if (possibleSubnet.IsValid() && m_ipc_node.unban(possibleSubnet)) { - g_connman->Unban(possibleSubnet); clientModel->getBanTableModel()->refresh(); } } diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index da06818f877..01d5f0ff6b2 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -18,6 +18,10 @@ class ClientModel; class PlatformStyle; class RPCTimerInterface; +namespace ipc { + class Node; +} + namespace Ui { class RPCConsole; } @@ -33,12 +37,12 @@ class RPCConsole: public QWidget Q_OBJECT public: - explicit RPCConsole(const PlatformStyle *platformStyle, QWidget *parent); + explicit RPCConsole(ipc::Node& ipc_node, const PlatformStyle *platformStyle, QWidget *parent); ~RPCConsole(); - static bool RPCParseCommandLine(std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr); - static bool RPCExecuteCommandLine(std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr) { - return RPCParseCommandLine(strResult, strCommand, true, pstrFilteredOut); + static bool RPCParseCommandLine(ipc::Node* ipc_node, std::string &strResult, const std::string &strCommand, bool fExecute, std::string * const pstrFilteredOut = nullptr); + static bool RPCExecuteCommandLine(ipc::Node& ipc_node, std::string &strResult, const std::string &strCommand, std::string * const pstrFilteredOut = nullptr) { + return RPCParseCommandLine(&ipc_node, strResult, strCommand, true, pstrFilteredOut); } void setClientModel(ClientModel *model); @@ -139,6 +143,7 @@ public Q_SLOTS: }; + ipc::Node& m_ipc_node; Ui::RPCConsole *ui; ClientModel *clientModel; QStringList history; diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index e2abfb3742e..1ff19105f87 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -7,6 +7,7 @@ #include "chainparams.h" #include "consensus/validation.h" #include "fs.h" +#include "ipc/interfaces.h" #include "validation.h" #include "rpc/register.h" #include "rpc/server.h" @@ -51,90 +52,91 @@ void RPCNestedTests::rpcNestedTests() std::string result; std::string result2; std::string filtered; - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path + auto ipc_node = ipc::MakeNode(ipc::LOCAL); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo()[chain]", &filtered); //simple result filtering with path QVERIFY(result=="main"); QVERIFY(filtered == "getblockchaininfo()[chain]"); - RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())"); //simple 2 level nesting - RPCConsole::RPCExecuteCommandLine(result, "getblock(getblock(getbestblockhash())[hash], true)"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblock(getbestblockhash())"); //simple 2 level nesting + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblock(getblock(getbestblockhash())[hash], true)"); - RPCConsole::RPCExecuteCommandLine(result, "getblock( getblock( getblock(getbestblockhash())[hash] )[hash], true)"); //4 level nesting with whitespace, filtering path and boolean parameter + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblock( getblock( getblock(getbestblockhash())[hash] )[hash], true)"); //4 level nesting with whitespace, filtering path and boolean parameter - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo"); QVERIFY(result.substr(0,1) == "{"); - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo()"); QVERIFY(result.substr(0,1) == "{"); - RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo "); //whitespace at the end will be tolerated + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo "); //whitespace at the end will be tolerated QVERIFY(result.substr(0,1) == "{"); - (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child contaning the quotes in the key + (RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo()[\"chain\"]")); //Quote path identifier are allowed, but look after a child contaning the quotes in the key QVERIFY(result == "null"); - (RPCConsole::RPCExecuteCommandLine(result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed - (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed + (RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "createrawtransaction [] {} 0")); //parameter not in brackets are allowed + (RPCConsole::RPCExecuteCommandLine(*ipc_node, result2, "createrawtransaction([],{},0)")); //parameter in brackets are allowed QVERIFY(result == result2); - (RPCConsole::RPCExecuteCommandLine(result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parametres is allowed + (RPCConsole::RPCExecuteCommandLine(*ipc_node, result2, "createrawtransaction( [], {} , 0 )")); //whitespace between parametres is allowed QVERIFY(result == result2); - RPCConsole::RPCExecuteCommandLine(result, "getblock(getbestblockhash())[tx][0]", &filtered); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblock(getbestblockhash())[tx][0]", &filtered); QVERIFY(result == "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); QVERIFY(filtered == "getblock(getbestblockhash())[tx][0]"); - RPCConsole::RPCParseCommandLine(result, "importprivkey", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "importprivkey", false, &filtered); QVERIFY(filtered == "importprivkey(…)"); - RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc", false, &filtered); QVERIFY(filtered == "signmessagewithprivkey(…)"); - RPCConsole::RPCParseCommandLine(result, "signmessagewithprivkey abc,def", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "signmessagewithprivkey abc,def", false, &filtered); QVERIFY(filtered == "signmessagewithprivkey(…)"); - RPCConsole::RPCParseCommandLine(result, "signrawtransaction(abc)", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "signrawtransaction(abc)", false, &filtered); QVERIFY(filtered == "signrawtransaction(…)"); - RPCConsole::RPCParseCommandLine(result, "walletpassphrase(help())", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrase(help())", false, &filtered); QVERIFY(filtered == "walletpassphrase(…)"); - RPCConsole::RPCParseCommandLine(result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "walletpassphrasechange(help(walletpassphrasechange(abc)))", false, &filtered); QVERIFY(filtered == "walletpassphrasechange(…)"); - RPCConsole::RPCParseCommandLine(result, "help(encryptwallet(abc, def))", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "help(encryptwallet(abc, def))", false, &filtered); QVERIFY(filtered == "help(encryptwallet(…))"); - RPCConsole::RPCParseCommandLine(result, "help(importprivkey())", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey())", false, &filtered); QVERIFY(filtered == "help(importprivkey(…))"); - RPCConsole::RPCParseCommandLine(result, "help(importprivkey(help()))", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey(help()))", false, &filtered); QVERIFY(filtered == "help(importprivkey(…))"); - RPCConsole::RPCParseCommandLine(result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered); + RPCConsole::RPCParseCommandLine(nullptr, result, "help(importprivkey(abc), walletpassphrase(def))", false, &filtered); QVERIFY(filtered == "help(importprivkey(…), walletpassphrase(…))"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest"); QVERIFY(result == "[]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest ''"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest ''"); QVERIFY(result == "[\"\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest \"\""); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest \"\""); QVERIFY(result == "[\"\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest '' abc"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest '' abc"); QVERIFY(result == "[\"\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc '' abc"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest abc '' abc"); QVERIFY(result == "[\"abc\",\"\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc abc"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest abc abc"); QVERIFY(result == "[\"abc\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc\t\tabc"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest abc\t\tabc"); QVERIFY(result == "[\"abc\",\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc )"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest(abc )"); QVERIFY(result == "[\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc )"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest( abc )"); QVERIFY(result == "[\"abc\"]"); - RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest( abc , cba )"); + RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest( abc , cba )"); QVERIFY(result == "[\"abc\",\"cba\"]"); #if QT_VERSION >= 0x050300 // do the QVERIFY_EXCEPTION_THROWN checks only with Qt5.3 and higher (QVERIFY_EXCEPTION_THROWN was introduced in Qt5.3) - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax - (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments - (RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo()()()")); //tolerate non command brackts - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "getblockchaininfo(True)"), UniValue); //invalid argument - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "a(getblockchaininfo(True))"), UniValue); //method not found - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using , - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , - QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo() .\n"), std::runtime_error); //invalid syntax + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo() getblockchaininfo()"), std::runtime_error); //invalid syntax + (RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo(")); //tolerate non closing brackets if we have no arguments + (RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo()()()")); //tolerate non command brackts + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "getblockchaininfo(True)"), UniValue); //invalid argument + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "a(getblockchaininfo(True))"), UniValue); //method not found + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest abc,,abc"), std::runtime_error); //don't tollerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , + QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(*ipc_node, result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , #endif fs::remove_all(fs::path(path)); From c3c2210cf6ba5d4e068c10d88b638cfb880929ca Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 16:43:47 -0400 Subject: [PATCH 12/20] Remove direct bitcoin calls from qt/optionsdialog.cpp --- src/qt/optionsdialog.cpp | 11 ++++------- src/qt/optionsmodel.h | 2 ++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index b80b6541ddd..8e39879b33b 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -13,14 +13,11 @@ #include "guiutil.h" #include "optionsmodel.h" +#include "ipc/interfaces.h" #include "validation.h" // for DEFAULT_SCRIPTCHECK_THREADS and MAX_SCRIPTCHECK_THREADS #include "netbase.h" #include "txdb.h" // for -dbcache defaults -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" // for CWallet::GetRequiredFee() -#endif - #include #include #include @@ -315,17 +312,17 @@ void OptionsDialog::updateDefaultProxyNets() std::string strProxy; QString strDefaultProxyGUI; - GetProxy(NET_IPV4, proxy); + model->getIpcNode().getProxy(NET_IPV4, proxy); strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv4->setChecked(true) : ui->proxyReachIPv4->setChecked(false); - GetProxy(NET_IPV6, proxy); + model->getIpcNode().getProxy(NET_IPV6, proxy); strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachIPv6->setChecked(true) : ui->proxyReachIPv6->setChecked(false); - GetProxy(NET_TOR, proxy); + model->getIpcNode().getProxy(NET_TOR, proxy); strProxy = proxy.proxy.ToStringIP() + ":" + proxy.proxy.ToStringPort(); strDefaultProxyGUI = ui->proxyIp->text() + ":" + ui->proxyPort->text(); (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 4c83c66dd58..4ef1c8ff62e 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -76,6 +76,8 @@ class OptionsModel : public QAbstractListModel void setRestartRequired(bool fRequired); bool isRestartRequired(); + ipc::Node& getIpcNode() const { return m_ipc_node; } + private: ipc::Node& m_ipc_node; /* Qt-only settings */ From e9ec108d7df2eda5b7c9729a1f2d0cfb60f74dfa Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 18:56:44 -0400 Subject: [PATCH 13/20] Remove most direct bitcoin calls from qt/walletmodel.cpp --- src/ipc/interfaces.h | 205 ++++++++++++++++++++++++++- src/ipc/local/interfaces.cpp | 204 +++++++++++++++++++++++++++ src/qt/bitcoin.cpp | 4 +- src/qt/coincontroldialog.cpp | 12 +- src/qt/overviewpage.cpp | 8 +- src/qt/sendcoinsdialog.cpp | 13 +- src/qt/signverifymessagedialog.cpp | 2 +- src/qt/test/wallettests.cpp | 4 +- src/qt/transactionview.cpp | 10 +- src/qt/walletmodel.cpp | 275 ++++++++----------------------------- src/qt/walletmodel.h | 46 ++----- src/qt/walletmodeltransaction.cpp | 28 +--- src/qt/walletmodeltransaction.h | 15 +- src/qt/walletview.cpp | 4 +- 14 files changed, 523 insertions(+), 307 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 0bdf2bd56a7..735108cb92e 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -1,26 +1,39 @@ #ifndef BITCOIN_IPC_INTERFACES_H #define BITCOIN_IPC_INTERFACES_H -#include "addrdb.h" // For banmap_t -#include "init.h" // For HelpMessageMode -#include "net.h" // For CConnman::NumConnections -#include "netaddress.h" // For Network +#include "addrdb.h" // For banmap_t +#include "init.h" // For HelpMessageMode +#include "net.h" // For CConnman::NumConnections +#include "netaddress.h" // For Network +#include "pubkey.h" // For CTxDestination (CKeyID and CScriptID) +#include "script/ismine.h" // isminefilter, isminetype +#include "script/standard.h" // For CTxDestination +#include "support/allocators/secure.h" // For SecureString +#include "ui_interface.h" // For ChangeType #include #include #include #include +class CCoinControl; +class CKey; class CNodeStats; +class CValidationState; class RPCTimerInterface; class UniValue; class proxyType; struct CNodeStateStats; +struct CRecipient; namespace ipc { class Handler; class Wallet; +class PendingWalletTx; +struct WalletBalances; +using WalletOrderForm = std::vector>; +using WalletValueMap = std::map; //! Top-level interface for a bitcoin node (bitcoind process). class Node @@ -137,6 +150,15 @@ class Node //! Get network active. virtual bool getNetworkActive() = 0; + //! Get tx confirm target. + virtual unsigned int getTxConfirmTarget() = 0; + + //! Get wallet rbf. + virtual bool getWalletRbf() = 0; + + //! Get max tx fee. + virtual CAmount getMaxTxFee() = 0; + //! Execute rpc command. virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; @@ -149,6 +171,9 @@ class Node //! Unset RPC timer interface. virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0; + //! Return interface for accessing the wallet. + virtual std::unique_ptr getWallet(size_t index) = 0; + //! Register handler for init messages. using InitMessageFn = std::function; virtual std::unique_ptr handleInitMessage(InitMessageFn fn) = 0; @@ -206,9 +231,142 @@ class Wallet public: virtual ~Wallet() {} + //! Encrypt wallet. + virtual bool encryptWallet(const SecureString& wallet_passphrase) = 0; + + //! Return whether wallet is encrypted. + virtual bool isCrypted() = 0; + + //! Lock wallet. + virtual bool lock() = 0; + + //! Unlock wallet. + virtual bool unlock(const SecureString& wallet_passphrase) = 0; + + //! Return whether wallet is locked. + virtual bool isLocked() = 0; + + //! Change wallet passphrase. + virtual bool changeWalletPassphrase(const SecureString& old_wallet_passphrase, + const SecureString& new_wallet_passphrase) = 0; + + //! Back up wallet. + virtual bool backupWallet(const std::string& filename) = 0; + + //! Get public key. + virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0; + + //! Get private key. + virtual bool getPrivKey(const CKeyID& address, CKey& key) = 0; + + //! Return whether wallet has private key. + virtual bool havePrivKey(const CKeyID& address) = 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::string& purpose) = 0; + + //! Look up address in wallet, return whether exists. + virtual bool getAddress(const CTxDestination& dest, + std::string* name = nullptr, + isminetype* is_mine = nullptr) = 0; + + //! Add dest data. + virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0; + + //! Erase dest data. + virtual bool eraseDestData(const CTxDestination& dest, const std::string& key) = 0; + + //! Get dest values with prefix. + virtual std::vector getDestValues(const std::string& prefix) = 0; + + //! Lock coin. + virtual void lockCoin(const COutPoint& output) = 0; + + //! Unlock coin. + virtual void unlockCoin(const COutPoint& output) = 0; + + //! Return whether coin is locked. + virtual bool isLockedCoin(const COutPoint& output) = 0; + + //! List locked coins. + virtual void listLockedCoins(std::vector& outputs) = 0; + + //! Create transaction. + virtual std::unique_ptr createTransaction(const std::vector& recipients, + const CCoinControl& coin_control, + bool sign, + int& change_pos, + CAmount& fee, + std::string& fail_reason) = 0; + + //! Return whether transaction can be abandoned. + virtual bool transactionCanBeAbandoned(const uint256& txid) = 0; + + //! Abandon transaction. + virtual bool abandonTransaction(const uint256& txid) = 0; + + //! Return whether transaction can be bumped. + virtual bool transactionCanBeBumped(const uint256& txid) = 0; + + //! Create bump transaction. + virtual bool createBumpTransaction(const uint256& txid, + const CCoinControl& coin_control, + CAmount total_fee, + std::vector& errors, + CAmount& old_fee, + CAmount& new_fee, + CMutableTransaction& mtx) = 0; + + //! Sign bump transaction. + virtual bool signBumpTransaction(CMutableTransaction& mtx) = 0; + + //! Commit bump transaction. + virtual bool commitBumpTransaction(const uint256& txid, + CMutableTransaction&& mtx, + std::vector& errors, + uint256& bumped_txid) = 0; + + //! Get balances. + virtual WalletBalances getBalances() = 0; + + //! Get balances if possible without blocking. + virtual bool tryGetBalances(WalletBalances& balances, int& num_blocks) = 0; + + //! Get balance. + virtual CAmount getBalance() = 0; + + //! Get available balance. + virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0; + + // Return whether HD enabled. + virtual bool hdEnabled() = 0; + //! Register handler for show progress messages. using ShowProgressFn = std::function; virtual std::unique_ptr handleShowProgress(ShowProgressFn fn) = 0; + + //! Register handler for status changed messages. + using StatusChangedFn = std::function; + virtual std::unique_ptr handleStatusChanged(StatusChangedFn fn) = 0; + + //! Register handler for address book changed messages. + using AddressBookChangedFn = std::function; + virtual std::unique_ptr handleAddressBookChanged(AddressBookChangedFn fn) = 0; + + //! Register handler for transaction changed messages. + using TransactionChangedFn = std::function; + virtual std::unique_ptr handleTransactionChanged(TransactionChangedFn fn) = 0; + + //! Register handler for watchonly changed messages. + using WatchOnlyChangedFn = std::function; + virtual std::unique_ptr handleWatchOnlyChanged(WatchOnlyChangedFn fn) = 0; }; //! Interface for managing a registered handler. @@ -221,6 +379,45 @@ class Handler virtual void disconnect() = 0; }; +//! Tracking object returned by CreateTransaction and passed to CommitTransaction. +class PendingWalletTx +{ +public: + virtual ~PendingWalletTx() {} + + //! Get transaction data. + virtual const CTransaction& get() = 0; + + //! Get virtual transaction size. + virtual int64_t getVirtualSize() = 0; + + //! Send pending transaction and commit to wallet. + virtual bool commit(WalletValueMap value_map, + WalletOrderForm order_form, + std::string from_account, + std::string& reject_reason) = 0; +}; + +//! Collection of wallet balances. +struct WalletBalances +{ + CAmount balance = 0; + CAmount unconfirmed_balance = 0; + CAmount immature_balance = 0; + bool have_watch_only = false; + CAmount watch_only_balance = 0; + CAmount unconfirmed_watch_only_balance = 0; + CAmount immature_watch_only_balance = 0; + + bool balanceChanged(const WalletBalances& prev) const + { + return balance != prev.balance || unconfirmed_balance != prev.unconfirmed_balance || + immature_balance != prev.immature_balance || watch_only_balance != prev.watch_only_balance || + unconfirmed_watch_only_balance != prev.unconfirmed_watch_only_balance || + immature_watch_only_balance != prev.immature_watch_only_balance; + } +}; + //! Protocol IPC interface should use to communicate with implementation. enum Protocol { LOCAL, //!< Call functions linked into current executable. diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 4777cbac536..06f3f797302 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -1,11 +1,15 @@ #include #include +#include #include #include #include #include #include +#include +#include +#include #include #include #include @@ -18,6 +22,7 @@ #include "config/bitcoin-config.h" #endif #ifdef ENABLE_WALLET +#include "wallet/feebumper.h" #include "wallet/wallet.h" #endif @@ -45,15 +50,206 @@ class HandlerImpl : public Handler }; #ifdef ENABLE_WALLET +class PendingWalletTxImpl : public PendingWalletTx +{ +public: + PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {} + + const CTransaction& get() override { return *m_wtx.tx; } + + int64_t getVirtualSize() override { return ::GetVirtualTransactionSize(*m_wtx.tx); } + + bool commit(WalletValueMap value_map, + WalletOrderForm order_form, + std::string from_account, + std::string& reject_reason) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + m_wtx.mapValue = std::move(value_map); + m_wtx.vOrderForm = std::move(order_form); + m_wtx.strFromAccount = std::move(from_account); + CValidationState state; + if (!m_wallet.CommitTransaction(m_wtx, m_key, ::g_connman.get(), state)) { + reject_reason = state.GetRejectReason(); + return false; + } + return true; + } + + CWalletTx m_wtx; + CWallet& m_wallet; + CReserveKey m_key; +}; + class WalletImpl : public Wallet { public: WalletImpl(CWallet& wallet) : m_wallet(wallet) {} + bool encryptWallet(const SecureString& wallet_passphrase) override + { + return m_wallet.EncryptWallet(wallet_passphrase); + } + bool isCrypted() override { return m_wallet.IsCrypted(); } + bool lock() override { return m_wallet.Lock(); } + bool unlock(const SecureString& wallet_passphrase) override { return m_wallet.Unlock(wallet_passphrase); } + bool isLocked() override { return m_wallet.IsLocked(); } + bool changeWalletPassphrase(const SecureString& old_wallet_passphrase, + const SecureString& new_wallet_passphrase) override + { + return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase); + } + bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); } + bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet.GetPubKey(address, pub_key); } + bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet.GetKey(address, key); } + bool havePrivKey(const CKeyID& address) override { return m_wallet.HaveKey(address); } + bool haveWatchOnly() override { return m_wallet.HaveWatchOnly(); }; + bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) override + { + LOCK(m_wallet.cs_wallet); + return m_wallet.SetAddressBook(dest, name, purpose); + } + bool getAddress(const CTxDestination& dest, std::string* name, isminetype* is_mine) override + { + LOCK(m_wallet.cs_wallet); + auto it = m_wallet.mapAddressBook.find(dest); + if (it == m_wallet.mapAddressBook.end()) { + return false; + } + if (name) { + *name = it->second.name; + } + if (is_mine) { + *is_mine = ::IsMine(m_wallet, dest); + } + return true; + } + bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override + { + LOCK(m_wallet.cs_wallet); + return m_wallet.AddDestData(dest, key, value); + } + bool eraseDestData(const CTxDestination& dest, const std::string& key) override + { + LOCK(m_wallet.cs_wallet); + return m_wallet.EraseDestData(dest, key); + } + std::vector getDestValues(const std::string& prefix) override + { + return m_wallet.GetDestValues(prefix); + } + void lockCoin(const COutPoint& output) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.LockCoin(output); + } + void unlockCoin(const COutPoint& output) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.UnlockCoin(output); + } + bool isLockedCoin(const COutPoint& output) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.IsLockedCoin(output.hash, output.n); + } + void listLockedCoins(std::vector& outputs) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.ListLockedCoins(outputs); + } + std::unique_ptr createTransaction(const std::vector& recipients, + const CCoinControl& coin_control, + bool sign, + int& change_pos, + CAmount& fee, + std::string& fail_reason) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + auto pending = MakeUnique(m_wallet); + if (!m_wallet.CreateTransaction( + recipients, pending->m_wtx, pending->m_key, fee, change_pos, fail_reason, coin_control, sign)) { + return {}; + } + return std::move(pending); + } + bool transactionCanBeAbandoned(const uint256& txid) override { return m_wallet.TransactionCanBeAbandoned(txid); } + bool abandonTransaction(const uint256& txid) override { return m_wallet.AbandonTransaction(txid); } + bool transactionCanBeBumped(const uint256& txid) { return FeeBumper::TransactionCanBeBumped(&m_wallet, txid); } + bool createBumpTransaction(const uint256& txid, + const CCoinControl& coin_control, + CAmount total_fee, + std::vector& errors, + CAmount& old_fee, + CAmount& new_fee, + CMutableTransaction& mtx) + { + return FeeBumper::CreateTransaction(&m_wallet, txid, coin_control, total_fee, errors, old_fee, new_fee, mtx) == + BumpFeeResult::OK; + } + bool signBumpTransaction(CMutableTransaction& mtx) { return FeeBumper::SignTransaction(&m_wallet, mtx); } + bool commitBumpTransaction(const uint256& txid, + CMutableTransaction&& mtx, + std::vector& errors, + uint256& bumped_txid) + { + return FeeBumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) == BumpFeeResult::OK; + } + WalletBalances getBalances() override + { + WalletBalances result; + result.balance = m_wallet.GetBalance(); + result.unconfirmed_balance = m_wallet.GetUnconfirmedBalance(); + result.immature_balance = m_wallet.GetImmatureBalance(); + result.have_watch_only = m_wallet.HaveWatchOnly(); + if (result.have_watch_only) { + result.watch_only_balance = m_wallet.GetWatchOnlyBalance(); + result.unconfirmed_watch_only_balance = m_wallet.GetUnconfirmedWatchOnlyBalance(); + result.immature_watch_only_balance = m_wallet.GetImmatureWatchOnlyBalance(); + } + return result; + } + bool tryGetBalances(WalletBalances& balances, int& num_blocks) override + { + TRY_LOCK(::cs_main, lockMain); + if (!lockMain) return false; + TRY_LOCK(m_wallet.cs_wallet, lockWallet); + if (!lockWallet) { + return false; + } + balances = getBalances(); + num_blocks = ::chainActive.Height(); + return true; + } + CAmount getBalance() override { return m_wallet.GetBalance(); } + CAmount getAvailableBalance(const CCoinControl& coin_control) override + { + return m_wallet.GetAvailableBalance(&coin_control); + } + bool hdEnabled() override { return m_wallet.IsHDEnabled(); } std::unique_ptr handleShowProgress(ShowProgressFn fn) override { return MakeUnique(m_wallet.ShowProgress.connect(fn)); } + std::unique_ptr handleStatusChanged(StatusChangedFn fn) override + { + return MakeUnique(m_wallet.NotifyStatusChanged.connect([fn](CCryptoKeyStore*) { fn(); })); + } + std::unique_ptr handleAddressBookChanged(AddressBookChangedFn fn) override + { + return MakeUnique(m_wallet.NotifyAddressBookChanged.connect( + [fn](CWallet*, const CTxDestination& address, const std::string& label, bool is_mine, + const std::string& purpose, ChangeType status) { fn(address, label, is_mine, purpose, status); })); + } + std::unique_ptr handleTransactionChanged(TransactionChangedFn fn) override + { + return MakeUnique(m_wallet.NotifyTransactionChanged.connect( + [fn, this](CWallet*, const uint256& txid, ChangeType status) { fn(txid, status); })); + } + std::unique_ptr handleWatchOnlyChanged(WatchOnlyChangedFn fn) override + { + return MakeUnique(m_wallet.NotifyWatchonlyChanged.connect(fn)); + } CWallet& m_wallet; }; @@ -206,6 +402,9 @@ class NodeImpl : public Node } } bool getNetworkActive() override { return ::g_connman && ::g_connman->GetNetworkActive(); } + unsigned int getTxConfirmTarget() override { CHECK_WALLET(return ::nTxConfirmTarget); } + bool getWalletRbf() override { CHECK_WALLET(return ::fWalletRbf); } + CAmount getMaxTxFee() override { return ::maxTxFee; } UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override { JSONRPCRequest req; @@ -217,6 +416,11 @@ class NodeImpl : public Node std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { ::RPCSetTimerInterfaceIfUnset(iface); } void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { ::RPCUnsetTimerInterface(iface); } + std::unique_ptr getWallet(size_t index) override + { + CHECK_WALLET(return index < ::vpwallets.size() ? MakeUnique(*::vpwallets[index]) : + std::unique_ptr()); + } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeUnique(::uiInterface.InitMessage.connect(fn)); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 54b85031a83..f1a2ab304fe 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -460,9 +460,9 @@ void BitcoinApplication::initializeResult(bool success) #ifdef ENABLE_WALLET // TODO: Expose secondary wallets - if (!vpwallets.empty()) + if (auto walletMain = m_ipc_node.getWallet(0)) { - walletModel = new WalletModel(platformStyle, vpwallets[0], optionsModel); + walletModel = new WalletModel(std::move(walletMain), m_ipc_node, platformStyle, vpwallets[0], optionsModel); window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index f3ee0fbe393..8dd57ea2130 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -210,7 +210,7 @@ void CoinControlDialog::showMenu(const QPoint &point) if (item->text(COLUMN_TXHASH).length() == 64) // transaction hash is 64 characters (this means its a child node, so its not a parent node in tree mode) { copyTransactionHashAction->setEnabled(true); - if (model->isLockedCoin(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt())) + if (model->getIpcWallet().isLockedCoin(COutPoint(uint256S(item->text(COLUMN_TXHASH).toStdString()), item->text(COLUMN_VOUT_INDEX).toUInt()))) { lockAction->setEnabled(false); unlockAction->setEnabled(true); @@ -270,7 +270,7 @@ void CoinControlDialog::lockCoin() contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); - model->lockCoin(outpt); + model->getIpcWallet().lockCoin(outpt); contextMenuItem->setDisabled(true); contextMenuItem->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); updateLabelLocked(); @@ -280,7 +280,7 @@ void CoinControlDialog::lockCoin() void CoinControlDialog::unlockCoin() { COutPoint outpt(uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); - model->unlockCoin(outpt); + model->getIpcWallet().unlockCoin(outpt); contextMenuItem->setDisabled(false); contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon()); updateLabelLocked(); @@ -406,7 +406,7 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) void CoinControlDialog::updateLabelLocked() { std::vector vOutpts; - model->listLockedCoins(vOutpts); + model->getIpcWallet().listLockedCoins(vOutpts); if (vOutpts.size() > 0) { ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); @@ -480,7 +480,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CPubKey pubkey; CKeyID *keyid = boost::get(&address); - if (keyid && model->getPubKey(*keyid, pubkey)) + if (keyid && model->getIpcWallet().getPubKey(*keyid, pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); } @@ -701,7 +701,7 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); // disable locked coins - if (model->isLockedCoin(txhash, out.i)) + if (model->getIpcWallet().isLockedCoin(COutPoint(txhash, out.i))) { COutPoint outpt(txhash, out.i); coinControl->UnSelect(outpt); // just to be sure diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index ba344f4dbf8..94d1f531d43 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -231,13 +231,15 @@ void OverviewPage::setWalletModel(WalletModel *model) ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), - model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); + ipc::Wallet& ipc_wallet = model->getIpcWallet(); + ipc::WalletBalances balances = ipc_wallet.getBalances(); + setBalance(balances.balance, balances.unconfirmed_balance, balances.immature_balance, + balances.watch_only_balance, balances.unconfirmed_watch_only_balance, balances.immature_watch_only_balance); connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - updateWatchOnlyLabels(model->haveWatchOnly()); + updateWatchOnlyLabels(ipc_wallet.haveWatchOnly()); connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); } diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a01886c3ea8..04589ca44fe 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -158,8 +158,9 @@ void SendCoinsDialog::setModel(WalletModel *_model) } } - setBalance(_model->getBalance(), _model->getUnconfirmedBalance(), _model->getImmatureBalance(), - _model->getWatchBalance(), _model->getWatchUnconfirmedBalance(), _model->getWatchImmatureBalance()); + ipc::WalletBalances balances = _model->getIpcWallet().getBalances(); + setBalance(balances.balance, balances.unconfirmed_balance, balances.immature_balance, + balances.watch_only_balance, balances.unconfirmed_watch_only_balance, balances.immature_watch_only_balance); connect(_model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount))); connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); updateDisplayUnit(); @@ -191,7 +192,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) updateSmartFeeLabel(); // set default rbf checkbox state - ui->optInRBF->setCheckState(model->getDefaultWalletRbf() ? Qt::Checked : Qt::Unchecked); + ui->optInRBF->setCheckState(model->getIpcNode().getWalletRbf() ? Qt::Checked : Qt::Unchecked); // set the smartfee-sliders default value (wallets default conf.target or last stored value) QSettings settings; @@ -203,7 +204,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) settings.remove("nSmartFeeSliderPosition"); } if (settings.value("nConfTarget").toInt() == 0) - ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->getDefaultConfirmTarget())); + ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(model->getIpcNode().getTxConfirmTarget())); else ui->confTargetSelector->setCurrentIndex(getIndexForConfTarget(settings.value("nConfTarget").toInt())); } @@ -530,7 +531,7 @@ void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfir void SendCoinsDialog::updateDisplayUnit() { - setBalance(model->getBalance(), 0, 0, 0, 0, 0); + setBalance(model->getIpcWallet().getBalance(), 0, 0, 0, 0, 0); ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit()); updateMinFeeLabel(); updateSmartFeeLabel(); @@ -791,7 +792,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) { CKeyID keyid; addr.GetKeyID(keyid); - if (!model->havePrivKey(keyid)) // Unknown change address + if (!model->getIpcWallet().havePrivKey(keyid)) // Unknown change address { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 0950ed02342..9b55f522273 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -142,7 +142,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() } CKey key; - if (!model->getPrivKey(keyID, key)) + if (!model->getIpcWallet().getPrivKey(keyID, key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 551f093fb4a..f762ae6fd4f 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -167,7 +167,9 @@ void TestSendCoins() TransactionView transactionView(platformStyle.get()); auto ipc_node = ipc::MakeNode(ipc::LOCAL); OptionsModel optionsModel(*ipc_node); - WalletModel walletModel(platformStyle.get(), &wallet, &optionsModel); + vpwallets.insert(vpwallets.begin(), &wallet); + WalletModel walletModel(ipc_node->getWallet(0), *ipc_node, platformStyle.get(), &wallet, &optionsModel); + vpwallets.erase(vpwallets.begin()); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 53c38da9db3..2cbe70f3bc1 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -241,7 +241,7 @@ void TransactionView::setModel(WalletModel *_model) } // show/hide column Watch-only - updateWatchOnlyColumn(_model->haveWatchOnly()); + updateWatchOnlyColumn(_model->getIpcWallet().haveWatchOnly()); // Watch-only signal connect(_model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyColumn(bool))); @@ -353,7 +353,7 @@ void TransactionView::exportClicked() // name, column, role writer.setModel(transactionProxyModel); writer.addColumn(tr("Confirmed"), 0, TransactionTableModel::ConfirmedRole); - if (model && model->haveWatchOnly()) + if (model && model->getIpcWallet().haveWatchOnly()) writer.addColumn(tr("Watch-only"), TransactionTableModel::Watchonly); writer.addColumn(tr("Date"), 0, TransactionTableModel::DateRole); writer.addColumn(tr("Type"), TransactionTableModel::Type, Qt::EditRole); @@ -382,8 +382,8 @@ void TransactionView::contextualMenu(const QPoint &point) // check if transaction can be abandoned, disable context menu action in case it doesn't uint256 hash; hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString()); - abandonAction->setEnabled(model->transactionCanBeAbandoned(hash)); - bumpFeeAction->setEnabled(model->transactionCanBeBumped(hash)); + abandonAction->setEnabled(model->getIpcWallet().transactionCanBeAbandoned(hash)); + bumpFeeAction->setEnabled(model->getIpcWallet().transactionCanBeBumped(hash)); if(index.isValid()) { @@ -403,7 +403,7 @@ void TransactionView::abandonTx() hash.SetHex(hashQStr.toStdString()); // Abandon the wallet transaction over the walletModel - model->abandonTransaction(hash); + model->getIpcWallet().abandonTransaction(hash); // Update the table model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 513f04d6584..ff3ccbea51e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -20,14 +20,11 @@ #include "validation.h" #include "net.h" // for g_connman #include "policy/fees.h" -#include "policy/rbf.h" #include "sync.h" #include "ui_interface.h" #include "util.h" // for GetBoolArg #include "wallet/coincontrol.h" -#include "wallet/feebumper.h" #include "wallet/wallet.h" -#include "wallet/walletdb.h" // for BackupWallet #include @@ -37,15 +34,14 @@ #include -WalletModel::WalletModel(const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : - QObject(parent), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), +WalletModel::WalletModel(std::unique_ptr _ipcWallet, ipc::Node& _ipcNode, const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : + QObject(parent), m_ipc_wallet(std::move(_ipcWallet)), m_ipc_node(_ipcNode), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), transactionTableModel(0), recentRequestsTableModel(0), - cachedBalance(0), cachedUnconfirmedBalance(0), cachedImmatureBalance(0), cachedEncryptionStatus(Unencrypted), cachedNumBlocks(0) { - fHaveWatchOnly = wallet->HaveWatchOnly(); + fHaveWatchOnly = m_ipc_wallet->haveWatchOnly(); fForceCheckBalanceChanged = false; addressTableModel = new AddressTableModel(wallet, this); @@ -65,46 +61,6 @@ WalletModel::~WalletModel() unsubscribeFromCoreSignals(); } -CAmount WalletModel::getBalance(const CCoinControl *coinControl) const -{ - if (coinControl) - { - return wallet->GetAvailableBalance(coinControl); - } - - return wallet->GetBalance(); -} - -CAmount WalletModel::getUnconfirmedBalance() const -{ - return wallet->GetUnconfirmedBalance(); -} - -CAmount WalletModel::getImmatureBalance() const -{ - return wallet->GetImmatureBalance(); -} - -bool WalletModel::haveWatchOnly() const -{ - return fHaveWatchOnly; -} - -CAmount WalletModel::getWatchBalance() const -{ - return wallet->GetWatchOnlyBalance(); -} - -CAmount WalletModel::getWatchUnconfirmedBalance() const -{ - return wallet->GetUnconfirmedWatchOnlyBalance(); -} - -CAmount WalletModel::getWatchImmatureBalance() const -{ - return wallet->GetImmatureWatchOnlyBalance(); -} - void WalletModel::updateStatus() { EncryptionStatus newEncryptionStatus = getEncryptionStatus(); @@ -118,52 +74,30 @@ void WalletModel::pollBalanceChanged() // Get required locks upfront. This avoids the GUI from getting stuck on // periodical polls if the core is holding the locks for a longer time - // for example, during a wallet rescan. - TRY_LOCK(cs_main, lockMain); - if(!lockMain) - return; - TRY_LOCK(wallet->cs_wallet, lockWallet); - if(!lockWallet) + ipc::WalletBalances new_balances; + int numBlocks = -1; + if (!m_ipc_wallet->tryGetBalances(new_balances, numBlocks)) { return; + } - if(fForceCheckBalanceChanged || chainActive.Height() != cachedNumBlocks) + if(fForceCheckBalanceChanged || m_ipc_node.getNumBlocks() != cachedNumBlocks) { fForceCheckBalanceChanged = false; // Balance and number of transactions might have changed - cachedNumBlocks = chainActive.Height(); + cachedNumBlocks = m_ipc_node.getNumBlocks(); - checkBalanceChanged(); + checkBalanceChanged(new_balances); if(transactionTableModel) transactionTableModel->updateConfirmations(); } } -void WalletModel::checkBalanceChanged() +void WalletModel::checkBalanceChanged(const ipc::WalletBalances& new_balances) { - CAmount newBalance = getBalance(); - CAmount newUnconfirmedBalance = getUnconfirmedBalance(); - CAmount newImmatureBalance = getImmatureBalance(); - CAmount newWatchOnlyBalance = 0; - CAmount newWatchUnconfBalance = 0; - CAmount newWatchImmatureBalance = 0; - if (haveWatchOnly()) - { - newWatchOnlyBalance = getWatchBalance(); - newWatchUnconfBalance = getWatchUnconfirmedBalance(); - newWatchImmatureBalance = getWatchImmatureBalance(); - } - - if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance || - cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance) - { - cachedBalance = newBalance; - cachedUnconfirmedBalance = newUnconfirmedBalance; - cachedImmatureBalance = newImmatureBalance; - cachedWatchOnlyBalance = newWatchOnlyBalance; - cachedWatchUnconfBalance = newWatchUnconfBalance; - cachedWatchImmatureBalance = newWatchImmatureBalance; - Q_EMIT balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance, - newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance); + if(new_balances.balanceChanged(m_cached_balances)) { + m_cached_balances = new_balances; + Q_EMIT balanceChanged(new_balances.balance, new_balances.unconfirmed_balance, new_balances.immature_balance, new_balances.watch_only_balance, new_balances.unconfirmed_watch_only_balance, new_balances.immature_watch_only_balance); } } @@ -259,7 +193,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact return DuplicateAddress; } - CAmount nBalance = getBalance(&coinControl); + CAmount nBalance = m_ipc_wallet->getAvailableBalance(coinControl); if(total > nBalance) { @@ -267,22 +201,18 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact } { - LOCK2(cs_main, wallet->cs_wallet); - - transaction.newPossibleKeyChange(wallet); CAmount nFeeRequired = 0; int nChangePosRet = -1; std::string strFailReason; - CWalletTx *newTx = transaction.getTransaction(); - CReserveKey *keyChange = transaction.getPossibleKeyChange(); - bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl); + auto& newTx = transaction.getWtx(); + newTx = m_ipc_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason); transaction.setTransactionFee(nFeeRequired); - if (fSubtractFeeFromAmount && fCreated) + if (fSubtractFeeFromAmount && newTx) transaction.reassignAmounts(nChangePosRet); - if(!fCreated) + if(!newTx) { if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) { @@ -296,7 +226,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact // reject absurdly high fee. (This can never happen because the // wallet caps the fee at maxTxFee. This merely serves as a // belt-and-suspenders check) - if (nFeeRequired > maxTxFee) + if (nFeeRequired > m_ipc_node.getMaxTxFee()) return AbsurdFee; } @@ -308,9 +238,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran QByteArray transaction_array; /* store serialized transaction */ { - LOCK2(cs_main, wallet->cs_wallet); - CWalletTx *newTx = transaction.getTransaction(); - + std::vector> vOrderForm; for (const SendCoinsRecipient &rcp : transaction.getRecipients()) { if (rcp.paymentRequest.IsInitialized()) @@ -321,22 +249,21 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran } // Store PaymentRequests in wtx.vOrderForm in wallet. - std::string key("PaymentRequest"); std::string value; rcp.paymentRequest.SerializeToString(&value); - newTx->vOrderForm.push_back(make_pair(key, value)); + vOrderForm.emplace_back("PaymentRequest", std::move(value)); } else if (!rcp.message.isEmpty()) // Message from normal bitcoin:URI (bitcoin:123...?message=example) - newTx->vOrderForm.push_back(make_pair("Message", rcp.message.toStdString())); + vOrderForm.emplace_back("Message", rcp.message.toStdString()); } - CReserveKey *keyChange = transaction.getPossibleKeyChange(); - CValidationState state; - if(!wallet->CommitTransaction(*newTx, *keyChange, g_connman.get(), state)) - return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(state.GetRejectReason())); + auto& newTx = transaction.getWtx(); + std::string rejectReason; + if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, rejectReason)) + return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(rejectReason)); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); - ssTx << *newTx->tx; + ssTx << newTx->get(); transaction_array.append(&(ssTx[0]), ssTx.size()); } @@ -351,24 +278,22 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran CTxDestination dest = CBitcoinAddress(strAddress).Get(); std::string strLabel = rcp.label.toStdString(); { - LOCK(wallet->cs_wallet); - - std::map::iterator mi = wallet->mapAddressBook.find(dest); - // Check if we have a new address or an updated label - if (mi == wallet->mapAddressBook.end()) + std::string name; + if (!m_ipc_wallet->getAddress(dest, &name)) { - wallet->SetAddressBook(dest, strLabel, "send"); + m_ipc_wallet->setAddressBook(dest, strLabel, "send"); } - else if (mi->second.name != strLabel) + else if (name != strLabel) { - wallet->SetAddressBook(dest, strLabel, ""); // "" means don't change purpose + m_ipc_wallet->setAddressBook(dest, strLabel, ""); // "" means don't change purpose } } } Q_EMIT coinsSent(wallet, rcp, transaction_array); } - checkBalanceChanged(); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits + + checkBalanceChanged(m_ipc_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits return SendCoinsReturn(OK); } @@ -395,11 +320,11 @@ RecentRequestsTableModel *WalletModel::getRecentRequestsTableModel() WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const { - if(!wallet->IsCrypted()) + if(!m_ipc_wallet->isCrypted()) { return Unencrypted; } - else if(wallet->IsLocked()) + else if(m_ipc_wallet->isLocked()) { return Locked; } @@ -414,7 +339,7 @@ bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphr if(encrypted) { // Encrypt - return wallet->EncryptWallet(passphrase); + return m_ipc_wallet->encryptWallet(passphrase); } else { @@ -428,12 +353,12 @@ bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase) if(locked) { // Lock - return wallet->Lock(); + return m_ipc_wallet->lock(); } else { // Unlock - return wallet->Unlock(passPhrase); + return m_ipc_wallet->unlock(passPhrase); } } @@ -441,26 +366,20 @@ bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureStri { bool retval; { - LOCK(wallet->cs_wallet); - wallet->Lock(); // Make sure wallet is locked before attempting pass change - retval = wallet->ChangeWalletPassphrase(oldPass, newPass); + m_ipc_wallet->lock(); // Make sure wallet is locked before attempting pass change + retval = m_ipc_wallet->changeWalletPassphrase(oldPass, newPass); } return retval; } -bool WalletModel::backupWallet(const QString &filename) -{ - return wallet->BackupWallet(filename.toLocal8Bit().data()); -} - // Handlers for core signals -static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel, CCryptoKeyStore *wallet) +static void NotifyKeyStoreStatusChanged(WalletModel *walletmodel) { qDebug() << "NotifyKeyStoreStatusChanged"; QMetaObject::invokeMethod(walletmodel, "updateStatus", Qt::QueuedConnection); } -static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, +static void NotifyAddressBookChanged(WalletModel *walletmodel, const CTxDestination &address, const std::string &label, bool isMine, const std::string &purpose, ChangeType status) { @@ -477,9 +396,8 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, Q_ARG(int, status)); } -static void NotifyTransactionChanged(WalletModel *walletmodel, CWallet *wallet, const uint256 &hash, ChangeType status) +static void NotifyTransactionChanged(WalletModel *walletmodel, const uint256 &hash, ChangeType status) { - Q_UNUSED(wallet); Q_UNUSED(hash); Q_UNUSED(status); QMetaObject::invokeMethod(walletmodel, "updateTransaction", Qt::QueuedConnection); @@ -502,21 +420,21 @@ static void NotifyWatchonlyChanged(WalletModel *walletmodel, bool fHaveWatchonly void WalletModel::subscribeToCoreSignals() { // Connect signals to wallet - wallet->NotifyStatusChanged.connect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); - wallet->NotifyAddressBookChanged.connect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); - wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - wallet->NotifyWatchonlyChanged.connect(boost::bind(NotifyWatchonlyChanged, this, _1)); + m_handler_status_changed = m_ipc_wallet->handleStatusChanged(boost::bind(&NotifyKeyStoreStatusChanged, this)); + m_handler_address_book_changed = m_ipc_wallet->handleAddressBookChanged(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5)); + m_handler_transaction_changed = m_ipc_wallet->handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2)); + m_handler_show_progress = m_ipc_wallet->handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); + m_handler_watch_only_changed = m_ipc_wallet->handleWatchOnlyChanged(boost::bind(NotifyWatchonlyChanged, this, _1)); } void WalletModel::unsubscribeFromCoreSignals() { // Disconnect signals from wallet - wallet->NotifyStatusChanged.disconnect(boost::bind(&NotifyKeyStoreStatusChanged, this, _1)); - wallet->NotifyAddressBookChanged.disconnect(boost::bind(NotifyAddressBookChanged, this, _1, _2, _3, _4, _5, _6)); - wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); - wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); - wallet->NotifyWatchonlyChanged.disconnect(boost::bind(NotifyWatchonlyChanged, this, _1)); + m_handler_status_changed->disconnect(); + m_handler_address_book_changed->disconnect(); + m_handler_transaction_changed->disconnect(); + m_handler_show_progress->disconnect(); + m_handler_watch_only_changed->disconnect(); } // WalletModel::UnlockContext implementation @@ -556,21 +474,6 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) rhs.relock = false; } -bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const -{ - return wallet->GetPubKey(address, vchPubKeyOut); -} - -bool WalletModel::havePrivKey(const CKeyID &address) const -{ - return wallet->HaveKey(address); -} - -bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const -{ - return wallet->GetKey(address, vchPrivKeyOut); -} - // returns a list of COutputs from COutPoints void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) { @@ -602,33 +505,9 @@ void WalletModel::listCoins(std::map >& mapCoins) } } -bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const -{ - LOCK2(cs_main, wallet->cs_wallet); - return wallet->IsLockedCoin(hash, n); -} - -void WalletModel::lockCoin(COutPoint& output) -{ - LOCK2(cs_main, wallet->cs_wallet); - wallet->LockCoin(output); -} - -void WalletModel::unlockCoin(COutPoint& output) -{ - LOCK2(cs_main, wallet->cs_wallet); - wallet->UnlockCoin(output); -} - -void WalletModel::listLockedCoins(std::vector& vOutpts) -{ - LOCK2(cs_main, wallet->cs_wallet); - wallet->ListLockedCoins(vOutpts); -} - void WalletModel::loadReceiveRequests(std::vector& vReceiveRequests) { - vReceiveRequests = wallet->GetDestValues("rr"); // receive request + vReceiveRequests = m_ipc_wallet->getDestValues("rr"); // receive request } bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) @@ -639,27 +518,10 @@ bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t ss << nId; std::string key = "rr" + ss.str(); // "rr" prefix = "receive request" in destdata - LOCK(wallet->cs_wallet); if (sRequest.empty()) - return wallet->EraseDestData(dest, key); + return m_ipc_wallet->eraseDestData(dest, key); else - return wallet->AddDestData(dest, key, sRequest); -} - -bool WalletModel::transactionCanBeAbandoned(uint256 hash) const -{ - return wallet->TransactionCanBeAbandoned(hash); -} - -bool WalletModel::abandonTransaction(uint256 hash) const -{ - LOCK2(cs_main, wallet->cs_wallet); - return wallet->AbandonTransaction(hash); -} - -bool WalletModel::transactionCanBeBumped(uint256 hash) const -{ - return FeeBumper::TransactionCanBeBumped(wallet, hash); + return m_ipc_wallet->addDestData(dest, key, sRequest); } bool WalletModel::bumpFee(uint256 hash) @@ -670,7 +532,7 @@ bool WalletModel::bumpFee(uint256 hash) CAmount oldFee; CAmount newFee; CMutableTransaction mtx; - if (FeeBumper::CreateTransaction(wallet, hash, coin_control, 0 /* totalFee */, errors, oldFee, newFee, mtx) != BumpFeeResult::OK) { + if (!m_ipc_wallet->createBumpTransaction(hash, coin_control, 0 /* totalFee */, errors, oldFee, newFee, mtx)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "
(" + (errors.size() ? QString::fromStdString(errors[0]) : "") +")"); return false; @@ -709,13 +571,13 @@ bool WalletModel::bumpFee(uint256 hash) } // sign bumped transaction - if (!FeeBumper::SignTransaction(wallet, mtx)) { + if (!m_ipc_wallet->signBumpTransaction(mtx)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction.")); return false; } // commit the bumped transaction uint256 txid; - if(FeeBumper::CommitTransaction(wallet, hash, std::move(mtx), errors, txid) != BumpFeeResult::OK) { + if(!m_ipc_wallet->commitBumpTransaction(hash, std::move(mtx), errors, txid)) { QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "
(" + QString::fromStdString(errors[0])+")"); return false; @@ -727,18 +589,3 @@ bool WalletModel::isWalletEnabled() { return !gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); } - -bool WalletModel::hdEnabled() const -{ - return wallet->IsHDEnabled(); -} - -int WalletModel::getDefaultConfirmTarget() const -{ - return nTxConfirmTarget; -} - -bool WalletModel::getDefaultWalletRbf() const -{ - return fWalletRbf; -} diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6be36a57e25..7ca951fb0fc 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -8,6 +8,7 @@ #include "paymentrequestplus.h" #include "walletmodeltransaction.h" +#include "ipc/interfaces.h" #include "support/allocators/secure.h" #include @@ -100,7 +101,7 @@ class WalletModel : public QObject Q_OBJECT public: - explicit WalletModel(const PlatformStyle *platformStyle, CWallet *wallet, OptionsModel *optionsModel, QObject *parent = 0); + explicit WalletModel(std::unique_ptr ipc_wallet, ipc::Node& ipc_node, const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *optionsModel, QObject *parent = 0); ~WalletModel(); enum StatusCode // Returned by sendCoins @@ -129,13 +130,6 @@ class WalletModel : public QObject TransactionTableModel *getTransactionTableModel(); RecentRequestsTableModel *getRecentRequestsTableModel(); - CAmount getBalance(const CCoinControl *coinControl = nullptr) const; - CAmount getUnconfirmedBalance() const; - CAmount getImmatureBalance() const; - bool haveWatchOnly() const; - CAmount getWatchBalance() const; - CAmount getWatchUnconfirmedBalance() const; - CAmount getWatchImmatureBalance() const; EncryptionStatus getEncryptionStatus() const; // Check address for validity @@ -164,8 +158,6 @@ class WalletModel : public QObject // Passphrase only needed when unlocking bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString()); bool changePassphrase(const SecureString &oldPass, const SecureString &newPass); - // Wallet backup - bool backupWallet(const QString &filename); // RAI object for unlocking wallet, returned by requestUnlock() class UnlockContext @@ -189,36 +181,29 @@ class WalletModel : public QObject UnlockContext requestUnlock(); - bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - bool havePrivKey(const CKeyID &address) const; - bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const; void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); bool isSpent(const COutPoint& outpoint) const; void listCoins(std::map >& mapCoins) const; - bool isLockedCoin(uint256 hash, unsigned int n) const; - void lockCoin(COutPoint& output); - void unlockCoin(COutPoint& output); - void listLockedCoins(std::vector& vOutpts); - void loadReceiveRequests(std::vector& vReceiveRequests); bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest); - bool transactionCanBeAbandoned(uint256 hash) const; - bool abandonTransaction(uint256 hash) const; - - bool transactionCanBeBumped(uint256 hash) const; bool bumpFee(uint256 hash); static bool isWalletEnabled(); - bool hdEnabled() const; - - int getDefaultConfirmTarget() const; + ipc::Node& getIpcNode() const { return m_ipc_node; } - bool getDefaultWalletRbf() const; + ipc::Wallet& getIpcWallet() const { return *m_ipc_wallet; } private: + std::unique_ptr m_ipc_wallet; + std::unique_ptr m_handler_status_changed; + std::unique_ptr m_handler_address_book_changed; + std::unique_ptr m_handler_transaction_changed; + std::unique_ptr m_handler_show_progress; + std::unique_ptr m_handler_watch_only_changed; + ipc::Node& m_ipc_node; CWallet *wallet; bool fHaveWatchOnly; bool fForceCheckBalanceChanged; @@ -232,12 +217,7 @@ class WalletModel : public QObject RecentRequestsTableModel *recentRequestsTableModel; // Cache some values to be able to detect changes - CAmount cachedBalance; - CAmount cachedUnconfirmedBalance; - CAmount cachedImmatureBalance; - CAmount cachedWatchOnlyBalance; - CAmount cachedWatchUnconfBalance; - CAmount cachedWatchImmatureBalance; + ipc::WalletBalances m_cached_balances; EncryptionStatus cachedEncryptionStatus; int cachedNumBlocks; @@ -245,7 +225,7 @@ class WalletModel : public QObject void subscribeToCoreSignals(); void unsubscribeFromCoreSignals(); - void checkBalanceChanged(); + void checkBalanceChanged(const ipc::WalletBalances& new_balances); Q_SIGNALS: // Signal that balance in wallet changed diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 8bc9ef725e4..7f74232013b 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -4,22 +4,17 @@ #include "walletmodeltransaction.h" +#include "ipc/interfaces.h" #include "policy/policy.h" -#include "wallet/wallet.h" WalletModelTransaction::WalletModelTransaction(const QList &_recipients) : recipients(_recipients), - walletTransaction(0), - keyChange(0), fee(0) { - walletTransaction = new CWalletTx(); } WalletModelTransaction::~WalletModelTransaction() { - delete keyChange; - delete walletTransaction; } QList WalletModelTransaction::getRecipients() @@ -27,14 +22,14 @@ QList WalletModelTransaction::getRecipients() return recipients; } -CWalletTx *WalletModelTransaction::getTransaction() +std::unique_ptr& WalletModelTransaction::getWtx() { - return walletTransaction; + return wtx; } unsigned int WalletModelTransaction::getTransactionSize() { - return (!walletTransaction ? 0 : ::GetVirtualTransactionSize(*walletTransaction)); + return wtx ? wtx->getVirtualSize() : 0; } CAmount WalletModelTransaction::getTransactionFee() @@ -49,6 +44,7 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee) void WalletModelTransaction::reassignAmounts(int nChangePosRet) { + const CTransaction* walletTransaction = &wtx->get(); int i = 0; for (QList::iterator it = recipients.begin(); it != recipients.end(); ++it) { @@ -64,7 +60,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) if (out.amount() <= 0) continue; if (i == nChangePosRet) i++; - subtotal += walletTransaction->tx->vout[i].nValue; + subtotal += walletTransaction->vout[i].nValue; i++; } rcp.amount = subtotal; @@ -73,7 +69,7 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) { if (i == nChangePosRet) i++; - rcp.amount = walletTransaction->tx->vout[i].nValue; + rcp.amount = walletTransaction->vout[i].nValue; i++; } } @@ -88,13 +84,3 @@ CAmount WalletModelTransaction::getTotalTransactionAmount() } return totalTransactionAmount; } - -void WalletModelTransaction::newPossibleKeyChange(CWallet *wallet) -{ - keyChange = new CReserveKey(wallet); -} - -CReserveKey *WalletModelTransaction::getPossibleKeyChange() -{ - return keyChange; -} diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 64922efada5..9b406b6a33b 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -11,9 +11,10 @@ class SendCoinsRecipient; -class CReserveKey; -class CWallet; -class CWalletTx; +namespace ipc { +class Node; +class PendingWalletTx; +} /** Data model for a walletmodel transaction. */ class WalletModelTransaction @@ -24,7 +25,7 @@ class WalletModelTransaction QList getRecipients(); - CWalletTx *getTransaction(); + std::unique_ptr& getWtx(); unsigned int getTransactionSize(); void setTransactionFee(const CAmount& newFee); @@ -32,15 +33,11 @@ class WalletModelTransaction CAmount getTotalTransactionAmount(); - void newPossibleKeyChange(CWallet *wallet); - CReserveKey *getPossibleKeyChange(); - void reassignAmounts(int nChangePosRet); // needed for the subtract-fee-from-amount feature private: QList recipients; - CWalletTx *walletTransaction; - CReserveKey *keyChange; + std::unique_ptr wtx; CAmount fee; }; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 24c827fea6c..342e2c039f7 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -136,7 +136,7 @@ void WalletView::setWalletModel(WalletModel *_walletModel) updateEncryptionStatus(); // update HD status - Q_EMIT hdEnabledStatusChanged(_walletModel->hdEnabled()); + Q_EMIT hdEnabledStatusChanged(_walletModel->getIpcWallet().hdEnabled()); // Balloon pop-up for new transaction connect(_walletModel->getTransactionTableModel(), SIGNAL(rowsInserted(QModelIndex,int,int)), @@ -252,7 +252,7 @@ void WalletView::backupWallet() if (filename.isEmpty()) return; - if (!walletModel->backupWallet(filename)) { + if (!walletModel->getIpcWallet().backupWallet(filename.toLocal8Bit().data())) { Q_EMIT message(tr("Backup Failed"), tr("There was an error trying to save the wallet data to %1.").arg(filename), CClientUIInterface::MSG_ERROR); } From d7e84a528d739b68d886e6f6247793da39a16eb9 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 19:46:08 -0400 Subject: [PATCH 14/20] Remove direct bitcoin calls from qt/coincontroldialog.cpp --- src/ipc/interfaces.h | 34 +++++++++++++++++++++++ src/ipc/local/interfaces.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++ src/qt/coincontroldialog.cpp | 66 +++++++++++++++++++++----------------------- src/qt/walletmodel.cpp | 31 --------------------- src/qt/walletmodel.h | 4 --- 5 files changed, 128 insertions(+), 70 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 735108cb92e..5b68cf570b5 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -23,6 +23,7 @@ class CValidationState; class RPCTimerInterface; class UniValue; class proxyType; +enum class FeeReason; struct CNodeStateStats; struct CRecipient; @@ -32,6 +33,7 @@ class Handler; class Wallet; class PendingWalletTx; struct WalletBalances; +struct WalletTxOut; using WalletOrderForm = std::vector>; using WalletValueMap = std::map; @@ -156,9 +158,24 @@ class Node //! Get wallet rbf. virtual bool getWalletRbf() = 0; + //! Get required fee. + virtual CAmount getRequiredFee(unsigned int tx_bytes) = 0; + + //! Get minimum fee. + virtual CAmount getMinimumFee(unsigned int tx_bytes, const CCoinControl& coin_control, int* returned_target, FeeReason* reason) = 0; + //! Get max tx fee. virtual CAmount getMaxTxFee() = 0; + //! Estimate smart fee. + virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* answer_found_at_blocks = nullptr) = 0; + + //! Get dust relay fee. + virtual CFeeRate getDustRelayFee() = 0; + + //! Get pay tx fee. + virtual CFeeRate getPayTxFee() = 0; + //! Execute rpc command. virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; @@ -341,6 +358,14 @@ class Wallet //! Get available balance. virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0; + //! Return AvailableCoins + LockedCoins grouped by wallet address. + //! (put change in one group with wallet address) + using CoinsList = std::map>>; + virtual CoinsList listCoins() = 0; + + //! Return wallet transaction output information. + virtual std::vector getCoins(const std::vector& outputs) = 0; + // Return whether HD enabled. virtual bool hdEnabled() = 0; @@ -418,6 +443,15 @@ struct WalletBalances } }; +//! Wallet transaction output. +struct WalletTxOut +{ + CTxOut txout; + int64_t time; + int depth_in_main_chain = -1; + bool is_spent = false; +}; + //! Protocol IPC interface should use to communicate with implementation. enum Protocol { LOCAL, //!< Call functions linked into current executable. diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 06f3f797302..b8597a557ab 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -81,6 +81,17 @@ class PendingWalletTxImpl : public PendingWalletTx CReserveKey m_key; }; +//! Construct wallet TxOut struct. +WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) +{ + WalletTxOut result; + result.txout = wtx.tx->vout[n]; + result.time = wtx.GetTxTime(); + result.depth_in_main_chain = depth; + result.is_spent = wallet.IsSpent(wtx.GetHash(), n); + return result; +} + class WalletImpl : public Wallet { public: @@ -226,6 +237,36 @@ class WalletImpl : public Wallet { return m_wallet.GetAvailableBalance(&coin_control); } + CoinsList listCoins() override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + CoinsList result; + for (const auto& entry : m_wallet.ListCoins()) { + auto& group = result[entry.first]; + for (const auto& coin : entry.second) { + group.emplace_back( + COutPoint(coin.tx->GetHash(), coin.i), MakeWalletTxOut(m_wallet, *coin.tx, coin.i, coin.nDepth)); + } + } + return result; + } + std::vector getCoins(const std::vector& outputs) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + std::vector result; + result.reserve(outputs.size()); + for (const auto& output : outputs) { + result.emplace_back(); + auto it = m_wallet.mapWallet.find(output.hash); + if (it != m_wallet.mapWallet.end()) { + int depth = it->second.GetDepthInMainChain(); + if (depth >= 0) { + result.back() = MakeWalletTxOut(m_wallet, it->second, output.n, depth); + } + } + } + return result; + } bool hdEnabled() override { return m_wallet.IsHDEnabled(); } std::unique_ptr handleShowProgress(ShowProgressFn fn) override { @@ -404,7 +445,29 @@ class NodeImpl : public Node bool getNetworkActive() override { return ::g_connman && ::g_connman->GetNetworkActive(); } unsigned int getTxConfirmTarget() override { CHECK_WALLET(return ::nTxConfirmTarget); } bool getWalletRbf() override { CHECK_WALLET(return ::fWalletRbf); } + CAmount getRequiredFee(unsigned int tx_bytes) override { CHECK_WALLET(return CWallet::GetRequiredFee(tx_bytes)); } + CAmount getMinimumFee(unsigned int tx_bytes, + const CCoinControl& coin_control, + int* returned_target, + FeeReason* reason) override + { + FeeCalculation fee_calc; + CHECK_WALLET(return CWallet::GetMinimumFee(tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc)); + if (returned_target) *returned_target = fee_calc.returnedTarget; + if (reason) *reason = fee_calc.reason; + } CAmount getMaxTxFee() override { return ::maxTxFee; } + CFeeRate estimateSmartFee(int num_blocks, bool conservative, int* returned_target = nullptr) override + { + FeeCalculation fee_calc; + CFeeRate result = ::feeEstimator.estimateSmartFee(num_blocks, &fee_calc, conservative); + if (returned_target) { + *returned_target = fee_calc.returnedTarget; + } + return result; + } + CFeeRate getDustRelayFee() override { return ::dustRelayFee; } + CFeeRate getPayTxFee() override { CHECK_WALLET(return ::payTxFee); } UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override { JSONRPCRequest req; diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 8dd57ea2130..576df181fb3 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -15,10 +15,9 @@ #include "wallet/coincontrol.h" #include "init.h" -#include "policy/fees.h" +#include "ipc/interfaces.h" #include "policy/policy.h" #include "validation.h" // For mempool -#include "wallet/wallet.h" #include #include @@ -432,7 +431,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CTxOut txout(amount, (CScript)std::vector(24, 0)); txDummy.vout.push_back(txout); - fDust |= IsDust(txout, ::dustRelayFee); + fDust |= IsDust(txout, model->getIpcNode().getDustRelayFee()); } } @@ -446,16 +445,16 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) bool fWitness = false; std::vector vCoinControl; - std::vector vOutputs; coinControl->ListSelected(vCoinControl); - model->getOutputs(vCoinControl, vOutputs); - for (const COutput& out : vOutputs) { + size_t i = 0; + for (const auto& out : model->getIpcWallet().getCoins(vCoinControl)) { + if (out.depth_in_main_chain < 0) continue; + // unselect already spent, very unlikely scenario, this could happen // when selected are spent elsewhere, like rpc or another computer - uint256 txhash = out.tx->GetHash(); - COutPoint outpt(txhash, out.i); - if (model->isSpent(outpt)) + const COutPoint& outpt = vCoinControl[i++]; + if (out.is_spent) { coinControl->UnSelect(outpt); continue; @@ -465,18 +464,18 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nQuantity++; // Amount - nAmount += out.tx->tx->vout[out.i].nValue; + nAmount += out.txout.nValue; // Bytes CTxDestination address; int witnessversion = 0; std::vector witnessprogram; - if (out.tx->tx->vout[out.i].scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) + if (out.txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { nBytesInputs += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); fWitness = true; } - else if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address)) + else if(ExtractDestination(out.txout.scriptPubKey, address)) { CPubKey pubkey; CKeyID *keyid = boost::get(&address); @@ -510,7 +509,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = CWallet::GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nPayFee = model->getIpcNode().getMinimumFee(nBytes, *coinControl, nullptr /* returned_target */, nullptr /* reason */); if (nPayAmount > 0) { @@ -522,7 +521,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nChange > 0 && nChange < MIN_CHANGE) { CTxOut txout(nChange, (CScript)std::vector(24, 0)); - if (IsDust(txout, ::dustRelayFee)) + if (IsDust(txout, model->getIpcNode().getDustRelayFee())) { nPayFee += nChange; nChange = 0; @@ -616,13 +615,10 @@ void CoinControlDialog::updateView() int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); - std::map > mapCoins; - model->listCoins(mapCoins); - - for (const std::pair>& coins : mapCoins) { + for (const auto& coins : model->getIpcWallet().listCoins()) { CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem(); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - QString sWalletAddress = coins.first; + QString sWalletAddress = QString::fromStdString(CBitcoinAddress(coins.first).ToString()); QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); if (sWalletLabel.isEmpty()) sWalletLabel = tr("(no label)"); @@ -644,8 +640,10 @@ void CoinControlDialog::updateView() CAmount nSum = 0; int nChildren = 0; - for (const COutput& out : coins.second) { - nSum += out.tx->tx->vout[out.i].nValue; + for (const auto& outpair : coins.second) { + const COutPoint& output = std::get<0>(outpair); + const ipc::WalletTxOut& out = std::get<1>(outpair); + nSum += out.txout.nValue; nChildren++; CCoinControlWidgetItem *itemOutput; @@ -657,7 +655,7 @@ void CoinControlDialog::updateView() // address CTxDestination outputAddress; QString sAddress = ""; - if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) + if(ExtractDestination(out.txout.scriptPubKey, outputAddress)) { sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString()); @@ -682,35 +680,33 @@ void CoinControlDialog::updateView() } // amount - itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue)); - itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.tx->tx->vout[out.i].nValue)); // padding so that sorting works correctly + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.txout.nValue)); + itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong)out.txout.nValue)); // padding so that sorting works correctly // date - itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.tx->GetTxTime())); - itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.tx->GetTxTime())); + itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.time)); + itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.time)); // confirmations - itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.nDepth)); - itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.nDepth)); + itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(out.depth_in_main_chain)); + itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong)out.depth_in_main_chain)); // transaction hash - uint256 txhash = out.tx->GetHash(); - itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); + itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(output.hash.GetHex())); // vout index - itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); + itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(output.n)); // disable locked coins - if (model->getIpcWallet().isLockedCoin(COutPoint(txhash, out.i))) + if (model->getIpcWallet().isLockedCoin(output)) { - COutPoint outpt(txhash, out.i); - coinControl->UnSelect(outpt); // just to be sure + coinControl->UnSelect(output); // just to be sure itemOutput->setDisabled(true); itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); } // set checkbox - if (coinControl->IsSelected(COutPoint(txhash, out.i))) + if (coinControl->IsSelected(output)) itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index ff3ccbea51e..94bf9cff596 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -474,37 +474,6 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) rhs.relock = false; } -// returns a list of COutputs from COutPoints -void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) -{ - LOCK2(cs_main, wallet->cs_wallet); - for (const COutPoint& outpoint : vOutpoints) - { - if (!wallet->mapWallet.count(outpoint.hash)) continue; - int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain(); - if (nDepth < 0) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */); - vOutputs.push_back(out); - } -} - -bool WalletModel::isSpent(const COutPoint& outpoint) const -{ - LOCK2(cs_main, wallet->cs_wallet); - return wallet->IsSpent(outpoint.hash, outpoint.n); -} - -// AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) -void WalletModel::listCoins(std::map >& mapCoins) const -{ - for (auto& group : wallet->ListCoins()) { - auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())]; - for (auto& coin : group.second) { - resultGroup.emplace_back(std::move(coin)); - } - } -} - void WalletModel::loadReceiveRequests(std::vector& vReceiveRequests) { vReceiveRequests = m_ipc_wallet->getDestValues("rr"); // receive request diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 7ca951fb0fc..9ba9df9ceaa 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -181,10 +181,6 @@ class WalletModel : public QObject UnlockContext requestUnlock(); - void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); - bool isSpent(const COutPoint& outpoint) const; - void listCoins(std::map >& mapCoins) const; - void loadReceiveRequests(std::vector& vReceiveRequests); bool saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest); From fcbadfec5ea52541e468b0b6d96f463526c32ddc Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 18 Apr 2017 13:01:23 -0400 Subject: [PATCH 15/20] Remove direct bitcoin calls from qt/addresstablemodel.cpp --- src/ipc/interfaces.h | 19 +++++++++++++++ src/ipc/local/interfaces.cpp | 22 +++++++++++++++++ src/qt/addresstablemodel.cpp | 58 ++++++++++++++++++-------------------------- src/qt/addresstablemodel.h | 7 +++--- src/qt/walletmodel.cpp | 2 +- 5 files changed, 70 insertions(+), 38 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 5b68cf570b5..9faf7105651 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -32,6 +32,7 @@ namespace ipc { class Handler; class Wallet; class PendingWalletTx; +struct WalletAddress; struct WalletBalances; struct WalletTxOut; using WalletOrderForm = std::vector>; @@ -270,6 +271,9 @@ class Wallet //! Back up wallet. virtual bool backupWallet(const std::string& filename) = 0; + // Get key from pool. + virtual bool getKeyFromPool(CPubKey& pub_key, bool internal = false) = 0; + //! Get public key. virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0; @@ -285,11 +289,17 @@ class Wallet //! Add or update address. virtual bool setAddressBook(const CTxDestination& dest, const std::string& name, const std::string& purpose) = 0; + // Remove address. + virtual bool delAddressBook(const CTxDestination& dest) = 0; + //! Look up address in wallet, return whether exists. virtual bool getAddress(const CTxDestination& dest, std::string* name = nullptr, isminetype* is_mine = nullptr) = 0; + //! Get wallet address list. + virtual std::vector getAddresses() = 0; + //! Add dest data. virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0; @@ -423,6 +433,15 @@ class PendingWalletTx std::string& reject_reason) = 0; }; +//! Information about one wallet address. +struct WalletAddress +{ + CTxDestination dest; + isminetype is_mine; + std::string name; + std::string purpose; +}; + //! Collection of wallet balances. struct WalletBalances { diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index b8597a557ab..6f7bf58be77 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -111,6 +111,10 @@ class WalletImpl : public Wallet return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase); } bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); } + bool getKeyFromPool(CPubKey& pub_key, bool internal) override + { + return m_wallet.GetKeyFromPool(pub_key, internal); + } bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet.GetPubKey(address, pub_key); } bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet.GetKey(address, key); } bool havePrivKey(const CKeyID& address) override { return m_wallet.HaveKey(address); } @@ -120,6 +124,11 @@ class WalletImpl : public Wallet LOCK(m_wallet.cs_wallet); return m_wallet.SetAddressBook(dest, name, purpose); } + bool delAddressBook(const CTxDestination& dest) override + { + LOCK(m_wallet.cs_wallet); + return m_wallet.DelAddressBook(dest); + } bool getAddress(const CTxDestination& dest, std::string* name, isminetype* is_mine) override { LOCK(m_wallet.cs_wallet); @@ -135,6 +144,19 @@ class WalletImpl : public Wallet } return true; } + std::vector getAddresses() override + { + LOCK(m_wallet.cs_wallet); + std::vector result; + for (const auto& item : m_wallet.mapAddressBook) { + result.emplace_back(); + result.back().dest = item.first; + result.back().is_mine = ::IsMine(m_wallet, item.first); + result.back().name = item.second.name; + result.back().purpose = item.second.purpose; + } + return result; + } bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override { LOCK(m_wallet.cs_wallet); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 2fa032abdc4..70a841e9d9c 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -8,7 +8,7 @@ #include "walletmodel.h" #include "base58.h" -#include "wallet/wallet.h" +#include "ipc/interfaces.h" #include @@ -68,28 +68,23 @@ static AddressTableEntry::Type translateTransactionType(const QString &strPurpos class AddressTablePriv { public: - CWallet *wallet; QList cachedAddressTable; AddressTableModel *parent; - AddressTablePriv(CWallet *_wallet, AddressTableModel *_parent): - wallet(_wallet), parent(_parent) {} + AddressTablePriv(AddressTableModel *_parent): + parent(_parent) {} - void refreshAddressTable() + void refreshAddressTable(ipc::Wallet& ipc_wallet) { cachedAddressTable.clear(); { - LOCK(wallet->cs_wallet); - for (const std::pair& item : wallet->mapAddressBook) + for (const auto& address : ipc_wallet.getAddresses()) { - const CBitcoinAddress& address = item.first; - bool fMine = IsMine(*wallet, address.Get()); AddressTableEntry::Type addressType = translateTransactionType( - QString::fromStdString(item.second.purpose), fMine); - const std::string& strName = item.second.name; + QString::fromStdString(address.purpose), address.is_mine); cachedAddressTable.append(AddressTableEntry(addressType, - QString::fromStdString(strName), - QString::fromStdString(address.ToString()))); + QString::fromStdString(address.name), + QString::fromStdString(CBitcoinAddress(address.dest).ToString()))); } } // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order @@ -163,12 +158,12 @@ class AddressTablePriv } }; -AddressTableModel::AddressTableModel(CWallet *_wallet, WalletModel *parent) : - QAbstractTableModel(parent),walletModel(parent),wallet(_wallet),priv(0) +AddressTableModel::AddressTableModel(WalletModel *parent) : + QAbstractTableModel(parent),walletModel(parent),priv(0) { columns << tr("Label") << tr("Address"); - priv = new AddressTablePriv(wallet, this); - priv->refreshAddressTable(); + priv = new AddressTablePriv(this); + priv->refreshAddressTable(parent->getIpcWallet()); } AddressTableModel::~AddressTableModel() @@ -245,7 +240,6 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, if(role == Qt::EditRole) { - LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */ CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get(); if(index.column() == Label) { @@ -255,7 +249,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, editStatus = NO_CHANGES; return false; } - wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose); + walletModel->getIpcWallet().setAddressBook(curAddress, value.toString().toStdString(), strPurpose); } else if(index.column() == Address) { CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get(); // Refuse to set invalid address, set error status and return false @@ -272,7 +266,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, } // Check for duplicate addresses to prevent accidental deletion of addresses, if you try // to paste an existing address over another address (with a different label) - else if(wallet->mapAddressBook.count(newAddress)) + if (walletModel->getIpcWallet().getAddress(newAddress)) { editStatus = DUPLICATE_ADDRESS; return false; @@ -281,9 +275,9 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, else if(rec->type == AddressTableEntry::Sending) { // Remove old entry - wallet->DelAddressBook(curAddress); + walletModel->getIpcWallet().delAddressBook(curAddress); // Add new entry with new address - wallet->SetAddressBook(newAddress, rec->label.toStdString(), strPurpose); + walletModel->getIpcWallet().setAddressBook(newAddress, value.toString().toStdString(), strPurpose); } } return true; @@ -357,8 +351,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con } // Check for duplicate addresses { - LOCK(wallet->cs_wallet); - if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get())) + if(walletModel->getIpcWallet().getAddress(CBitcoinAddress(strAddress).Get())) { editStatus = DUPLICATE_ADDRESS; return QString(); @@ -369,7 +362,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con { // Generate a new address to associate with given label CPubKey newKey; - if(!wallet->GetKeyFromPool(newKey)) + if(!walletModel->getIpcWallet().getKeyFromPool(newKey)) { WalletModel::UnlockContext ctx(walletModel->requestUnlock()); if(!ctx.isValid()) @@ -378,7 +371,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con editStatus = WALLET_UNLOCK_FAILURE; return QString(); } - if(!wallet->GetKeyFromPool(newKey)) + if(!walletModel->getIpcWallet().getKeyFromPool(newKey)) { editStatus = KEY_GENERATION_FAILURE; return QString(); @@ -393,8 +386,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Add entry { - LOCK(wallet->cs_wallet); - wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel, + walletModel->getIpcWallet().setAddressBook(CBitcoinAddress(strAddress).Get(), strLabel, (type == Send ? "send" : "receive")); } return QString::fromStdString(strAddress); @@ -411,8 +403,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent return false; } { - LOCK(wallet->cs_wallet); - wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get()); + walletModel->getIpcWallet().delAddressBook(CBitcoinAddress(rec->address.toStdString()).Get()); } return true; } @@ -422,12 +413,11 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent QString AddressTableModel::labelForAddress(const QString &address) const { { - LOCK(wallet->cs_wallet); CBitcoinAddress address_parsed(address.toStdString()); - std::map::iterator mi = wallet->mapAddressBook.find(address_parsed.Get()); - if (mi != wallet->mapAddressBook.end()) + std::string name; + if (walletModel->getIpcWallet().getAddress(address_parsed.Get(), &name)) { - return QString::fromStdString(mi->second.name); + return QString::fromStdString(name); } } return QString(); diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index d04b95ebaeb..c9fe1987236 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -11,7 +11,9 @@ class AddressTablePriv; class WalletModel; -class CWallet; +namespace ipc { +class Wallet; +} /** Qt model of the address book in the core. This allows views to access and modify the address book. @@ -21,7 +23,7 @@ class AddressTableModel : public QAbstractTableModel Q_OBJECT public: - explicit AddressTableModel(CWallet *wallet, WalletModel *parent = 0); + explicit AddressTableModel(WalletModel *parent = 0); ~AddressTableModel(); enum ColumnIndex { @@ -76,7 +78,6 @@ class AddressTableModel : public QAbstractTableModel private: WalletModel *walletModel; - CWallet *wallet; AddressTablePriv *priv; QStringList columns; EditStatus editStatus; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 94bf9cff596..10f4b2e8b21 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -44,7 +44,7 @@ WalletModel::WalletModel(std::unique_ptr _ipcWallet, ipc::Node& _ip fHaveWatchOnly = m_ipc_wallet->haveWatchOnly(); fForceCheckBalanceChanged = false; - addressTableModel = new AddressTableModel(wallet, this); + addressTableModel = new AddressTableModel(this); transactionTableModel = new TransactionTableModel(platformStyle, wallet, this); recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); From fbc6270149eb3d8268ca00c464513922dc1bd6db Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 18 Apr 2017 13:23:23 -0400 Subject: [PATCH 16/20] Remove direct bitcoin calls from qt/paymentserver.cpp --- src/ipc/interfaces.h | 7 +++++++ src/ipc/local/interfaces.cpp | 5 +++++ src/qt/bitcoin.cpp | 6 +++--- src/qt/paymentserver.cpp | 32 +++++++++++++++++--------------- src/qt/paymentserver.h | 8 +++----- src/qt/test/paymentservertests.cpp | 2 +- src/qt/walletmodel.cpp | 2 +- src/qt/walletmodel.h | 2 +- 8 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index 9faf7105651..cb0707a9bb2 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -12,6 +12,7 @@ #include "ui_interface.h" // For ChangeType #include +#include #include #include #include @@ -59,6 +60,9 @@ class Node //! Choose network parameters. virtual void selectParams(const std::string& network) = 0; + //! Get network name. + virtual std::string getNetwork() = 0; + //! Init logging. virtual void initLogging() = 0; @@ -300,6 +304,9 @@ class Wallet //! Get wallet address list. virtual std::vector getAddresses() = 0; + // Get account addresses. + virtual std::set getAccountAddresses(const std::string& account) = 0; + //! Add dest data. virtual bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) = 0; diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 6f7bf58be77..a61c2b6b693 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -157,6 +157,10 @@ class WalletImpl : public Wallet } return result; } + std::set getAccountAddresses(const std::string& account) override + { + return m_wallet.GetAccountAddresses(account); + } bool addDestData(const CTxDestination& dest, const std::string& key, const std::string& value) override { LOCK(m_wallet.cs_wallet); @@ -326,6 +330,7 @@ class NodeImpl : public Node bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string& network) override { ::SelectParams(network); } + std::string getNetwork() override { return ::Params().NetworkIDString(); } void initLogging() override { ::InitLogging(); } void initParameterInteraction() override { ::InitParameterInteraction(); } std::string getWarnings(const std::string& type) override { return ::GetWarnings(type); } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index f1a2ab304fe..230a407d357 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -467,8 +467,8 @@ void BitcoinApplication::initializeResult(bool success) window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); - connect(walletModel, SIGNAL(coinsSent(CWallet*,SendCoinsRecipient,QByteArray)), - paymentServer, SLOT(fetchPaymentACK(CWallet*,const SendCoinsRecipient&,QByteArray))); + connect(walletModel, SIGNAL(coinsSent(WalletModel*,SendCoinsRecipient,QByteArray)), + paymentServer, SLOT(fetchPaymentACK(WalletModel*,const SendCoinsRecipient&,QByteArray))); } #endif @@ -626,7 +626,7 @@ int main(int argc, char *argv[]) } #ifdef ENABLE_WALLET // Parse URIs on command line -- this can affect Params() - PaymentServer::ipcParseCommandLine(argc, argv); + PaymentServer::ipcParseCommandLine(*ipc_node, argc, argv); #endif QScopedPointer networkStyle(NetworkStyle::instantiate(QString::fromStdString(Params().NetworkIDString()))); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index 67fe3201857..df3530ef32d 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -10,10 +10,12 @@ #include "base58.h" #include "chainparams.h" +#include "clientversion.h" +#include "ipc/interfaces.h" #include "policy/policy.h" #include "ui_interface.h" #include "util.h" -#include "wallet/wallet.h" +#include "utilstrencodings.h" #include @@ -199,7 +201,7 @@ void PaymentServer::LoadRootCAs(X509_STORE* _store) // Warning: ipcSendCommandLine() is called early in init, // so don't use "Q_EMIT message()", but "QMessageBox::"! // -void PaymentServer::ipcParseCommandLine(int argc, char* argv[]) +void PaymentServer::ipcParseCommandLine(ipc::Node& ipc_node, int argc, char* argv[]) { for (int i = 1; i < argc; i++) { @@ -223,12 +225,12 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[]) if (address.IsValid(*tempChainParams)) { - SelectParams(CBaseChainParams::MAIN); + ipc_node.selectParams(CBaseChainParams::MAIN); } else { tempChainParams = CreateChainParams(CBaseChainParams::TESTNET); if (address.IsValid(*tempChainParams)) - SelectParams(CBaseChainParams::TESTNET); + ipc_node.selectParams(CBaseChainParams::TESTNET); } } } @@ -241,11 +243,11 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[]) { if (request.getDetails().network() == "main") { - SelectParams(CBaseChainParams::MAIN); + ipc_node.selectParams(CBaseChainParams::MAIN); } else if (request.getDetails().network() == "test") { - SelectParams(CBaseChainParams::TESTNET); + ipc_node.selectParams(CBaseChainParams::TESTNET); } } } @@ -525,7 +527,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen if (request.IsInitialized()) { // Payment request network matches client network? - if (!verifyNetwork(request.getDetails())) { + if (!verifyNetwork(optionsModel->getIpcNode(), request.getDetails())) { Q_EMIT message(tr("Payment request rejected"), tr("Payment request network doesn't match client network."), CClientUIInterface::MSG_ERROR); @@ -582,7 +584,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); - if (IsDust(txOut, ::dustRelayFee)) { + if (IsDust(txOut, optionsModel->getIpcNode().getDustRelayFee())) { Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), CClientUIInterface::MSG_ERROR); @@ -620,7 +622,7 @@ void PaymentServer::fetchRequest(const QUrl& url) netManager->get(netRequest); } -void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction) +void PaymentServer::fetchPaymentACK(WalletModel* walletModel, SendCoinsRecipient recipient, QByteArray transaction) { const payments::PaymentDetails& details = recipient.paymentRequest.getDetails(); if (!details.has_payment_url()) @@ -640,7 +642,7 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien // Create a new refund address, or re-use: QString account = tr("Refund from %1").arg(recipient.authenticatedMerchant); std::string strAccount = account.toStdString(); - std::set refundAddresses = wallet->GetAccountAddresses(strAccount); + std::set refundAddresses = walletModel->getIpcWallet().getAccountAddresses(strAccount); if (!refundAddresses.empty()) { CScript s = GetScriptForDestination(*refundAddresses.begin()); payments::Output* refund_to = payment.add_refund_to(); @@ -648,9 +650,9 @@ void PaymentServer::fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipien } else { CPubKey newKey; - if (wallet->GetKeyFromPool(newKey)) { + if (walletModel->getIpcWallet().getKeyFromPool(newKey)) { CKeyID keyID = newKey.GetID(); - wallet->SetAddressBook(keyID, strAccount, "refund"); + walletModel->getIpcWallet().setAddressBook(keyID, strAccount, "refund"); CScript s = GetScriptForDestination(keyID); payments::Output* refund_to = payment.add_refund_to(); @@ -760,14 +762,14 @@ void PaymentServer::handlePaymentACK(const QString& paymentACKMsg) Q_EMIT message(tr("Payment acknowledged"), paymentACKMsg, CClientUIInterface::ICON_INFORMATION | CClientUIInterface::MODAL); } -bool PaymentServer::verifyNetwork(const payments::PaymentDetails& requestDetails) +bool PaymentServer::verifyNetwork(ipc::Node& ipc_node, const payments::PaymentDetails& requestDetails) { - bool fVerified = requestDetails.network() == Params().NetworkIDString(); + bool fVerified = requestDetails.network() == ipc_node.getNetwork(); if (!fVerified) { qWarning() << QString("PaymentServer::%1: Payment request network \"%2\" doesn't match client network \"%3\".") .arg(__func__) .arg(QString::fromStdString(requestDetails.network())) - .arg(QString::fromStdString(Params().NetworkIDString())); + .arg(QString::fromStdString(ipc_node.getNetwork())); } return fVerified; } diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 2fe6e3e7b1d..4e80c4b42bf 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -40,8 +40,6 @@ class OptionsModel; -class CWallet; - QT_BEGIN_NAMESPACE class QApplication; class QByteArray; @@ -62,7 +60,7 @@ class PaymentServer : public QObject public: // Parse URIs on command line // Returns false on error - static void ipcParseCommandLine(int argc, char *argv[]); + static void ipcParseCommandLine(ipc::Node& ipc_node, int argc, char *argv[]); // Returns true if there were URIs on the command line // which were successfully sent to an already-running @@ -89,7 +87,7 @@ class PaymentServer : public QObject void setOptionsModel(OptionsModel *optionsModel); // Verify that the payment request network matches the client network - static bool verifyNetwork(const payments::PaymentDetails& requestDetails); + static bool verifyNetwork(ipc::Node& ipc_node, const payments::PaymentDetails& requestDetails); // Verify if the payment request is expired static bool verifyExpired(const payments::PaymentDetails& requestDetails); // Verify the payment request size is valid as per BIP70 @@ -113,7 +111,7 @@ public Q_SLOTS: void uiReady(); // Submit Payment message to a merchant, get back PaymentACK: - void fetchPaymentACK(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction); + void fetchPaymentACK(WalletModel* walletModel, SendCoinsRecipient recipient, QByteArray transaction); // Handle an incoming URI, URI with local file scheme or file void handleURIOrFile(const QString& s); diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index 27aeb5f1174..1370291fde5 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -146,7 +146,7 @@ void PaymentServerTests::paymentServerTests() // Ensure the request is initialized, because network "main" is default, even for // uninitialized payment requests and that will fail our test here. QVERIFY(r.paymentRequest.IsInitialized()); - QCOMPARE(PaymentServer::verifyNetwork(r.paymentRequest.getDetails()), false); + QCOMPARE(PaymentServer::verifyNetwork(*ipc_node, r.paymentRequest.getDetails()), false); // Expired payment request (expires is set to 1 = 1970-01-01 00:00:01): data = DecodeBase64(paymentrequest2_cert2_BASE64); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 10f4b2e8b21..bc03591af83 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -290,7 +290,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran } } } - Q_EMIT coinsSent(wallet, rcp, transaction_array); + Q_EMIT coinsSent(this, rcp, transaction_array); } checkBalanceChanged(m_ipc_wallet->getBalances()); // update balance immediately, otherwise there could be a short noticeable delay until pollBalanceChanged hits diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 9ba9df9ceaa..f5a2aac8b81 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -240,7 +240,7 @@ class WalletModel : public QObject void message(const QString &title, const QString &message, unsigned int style); // Coins sent: from wallet, to recipient, in (serialized) transaction: - void coinsSent(CWallet* wallet, SendCoinsRecipient recipient, QByteArray transaction); + void coinsSent(WalletModel* wallet, SendCoinsRecipient recipient, QByteArray transaction); // Show progress dialog e.g. for rescan void showProgress(const QString &title, int nProgress); From 424d70fe24311296d27e7126062c55b01220f385 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 18 Apr 2017 16:42:30 -0400 Subject: [PATCH 17/20] Remove direct bitcoin calls from qt transaction table files --- src/ipc/interfaces.h | 76 +++++++++++++++++++ src/ipc/local/interfaces.cpp | 144 ++++++++++++++++++++++++++++++++++++ src/qt/bitcoin.cpp | 6 +- src/qt/recentrequeststablemodel.cpp | 3 +- src/qt/recentrequeststablemodel.h | 4 +- src/qt/test/wallettests.cpp | 2 +- src/qt/transactiondesc.cpp | 141 +++++++++++++++++++---------------- src/qt/transactiondesc.h | 12 ++- src/qt/transactionrecord.cpp | 87 ++++++++++------------ src/qt/transactionrecord.h | 16 ++-- src/qt/transactiontablemodel.cpp | 99 +++++++++---------------- src/qt/transactiontablemodel.h | 13 +++- src/qt/walletmodel.cpp | 8 +- src/qt/walletmodel.h | 4 +- 14 files changed, 409 insertions(+), 206 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index cb0707a9bb2..f504c1c7533 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -21,6 +21,7 @@ class CCoinControl; class CKey; class CNodeStats; class CValidationState; +class Coin; class RPCTimerInterface; class UniValue; class proxyType; @@ -35,7 +36,9 @@ class Wallet; class PendingWalletTx; struct WalletAddress; struct WalletBalances; +struct WalletTx; struct WalletTxOut; +struct WalletTxStatus; using WalletOrderForm = std::vector>; using WalletValueMap = std::map; @@ -72,6 +75,9 @@ class Node //! Get warnings. virtual std::string getWarnings(const std::string& type) = 0; + // Get log flags. + virtual uint32_t getLogCategories() = 0; + //! Initialize app dependencies. virtual bool baseInitialize() = 0; @@ -193,6 +199,9 @@ class Node //! Unset RPC timer interface. virtual void rpcUnsetTimerInterface(RPCTimerInterface* iface) = 0; + //! Get unspent outputs associated with a transaction. + virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0; + //! Return interface for accessing the wallet. virtual std::unique_ptr getWallet(size_t index) = 0; @@ -363,6 +372,29 @@ class Wallet std::vector& errors, uint256& bumped_txid) = 0; + //! Get a transaction. + virtual CTransactionRef getTx(const uint256& txid) = 0; + + //! Get transaction information. + virtual WalletTx getWalletTx(const uint256& txid) = 0; + + //! Get list of all wallet transactions. + virtual std::vector getWalletTxs() = 0; + + //! Try to get updated status for a particular transaction, if possible without blocking. + virtual bool tryGetTxStatus(const uint256& txid, + WalletTxStatus& tx_status, + int& num_blocks, + int64_t& adjusted_time) = 0; + + //! Get transaction details. + virtual WalletTx getWalletTxDetails(const uint256& txid, + WalletTxStatus& tx_status, + WalletOrderForm& order_form, + bool& in_mempool, + int& num_blocks, + int64_t& adjusted_time) = 0; + //! Get balances. virtual WalletBalances getBalances() = 0; @@ -375,6 +407,18 @@ class Wallet //! Get available balance. virtual CAmount getAvailableBalance(const CCoinControl& coin_control) = 0; + //! Return whether transaction input belongs to wallet. + virtual isminetype isMine(const CTxIn& txin) = 0; + + //! Return whether transaction output belongs to wallet. + virtual isminetype isMine(const CTxOut& txout) = 0; + + //! Return debit amount if transaction input belongs to wallet. + virtual CAmount getDebit(const CTxIn& txin, isminefilter filter) = 0; + + //! Return credit amount if transaction input belongs to wallet. + virtual CAmount getCredit(const CTxOut& txout, isminefilter filter) = 0; + //! Return AvailableCoins + LockedCoins grouped by wallet address. //! (put change in one group with wallet address) using CoinsList = std::map>>; @@ -469,6 +513,38 @@ struct WalletBalances } }; +// Wallet transaction information. +struct WalletTx +{ + CTransactionRef tx; + std::vector txin_is_mine; + std::vector txout_is_mine; + std::vector txout_address; + std::vector txout_address_is_mine; + CAmount credit; + CAmount debit; + CAmount change; + int64_t time; + std::map value_map; + bool is_coinbase; +}; + +//! Updated transaction status. +struct WalletTxStatus +{ + int block_height; + int blocks_to_maturity; + int depth_in_main_chain; + int request_count; + unsigned int time_received; + uint32_t lock_time; + bool is_final; + bool is_trusted; + bool is_abandoned; + bool is_coinbase; + bool is_in_main_chain; +}; + //! Wallet transaction output. struct WalletTxOut { diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index a61c2b6b693..538a647e466 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -81,6 +81,55 @@ class PendingWalletTxImpl : public PendingWalletTx CReserveKey m_key; }; +//! Construct wallet tx struct. +WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) +{ + WalletTx result; + result.tx = wtx.tx; + result.txin_is_mine.reserve(wtx.tx->vin.size()); + for (const auto& txin : wtx.tx->vin) { + result.txin_is_mine.emplace_back(wallet.IsMine(txin)); + } + result.txout_is_mine.reserve(wtx.tx->vout.size()); + result.txout_address.reserve(wtx.tx->vout.size()); + result.txout_address_is_mine.reserve(wtx.tx->vout.size()); + for (const auto& txout : wtx.tx->vout) { + result.txout_is_mine.emplace_back(wallet.IsMine(txout)); + result.txout_address.emplace_back(); + result.txout_address_is_mine.emplace_back( + ::ExtractDestination(txout.scriptPubKey, result.txout_address.back()) ? + ::IsMine(wallet, result.txout_address.back()) : + ISMINE_NO); + } + result.credit = wtx.GetCredit(ISMINE_ALL); + result.debit = wtx.GetDebit(ISMINE_ALL); + result.change = wtx.GetChange(); + result.time = wtx.GetTxTime(); + result.value_map = wtx.mapValue; + result.is_coinbase = wtx.IsCoinBase(); + return result; +} + +//! Construct wallet tx status struct. +WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx) +{ + WalletTxStatus result; + auto mi = ::mapBlockIndex.find(wtx.hashBlock); + CBlockIndex* block = mi != ::mapBlockIndex.end() ? mi->second : nullptr; + result.block_height = (block ? block->nHeight : std::numeric_limits::max()), + result.blocks_to_maturity = wtx.GetBlocksToMaturity(); + result.depth_in_main_chain = wtx.GetDepthInMainChain(); + result.request_count = wtx.GetRequestCount(); + result.time_received = wtx.nTimeReceived; + result.lock_time = wtx.tx->nLockTime; + result.is_final = ::CheckFinalTx(*wtx.tx); + result.is_trusted = wtx.IsTrusted(); + result.is_abandoned = wtx.isAbandoned(); + result.is_coinbase = wtx.IsCoinBase(); + result.is_in_main_chain = wtx.IsInMainChain(); + return result; +} + //! Construct wallet TxOut struct. WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) { @@ -232,6 +281,75 @@ class WalletImpl : public Wallet { return FeeBumper::CommitTransaction(&m_wallet, txid, std::move(mtx), errors, bumped_txid) == BumpFeeResult::OK; } + CTransactionRef getTx(const uint256& txid) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + auto mi = m_wallet.mapWallet.find(txid); + if (mi != m_wallet.mapWallet.end()) { + return mi->second.tx; + } + return {}; + } + WalletTx getWalletTx(const uint256& txid) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + auto mi = m_wallet.mapWallet.find(txid); + if (mi != m_wallet.mapWallet.end()) { + return MakeWalletTx(m_wallet, mi->second); + } + return {}; + } + std::vector getWalletTxs() override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + std::vector result; + result.reserve(m_wallet.mapWallet.size()); + for (const auto& entry : m_wallet.mapWallet) { + result.emplace_back(MakeWalletTx(m_wallet, entry.second)); + } + return result; + } + bool tryGetTxStatus(const uint256& txid, + ipc::WalletTxStatus& tx_status, + int& num_blocks, + int64_t& adjusted_time) override + { + TRY_LOCK(::cs_main, lockMain); + if (!lockMain) { + return false; + } + TRY_LOCK(m_wallet.cs_wallet, lockWallet); + if (!lockWallet) { + return false; + } + auto mi = m_wallet.mapWallet.find(txid); + if (mi == m_wallet.mapWallet.end()) { + return false; + } + num_blocks = ::chainActive.Height(); + adjusted_time = ::GetAdjustedTime(); + tx_status = MakeWalletTxStatus(mi->second); + return true; + } + WalletTx getWalletTxDetails(const uint256& txid, + WalletTxStatus& tx_status, + WalletOrderForm& order_form, + bool& in_mempool, + int& num_blocks, + int64_t& adjusted_time) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + auto mi = m_wallet.mapWallet.find(txid); + if (mi != m_wallet.mapWallet.end()) { + num_blocks = ::chainActive.Height(); + adjusted_time = ::GetAdjustedTime(); + in_mempool = mi->second.InMempool(); + order_form = mi->second.vOrderForm; + tx_status = MakeWalletTxStatus(mi->second); + return MakeWalletTx(m_wallet, mi->second); + } + return {}; + } WalletBalances getBalances() override { WalletBalances result; @@ -263,6 +381,26 @@ class WalletImpl : public Wallet { return m_wallet.GetAvailableBalance(&coin_control); } + isminetype isMine(const CTxIn& txin) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.IsMine(txin); + } + isminetype isMine(const CTxOut& txout) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.IsMine(txout); + } + CAmount getDebit(const CTxIn& txin, isminefilter filter) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.GetDebit(txin, filter); + } + CAmount getCredit(const CTxOut& txout, isminefilter filter) override + { + LOCK2(::cs_main, m_wallet.cs_wallet); + return m_wallet.GetCredit(txout, filter); + } CoinsList listCoins() override { LOCK2(::cs_main, m_wallet.cs_wallet); @@ -334,6 +472,7 @@ class NodeImpl : public Node void initLogging() override { ::InitLogging(); } void initParameterInteraction() override { ::InitParameterInteraction(); } std::string getWarnings(const std::string& type) override { return ::GetWarnings(type); } + uint32_t getLogCategories() override { return ::logCategories; } bool baseInitialize() override { return ::AppInitBasicSetup() && ::AppInitParameterInteraction() && ::AppInitSanityChecks() && @@ -506,6 +645,11 @@ class NodeImpl : public Node std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { ::RPCSetTimerInterfaceIfUnset(iface); } void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { ::RPCUnsetTimerInterface(iface); } + bool getUnspentOutput(const COutPoint& output, Coin& coin) override + { + LOCK(::cs_main); + return ::pcoinsTip->GetCoin(output, coin); + } std::unique_ptr getWallet(size_t index) override { CHECK_WALLET(return index < ::vpwallets.size() ? MakeUnique(*::vpwallets[index]) : diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 230a407d357..88a7d01f72b 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -34,10 +34,6 @@ #include "util.h" #include "warnings.h" -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" -#endif - #include #include @@ -462,7 +458,7 @@ void BitcoinApplication::initializeResult(bool success) // TODO: Expose secondary wallets if (auto walletMain = m_ipc_node.getWallet(0)) { - walletModel = new WalletModel(std::move(walletMain), m_ipc_node, platformStyle, vpwallets[0], optionsModel); + walletModel = new WalletModel(std::move(walletMain), m_ipc_node, platformStyle, optionsModel); window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 1c4f7aca863..2fd3e413881 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -12,10 +12,9 @@ #include "streams.h" -RecentRequestsTableModel::RecentRequestsTableModel(CWallet *wallet, WalletModel *parent) : +RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) : QAbstractTableModel(parent), walletModel(parent) { - Q_UNUSED(wallet); nReceiveRequestsMaxId = 0; // Load entries from wallet diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 0c02968f928..680bc79c52e 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -11,8 +11,6 @@ #include #include -class CWallet; - class RecentRequestEntry { public: @@ -60,7 +58,7 @@ class RecentRequestsTableModel: public QAbstractTableModel Q_OBJECT public: - explicit RecentRequestsTableModel(CWallet *wallet, WalletModel *parent); + explicit RecentRequestsTableModel(WalletModel *parent); ~RecentRequestsTableModel(); enum ColumnIndex { diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index f762ae6fd4f..5bba6e9f450 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -168,7 +168,7 @@ void TestSendCoins() auto ipc_node = ipc::MakeNode(ipc::LOCAL); OptionsModel optionsModel(*ipc_node); vpwallets.insert(vpwallets.begin(), &wallet); - WalletModel walletModel(ipc_node->getWallet(0), *ipc_node, platformStyle.get(), &wallet, &optionsModel); + WalletModel walletModel(ipc_node->getWallet(0), *ipc_node, platformStyle.get(), &optionsModel); vpwallets.erase(vpwallets.begin()); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index bcacc47ef3e..fe3fa3b7d77 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -11,35 +11,34 @@ #include "base58.h" #include "consensus/consensus.h" +#include "ipc/interfaces.h" #include "validation.h" #include "script/script.h" #include "timedata.h" #include "util.h" -#include "wallet/db.h" -#include "wallet/wallet.h" +#include "utilstrencodings.h" #include #include -QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) +QString TransactionDesc::FormatTxStatus(const ipc::WalletTx& wtx, const ipc::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime) { - AssertLockHeld(cs_main); - if (!CheckFinalTx(wtx)) + if (!status.is_final) { if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) - return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - chainActive.Height()); + return tr("Open for %n more block(s)", "", wtx.tx->nLockTime - numBlocks); else return tr("Open until %1").arg(GUIUtil::dateTimeStr(wtx.tx->nLockTime)); } else { - int nDepth = wtx.GetDepthInMainChain(); + int nDepth = status.depth_in_main_chain; if (nDepth < 0) return tr("conflicted with a transaction with %1 confirmations").arg(-nDepth); - else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + else if (adjustedTime - status.time_received > 2 * 60 && status.request_count == 0) return tr("%1/offline").arg(nDepth); else if (nDepth == 0) - return tr("0/unconfirmed, %1").arg((wtx.InMempool() ? tr("in memory pool") : tr("not in memory pool"))) + (wtx.isAbandoned() ? ", "+tr("abandoned") : ""); + return tr("0/unconfirmed, %1").arg((inMempool ? tr("in memory pool") : tr("not in memory pool"))) + (status.is_abandoned ? ", "+tr("abandoned") : ""); else if (nDepth < 6) return tr("%1/unconfirmed").arg(nDepth); else @@ -47,21 +46,27 @@ QString TransactionDesc::FormatTxStatus(const CWalletTx& wtx) } } -QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit) +QString TransactionDesc::toHTML(ipc::Node& ipc_node, ipc::Wallet& ipc_wallet, TransactionRecord *rec, int unit) { + int numBlocks; + int64_t adjustedTime; + ipc::WalletTxStatus status; + ipc::WalletOrderForm orderForm; + bool inMempool; + ipc::WalletTx wtx = ipc_wallet.getWalletTxDetails(rec->hash, status, orderForm, inMempool, numBlocks, adjustedTime); + QString strHTML; - LOCK2(cs_main, wallet->cs_wallet); strHTML.reserve(4000); strHTML += ""; - int64_t nTime = wtx.GetTxTime(); - CAmount nCredit = wtx.GetCredit(ISMINE_ALL); - CAmount nDebit = wtx.GetDebit(ISMINE_ALL); + int64_t nTime = wtx.time; + CAmount nCredit = wtx.credit; + CAmount nDebit = wtx.debit; CAmount nNet = nCredit - nDebit; - strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx); - int nRequests = wtx.GetRequestCount(); + strHTML += "" + tr("Status") + ": " + FormatTxStatus(wtx, status, inMempool, numBlocks, adjustedTime); + int nRequests = status.request_count; if (nRequests != -1) { if (nRequests == 0) @@ -76,14 +81,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // From // - if (wtx.IsCoinBase()) + if (wtx.is_coinbase) { strHTML += "" + tr("Source") + ": " + tr("Generated") + "
"; } - else if (wtx.mapValue.count("from") && !wtx.mapValue["from"].empty()) + else if (wtx.value_map.count("from") && !wtx.value_map["from"].empty()) { // Online transaction - strHTML += "" + tr("From") + ": " + GUIUtil::HtmlEscape(wtx.mapValue["from"]) + "
"; + strHTML += "" + tr("From") + ": " + GUIUtil::HtmlEscape(wtx.value_map["from"]) + "
"; } else { @@ -94,14 +99,16 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (CBitcoinAddress(rec->address).IsValid()) { CTxDestination address = CBitcoinAddress(rec->address).Get(); - if (wallet->mapAddressBook.count(address)) + std::string name; + isminetype ismine; + if (ipc_wallet.getAddress(address, &name, &ismine)) { strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; strHTML += "" + tr("To") + ": "; strHTML += GUIUtil::HtmlEscape(rec->address); - QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only"); - if (!wallet->mapAddressBook[address].name.empty()) - strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; + QString addressOwned = ismine == ISMINE_SPENDABLE ? tr("own address") : tr("watch-only"); + if (!name.empty()) + strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(name) + ")"; else strHTML += " (" + addressOwned + ")"; strHTML += "
"; @@ -113,31 +120,32 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // To // - if (wtx.mapValue.count("to") && !wtx.mapValue["to"].empty()) + if (wtx.value_map.count("to") && !wtx.value_map["to"].empty()) { // Online transaction - std::string strAddress = wtx.mapValue["to"]; + std::string strAddress = wtx.value_map["to"]; strHTML += "" + tr("To") + ": "; CTxDestination dest = CBitcoinAddress(strAddress).Get(); - if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) - strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; + std::string name; + if (ipc_wallet.getAddress(dest, &name) && !name.empty()) + strHTML += GUIUtil::HtmlEscape(name) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "
"; } // // Amount // - if (wtx.IsCoinBase() && nCredit == 0) + if (wtx.is_coinbase && nCredit == 0) { // // Coinbase // CAmount nUnmatured = 0; for (const CTxOut& txout : wtx.tx->vout) - nUnmatured += wallet->GetCredit(txout, ISMINE_ALL); + nUnmatured += ipc_wallet.getCredit(txout, ISMINE_ALL); strHTML += "" + tr("Credit") + ": "; - if (wtx.IsInMainChain()) - strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; + if (status.is_in_main_chain) + strHTML += BitcoinUnits::formatHtmlWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", status.blocks_to_maturity) + ")"; else strHTML += "(" + tr("not accepted") + ")"; strHTML += "
"; @@ -152,16 +160,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco else { isminetype fAllFromMe = ISMINE_SPENDABLE; - for (const CTxIn& txin : wtx.tx->vin) + for (isminetype mine : wtx.txin_is_mine) { - isminetype mine = wallet->IsMine(txin); if(fAllFromMe > mine) fAllFromMe = mine; } isminetype fAllToMe = ISMINE_SPENDABLE; - for (const CTxOut& txout : wtx.tx->vout) + for (isminetype mine : wtx.txout_is_mine) { - isminetype mine = wallet->IsMine(txout); if(fAllToMe > mine) fAllToMe = mine; } @@ -173,22 +179,24 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Debit // + auto mine = wtx.txout_is_mine.begin(); for (const CTxOut& txout : wtx.tx->vout) { // Ignore change - isminetype toSelf = wallet->IsMine(txout); + isminetype toSelf = *(mine++); if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE)) continue; - if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) + if (!wtx.value_map.count("to") || wtx.value_map["to"].empty()) { // Offline transaction CTxDestination address; if (ExtractDestination(txout.scriptPubKey, address)) { strHTML += "" + tr("To") + ": "; - if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) - strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; + std::string name; + if (ipc_wallet.getAddress(address, &name) && !name.empty()) + strHTML += GUIUtil::HtmlEscape(name) + " "; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); if(toSelf == ISMINE_SPENDABLE) strHTML += " (own address)"; @@ -206,7 +214,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (fAllToMe) { // Payment to self - CAmount nChange = wtx.GetChange(); + CAmount nChange = wtx.change; CAmount nValue = nCredit - nChange; strHTML += "" + tr("Total debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -nValue) + "
"; strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, nValue) + "
"; @@ -221,12 +229,18 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Mixed debit transaction // - for (const CTxIn& txin : wtx.tx->vin) - if (wallet->IsMine(txin)) - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
"; - for (const CTxOut& txout : wtx.tx->vout) - if (wallet->IsMine(txout)) - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; + auto mine = wtx.txin_is_mine.begin(); + for (const CTxIn& txin : wtx.tx->vin) { + if (*(mine++)) { + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -ipc_wallet.getDebit(txin, ISMINE_ALL)) + "
"; + } + } + mine = wtx.txout_is_mine.begin(); + for (const CTxOut& txout : wtx.tx->vout) { + if (*(mine++)) { + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, ipc_wallet.getCredit(txout, ISMINE_ALL)) + "
"; + } + } } } @@ -235,24 +249,24 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Message // - if (wtx.mapValue.count("message") && !wtx.mapValue["message"].empty()) - strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["message"], true) + "
"; - if (wtx.mapValue.count("comment") && !wtx.mapValue["comment"].empty()) - strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.mapValue["comment"], true) + "
"; + if (wtx.value_map.count("message") && !wtx.value_map["message"].empty()) + strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(wtx.value_map["message"], true) + "
"; + if (wtx.value_map.count("comment") && !wtx.value_map["comment"].empty()) + strHTML += "
" + tr("Comment") + ":
" + GUIUtil::HtmlEscape(wtx.value_map["comment"], true) + "
"; strHTML += "" + tr("Transaction ID") + ": " + rec->getTxID() + "
"; strHTML += "" + tr("Transaction total size") + ": " + QString::number(wtx.tx->GetTotalSize()) + " bytes
"; strHTML += "" + tr("Output index") + ": " + QString::number(rec->getOutputIndex()) + "
"; // Message from normal bitcoin:URI (bitcoin:123...?message=example) - for (const std::pair& r : wtx.vOrderForm) + for (const std::pair& r : orderForm) if (r.first == "Message") strHTML += "
" + tr("Message") + ":
" + GUIUtil::HtmlEscape(r.second, true) + "
"; // // PaymentRequest info: // - for (const std::pair& r : wtx.vOrderForm) + for (const std::pair& r : orderForm) { if (r.first == "PaymentRequest") { @@ -264,7 +278,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco } } - if (wtx.IsCoinBase()) + if (wtx.is_coinbase) { quint32 numBlocksToMaturity = COINBASE_MATURITY + 1; strHTML += "
" + tr("Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to \"not accepted\" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.").arg(QString::number(numBlocksToMaturity)) + "
"; @@ -273,15 +287,15 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // // Debug view // - if (logCategories != BCLog::NONE) + if (ipc_node.getLogCategories() != BCLog::NONE) { strHTML += "

" + tr("Debug information") + "

"; for (const CTxIn& txin : wtx.tx->vin) - if(wallet->IsMine(txin)) - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "
"; + if(ipc_wallet.isMine(txin)) + strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, -ipc_wallet.getDebit(txin, ISMINE_ALL)) + "
"; for (const CTxOut& txout : wtx.tx->vout) - if(wallet->IsMine(txout)) - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "
"; + if(ipc_wallet.isMine(txout)) + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatHtmlWithUnit(unit, ipc_wallet.getCredit(txout, ISMINE_ALL)) + "
"; strHTML += "
" + tr("Transaction") + ":
"; strHTML += GUIUtil::HtmlEscape(wtx.tx->ToString(), true); @@ -294,7 +308,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco COutPoint prevout = txin.prevout; Coin prev; - if(pcoinsTip->GetCoin(prevout, prev)) + if(ipc_node.getUnspentOutput(prevout, prev)) { { strHTML += "
  • "; @@ -302,13 +316,14 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco CTxDestination address; if (ExtractDestination(vout.scriptPubKey, address)) { - if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) - strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; + std::string name; + if (ipc_wallet.getAddress(address, &name) && !name.empty()) + strHTML += GUIUtil::HtmlEscape(name) + " "; strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); - strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "
  • "; - strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + ""; + strHTML = strHTML + " IsMine=" + (ipc_wallet.isMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + ""; + strHTML = strHTML + " IsWatchOnly=" + (ipc_wallet.isMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + ""; } } } diff --git a/src/qt/transactiondesc.h b/src/qt/transactiondesc.h index 01b90b130f6..31aaf3aae84 100644 --- a/src/qt/transactiondesc.h +++ b/src/qt/transactiondesc.h @@ -10,8 +10,12 @@ class TransactionRecord; -class CWallet; -class CWalletTx; +namespace ipc { +class Node; +class Wallet; +class WalletTx; +class WalletTxStatus; +} /** Provide a human-readable extended HTML description of a transaction. */ @@ -20,12 +24,12 @@ class TransactionDesc: public QObject Q_OBJECT public: - static QString toHTML(CWallet *wallet, CWalletTx &wtx, TransactionRecord *rec, int unit); + static QString toHTML(ipc::Node& ipc_node, ipc::Wallet& ipc_wallet, TransactionRecord *rec, int unit); private: TransactionDesc() {} - static QString FormatTxStatus(const CWalletTx& wtx); + static QString FormatTxStatus(const ipc::WalletTx& wtx, const ipc::WalletTxStatus& status, bool inMempool, int numBlocks, int64_t adjustedTime); }; #endif // BITCOIN_QT_TRANSACTIONDESC_H diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 2ece0d0f289..18d4ac06da9 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -6,16 +6,16 @@ #include "base58.h" #include "consensus/consensus.h" +#include "ipc/interfaces.h" #include "validation.h" #include "timedata.h" -#include "wallet/wallet.h" #include /* Return positive answer if transaction should be shown in list. */ -bool TransactionRecord::showTransaction(const CWalletTx &wtx) +bool TransactionRecord::showTransaction() { // There are currently no cases where we hide transactions, but // we may want to use this in the future for things like RBF. @@ -25,17 +25,17 @@ bool TransactionRecord::showTransaction(const CWalletTx &wtx) /* * Decompose CWallet transaction to model transaction records. */ -QList TransactionRecord::decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx) +QList TransactionRecord::decomposeTransaction(const ipc::WalletTx& wtx) { QList parts; - int64_t nTime = wtx.GetTxTime(); - CAmount nCredit = wtx.GetCredit(ISMINE_ALL); - CAmount nDebit = wtx.GetDebit(ISMINE_ALL); + int64_t nTime = wtx.time; + CAmount nCredit = wtx.credit; + CAmount nDebit = wtx.debit; CAmount nNet = nCredit - nDebit; - uint256 hash = wtx.GetHash(); - std::map mapValue = wtx.mapValue; + uint256 hash = wtx.tx->GetHash(); + std::map mapValue = wtx.value_map; - if (nNet > 0 || wtx.IsCoinBase()) + if (nNet > 0 || wtx.is_coinbase) { // // Credit @@ -43,7 +43,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * for(unsigned int i = 0; i < wtx.tx->vout.size(); i++) { const CTxOut& txout = wtx.tx->vout[i]; - isminetype mine = wallet->IsMine(txout); + isminetype mine = wtx.txout_is_mine[i]; if(mine) { TransactionRecord sub(hash, nTime); @@ -51,11 +51,11 @@ QList TransactionRecord::decomposeTransaction(const CWallet * sub.idx = i; // vout index sub.credit = txout.nValue; sub.involvesWatchAddress = mine & ISMINE_WATCH_ONLY; - if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address)) + if (wtx.txout_address_is_mine[i]) { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = CBitcoinAddress(wtx.txout_address[i]).ToString(); } else { @@ -63,7 +63,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * sub.type = TransactionRecord::RecvFromOther; sub.address = mapValue["from"]; } - if (wtx.IsCoinBase()) + if (wtx.is_coinbase) { // Generated sub.type = TransactionRecord::Generated; @@ -77,17 +77,15 @@ QList TransactionRecord::decomposeTransaction(const CWallet * { bool involvesWatchAddress = false; isminetype fAllFromMe = ISMINE_SPENDABLE; - for (const CTxIn& txin : wtx.tx->vin) + for (isminetype mine : wtx.txin_is_mine) { - isminetype mine = wallet->IsMine(txin); if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(fAllFromMe > mine) fAllFromMe = mine; } isminetype fAllToMe = ISMINE_SPENDABLE; - for (const CTxOut& txout : wtx.tx->vout) + for (isminetype mine : wtx.txout_is_mine) { - isminetype mine = wallet->IsMine(txout); if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(fAllToMe > mine) fAllToMe = mine; } @@ -95,7 +93,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * if (fAllFromMe && fAllToMe) { // Payment to self - CAmount nChange = wtx.GetChange(); + CAmount nChange = wtx.change; parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "", -(nDebit - nChange), nCredit - nChange)); @@ -115,19 +113,18 @@ QList TransactionRecord::decomposeTransaction(const CWallet * sub.idx = nOut; sub.involvesWatchAddress = involvesWatchAddress; - if(wallet->IsMine(txout)) + if(wtx.txout_is_mine[nOut]) { // Ignore parts sent to self, as this is usually the change // from a transaction sent back to our own address. continue; } - CTxDestination address; - if (ExtractDestination(txout.scriptPubKey, address)) + if (!boost::get(&wtx.txout_address[nOut])) { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = CBitcoinAddress(wtx.txout_address[nOut]).ToString(); } else { @@ -161,53 +158,46 @@ QList TransactionRecord::decomposeTransaction(const CWallet * return parts; } -void TransactionRecord::updateStatus(const CWalletTx &wtx) +void TransactionRecord::updateStatus(const ipc::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime) { - AssertLockHeld(cs_main); // Determine transaction status - // Find the block the tx is in - CBlockIndex* pindex = nullptr; - BlockMap::iterator mi = mapBlockIndex.find(wtx.hashBlock); - if (mi != mapBlockIndex.end()) - pindex = (*mi).second; - // Sort order, unrecorded transactions sort to the top status.sortKey = strprintf("%010d-%01d-%010u-%03d", - (pindex ? pindex->nHeight : std::numeric_limits::max()), - (wtx.IsCoinBase() ? 1 : 0), - wtx.nTimeReceived, + wtx.block_height, + wtx.is_coinbase ? 1 : 0, + wtx.time_received, idx); - status.countsForBalance = wtx.IsTrusted() && !(wtx.GetBlocksToMaturity() > 0); - status.depth = wtx.GetDepthInMainChain(); - status.cur_num_blocks = chainActive.Height(); + status.countsForBalance = wtx.is_trusted && !(wtx.blocks_to_maturity > 0); + status.depth = wtx.depth_in_main_chain; + status.cur_num_blocks = numBlocks; - if (!CheckFinalTx(wtx)) + if (!wtx.is_final) { - if (wtx.tx->nLockTime < LOCKTIME_THRESHOLD) + if (wtx.lock_time < LOCKTIME_THRESHOLD) { status.status = TransactionStatus::OpenUntilBlock; - status.open_for = wtx.tx->nLockTime - chainActive.Height(); + status.open_for = wtx.lock_time - numBlocks; } else { status.status = TransactionStatus::OpenUntilDate; - status.open_for = wtx.tx->nLockTime; + status.open_for = wtx.lock_time; } } // For generated transactions, determine maturity else if(type == TransactionRecord::Generated) { - if (wtx.GetBlocksToMaturity() > 0) + if (wtx.blocks_to_maturity > 0) { status.status = TransactionStatus::Immature; - if (wtx.IsInMainChain()) + if (wtx.is_in_main_chain) { - status.matures_in = wtx.GetBlocksToMaturity(); + status.matures_in = wtx.blocks_to_maturity; // Check if the block was requested by anyone - if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0) status.status = TransactionStatus::MaturesWarning; } else @@ -226,14 +216,14 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) { status.status = TransactionStatus::Conflicted; } - else if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0) + else if (adjustedTime - wtx.time_received > 2 * 60 && wtx.request_count == 0) { status.status = TransactionStatus::Offline; } else if (status.depth == 0) { status.status = TransactionStatus::Unconfirmed; - if (wtx.isAbandoned()) + if (wtx.is_abandoned) status.status = TransactionStatus::Abandoned; } else if (status.depth < RecommendedNumConfirmations) @@ -248,10 +238,9 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) status.needsUpdate = false; } -bool TransactionRecord::statusUpdateNeeded() +bool TransactionRecord::statusUpdateNeeded(int numBlocks) { - AssertLockHeld(cs_main); - return status.cur_num_blocks != chainActive.Height() || status.needsUpdate; + return status.cur_num_blocks != numBlocks || status.needsUpdate; } QString TransactionRecord::getTxID() const diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 59f681224fc..1962503f064 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -11,8 +11,12 @@ #include #include -class CWallet; -class CWalletTx; +namespace ipc { +class Node; +class Wallet; +struct WalletTx; +struct WalletTxStatus; +} /** UI model for transaction status. The transaction status is the part of a transaction that will change over time. */ @@ -106,8 +110,8 @@ class TransactionRecord /** Decompose CWallet transaction to model transaction records. */ - static bool showTransaction(const CWalletTx &wtx); - static QList decomposeTransaction(const CWallet *wallet, const CWalletTx &wtx); + static bool showTransaction(); + static QList decomposeTransaction(const ipc::WalletTx& wtx); /** @name Immutable transaction attributes @{*/ @@ -136,11 +140,11 @@ class TransactionRecord /** Update status from core wallet tx. */ - void updateStatus(const CWalletTx &wtx); + void updateStatus(const ipc::WalletTxStatus& wtx, int numBlocks, int64_t adjustedTime); /** Return whether a status update is needed. */ - bool statusUpdateNeeded(); + bool statusUpdateNeeded(int numBlocks); }; #endif // BITCOIN_QT_TRANSACTIONRECORD_H diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 59cef555b1a..5588ebb15f6 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -14,11 +14,11 @@ #include "walletmodel.h" #include "core_io.h" +#include "ipc/interfaces.h" #include "validation.h" #include "sync.h" #include "uint256.h" #include "util.h" -#include "wallet/wallet.h" #include #include @@ -57,13 +57,11 @@ struct TxLessThan class TransactionTablePriv { public: - TransactionTablePriv(CWallet *_wallet, TransactionTableModel *_parent) : - wallet(_wallet), + TransactionTablePriv(TransactionTableModel *_parent) : parent(_parent) { } - CWallet *wallet; TransactionTableModel *parent; /* Local cache of wallet. @@ -74,16 +72,15 @@ class TransactionTablePriv /* Query entire wallet anew from core. */ - void refreshWallet() + void refreshWallet(ipc::Wallet& ipc_wallet) { qDebug() << "TransactionTablePriv::refreshWallet"; cachedWallet.clear(); { - LOCK2(cs_main, wallet->cs_wallet); - for(std::map::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it) - { - if(TransactionRecord::showTransaction(it->second)) - cachedWallet.append(TransactionRecord::decomposeTransaction(wallet, it->second)); + for (const auto& wtx : ipc_wallet.getWalletTxs()) { + if (TransactionRecord::showTransaction()) { + cachedWallet.append(TransactionRecord::decomposeTransaction(wtx)); + } } } } @@ -93,7 +90,7 @@ class TransactionTablePriv Call with transaction that was added, removed or changed. */ - void updateWallet(const uint256 &hash, int status, bool showTransaction) + void updateWallet(ipc::Wallet& ipc_wallet, const uint256 &hash, int status, bool showTransaction) { qDebug() << "TransactionTablePriv::updateWallet: " + QString::fromStdString(hash.ToString()) + " " + QString::number(status); @@ -128,17 +125,16 @@ class TransactionTablePriv } if(showTransaction) { - LOCK2(cs_main, wallet->cs_wallet); // Find transaction in wallet - std::map::iterator mi = wallet->mapWallet.find(hash); - if(mi == wallet->mapWallet.end()) + ipc::WalletTx wtx = ipc_wallet.getWalletTx(hash); + if(!wtx.tx) { qWarning() << "TransactionTablePriv::updateWallet: Warning: Got CT_NEW, but transaction is not in wallet"; break; } // Added -- insert at the right position QList toInsert = - TransactionRecord::decomposeTransaction(wallet, mi->second); + TransactionRecord::decomposeTransaction(wtx); if(!toInsert.isEmpty()) /* only if something to insert */ { parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1); @@ -179,7 +175,7 @@ class TransactionTablePriv return cachedWallet.size(); } - TransactionRecord *index(int idx) + TransactionRecord *index(ipc::Wallet& ipc_wallet, int idx) { if(idx >= 0 && idx < cachedWallet.size()) { @@ -192,61 +188,42 @@ class TransactionTablePriv // If a status update is needed (blocks came in since last check), // update the status of this transaction from the wallet. Otherwise, // simply re-use the cached status. - TRY_LOCK(cs_main, lockMain); - if(lockMain) - { - TRY_LOCK(wallet->cs_wallet, lockWallet); - if(lockWallet && rec->statusUpdateNeeded()) - { - std::map::iterator mi = wallet->mapWallet.find(rec->hash); - - if(mi != wallet->mapWallet.end()) - { - rec->updateStatus(mi->second); - } - } + ipc::WalletTxStatus wtx; + int numBlocks; + int64_t adjustedTime; + if (ipc_wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, adjustedTime) && rec->statusUpdateNeeded(numBlocks)) { + rec->updateStatus(wtx, numBlocks, adjustedTime); } return rec; } return 0; } - QString describe(TransactionRecord *rec, int unit) + QString describe(ipc::Node& ipc_node, ipc::Wallet& ipc_wallet, TransactionRecord *rec, int unit) { - { - LOCK2(cs_main, wallet->cs_wallet); - std::map::iterator mi = wallet->mapWallet.find(rec->hash); - if(mi != wallet->mapWallet.end()) - { - return TransactionDesc::toHTML(wallet, mi->second, rec, unit); - } - } - return QString(); + return TransactionDesc::toHTML(ipc_node, ipc_wallet, rec, unit); } - QString getTxHex(TransactionRecord *rec) + QString getTxHex(ipc::Wallet& ipc_wallet, TransactionRecord *rec) { - LOCK2(cs_main, wallet->cs_wallet); - std::map::iterator mi = wallet->mapWallet.find(rec->hash); - if(mi != wallet->mapWallet.end()) - { - std::string strHex = EncodeHexTx(static_cast(mi->second)); + auto tx = ipc_wallet.getTx(rec->hash); + if (tx) { + std::string strHex = EncodeHexTx(*tx); return QString::fromStdString(strHex); } return QString(); } }; -TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, CWallet* _wallet, WalletModel *parent): +TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle, WalletModel *parent): QAbstractTableModel(parent), - wallet(_wallet), walletModel(parent), - priv(new TransactionTablePriv(_wallet, this)), + priv(new TransactionTablePriv(this)), fProcessingQueuedTransactions(false), platformStyle(_platformStyle) { columns << QString() << QString() << tr("Date") << tr("Type") << tr("Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); - priv->refreshWallet(); + priv->refreshWallet(walletModel->getIpcWallet()); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); @@ -271,7 +248,7 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status, b uint256 updated; updated.SetHex(hash.toStdString()); - priv->updateWallet(updated, status, showTransaction); + priv->updateWallet(walletModel->getIpcWallet(), updated, status, showTransaction); } void TransactionTableModel::updateConfirmations() @@ -608,7 +585,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case WatchonlyDecorationRole: return txWatchonlyDecoration(rec); case LongDescriptionRole: - return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit()); + return priv->describe(walletModel->getIpcNode(), walletModel->getIpcWallet(), rec, walletModel->getOptionsModel()->getDisplayUnit()); case AddressRole: return QString::fromStdString(rec->address); case LabelRole: @@ -620,7 +597,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const case TxHashRole: return QString::fromStdString(rec->hash.ToString()); case TxHexRole: - return priv->getTxHex(rec); + return priv->getTxHex(walletModel->getIpcWallet(), rec); case TxPlainTextRole: { QString details; @@ -696,10 +673,10 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const { Q_UNUSED(parent); - TransactionRecord *data = priv->index(row); + TransactionRecord *data = priv->index(walletModel->getIpcWallet(), row); if(data) { - return createIndex(row, column, priv->index(row)); + return createIndex(row, column, priv->index(walletModel->getIpcWallet(), row)); } return QModelIndex(); } @@ -737,13 +714,11 @@ struct TransactionNotification static bool fQueueNotifications = false; static std::vector< TransactionNotification > vQueueNotifications; -static void NotifyTransactionChanged(TransactionTableModel *ttm, CWallet *wallet, const uint256 &hash, ChangeType status) +static void NotifyTransactionChanged(TransactionTableModel *ttm, const uint256 &hash, ChangeType status) { // Find transaction in wallet - std::map::iterator mi = wallet->mapWallet.find(hash); // Determine whether to show transaction or not (determine this here so that no relocking is needed in GUI thread) - bool inWallet = mi != wallet->mapWallet.end(); - bool showTransaction = (inWallet && TransactionRecord::showTransaction(mi->second)); + bool showTransaction = TransactionRecord::showTransaction(); TransactionNotification notification(hash, status, showTransaction); @@ -779,13 +754,13 @@ static void ShowProgress(TransactionTableModel *ttm, const std::string &title, i void TransactionTableModel::subscribeToCoreSignals() { // Connect signals to wallet - wallet->NotifyTransactionChanged.connect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + m_handler_transaction_changed = walletModel->getIpcWallet().handleTransactionChanged(boost::bind(NotifyTransactionChanged, this, _1, _2)); + m_handler_show_progress = walletModel->getIpcWallet().handleShowProgress(boost::bind(ShowProgress, this, _1, _2)); } void TransactionTableModel::unsubscribeFromCoreSignals() { // Disconnect signals from wallet - wallet->NotifyTransactionChanged.disconnect(boost::bind(NotifyTransactionChanged, this, _1, _2, _3)); - wallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + m_handler_transaction_changed->disconnect(); + m_handler_show_progress->disconnect(); } diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 80aeb64c41d..a0c648559e9 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -10,13 +10,17 @@ #include #include +#include + +namespace ipc { +class Handler; +} + class PlatformStyle; class TransactionRecord; class TransactionTablePriv; class WalletModel; -class CWallet; - /** UI model for the transaction table of a wallet. */ class TransactionTableModel : public QAbstractTableModel @@ -24,7 +28,7 @@ class TransactionTableModel : public QAbstractTableModel Q_OBJECT public: - explicit TransactionTableModel(const PlatformStyle *platformStyle, CWallet* wallet, WalletModel *parent = 0); + explicit TransactionTableModel(const PlatformStyle *platformStyle, WalletModel *parent = 0); ~TransactionTableModel(); enum ColumnIndex { @@ -82,8 +86,9 @@ class TransactionTableModel : public QAbstractTableModel bool processingQueuedTransactions() { return fProcessingQueuedTransactions; } private: - CWallet* wallet; WalletModel *walletModel; + std::unique_ptr m_handler_transaction_changed; + std::unique_ptr m_handler_show_progress; QStringList columns; TransactionTablePriv *priv; bool fProcessingQueuedTransactions; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index bc03591af83..c7a99a3c45f 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -34,8 +34,8 @@ #include -WalletModel::WalletModel(std::unique_ptr _ipcWallet, ipc::Node& _ipcNode, const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *_optionsModel, QObject *parent) : - QObject(parent), m_ipc_wallet(std::move(_ipcWallet)), m_ipc_node(_ipcNode), wallet(_wallet), optionsModel(_optionsModel), addressTableModel(0), +WalletModel::WalletModel(std::unique_ptr _ipcWallet, ipc::Node& _ipcNode, const PlatformStyle *platformStyle, OptionsModel *_optionsModel, QObject *parent) : + QObject(parent), m_ipc_wallet(std::move(_ipcWallet)), m_ipc_node(_ipcNode), optionsModel(_optionsModel), addressTableModel(0), transactionTableModel(0), recentRequestsTableModel(0), cachedEncryptionStatus(Unencrypted), @@ -45,8 +45,8 @@ WalletModel::WalletModel(std::unique_ptr _ipcWallet, ipc::Node& _ip fForceCheckBalanceChanged = false; addressTableModel = new AddressTableModel(this); - transactionTableModel = new TransactionTableModel(platformStyle, wallet, this); - recentRequestsTableModel = new RecentRequestsTableModel(wallet, this); + transactionTableModel = new TransactionTableModel(platformStyle, this); + recentRequestsTableModel = new RecentRequestsTableModel(this); // This timer will be fired repeatedly to update the balance pollTimer = new QTimer(this); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index f5a2aac8b81..17203091db1 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -28,7 +28,6 @@ class CKeyID; class COutPoint; class COutput; class CPubKey; -class CWallet; class uint256; QT_BEGIN_NAMESPACE @@ -101,7 +100,7 @@ class WalletModel : public QObject Q_OBJECT public: - explicit WalletModel(std::unique_ptr ipc_wallet, ipc::Node& ipc_node, const PlatformStyle *platformStyle, CWallet *_wallet, OptionsModel *optionsModel, QObject *parent = 0); + explicit WalletModel(std::unique_ptr ipc_wallet, ipc::Node& ipc_node, const PlatformStyle *platformStyle, OptionsModel *optionsModel, QObject *parent = 0); ~WalletModel(); enum StatusCode // Returned by sendCoins @@ -200,7 +199,6 @@ class WalletModel : public QObject std::unique_ptr m_handler_show_progress; std::unique_ptr m_handler_watch_only_changed; ipc::Node& m_ipc_node; - CWallet *wallet; bool fHaveWatchOnly; bool fForceCheckBalanceChanged; From a32163cedae60d71aee86476cbec361d6ed4b4d1 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Tue, 18 Apr 2017 17:06:13 -0400 Subject: [PATCH 18/20] Remove direct bitcoin access from qt/guiutil.cpp --- src/qt/guiutil.cpp | 5 +++-- src/qt/guiutil.h | 7 ++++++- src/qt/sendcoinsdialog.cpp | 2 +- src/qt/sendcoinsentry.cpp | 4 ++-- src/qt/sendcoinsentry.h | 2 +- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f3c5daebec9..01389081402 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -12,6 +12,7 @@ #include "fs.h" #include "primitives/transaction.h" #include "init.h" +#include "ipc/interfaces.h" #include "policy/policy.h" #include "protocol.h" #include "script/script.h" @@ -246,12 +247,12 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) return ret; } -bool isDust(const QString& address, const CAmount& amount) +bool isDust(ipc::Node& ipc_node, const QString& address, const CAmount& amount) { CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); - return IsDust(txOut, ::dustRelayFee); + return IsDust(txOut, ipc_node.getDustRelayFee()); } QString HtmlEscape(const QString& str, bool fMultiLine) diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d6aa8c4ea65..c20105ddc7c 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -20,6 +20,11 @@ class QValidatedLineEdit; class SendCoinsRecipient; +namespace ipc +{ + class Node; +} + QT_BEGIN_NAMESPACE class QAbstractItemView; class QDateTime; @@ -50,7 +55,7 @@ namespace GUIUtil QString formatBitcoinURI(const SendCoinsRecipient &info); // Returns true if given address+amount meets "dust" definition - bool isDust(const QString& address, const CAmount& amount); + bool isDust(ipc::Node& ipc_node, const QString& address, const CAmount& amount); // HTML escaping for rich text controls QString HtmlEscape(const QString& str, bool fMultiLine=false); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 04589ca44fe..a703745a55d 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -236,7 +236,7 @@ void SendCoinsDialog::on_sendButton_clicked() SendCoinsEntry *entry = qobject_cast(ui->entries->itemAt(i)->widget()); if(entry) { - if(entry->validate()) + if(entry->validate(model->getIpcNode())) { recipients.append(entry->getValue()); } diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index bb0f47b21c0..2a960dd2da8 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -117,7 +117,7 @@ void SendCoinsEntry::deleteClicked() Q_EMIT removeEntry(this); } -bool SendCoinsEntry::validate() +bool SendCoinsEntry::validate(ipc::Node& ipc_node) { if (!model) return false; @@ -148,7 +148,7 @@ bool SendCoinsEntry::validate() } // Reject dust outputs: - if (retval && GUIUtil::isDust(ui->payTo->text(), ui->payAmount->value())) { + if (retval && GUIUtil::isDust(ipc_node, ui->payTo->text(), ui->payAmount->value())) { ui->payAmount->setValid(false); retval = false; } diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index a8be670c2aa..265e5d4f73a 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -30,7 +30,7 @@ class SendCoinsEntry : public QStackedWidget ~SendCoinsEntry(); void setModel(WalletModel *model); - bool validate(); + bool validate(ipc::Node& ipc_node); SendCoinsRecipient getValue(); /** Return whether the entry is still empty and unedited */ From 0b34d50d7cfd6f8b34709bb15bc11bdd54d79590 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Thu, 20 Apr 2017 12:28:58 -0400 Subject: [PATCH 19/20] Remove direct bitcoin calls from qt/sendcoinsdialog.cpp --- src/ipc/interfaces.h | 6 ++++++ src/ipc/local/interfaces.cpp | 2 ++ src/qt/sendcoinsdialog.cpp | 19 +++++++++---------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ipc/interfaces.h b/src/ipc/interfaces.h index f504c1c7533..3b6664be720 100644 --- a/src/ipc/interfaces.h +++ b/src/ipc/interfaces.h @@ -184,9 +184,15 @@ class Node //! Get dust relay fee. virtual CFeeRate getDustRelayFee() = 0; + //! Get fallback fee. + virtual CFeeRate getFallbackFee() = 0; + //! Get pay tx fee. virtual CFeeRate getPayTxFee() = 0; + //! Set pay tx fee. + virtual void setPayTxFee(CFeeRate rate) = 0; + //! Execute rpc command. virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; diff --git a/src/ipc/local/interfaces.cpp b/src/ipc/local/interfaces.cpp index 538a647e466..d59d04c73a6 100644 --- a/src/ipc/local/interfaces.cpp +++ b/src/ipc/local/interfaces.cpp @@ -633,7 +633,9 @@ class NodeImpl : public Node return result; } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } + CFeeRate getFallbackFee() override { CHECK_WALLET(return CWallet::fallbackFee); } CFeeRate getPayTxFee() override { CHECK_WALLET(return ::payTxFee); } + void setPayTxFee(CFeeRate rate) override { CHECK_WALLET(::payTxFee = rate); } UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override { JSONRPCRequest req; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a703745a55d..cae4e6244a0 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -18,10 +18,8 @@ #include "base58.h" #include "chainparams.h" #include "wallet/coincontrol.h" -#include "validation.h" // mempool and minRelayTxFee #include "ui_interface.h" #include "txmempool.h" -#include "policy/fees.h" #include "wallet/wallet.h" #include @@ -186,7 +184,7 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); - ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000)); + ui->customFee->setSingleStep(model->getIpcNode().getRequiredFee(1000)); updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); @@ -572,7 +570,7 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn msgParams.second = CClientUIInterface::MSG_ERROR; break; case WalletModel::AbsurdFee: - msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee)); + msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getIpcNode().getMaxTxFee())); break; case WalletModel::PaymentRequestExpired: msgParams.first = tr("Payment request expired."); @@ -611,7 +609,7 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() void SendCoinsDialog::setMinimumFee() { ui->radioCustomPerKilobyte->setChecked(true); - ui->customFee->setValue(CWallet::GetRequiredFee(1000)); + ui->customFee->setValue(model->getIpcNode().getRequiredFee(1000)); } void SendCoinsDialog::updateFeeSectionControls() @@ -644,7 +642,7 @@ void SendCoinsDialog::updateMinFeeLabel() { if (model && model->getOptionsModel()) ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg( - BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)) + "/kB") + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getIpcNode().getRequiredFee(1000)) + "/kB") ); } @@ -668,12 +666,13 @@ void SendCoinsDialog::updateSmartFeeLabel() CCoinControl coin_control; updateCoinControlState(coin_control); coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels - FeeCalculation feeCalc; - CFeeRate feeRate = CFeeRate(CWallet::GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc)); + int returned_target; + FeeReason reason; + CFeeRate feeRate = CFeeRate(model->getIpcNode().getMinimumFee(1000, coin_control, &returned_target, &reason)); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); - if (feeCalc.reason == FeeReason::FALLBACK) { + if (reason == FeeReason::FALLBACK) { ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelFeeEstimation->setText(""); ui->fallbackFeeWarningLabel->setVisible(true); @@ -685,7 +684,7 @@ void SendCoinsDialog::updateSmartFeeLabel() else { ui->labelSmartFee2->hide(); - ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", feeCalc.returnedTarget)); + ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", returned_target)); ui->fallbackFeeWarningLabel->setVisible(false); } From 4aeb943ff07a8c391e1ce8039557aa508798ba18 Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 27 Mar 2017 16:55:24 -0400 Subject: [PATCH 20/20] Add src/ipc/README.md --- src/ipc/README.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/ipc/README.md diff --git a/src/ipc/README.md b/src/ipc/README.md new file mode 100644 index 00000000000..2c2394d9f1f --- /dev/null +++ b/src/ipc/README.md @@ -0,0 +1,64 @@ +src/ipc -- Bitcoin Interprocess Communication +============================================= + +The IPC functions and classes in [`src/ipc`](.) define interfaces that allow +bitcoin code running in separate processes to communicate. + +The interfaces are designed to be protocol agnostic, so support for different +IPC mechanisms (Capn'Proto, gRPC, JSON-RPC, or custom protocols) could be added +without having to change application code outside the [`src/ipc`](.) directory. + +The interfaces are also expected to evolve over time, to allow gradually +increasing bitcoin code modularity, starting with a simple division between +bitcoin GUI (`bitcoin-qt`) and bitcoind daemon (`bitcoind`) code, and +supporting more subdivisions in the future, such as subdivisions between +bitcoin wallet code and bitcoin P2P networking code, or between P2P networking +and database storage code. + +Current Status +-------------- + +* [`src/ipc/interfaces.h`](interfaces.h) currently contains interfaces + `ipc::Node` and `ipc::Wallet` allowing the bitcoin GUI to access bitcoin + node and wallet functionality. More interfaces could be added in the future. +* [`src/ipc/interfaces.h`](interfaces.h) currently only defines a `LOCAL` IPC protocol + for calling functionality already linked into the current process. The first + implementation of a remote protocol allowing real interprocess communication + is added in PR [#10102](https://github.com/bitcoin/bitcoin/pull/10102). + +FAQ +--- + +### Does having an IPC layer make Qt model classes redundant? + +The IPC layer is complementary to the Qt model classes. The IPC layer is only a +simple shim wrapping a selection of node and wallet functions that the GUI +needs access to. No real functionality is implemented there, and the most +complicated thing any IPC method in +[src/ipc/local/interfaces.cpp](local/interfaces.cpp) should do is a call a +single bitcoin function, or batch a few function calls together and group their +results. + +All real functionality is implemented outside of the IPC layer, either above it +in Qt model classes, or below it in bitcoin node and wallet code. The Qt model +classes in ([src/qt/walletmodel.cpp](../qt/walletmodel.cpp)), +([src/qt/clientmodel.cpp](../qt/clientmodel.cpp), etc) are the best place to +implement functionality that's useful to GUI code but not useful to other parts +of bitcoin. + +Examples of expected code organization: + +* `WalletImpl::getBalances()` in + [src/ipc/local/interfaces.cpp](local/interfaces.cpp) shows a typical IPC + method implementation which groups together the results of a few lower-level + bitcoin calls, but does not implement any real functionality of its own. + +* PR [#10231](https://github.com/bitcoin/bitcoin/pull/10231), which adds caching + of header information to GUI, shows an example of the type of functionality + that belongs in Qt model classes. The caching it adds could have been + implemented at a lower level, but it belongs in Qt because the information it + tracks is only useful to the GUI. + +* PR [#10251](https://github.com/bitcoin/bitcoin/pull/10251), which adds caching + of balance information, is useful to both the GUI and the JSON-RPC interfaces, + so was implemented at a lower level.