From 7bb2d25c3d8fab3c38a846bd0eca0910cfb35e4f Mon Sep 17 00:00:00 2001 From: Russell Yanofsky Date: Mon, 17 Apr 2017 19:46:08 -0400 Subject: [PATCH] Remove direct bitcoin calls from qt/coincontroldialog.cpp --- src/interface/node.cpp | 28 ++++++++++++ src/interface/node.h | 21 +++++++++ src/interface/wallet.cpp | 41 +++++++++++++++++ src/interface/wallet.h | 19 ++++++++ src/qt/coincontroldialog.cpp | 85 +++++++++++++++++------------------- src/qt/walletmodel.cpp | 32 -------------- src/qt/walletmodel.h | 4 -- 7 files changed, 149 insertions(+), 81 deletions(-) diff --git a/src/interface/node.cpp b/src/interface/node.cpp index cf52f8b64a..f09c5c1048 100644 --- a/src/interface/node.cpp +++ b/src/interface/node.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -29,6 +32,7 @@ #include #endif #ifdef ENABLE_WALLET +#include #include #define CHECK_WALLET(x) x #else @@ -194,7 +198,31 @@ class NodeImpl : public Node } bool getNetworkActive() override { return g_connman && g_connman->GetNetworkActive(); } unsigned int getTxConfirmTarget() override { CHECK_WALLET(return ::nTxConfirmTarget); } + CAmount getRequiredFee(unsigned int tx_bytes) override { CHECK_WALLET(return GetRequiredFee(tx_bytes)); } + CAmount getMinimumFee(unsigned int tx_bytes, + const CCoinControl& coin_control, + int* returned_target, + FeeReason* reason) override + { + FeeCalculation fee_calc; + CAmount result; + CHECK_WALLET(result = GetMinimumFee(tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc)); + if (returned_target) *returned_target = fee_calc.returnedTarget; + if (reason) *reason = fee_calc.reason; + return result; + } 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/interface/node.h b/src/interface/node.h index 85fc93a252..31598d367a 100644 --- a/src/interface/node.h +++ b/src/interface/node.h @@ -19,11 +19,14 @@ #include #include +class CCoinControl; class CDeterministicMNList; +class CFeeRate; class CNodeStats; class RPCTimerInterface; class UniValue; class proxyType; +enum class FeeReason; struct CNodeStateStats; namespace interface { @@ -149,9 +152,27 @@ public: //! Get tx confirm target. virtual unsigned int getTxConfirmTarget() = 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* returned_target = 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; diff --git a/src/interface/wallet.cpp b/src/interface/wallet.cpp index b2f1bab946..23f216a288 100644 --- a/src/interface/wallet.cpp +++ b/src/interface/wallet.cpp @@ -51,6 +51,17 @@ public: 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: @@ -220,6 +231,36 @@ public: 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 { diff --git a/src/interface/wallet.h b/src/interface/wallet.h index 78c22c31c1..867fb4aa5b 100644 --- a/src/interface/wallet.h +++ b/src/interface/wallet.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,7 @@ namespace interface { class Handler; class PendingWalletTx; struct WalletBalances; +struct WalletTxOut; using WalletOrderForm = std::vector>; using WalletValueMap = std::map; @@ -162,6 +164,14 @@ public: //! Get available balance. virtual CAmount getAvailableBalance(const CCoinControl& coin_control, bool fAnonymized = false) = 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; @@ -235,6 +245,15 @@ struct WalletBalances } }; +//! Wallet transaction output. +struct WalletTxOut +{ + CTxOut txout; + int64_t time; + int depth_in_main_chain = -1; + bool is_spent = false; +}; + //! Return implementation of Wallet interface. This function will be undefined //! in builds where ENABLE_WALLET is false. std::unique_ptr MakeWallet(CWallet& wallet); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 04ee10c8c1..1b4ed9e393 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include // For mempool @@ -221,13 +221,13 @@ void CoinControlDialog::buttonToggleLockClicked() if (coinControl()->IsUsingPrivateSend() && !fHideAdditional && !model->isFullyMixed(outpt)) { continue; } - if (model->isLockedCoin(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt())){ - model->unlockCoin(outpt); + if (model->wallet().isLockedCoin(outpt)) { + model->wallet().unlockCoin(outpt); item->setDisabled(false); item->setIcon(COLUMN_CHECKBOX, QIcon()); } else{ - model->lockCoin(outpt); + model->wallet().lockCoin(outpt); item->setDisabled(true); item->setIcon(COLUMN_CHECKBOX, GUIUtil::getIcon("lock_closed", GUIUtil::ThemedColor::RED)); } @@ -476,7 +476,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CTxOut txout(amount, static_cast(std::vector(24, 0))); txDummy.vout.push_back(txout); - fDust |= IsDust(txout, ::dustRelayFee); + fDust |= IsDust(txout, model->node().getDustRelayFee()); } } @@ -492,16 +492,16 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) bool fUnselectedNonMixed{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->wallet().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); fUnselectedSpent = true; @@ -512,11 +512,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nQuantity++; // Amount - nAmount += out.tx->tx->vout[out.i].nValue; + nAmount += out.txout.nValue; // Bytes CTxDestination address; - if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address)) + if(ExtractDestination(out.txout.scriptPubKey, address)) { CPubKey pubkey; CKeyID *keyid = boost::get(&address); @@ -542,7 +542,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = GetMinimumFee(nBytes, *coinControl(), ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nPayFee = model->node().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */); if (nPayAmount > 0) { @@ -563,7 +563,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nChange > 0 && nChange < MIN_CHANGE) { CTxOut txout(nChange, static_cast(std::vector(24, 0))); - if (IsDust(txout, ::dustRelayFee)) + if (IsDust(txout, model->node().getDustRelayFee())) { nPayFee += nChange; nChange = 0; @@ -716,13 +716,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->wallet().listCoins()) { CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem(); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - QString sWalletAddress = coins.first; + QString sWalletAddress = QString::fromStdString(EncodeDestination(coins.first)); QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); if (sWalletLabel.isEmpty()) sWalletLabel = tr("(no label)"); @@ -746,27 +743,27 @@ void CoinControlDialog::updateView() CAmount nSum = 0; int nChildren = 0; - for (const COutput& out : coins.second) { - COutPoint outpoint = COutPoint(out.tx->tx->GetHash(), out.i); + for (const auto& outpair : coins.second) { + const COutPoint& output = std::get<0>(outpair); + const interface::WalletTxOut& out = std::get<1>(outpair); bool fFullyMixed{false}; - CAmount nAmount = out.tx->tx->vout[out.i].nValue; - + CAmount nAmount = out.txout.nValue; bool fPrivateSendAmount = CPrivateSend::IsDenominatedAmount(nAmount) || CPrivateSend::IsCollateralAmount(nAmount); if (coinControl()->IsUsingPrivateSend()) { - fFullyMixed = model->isFullyMixed(outpoint); + fFullyMixed = model->isFullyMixed(output); if ((fHideAdditional && !fFullyMixed) || (!fHideAdditional && !fPrivateSendAmount)) { - coinControl()->UnSelect(outpoint); + coinControl()->UnSelect(output); continue; } } else { if (fHideAdditional && fPrivateSendAmount) { - coinControl()->UnSelect(outpoint); + coinControl()->UnSelect(output); continue; } } - nSum += out.tx->tx->vout[out.i].nValue; + nSum += out.txout.nValue; nChildren++; CCoinControlWidgetItem* itemOutput; @@ -781,7 +778,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(EncodeDestination(outputAddress)); // if listMode or change => show dash address. In tree mode, address is not shown again for direct wallet address outputs @@ -806,17 +803,17 @@ void CoinControlDialog::updateView() } // amount - itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->tx->vout[out.i].nValue)); - itemOutput->setToolTip(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->setToolTip(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->setToolTip(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->setToolTip(COLUMN_DATE, GUIUtil::dateTimeStr(out.time)); + itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong)out.time)); // PrivateSend rounds - int nRounds = model->getRealOutpointPrivateSendRounds(outpoint); + int nRounds = model->getRealOutpointPrivateSendRounds(output); if (nRounds >= 0 || LogAcceptCategory(BCLog::PRIVATESEND)) { itemOutput->setText(COLUMN_PRIVATESEND_ROUNDS, QString::number(nRounds)); } else { @@ -825,26 +822,24 @@ void CoinControlDialog::updateView() itemOutput->setData(COLUMN_PRIVATESEND_ROUNDS, Qt::UserRole, QVariant((qlonglong)nRounds)); // 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->setData(COLUMN_ADDRESS, TxHashRole, QString::fromStdString(txhash.GetHex())); + itemOutput->setData(COLUMN_ADDRESS, TxHashRole, QString::fromStdString(output.hash.GetHex())); // vout index - itemOutput->setData(COLUMN_ADDRESS, VOutRole, out.i); + itemOutput->setData(COLUMN_ADDRESS, VOutRole, output.n); // disable locked coins - if (model->wallet().isLockedCoin(COutPoint(txhash, out.i))) { - COutPoint outpt(txhash, out.i); - coinControl()->UnSelect(outpt); // just to be sure + if (model->wallet().isLockedCoin(output)) { + coinControl()->UnSelect(output); // just to be sure itemOutput->setDisabled(true); itemOutput->setIcon(COLUMN_CHECKBOX, GUIUtil::getIcon("lock_closed", GUIUtil::ThemedColor::RED)); } // 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 2b1d910149..0b4aec1285 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -565,38 +565,6 @@ void WalletModel::UnlockContext::CopyFrom(const UnlockContext& rhs) rhs.was_mixing = false; } -// returns a list of COutputs from COutPoints -void WalletModel::getOutputs(const std::vector& vOutpoints, std::vector& vOutputs) -{ - LOCK2(cs_main, cwallet->cs_wallet); - for (const COutPoint& outpoint : vOutpoints) - { - auto it = cwallet->mapWallet.find(outpoint.hash); - if (it == cwallet->mapWallet.end()) continue; - int nDepth = it->second.GetDepthInMainChain(); - if (nDepth < 0) continue; - COutput out(&it->second, outpoint.n, nDepth, true /* spendable */, true /* solvable */, true /* safe */); - vOutputs.push_back(out); - } -} - -bool WalletModel::isSpent(const COutPoint& outpoint) const -{ - LOCK2(cs_main, cwallet->cs_wallet); - return cwallet->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 : cwallet->ListCoins()) { - auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))]; - for (auto& coin : group.second) { - resultGroup.emplace_back(std::move(coin)); - } - } -} - void WalletModel::loadReceiveRequests(std::vector& vReceiveRequests) { vReceiveRequests = m_wallet->getDestValues("rr"); // receive request diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 2545a883a8..92359bde3f 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -189,10 +189,6 @@ public: UnlockContext requestUnlock(bool fForMixingOnly=false); - 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);