From 831d10bafd1e4cfdf23b1e995f64206caa417e85 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 20:34:00 -0300 Subject: [PATCH 01/20] wallet::AvailableCoins filter struct instead of having an endless amount of arguments in the function signature. --- src/qt/walletmodel.cpp | 9 ++++-- src/rpc/masternode.cpp | 5 ++- src/sapling/sapling_operation.cpp | 18 +++++------ src/wallet/rpcwallet.cpp | 10 ++---- src/wallet/wallet.cpp | 52 +++++++++++++------------------ src/wallet/wallet.h | 35 ++++++++++++++++----- 6 files changed, 71 insertions(+), 58 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 29fd901d5662..d90d7dbba528 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -97,8 +97,10 @@ CAmount WalletModel::getBalance(const CCoinControl* coinControl, bool fIncludeDe { if (coinControl) { CAmount nBalance = 0; + CWallet::AvailableCoinsFilter coinsFilter; + coinsFilter.fIncludeDelegated = fIncludeDelegated; std::vector vCoins; - wallet->AvailableCoins(&vCoins, coinControl, fIncludeDelegated); + wallet->AvailableCoins(&vCoins, coinControl, coinsFilter); for (const COutput& out : vCoins) { bool fSkip = fUnlockedOnly && isLockedCoin(out.tx->GetHash(), out.i); if (out.fSpendable && !fSkip) @@ -830,8 +832,11 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect // returns a COutPoint of 10000 PIV if found bool WalletModel::getMNCollateralCandidate(COutPoint& outPoint) { + CWallet::AvailableCoinsFilter coinsFilter; + coinsFilter.fIncludeDelegated = false; + coinsFilter.nCoinType = ONLY_10000; std::vector vCoins; - wallet->AvailableCoins(&vCoins, nullptr, false, false, ONLY_10000); + wallet->AvailableCoins(&vCoins, nullptr, coinsFilter); for (const COutput& out : vCoins) { // skip locked collaterals if (!isLockedCoin(out.tx->GetHash(), out.i)) { diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 997af1699357..97d62028d9a3 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -489,8 +489,11 @@ UniValue getmasternodeoutputs (const JSONRPCRequest& request) HelpExampleCli("getmasternodeoutputs", "") + HelpExampleRpc("getmasternodeoutputs", "")); // Find possible candidates + CWallet::AvailableCoinsFilter coinsFilter; + coinsFilter.fIncludeDelegated = false; + coinsFilter.nCoinType = ONLY_10000; std::vector possibleCoins; - pwalletMain->AvailableCoins(&possibleCoins, nullptr, false, false, ONLY_10000); + pwalletMain->AvailableCoins(&possibleCoins, nullptr, coinsFilter); UniValue ret(UniValue::VARR); for (COutput& out : possibleCoins) { diff --git a/src/sapling/sapling_operation.cpp b/src/sapling/sapling_operation.cpp index e735298adca5..e1a948a4b945 100644 --- a/src/sapling/sapling_operation.cpp +++ b/src/sapling/sapling_operation.cpp @@ -211,16 +211,14 @@ OperationResult SaplingOperation::loadUtxos(TxValues& txValues) { std::set destinations; if (fromAddress.isFromTAddress()) destinations.insert(fromAddress.fromTaddr); - if (!pwalletMain->AvailableCoins( - &transInputs, - nullptr, - false, - false, - ALL_COINS, - true, - true, - &destinations, - mindepth)) { + CWallet::AvailableCoinsFilter coinsFilter(false, + false, + ALL_COINS, + true, + true, + &destinations, + mindepth); + if (!pwalletMain->AvailableCoins(&transInputs, nullptr, coinsFilter)) { return errorOut("Insufficient funds, no available UTXO to spend"); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b37ee5247002..46af9472a8c7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3257,13 +3257,9 @@ UniValue listunspent(const JSONRPCRequest& request) std::vector vecOutputs; assert(pwalletMain != NULL); LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->AvailableCoins(&vecOutputs, - &coinControl, // coin control - true, // include delegated - false, // include cold staking - ALL_COINS, // coin type - false // only confirmed - ); + CWallet::AvailableCoinsFilter coinFilter; + coinFilter.fOnlyConfirmed = false; + pwalletMain->AvailableCoins(&vecOutputs, &coinControl, coinFilter); for (const COutput& out : vecOutputs) { if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) continue; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 63117b537a77..31915dac7e39 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2219,20 +2219,13 @@ CWallet::OutputAvailabilityResult CWallet::CheckOutputAvailability( */ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates when != nullptr const CCoinControl* coinControl, // Default: nullptr - bool fIncludeDelegated, // Default: true - bool fIncludeColdStaking, // Default: false - AvailableCoinsType nCoinType, // Default: ALL_COINS - bool fOnlyConfirmed, // Default: true - bool fOnlySpendable, // Default: false - std::set* onlyFilteredDest, // Default: nullptr - int minDepth // Default: 0 - ) const + AvailableCoinsFilter coinsFilter) const { if (pCoins) pCoins->clear(); const bool fCoinsSelected = (coinControl != nullptr) && coinControl->HasSelected(); // include delegated coins when coinControl is active - if (!fIncludeDelegated && fCoinsSelected) - fIncludeDelegated = true; + if (!coinsFilter.fIncludeDelegated && fCoinsSelected) + coinsFilter.fIncludeDelegated = true; { LOCK2(cs_main, cs_wallet); @@ -2242,22 +2235,22 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates // Check if the tx is selectable int nDepth; - if (!CheckTXAvailability(pcoin, fOnlyConfirmed, nDepth)) + if (!CheckTXAvailability(pcoin, coinsFilter.fOnlyConfirmed, nDepth)) continue; // Check min depth requirement for stake inputs - if (nCoinType == STAKEABLE_COINS && nDepth < Params().GetConsensus().nStakeMinDepth) continue; + if (coinsFilter.nCoinType == STAKEABLE_COINS && nDepth < Params().GetConsensus().nStakeMinDepth) continue; // Check min depth filtering requirements - if (nDepth < minDepth) continue; + if (nDepth < coinsFilter.minDepth) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) { const auto& output = pcoin->vout[i]; // Filter by specific destinations if needed - if (onlyFilteredDest && !onlyFilteredDest->empty()) { + if (coinsFilter.onlyFilteredDest && !coinsFilter.onlyFilteredDest->empty()) { CTxDestination address; - if (!ExtractDestination(output.scriptPubKey, address) || !onlyFilteredDest->count(address)) { + if (!ExtractDestination(output.scriptPubKey, address) || !coinsFilter.onlyFilteredDest->count(address)) { continue; } } @@ -2267,14 +2260,14 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates output, i, wtxid, - nCoinType, + coinsFilter.nCoinType, coinControl, fCoinsSelected, - fIncludeColdStaking, - fIncludeDelegated); + coinsFilter.fIncludeColdStaking, + coinsFilter.fIncludeDelegated); if (!res.available) continue; - if (fOnlySpendable && !res.spendable) continue; + if (coinsFilter.fOnlySpendable && !res.spendable) continue; // found valid coin if (!pCoins) return true; @@ -2287,14 +2280,12 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates std::map > CWallet::AvailableCoinsByAddress(bool fConfirmed, CAmount maxCoinValue) { + CWallet::AvailableCoinsFilter coinFilter; + coinFilter.fIncludeColdStaking = true; + coinFilter.fOnlyConfirmed = fConfirmed; std::vector vCoins; // include cold - AvailableCoins(&vCoins, - nullptr, // coin control - true, // fIncludeDelegated - true, // fIncludeColdStaking - ALL_COINS, // coin type - fConfirmed); // only confirmed + AvailableCoins(&vCoins, nullptr, coinFilter); std::map > mapCoins; for (COutput& out : vCoins) { @@ -2638,16 +2629,15 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CMutableTransaction txNew; CScript scriptChange; + CWallet::AvailableCoinsFilter coinFilter; + coinFilter.fIncludeDelegated = fIncludeDelegated; + coinFilter.nCoinType = coin_type; + { LOCK2(cs_main, cs_wallet); { std::vector vAvailableCoins; - AvailableCoins(&vAvailableCoins, - coinControl, - fIncludeDelegated, - false, // fIncludeColdStaking - coin_type, - true); // fOnlyConfirmed + AvailableCoins(&vAvailableCoins, coinControl, coinFilter); nFeeRet = 0; if (nFeePay > 0) nFeeRet = nFeePay; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index e8734f7fec8e..245db64791d1 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -441,16 +441,37 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface //! check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf); + struct AvailableCoinsFilter { + public: + AvailableCoinsFilter() {} + AvailableCoinsFilter(bool _fIncludeDelegated, + bool _fIncludeColdStaking, + AvailableCoinsType _nCoinType, + bool _fOnlyConfirmed, + bool _fOnlySpendable, + std::set* _onlyFilteredDest, + int _minDepth) : + fIncludeDelegated(_fIncludeDelegated), + fIncludeColdStaking(_fIncludeColdStaking), + nCoinType(_nCoinType), + fOnlyConfirmed(_fOnlyConfirmed), + fOnlySpendable(_fOnlySpendable), + onlyFilteredDest(_onlyFilteredDest), + minDepth(_minDepth) {} + + bool fIncludeDelegated{true}; + bool fIncludeColdStaking{false}; + AvailableCoinsType nCoinType{ALL_COINS}; + bool fOnlyConfirmed{true}; + bool fOnlySpendable{false}; + std::set* onlyFilteredDest{nullptr}; + int minDepth{0}; + }; + //! >> Available coins (generic) bool AvailableCoins(std::vector* pCoins, // --> populates when != nullptr const CCoinControl* coinControl = nullptr, - bool fIncludeDelegated = true, - bool fIncludeColdStaking = false, - AvailableCoinsType nCoinType = ALL_COINS, - bool fOnlyConfirmed = true, - bool fOnlySpendable = false, - std::set* = nullptr, - int minDepth = 0 + AvailableCoinsFilter coinsFilter = AvailableCoinsFilter() ) const; //! >> Available coins (spending) bool SelectCoinsToSpend(const std::vector& vAvailableCoins, const CAmount& nTargetValue, std::set >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl = nullptr) const; From 3b9bf04b21763cbf44f96072dc3a6ea2d4eee53d Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 20:38:26 -0300 Subject: [PATCH 02/20] wallet::AvailableCoins introduced flag for including or not locked coins. --- src/wallet/wallet.cpp | 11 +++++++---- src/wallet/wallet.h | 10 +++++++--- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 31915dac7e39..c8b90ec57f48 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2168,7 +2168,8 @@ CWallet::OutputAvailabilityResult CWallet::CheckOutputAvailability( const CCoinControl* coinControl, const bool fCoinsSelected, const bool fIncludeColdStaking, - const bool fIncludeDelegated) const + const bool fIncludeDelegated, + const bool fIncludeLocked) const { OutputAvailabilityResult res; @@ -2190,7 +2191,7 @@ CWallet::OutputAvailabilityResult CWallet::CheckOutputAvailability( if (mine == ISMINE_WATCH_ONLY && coinControl && !coinControl->fAllowWatchOnly) return res; // Skip locked utxo - if (IsLockedCoin(wtxid, outIndex) && nCoinType != ONLY_10000) return res; + if (!fIncludeLocked && IsLockedCoin(wtxid, outIndex) && nCoinType != ONLY_10000) return res; // Check if we should include zero value utxo if (output.nValue <= 0) return res; @@ -2264,7 +2265,8 @@ bool CWallet::AvailableCoins(std::vector* pCoins, // --> populates coinControl, fCoinsSelected, coinsFilter.fIncludeColdStaking, - coinsFilter.fIncludeDelegated); + coinsFilter.fIncludeDelegated, + coinsFilter.fIncludeLocked); if (!res.available) continue; if (coinsFilter.fOnlySpendable && !res.spendable) continue; @@ -2376,7 +2378,8 @@ bool CWallet::StakeableCoins(std::vector* pCoins) nullptr, // coin control false, // fIncludeDelegated fIncludeColdStaking, - false); + false, + false); // fIncludeLocked if (!res.available) continue; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 245db64791d1..f324f18acc88 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -340,7 +340,8 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface const CCoinControl* coinControl, const bool fCoinsSelected, const bool fIncludeColdStaking, - const bool fIncludeDelegated) const; + const bool fIncludeDelegated, + const bool fIncludeLocked) const; // Zerocoin wallet CzPIVWallet* zwallet{nullptr}; @@ -450,14 +451,16 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool _fOnlyConfirmed, bool _fOnlySpendable, std::set* _onlyFilteredDest, - int _minDepth) : + int _minDepth, + bool _fIncludeLocked = false) : fIncludeDelegated(_fIncludeDelegated), fIncludeColdStaking(_fIncludeColdStaking), nCoinType(_nCoinType), fOnlyConfirmed(_fOnlyConfirmed), fOnlySpendable(_fOnlySpendable), onlyFilteredDest(_onlyFilteredDest), - minDepth(_minDepth) {} + minDepth(_minDepth), + fIncludeLocked(_fIncludeLocked) {} bool fIncludeDelegated{true}; bool fIncludeColdStaking{false}; @@ -466,6 +469,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface bool fOnlySpendable{false}; std::set* onlyFilteredDest{nullptr}; int minDepth{0}; + bool fIncludeLocked{false}; }; //! >> Available coins (generic) From b65e6702915ec00a88502193fe5773b6366b2d95 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 20:43:24 -0300 Subject: [PATCH 03/20] walletModel: listCoins removing a unneeded ListLockedCoins call plus followup loop over the returned vector. --- src/qt/walletmodel.cpp | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d90d7dbba528..8c2ba94ac522 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -856,25 +856,12 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) void WalletModel::listCoins(std::map >& mapCoins) const { + CWallet::AvailableCoinsFilter filter; + filter.fIncludeLocked = true; std::vector vCoins; - wallet->AvailableCoins(&vCoins); - - LOCK2(cs_main, wallet->cs_wallet); // ListLockedCoins, mapWallet - std::vector vLockedCoins; - wallet->ListLockedCoins(vLockedCoins); - - // add locked coins - for (const COutPoint& outpoint : vLockedCoins) { - if (!wallet->mapWallet.count(outpoint.hash)) continue; - bool fConflicted; - int nDepth = wallet->mapWallet[outpoint.hash].GetDepthAndMempool(fConflicted); - if (nDepth < 0 || fConflicted) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); - if (outpoint.n < out.tx->vout.size() && - (wallet->IsMine(out.tx->vout[outpoint.n]) & ISMINE_SPENDABLE_ALL) != ISMINE_NO) - vCoins.push_back(out); - } + wallet->AvailableCoins(&vCoins, nullptr, filter); + LOCK(wallet->cs_wallet); for (const COutput& out : vCoins) { COutput cout = out; From a10cdcb66dcb9f0e30aec338ee3837715c5aae53 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 21:01:25 -0300 Subject: [PATCH 04/20] walletModel: listCoins cleaning an overkill to get the address that originated the change --- src/qt/walletmodel.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 8c2ba94ac522..851d0dedfc84 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -861,19 +861,11 @@ void WalletModel::listCoins(std::map >& mapCoins) std::vector vCoins; wallet->AvailableCoins(&vCoins, nullptr, filter); - LOCK(wallet->cs_wallet); for (const COutput& out : vCoins) { - COutput cout = out; - - while (wallet->IsChange(cout.tx->vout[cout.i]) && cout.tx->vin.size() > 0 && wallet->IsMine(cout.tx->vin[0])) { - if (!wallet->mapWallet.count(cout.tx->vin[0].prevout.hash)) break; - cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true, true); - } - CTxDestination address; - if (!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) + if (!out.fSpendable || !ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) continue; - mapCoins[QString::fromStdString(EncodeDestination(address))].push_back(out); + mapCoins[QString::fromStdString(EncodeDestination(address))].emplace_back(out); } } From 100c0063659eda2e66f9fbd503597a649b1a01b4 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 21:19:35 -0300 Subject: [PATCH 05/20] coin control, update view method refactored pulling the output basic values to the top of the method. --- src/qt/coincontroldialog.cpp | 64 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 69400eafd586..385022092361 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -805,36 +805,46 @@ void CoinControlDialog::updateView() int nChildren = 0; int nInputSum = 0; for(const COutput& out: coins.second) { + + // Basic values used in the entire process + const uint256& txhash = out.tx->GetHash(); + const uint32_t outIndex = out.i; + const CAmount nValue = out.tx->vout[out.i].nValue; + const int64_t nTime = out.tx->GetTxTime(); + const int nDepth = out.nDepth; + const bool isP2CS = out.tx->vout[outIndex].scriptPubKey.IsPayToColdStaking(); + ++nSelectableInputs; int nInputSize = 0; - nSum += out.tx->vout[out.i].nValue; + nSum += nValue; nChildren++; - CCoinControlWidgetItem* itemOutput; - if (treeMode) - itemOutput = new CCoinControlWidgetItem(itemWalletAddress); - else - itemOutput = new CCoinControlWidgetItem(ui->treeWidget); - itemOutput->setFlags(flgCheckbox); - itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - // address - const bool isP2CS = out.tx->vout[out.i].scriptPubKey.IsPayToColdStaking(); CTxDestination outputAddress; CTxDestination outputAddressStaker; - QString sAddress = ""; bool haveDest = false; if (isP2CS) { txnouttype type; std::vector addresses; int nRequired; - haveDest = (ExtractDestinations(out.tx->vout[out.i].scriptPubKey, type, addresses, nRequired) + haveDest = (ExtractDestinations(out.tx->vout[outIndex].scriptPubKey, type, addresses, nRequired) && addresses.size() == 2); if (haveDest) { outputAddressStaker = addresses[0]; outputAddress = addresses[1]; } } else { - haveDest = ExtractDestination(out.tx->vout[out.i].scriptPubKey, outputAddress); + haveDest = ExtractDestination(out.tx->vout[outIndex].scriptPubKey, outputAddress); } + + // + CCoinControlWidgetItem* itemOutput; + if (treeMode) + itemOutput = new CCoinControlWidgetItem(itemWalletAddress); + else + itemOutput = new CCoinControlWidgetItem(ui->treeWidget); + itemOutput->setFlags(flgCheckbox); + itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + + QString sAddress = ""; if (haveDest) { sAddress = QString::fromStdString(EncodeDestination(outputAddress)); @@ -864,42 +874,40 @@ void CoinControlDialog::updateView() } // amount - itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); - itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, out.tx->vout[out.i].nValue)); - itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong) out.tx->vout[out.i].nValue)); + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue)); + itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue)); + itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong) nValue)); // 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(nTime)); + itemOutput->setToolTip(COLUMN_DATE, GUIUtil::dateTimeStr(nTime)); + itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong) nTime)); // 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(nDepth)); + itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong) nDepth)); // priority - dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth + 1); + dPrioritySum += (double)nValue * (nDepth + 1); nInputSum += nInputSize; // transaction hash - uint256 txhash = out.tx->GetHash(); itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); // vout index - itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); + itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(outIndex)); // disable locked coins - const bool isLockedCoin = model->isLockedCoin(txhash, out.i); + const bool isLockedCoin = model->isLockedCoin(txhash, outIndex); if (isLockedCoin) { --nSelectableInputs; - COutPoint outpt(txhash, out.i); - coinControl->UnSelect(outpt); // just to be sure + coinControl->UnSelect({txhash, outIndex}); // just to be sure itemOutput->setDisabled(true); itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); } // set checkbox - if (coinControl->IsSelected(COutPoint(txhash, out.i))) + if (coinControl->IsSelected(COutPoint(txhash, outIndex))) itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); // outputs delegated (for cold staking) From cf4275772a431c371fc84e9f96938fe97dc3096d Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 25 Jun 2018 09:31:32 -0400 Subject: [PATCH 06/20] [qt] coincontrol: Remove unused qt4 workaround --- src/qt/coincontroldialog.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 385022092361..b5618dabcd01 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -165,10 +165,6 @@ CoinControlDialog::CoinControlDialog(QWidget* parent, bool _forDelegation) : QDi // Toggle lock state connect(ui->pushButtonToggleLock, &QPushButton::clicked, this, &CoinControlDialog::buttonToggleLockClicked); - // change coin control first column label due Qt4 bug. - // see https://github.com/bitcoin/bitcoin/issues/5716 - ui->treeWidget->headerItem()->setText(COLUMN_CHECKBOX, QString()); - ui->treeWidget->setColumnWidth(COLUMN_CHECKBOX, 84); ui->treeWidget->setColumnWidth(COLUMN_AMOUNT, 120); ui->treeWidget->setColumnWidth(COLUMN_LABEL, 170); From 031b006e835266eede2b9dc8e6b85ce9c41132b8 Mon Sep 17 00:00:00 2001 From: practicalswift Date: Fri, 3 Feb 2017 09:20:54 +0100 Subject: [PATCH 07/20] Use z = std::max(x - y, 0); instead of z = x - y; if (z < 0) z = 0; --- src/addrman.cpp | 9 +-------- src/qt/coincontroldialog.cpp | 4 +--- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/addrman.cpp b/src/addrman.cpp index b1205641ec23..0af77fdbbcbf 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -55,14 +55,7 @@ bool CAddrInfo::IsTerrible(int64_t nNow) const double CAddrInfo::GetChance(int64_t nNow) const { double fChance = 1.0; - - int64_t nSinceLastSeen = nNow - nTime; - int64_t nSinceLastTry = nNow - nLastTry; - - if (nSinceLastSeen < 0) - nSinceLastSeen = 0; - if (nSinceLastTry < 0) - nSinceLastTry = 0; + int64_t nSinceLastTry = std::max(nNow - nLastTry, 0); // deprioritize very recent attempts away if (nSinceLastTry < 60 * 10) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b5618dabcd01..33587e62b976 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -675,9 +675,7 @@ void CoinControlDialog::updateLabels() } // after fee - nAfterFee = nAmount - nPayFee; - if (nAfterFee < 0) - nAfterFee = 0; + nAfterFee = std::max(nAmount - nPayFee, 0); } // actually update labels From 6752f5bc016fa13b8baa01c5b87bd6f1ea8cd05d Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 21:41:00 -0300 Subject: [PATCH 08/20] coin control, update view: removing unused priority calculation. --- src/qt/coincontroldialog.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 33587e62b976..7c51ba8d5411 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -795,9 +795,7 @@ void CoinControlDialog::updateView() } CAmount nSum = 0; - double dPrioritySum = 0; int nChildren = 0; - int nInputSum = 0; for(const COutput& out: coins.second) { // Basic values used in the entire process @@ -809,7 +807,6 @@ void CoinControlDialog::updateView() const bool isP2CS = out.tx->vout[outIndex].scriptPubKey.IsPayToColdStaking(); ++nSelectableInputs; - int nInputSize = 0; nSum += nValue; nChildren++; @@ -847,11 +844,6 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_ADDRESS, sAddress); else itemOutput->setToolTip(COLUMN_ADDRESS, sAddress); - - CPubKey pubkey; - CKeyID* keyid = boost::get(&outputAddress); - if (keyid && model->getPubKey(*keyid, pubkey) && !pubkey.IsCompressed()) - nInputSize = 29; // 29 = 180 - 151 (public key is 180 bytes, priority free area is 151 bytes) } // label @@ -881,10 +873,6 @@ void CoinControlDialog::updateView() itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(nDepth)); itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong) nDepth)); - // priority - dPrioritySum += (double)nValue * (nDepth + 1); - nInputSum += nInputSize; - // transaction hash itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); @@ -918,7 +906,6 @@ void CoinControlDialog::updateView() // amount if (treeMode) { - dPrioritySum = dPrioritySum / (nInputSum + 78); itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); itemWalletAddress->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); From f2297a634ceed3e52106fb83d32427e11e45d5de Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 22:25:05 -0300 Subject: [PATCH 09/20] WalletModel::listCoins P2CS utxo grouped properly. --- src/qt/coincontroldialog.cpp | 9 ++++----- src/qt/walletmodel.cpp | 31 ++++++++++++++++++++++++++----- src/qt/walletmodel.h | 17 ++++++++++++++++- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 7c51ba8d5411..1e072506a0fd 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -764,16 +764,15 @@ void CoinControlDialog::updateView() QFlags flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); -// double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); - nSelectableInputs = 0; - std::map> mapCoins; + std::map> mapCoins; model->listCoins(mapCoins); - for (std::pair> coins : mapCoins) { + for (const auto& coins : mapCoins) { CCoinControlWidgetItem* itemWalletAddress = new CCoinControlWidgetItem(); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - QString sWalletAddress = coins.first; + WalletModel::ListCoinsKey keys = coins.first; + QString sWalletAddress = keys.address; QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); if (sWalletLabel.isEmpty()) sWalletLabel = tr("(no label)"); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 851d0dedfc84..acf553b9cc61 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -854,7 +854,7 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const } // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) -void WalletModel::listCoins(std::map >& mapCoins) const +void WalletModel::listCoins(std::map >& mapCoins) const { CWallet::AvailableCoinsFilter filter; filter.fIncludeLocked = true; @@ -862,10 +862,31 @@ void WalletModel::listCoins(std::map >& mapCoins) wallet->AvailableCoins(&vCoins, nullptr, filter); for (const COutput& out : vCoins) { - CTxDestination address; - if (!out.fSpendable || !ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) - continue; - mapCoins[QString::fromStdString(EncodeDestination(address))].emplace_back(out); + if (!out.fSpendable) continue; + + const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey; + const bool isP2CS = scriptPubKey.IsPayToColdStaking(); + + CTxDestination outputAddress; + CTxDestination outputAddressStaker; + if (isP2CS) { + txnouttype type; std::vector addresses; int nRequired; + if(!ExtractDestinations(scriptPubKey, type, addresses, nRequired) + || addresses.size() != 2) throw std::runtime_error("Cannot extract P2CS addresses from a stored transaction"); + outputAddressStaker = addresses[0]; + outputAddress = addresses[1]; + } else { + if (!ExtractDestination(scriptPubKey, outputAddress)) + continue; + } + + QString address = QString::fromStdString(EncodeDestination(outputAddress)); + Optional stakerAddr = IsValidDestination(outputAddressStaker) ? + Optional(QString::fromStdString(EncodeDestination(outputAddressStaker, CChainParams::STAKING_ADDRESS))) : + nullopt; + + ListCoinsKey key{address, stakerAddr}; + mapCoins[key].emplace_back(out); } } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ca61c0872394..6f85b27d4dd6 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -262,7 +262,22 @@ class WalletModel : public QObject void getOutputs(const std::vector& vOutpoints, std::vector& vOutputs); bool getMNCollateralCandidate(COutPoint& outPoint); bool isSpent(const COutPoint& outpoint) const; - void listCoins(std::map >& mapCoins) const; + + class ListCoinsKey { + public: + QString address; + Optional stakerAddress; // used only for P2CS utxo + + bool operator==(const ListCoinsKey& key2) const { + return address == key2.address && stakerAddress == key2.stakerAddress; + } + + bool operator<(const ListCoinsKey& key2) const { + return this->address < key2.address; + } + }; + + void listCoins(std::map >& mapCoins) const; bool isLockedCoin(uint256 hash, unsigned int n) const; void lockCoin(COutPoint& output); From a19eb5389c5988c8b493a3e466d93b78ee3e4350 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 22:34:22 -0300 Subject: [PATCH 10/20] CoinControlDialog: making use of the new optional staker addresses that is retrieved by ListCoins --- src/qt/coincontroldialog.cpp | 45 +++++++++--------------------------- 1 file changed, 11 insertions(+), 34 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 1e072506a0fd..3b692bea00b4 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -771,8 +771,9 @@ void CoinControlDialog::updateView() for (const auto& coins : mapCoins) { CCoinControlWidgetItem* itemWalletAddress = new CCoinControlWidgetItem(); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - WalletModel::ListCoinsKey keys = coins.first; - QString sWalletAddress = keys.address; + const WalletModel::ListCoinsKey& keys = coins.first; + const QString& sWalletAddress = keys.address; + const Optional& stakerAddress = keys.stakerAddres; QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); if (sWalletLabel.isEmpty()) sWalletLabel = tr("(no label)"); @@ -803,28 +804,11 @@ void CoinControlDialog::updateView() const CAmount nValue = out.tx->vout[out.i].nValue; const int64_t nTime = out.tx->GetTxTime(); const int nDepth = out.nDepth; - const bool isP2CS = out.tx->vout[outIndex].scriptPubKey.IsPayToColdStaking(); ++nSelectableInputs; nSum += nValue; nChildren++; - // address - CTxDestination outputAddress; - CTxDestination outputAddressStaker; - bool haveDest = false; - if (isP2CS) { - txnouttype type; std::vector addresses; int nRequired; - haveDest = (ExtractDestinations(out.tx->vout[outIndex].scriptPubKey, type, addresses, nRequired) - && addresses.size() == 2); - if (haveDest) { - outputAddressStaker = addresses[0]; - outputAddress = addresses[1]; - } - } else { - haveDest = ExtractDestination(out.tx->vout[outIndex].scriptPubKey, outputAddress); - } - // CCoinControlWidgetItem* itemOutput; if (treeMode) @@ -834,16 +818,12 @@ void CoinControlDialog::updateView() itemOutput->setFlags(flgCheckbox); itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - QString sAddress = ""; - if (haveDest) { - sAddress = QString::fromStdString(EncodeDestination(outputAddress)); - - // if listMode or change => show PIVX address. In tree mode, address is not shown again for direct wallet address outputs - if (!treeMode || (!(sAddress == sWalletAddress))) - itemOutput->setText(COLUMN_ADDRESS, sAddress); - else - itemOutput->setToolTip(COLUMN_ADDRESS, sAddress); - } + const QString& sAddress = sWalletAddress; + // if listMode or change => show PIVX address. In tree mode, address is not shown again for direct wallet address outputs + if (!treeMode) + itemOutput->setText(COLUMN_ADDRESS, sAddress); + else + itemOutput->setToolTip(COLUMN_ADDRESS, sAddress); // label if (!(sAddress == sWalletAddress)) // change @@ -892,14 +872,11 @@ void CoinControlDialog::updateView() itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); // outputs delegated (for cold staking) - if (isP2CS) { + if (stakerAddress) { itemOutput->setData(COLUMN_CHECKBOX, Qt::UserRole, QString("Delegated")); if (!isLockedCoin) itemOutput->setIcon(COLUMN_CHECKBOX, QIcon("://ic-check-cold-staking-off")); - if (haveDest) { - sAddress = QString::fromStdString(EncodeDestination(outputAddressStaker, CChainParams::STAKING_ADDRESS)); - itemOutput->setToolTip(COLUMN_CHECKBOX, tr("delegated to %1 for cold staking").arg(sAddress)); - } + itemOutput->setToolTip(COLUMN_CHECKBOX, tr("delegated to %1 for cold staking").arg(*stakerAddress)); } } From 8aac899ee535272414206c47705921e7fa35f512 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 22:37:01 -0300 Subject: [PATCH 11/20] CoinControlDialog::updateView removing never used dead code. The `sAddress == sWalletAddress` has been always true. --- src/qt/coincontroldialog.cpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 3b692bea00b4..793f39372d74 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -818,23 +818,17 @@ void CoinControlDialog::updateView() itemOutput->setFlags(flgCheckbox); itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - const QString& sAddress = sWalletAddress; // if listMode or change => show PIVX address. In tree mode, address is not shown again for direct wallet address outputs - if (!treeMode) - itemOutput->setText(COLUMN_ADDRESS, sAddress); - else - itemOutput->setToolTip(COLUMN_ADDRESS, sAddress); + if (!treeMode) { + itemOutput->setText(COLUMN_ADDRESS, sWalletAddress); + }else { + itemOutput->setToolTip(COLUMN_ADDRESS, sWalletAddress); + } // label - if (!(sAddress == sWalletAddress)) // change - { - // tooltip from where the change comes from - itemOutput->setToolTip(COLUMN_LABEL, tr("change from %1 (%2)").arg(sWalletLabel).arg(sWalletAddress)); - itemOutput->setText(COLUMN_LABEL, tr("(change)")); - } else if (!treeMode) { - QString sLabel = model->getAddressTableModel()->labelForAddress(sAddress); - if (sLabel.isEmpty()) - sLabel = tr("(no label)"); + if (!treeMode) { + QString sLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); + if (sLabel.isEmpty()) sLabel = tr("(no label)"); itemOutput->setText(COLUMN_LABEL, sLabel); } From f602fab776bb1e9f8d5e2f534f34e3106d662131 Mon Sep 17 00:00:00 2001 From: furszy Date: Mon, 16 Nov 2020 22:39:38 -0300 Subject: [PATCH 12/20] CoinControlDialog::updateView removing redundant labelForAddress call. --- src/qt/coincontroldialog.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 793f39372d74..51658b1039a8 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -827,9 +827,7 @@ void CoinControlDialog::updateView() // label if (!treeMode) { - QString sLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); - if (sLabel.isEmpty()) sLabel = tr("(no label)"); - itemOutput->setText(COLUMN_LABEL, sLabel); + itemOutput->setText(COLUMN_LABEL, sWalletLabel); } // amount From 9927b0849bf92f855b879a0e599ca04d88a8ae25 Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 17 Nov 2020 16:15:33 -0300 Subject: [PATCH 13/20] Wallet and GUI: remove unused tx priority calculation and send free transaction flag --- src/qt/coincontroldialog.cpp | 64 ++---------------------------------- src/qt/coincontroldialog.h | 8 ++--- src/qt/walletmodel.cpp | 8 ++--- src/wallet/wallet.cpp | 17 ---------- src/wallet/wallet.h | 1 - 5 files changed, 8 insertions(+), 90 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 51658b1039a8..6dc4df111e95 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -478,34 +478,6 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) } } -// return human readable label for priority number -QString CoinControlDialog::getPriorityLabel(double dPriority, double mempoolEstimatePriority) -{ - double dPriorityMedium = mempoolEstimatePriority; - - if (dPriorityMedium <= 0) - dPriorityMedium = AllowFreeThreshold(); // not enough data, back to hard-coded - - if (dPriority / 1000000 > dPriorityMedium) - return tr("highest"); - else if (dPriority / 100000 > dPriorityMedium) - return tr("higher"); - else if (dPriority / 10000 > dPriorityMedium) - return tr("high"); - else if (dPriority / 1000 > dPriorityMedium) - return tr("medium-high"); - else if (dPriority > dPriorityMedium) - return tr("medium"); - else if (dPriority * 10 > dPriorityMedium) - return tr("low-medium"); - else if (dPriority * 100 > dPriorityMedium) - return tr("low"); - else if (dPriority * 1000 > dPriorityMedium) - return tr("lower"); - else - return tr("lowest"); -} - // shows count of locked unspent outputs void CoinControlDialog::updateLabelLocked() { @@ -536,8 +508,7 @@ void CoinControlDialog::updateDialogLabels() for (const COutput& out : vOutputs) { // 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); + COutPoint outpt(out.tx->GetHash(), out.i); if(model->isSpent(outpt)) { coinControl->UnSelect(outpt); continue; @@ -568,18 +539,14 @@ void CoinControlDialog::updateLabels() } } - QString sPriorityLabel = tr("none"); CAmount nAmount = 0; CAmount nPayFee = 0; CAmount nAfterFee = 0; CAmount nChange = 0; unsigned int nBytes = 0; unsigned int nBytesInputs = 0; - double dPriority = 0; - double dPriorityInputs = 0; unsigned int nQuantity = 0; int nQuantityUncompressed = 0; - bool fAllowFree = false; std::vector vCoinControl; std::vector vOutputs; @@ -602,9 +569,6 @@ void CoinControlDialog::updateLabels() // Amount nAmount += out.tx->vout[out.i].nValue; - // Priority - dPriorityInputs += (double)out.tx->vout[out.i].nValue * (out.nDepth + 1); - // Bytes CTxDestination address; if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { @@ -640,24 +604,9 @@ void CoinControlDialog::updateLabels() // nVersion, nLockTime and vin/vout len sizes nBytes += 10; - // Priority - double mempoolEstimatePriority = mempool.estimateSmartPriority(nTxConfirmTarget); - dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority) - sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority); - // Fee nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool); - // Allow free? (require at least hard-coded threshold and default to that if no estimate) - double dPriorityNeeded = std::max(mempoolEstimatePriority, AllowFreeThreshold()); - if (dPriorityNeeded <= 0) - dPriorityNeeded = AllowFreeThreshold(); // not enough data, back to hard-coded - fAllowFree = (dPriority >= dPriorityNeeded); - - if (fSendFreeTransactions) - if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) - nPayFee = 0; - if (nPayAmount > 0) { nChange = nAmount - nPayFee - nPayAmount; @@ -714,10 +663,6 @@ void CoinControlDialog::updateLabels() toolTip1 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::GetRequiredFee(1000))) + "

"; toolTip1 += tr("Can vary +/- 1 byte per input."); - QString toolTip2 = tr("Transactions with higher priority are more likely to get included into a block.") + "

"; - toolTip2 += tr("This label turns red, if the priority is smaller than \"medium\".") + "

"; - toolTip2 += tr("This means a fee of at least %1 per kB is required.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, CWallet::GetRequiredFee(1000))); - QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, ::minRelayTxFee.GetFee(546))); // how many satoshis the estimated fee can vary per byte we guess wrong @@ -751,12 +696,7 @@ void CoinControlDialog::updateView() return; bool treeMode = ui->radioTreeMode->isChecked(); - - if(treeMode){ - ui->treeWidget->setRootIsDecorated(true); - }else{ - ui->treeWidget->setRootIsDecorated(false); - } + ui->treeWidget->setRootIsDecorated(treeMode); ui->treeWidget->clear(); ui->treeWidget->setEnabled(false); // performance, otherwise updateLabels would be called for every checked checkbox diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 120bb57a9d31..25105b7a59a1 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -19,8 +19,6 @@ #include class WalletModel; - -class MultisigDialog; class CCoinControl; class CTxMemPool; @@ -36,7 +34,7 @@ class CCoinControlWidgetItem : public QTreeWidgetItem explicit CCoinControlWidgetItem(int type = Type) : QTreeWidgetItem(type) {} explicit CCoinControlWidgetItem(QTreeWidgetItem *parent, int type = Type) : QTreeWidgetItem(parent, type) {} - bool operator<(const QTreeWidgetItem &other) const; + bool operator<(const QTreeWidgetItem &other) const override; }; class CoinControlDialog : public QDialog @@ -45,7 +43,7 @@ class CoinControlDialog : public QDialog public: explicit CoinControlDialog(QWidget* parent = nullptr, bool _forDelegation = false); - ~CoinControlDialog(); + ~CoinControlDialog() override; void setModel(WalletModel* model); void updateDialogLabels(); @@ -55,8 +53,6 @@ class CoinControlDialog : public QDialog void clearPayAmounts(); void addPayAmount(const CAmount& amount); - static QString getPriorityLabel(double dPriority, double mempoolEstimatePriority); - CCoinControl* coinControl; private: diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index acf553b9cc61..f4f3d8bd793d 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -820,12 +820,12 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect { LOCK2(cs_main, wallet->cs_wallet); for (const COutPoint& outpoint : vOutpoints) { - if (!wallet->mapWallet.count(outpoint.hash)) continue; + const auto* tx = wallet->GetWalletTx(outpoint.hash); + if (!tx) continue; bool fConflicted; - const int nDepth = wallet->mapWallet[outpoint.hash].GetDepthAndMempool(fConflicted); + const int nDepth = tx->GetDepthAndMempool(fConflicted); if (nDepth < 0 || fConflicted) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true, true); - vOutputs.push_back(out); + vOutputs.emplace_back(tx, outpoint.n, nDepth, true, true); } } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c8b90ec57f48..29e86dc848d3 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -30,7 +30,6 @@ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; unsigned int nTxConfirmTarget = 1; bool bdisableSystemnotifications = false; // Those bubbles can be annoying and slow down the UI when you get lots of trx -bool fSendFreeTransactions = false; bool fPayAtLeastCustomFee = true; bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; @@ -678,7 +677,6 @@ bool CWallet::ParameterInteraction() nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", 1); bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); bdisableSystemnotifications = gArgs.GetBoolArg("-disablesystemnotifications", false); - fSendFreeTransactions = gArgs.GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS); return true; } @@ -2651,7 +2649,6 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, wtxNew.fFromMe = true; CAmount nTotalValue = nValue + nFeeRet; - double dPriority = 0; // vouts to the payees if (coinControl && !coinControl->fSplitBlock) { @@ -2698,7 +2695,6 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, for (std::pair pcoin : setCoins) { if(pcoin.first->vout[pcoin.second].scriptPubKey.IsPayToColdStaking()) wtxNew.fStakeDelegationVoided = true; - CAmount nCredit = pcoin.first->vout[pcoin.second].nValue; //The coin age after the next block (depth+1) is used instead of the current, //reflecting an assumption the user would accept a bit more delay for //a chance at a free transaction. @@ -2707,7 +2703,6 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, assert(age >= 0); if (age != 0) age += 1; - dPriority += (double)nCredit * age; } CAmount nChange = nValueIn - nValue - nFeeRet; @@ -2829,17 +2824,6 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, return false; } - dPriority = wtxNew.ComputePriority(dPriority, nBytes); - - // Can we complete this as a free transaction? - if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) { - // Not enough fee: enough priority? - double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget); - // Require at least hard-coded AllowFree. - if (dPriority >= dPriorityNeeded && AllowFree(dPriority)) - break; - } - CAmount nFeeNeeded = std::max(nFeePay, GetMinimumFee(nBytes, nTxConfirmTarget, mempool)); if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) { @@ -3914,7 +3898,6 @@ std::string CWallet::GetWalletHelpString(bool showDebug) strUsage += HelpMessageOpt("-paytxfee=", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions") + " " + _("on startup")); strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet file") + " " + _("on startup")); - strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS)); strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); strUsage += HelpMessageOpt("-txconfirmtarget=", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), 1)); strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format") + " " + _("on startup")); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index f324f18acc88..7c91db87dc0d 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -58,7 +58,6 @@ extern CAmount maxTxFee; extern unsigned int nTxConfirmTarget; extern bool bSpendZeroConfChange; extern bool bdisableSystemnotifications; -extern bool fSendFreeTransactions; extern bool fPayAtLeastCustomFee; //! -paytxfee default From 30f67e040a243a8703b1a2a67662c240f0a984a0 Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 17 Nov 2020 16:26:19 -0300 Subject: [PATCH 14/20] wallet: moving ListLockedCoins to directly return the set instead of loop over it and then return it. --- src/qt/coincontroldialog.cpp | 5 ++--- src/qt/walletmodel.cpp | 6 +++--- src/qt/walletmodel.h | 3 +-- src/wallet/rpcwallet.cpp | 6 ++---- src/wallet/wallet.cpp | 10 +++------- src/wallet/wallet.h | 2 +- 6 files changed, 12 insertions(+), 20 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 6dc4df111e95..8d30d68c1059 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -481,9 +481,8 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) // shows count of locked unspent outputs void CoinControlDialog::updateLabelLocked() { - std::vector vOutpts; - model->listLockedCoins(vOutpts); - if (vOutpts.size() > 0) { + std::set vOutpts = model->listLockedCoins(); + if (!vOutpts.empty()) { ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); ui->labelLocked->setVisible(true); } else diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index f4f3d8bd793d..0dce059af4e7 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -908,10 +908,10 @@ void WalletModel::unlockCoin(COutPoint& output) wallet->UnlockCoin(output); } -void WalletModel::listLockedCoins(std::vector& vOutpts) +std::set WalletModel::listLockedCoins() { - LOCK2(cs_main, wallet->cs_wallet); - wallet->ListLockedCoins(vOutpts); + LOCK(wallet->cs_wallet); + return wallet->ListLockedCoins(); } void WalletModel::loadReceiveRequests(std::vector& vReceiveRequests) diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6f85b27d4dd6..366fa94eaba9 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -282,9 +282,8 @@ class WalletModel : public QObject bool isLockedCoin(uint256 hash, unsigned int n) const; void lockCoin(COutPoint& output); void unlockCoin(COutPoint& output); - void listLockedCoins(std::vector& vOutpts); + std::set listLockedCoins(); - std::string GetUniqueWalletBackupName(); void loadReceiveRequests(std::vector& vReceiveRequests); bool saveReceiveRequest(const std::string& sAddress, const int64_t nId, const std::string& sRequest); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 46af9472a8c7..8ccb6fdc7e60 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -3453,12 +3453,10 @@ UniValue listlockunspent(const JSONRPCRequest& request) LOCK2(cs_main, pwalletMain->cs_wallet); - std::vector vOutpts; - pwalletMain->ListLockedCoins(vOutpts); - + std::set vOutpts = pwalletMain->ListLockedCoins(); UniValue ret(UniValue::VARR); - for (COutPoint& outpt : vOutpts) { + for (const COutPoint& outpt : vOutpts) { UniValue o(UniValue::VOBJ); o.pushKV("txid", outpt.hash.GetHex()); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 29e86dc848d3..aeb9aa91732f 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -3520,14 +3520,10 @@ bool CWallet::IsLockedCoin(const uint256& hash, unsigned int n) const return (setLockedCoins.count(outpt) > 0); } -void CWallet::ListLockedCoins(std::vector& vOutpts) +std::set CWallet::ListLockedCoins() { - AssertLockHeld(cs_wallet); // setLockedCoins - for (std::set::iterator it = setLockedCoins.begin(); - it != setLockedCoins.end(); it++) { - COutPoint outpt = (*it); - vOutpts.push_back(outpt); - } + AssertLockHeld(cs_wallet); + return setLockedCoins; } /** @} */ // end of Actions diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7c91db87dc0d..7a35493d5d70 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -499,7 +499,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface void LockCoin(const COutPoint& output); void UnlockCoin(const COutPoint& output); void UnlockAllCoins(); - void ListLockedCoins(std::vector& vOutpts); + std::set ListLockedCoins(); // keystore implementation PairResult getNewAddress(CTxDestination& ret, const std::string addressLabel, const std::string purpose, From 3420701a915050ff6481d218ce2250a7a65b26ad Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 17 Nov 2020 17:11:04 -0300 Subject: [PATCH 15/20] GUI: coinControlDialog removing an totally redundant, unnecessary and badly coded function. --- src/qt/coincontroldialog.cpp | 37 ------------------------------------ src/qt/coincontroldialog.h | 1 - 2 files changed, 38 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 8d30d68c1059..a7d90938ebeb 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -211,7 +211,6 @@ void CoinControlDialog::setModel(WalletModel* model) updateView(); updateLabelLocked(); updateLabels(); - updateDialogLabels(); } } @@ -230,7 +229,6 @@ void CoinControlDialog::buttonSelectAllClicked() if (!fSelectAll) coinControl->UnSelectAll(); // just to be sure updateLabels(); - updateDialogLabels(); } // Toggle lock state @@ -261,7 +259,6 @@ void CoinControlDialog::buttonToggleLockClicked() } ui->treeWidget->setEnabled(true); updateLabels(); - updateDialogLabels(); } else { QMessageBox msgBox; msgBox.setObjectName("lockMessageBox"); @@ -465,7 +462,6 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) // selection changed -> update labels if (ui->treeWidget->isEnabled()){ // do not update on every click for (un)select all updateLabels(); - updateDialogLabels(); } } @@ -489,38 +485,6 @@ void CoinControlDialog::updateLabelLocked() ui->labelLocked->setVisible(false); } -void CoinControlDialog::updateDialogLabels() -{ - - if (this->parentWidget() == nullptr) { - updateLabels(); - return; - } - - std::vector vCoinControl; - std::vector vOutputs; - coinControl->ListSelected(vCoinControl); - model->getOutputs(vCoinControl, vOutputs); - - CAmount nAmount = 0; - unsigned int nQuantity = 0; - for (const COutput& out : vOutputs) { - // unselect already spent, very unlikely scenario, this could happen - // when selected are spent elsewhere, like rpc or another computer - COutPoint outpt(out.tx->GetHash(), out.i); - if(model->isSpent(outpt)) { - coinControl->UnSelect(outpt); - continue; - } - - // Quantity - nQuantity++; - - // Amount - nAmount += out.tx->vout[out.i].nValue; - } -} - void CoinControlDialog::updateLabels() { if (!model) @@ -837,7 +801,6 @@ void CoinControlDialog::refreshDialog() updateView(); updateLabelLocked(); updateLabels(); - updateDialogLabels(); } void CoinControlDialog::inform(const QString& text) diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 25105b7a59a1..a3c97481bc6b 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -46,7 +46,6 @@ class CoinControlDialog : public QDialog ~CoinControlDialog() override; void setModel(WalletModel* model); - void updateDialogLabels(); void updateLabels(); void updateView(); void refreshDialog(); From fd19626fe6414407dce020c6d689d3e76403c024 Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 17 Nov 2020 17:26:25 -0300 Subject: [PATCH 16/20] GUI: coinControlDialog removing unnecessary uncompressed keys size calculation. Our reference client is not creating them by default. Most likely never did it. --- src/qt/coincontroldialog.cpp | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index a7d90938ebeb..4cf88f567861 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -509,7 +509,6 @@ void CoinControlDialog::updateLabels() unsigned int nBytes = 0; unsigned int nBytesInputs = 0; unsigned int nQuantity = 0; - int nQuantityUncompressed = 0; std::vector vCoinControl; std::vector vOutputs; @@ -528,24 +527,10 @@ void CoinControlDialog::updateLabels() // Quantity nQuantity++; - // Amount nAmount += out.tx->vout[out.i].nValue; - // Bytes - CTxDestination address; - if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { - CPubKey pubkey; - CKeyID* keyid = boost::get(&address); - if (keyid && model->getPubKey(*keyid, pubkey)) { - nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); - if (!pubkey.IsCompressed()) - nQuantityUncompressed++; - } else - nBytesInputs += 148; // in all error cases, simply assume 148 here - } else - nBytesInputs += 148; - + nBytesInputs += 148; // Additional byte for P2CS if (out.tx->vout[out.i].scriptPubKey.IsPayToColdStaking()) nBytesInputs++; From 18e2761c59d36023cb68007b87ba7d1bac69d789 Mon Sep 17 00:00:00 2001 From: furszy Date: Tue, 17 Nov 2020 17:31:09 -0300 Subject: [PATCH 17/20] GUI: coinControlDialog removing now unneded qt5.3 and qt5.4 workaround. Our minimum version is qt5.5 --- src/qt/coincontroldialog.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 4cf88f567861..4ae2550dcc3c 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -464,14 +464,6 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) updateLabels(); } } - - // TODO: Remove this temporary qt5 fix after Qt5.3 and Qt5.4 are no longer used. - // Fixed in Qt5.5 and above: https://bugreports.qt.io/browse/QTBUG-43473 - else if (column == COLUMN_CHECKBOX && item->childCount() > 0) - { - if (item->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked && item->child(0)->checkState(COLUMN_CHECKBOX) == Qt::PartiallyChecked) - item->setCheckState(COLUMN_CHECKBOX, Qt::Checked); - } } // shows count of locked unspent outputs From 8ca9d45a47f8af13c5d6849c5724df7eedf98429 Mon Sep 17 00:00:00 2001 From: furszy Date: Wed, 18 Nov 2020 14:24:22 -0300 Subject: [PATCH 18/20] GUI: coin control dialog, abstracting out row load. --- src/qt/coincontroldialog.cpp | 140 ++++++++++++++++++++--------------- src/qt/coincontroldialog.h | 17 +++++ 2 files changed, 96 insertions(+), 61 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 4ae2550dcc3c..d50ad0709941 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -630,6 +630,82 @@ void CoinControlDialog::updateLabels() label->setVisible(nChange < 0); } +void CoinControlDialog::loadAvailableCoin(bool treeMode, + CCoinControlWidgetItem* itemWalletAddress, + QFlags flgCheckbox, + QFlags flgTristate, + int nDisplayUnit, + const QString& sWalletAddress, + const Optional& stakerAddress, + const QString& sWalletLabel, + const uint256& txhash, + const uint32_t outIndex, + const CAmount nValue, + const int64_t nTime, + const int nDepth) +{ + CCoinControlWidgetItem* itemOutput; + if (treeMode) + itemOutput = new CCoinControlWidgetItem(itemWalletAddress); + else + itemOutput = new CCoinControlWidgetItem(ui->treeWidget); + itemOutput->setFlags(flgCheckbox); + itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + + // if listMode or change => show PIVX address. In tree mode, address is not shown again for direct wallet address outputs + if (!treeMode) { + itemOutput->setText(COLUMN_ADDRESS, sWalletAddress); + }else { + itemOutput->setToolTip(COLUMN_ADDRESS, sWalletAddress); + } + + // label + if (!treeMode) { + itemOutput->setText(COLUMN_LABEL, sWalletLabel); + } + + // amount + itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue)); + itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue)); + itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong) nValue)); + + // date + itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(nTime)); + itemOutput->setToolTip(COLUMN_DATE, GUIUtil::dateTimeStr(nTime)); + itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong) nTime)); + + // confirmations + itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(nDepth)); + itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong) nDepth)); + + // transaction hash + itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); + + // vout index + itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(outIndex)); + + // disable locked coins + const bool isLockedCoin = model->isLockedCoin(txhash, outIndex); + if (isLockedCoin) { + --nSelectableInputs; + coinControl->UnSelect({txhash, outIndex}); // just to be sure + itemOutput->setDisabled(true); + itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); + } + + // set checkbox + if (coinControl->IsSelected(COutPoint(txhash, outIndex))) + itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); + + // outputs delegated (for cold staking) + if (stakerAddress) { + itemOutput->setData(COLUMN_CHECKBOX, Qt::UserRole, QString("Delegated")); + if (!isLockedCoin) + itemOutput->setIcon(COLUMN_CHECKBOX, QIcon("://ic-check-cold-staking-off")); + itemOutput->setToolTip(COLUMN_CHECKBOX, tr("delegated to %1 for cold staking").arg(*stakerAddress)); + } +} + void CoinControlDialog::updateView() { if (!model || !model->getOptionsModel() || !model->getAddressTableModel()) @@ -689,67 +765,9 @@ void CoinControlDialog::updateView() nSum += nValue; nChildren++; - // - CCoinControlWidgetItem* itemOutput; - if (treeMode) - itemOutput = new CCoinControlWidgetItem(itemWalletAddress); - else - itemOutput = new CCoinControlWidgetItem(ui->treeWidget); - itemOutput->setFlags(flgCheckbox); - itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); - - // if listMode or change => show PIVX address. In tree mode, address is not shown again for direct wallet address outputs - if (!treeMode) { - itemOutput->setText(COLUMN_ADDRESS, sWalletAddress); - }else { - itemOutput->setToolTip(COLUMN_ADDRESS, sWalletAddress); - } - - // label - if (!treeMode) { - itemOutput->setText(COLUMN_LABEL, sWalletLabel); - } - - // amount - itemOutput->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue)); - itemOutput->setToolTip(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nValue)); - itemOutput->setData(COLUMN_AMOUNT, Qt::UserRole, QVariant((qlonglong) nValue)); - - // date - itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(nTime)); - itemOutput->setToolTip(COLUMN_DATE, GUIUtil::dateTimeStr(nTime)); - itemOutput->setData(COLUMN_DATE, Qt::UserRole, QVariant((qlonglong) nTime)); - - // confirmations - itemOutput->setText(COLUMN_CONFIRMATIONS, QString::number(nDepth)); - itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, QVariant((qlonglong) nDepth)); - - // transaction hash - itemOutput->setText(COLUMN_TXHASH, QString::fromStdString(txhash.GetHex())); - - // vout index - itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(outIndex)); - - // disable locked coins - const bool isLockedCoin = model->isLockedCoin(txhash, outIndex); - if (isLockedCoin) { - --nSelectableInputs; - coinControl->UnSelect({txhash, outIndex}); // just to be sure - itemOutput->setDisabled(true); - itemOutput->setIcon(COLUMN_CHECKBOX, QIcon(":/icons/lock_closed")); - } - - // set checkbox - if (coinControl->IsSelected(COutPoint(txhash, outIndex))) - itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); - - // outputs delegated (for cold staking) - if (stakerAddress) { - itemOutput->setData(COLUMN_CHECKBOX, Qt::UserRole, QString("Delegated")); - if (!isLockedCoin) - itemOutput->setIcon(COLUMN_CHECKBOX, QIcon("://ic-check-cold-staking-off")); - itemOutput->setToolTip(COLUMN_CHECKBOX, tr("delegated to %1 for cold staking").arg(*stakerAddress)); - } + loadAvailableCoin(treeMode, itemWalletAddress, flgCheckbox, flgTristate, + nDisplayUnit, sWalletAddress, stakerAddress, sWalletLabel, + txhash, outIndex, nValue, nTime, nDepth); } // amount diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index a3c97481bc6b..0193ab4e7fb8 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -8,6 +8,8 @@ #include "amount.h" #include "qt/pivx/snackbar.h" +#include "optional.h" +#include "uint256.h" #include #include @@ -74,6 +76,21 @@ class CoinControlDialog : public QDialog void sortView(int, Qt::SortOrder); void inform(const QString& text); + // Load a row with coin's data + void loadAvailableCoin(bool treeMode, + CCoinControlWidgetItem* itemWalletAddress, + QFlags flgCheckbox, + QFlags flgTristate, + int nDisplayUnit, + const QString& sWalletAddress, + const Optional& stakerAddress, + const QString& sWalletLabel, + const uint256& txhash, + const uint32_t outIndex, + const CAmount nValue, + const int64_t nTime, + const int nDepth); + enum { COLUMN_CHECKBOX, COLUMN_AMOUNT, From a463ac221c7554fc63fe620f362c348a0a192c39 Mon Sep 17 00:00:00 2001 From: furszy Date: Wed, 18 Nov 2020 14:39:13 -0300 Subject: [PATCH 19/20] Model:ListCoins, abstracted returned value into a wrapper in order to be able to return unspent notes in the future as well. --- src/qt/coincontroldialog.cpp | 16 ++++------------ src/qt/walletmodel.cpp | 11 +++++++++-- src/qt/walletmodel.h | 11 ++++++++++- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index d50ad0709941..8e082cc100f7 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -721,7 +721,7 @@ void CoinControlDialog::updateView() int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); nSelectableInputs = 0; - std::map> mapCoins; + std::map> mapCoins; model->listCoins(mapCoins); for (const auto& coins : mapCoins) { @@ -752,22 +752,14 @@ void CoinControlDialog::updateView() CAmount nSum = 0; int nChildren = 0; - for(const COutput& out: coins.second) { - - // Basic values used in the entire process - const uint256& txhash = out.tx->GetHash(); - const uint32_t outIndex = out.i; - const CAmount nValue = out.tx->vout[out.i].nValue; - const int64_t nTime = out.tx->GetTxTime(); - const int nDepth = out.nDepth; - + for (const WalletModel::ListCoinsValue& out: coins.second) { ++nSelectableInputs; - nSum += nValue; + nSum += out.nValue; nChildren++; loadAvailableCoin(treeMode, itemWalletAddress, flgCheckbox, flgTristate, nDisplayUnit, sWalletAddress, stakerAddress, sWalletLabel, - txhash, outIndex, nValue, nTime, nDepth); + out.txhash, out.outIndex, out.nValue, out.nTime, out.nDepth); } // amount diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 0dce059af4e7..5b73f7d168ed 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -854,7 +854,7 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const } // AvailableCoins + LockedCoins grouped by wallet address (put change in one group with wallet address) -void WalletModel::listCoins(std::map >& mapCoins) const +void WalletModel::listCoins(std::map>& mapCoins) const { CWallet::AvailableCoinsFilter filter; filter.fIncludeLocked = true; @@ -886,7 +886,14 @@ void WalletModel::listCoins(std::map >& mapCo nullopt; ListCoinsKey key{address, stakerAddr}; - mapCoins[key].emplace_back(out); + ListCoinsValue value{ + out.tx->GetHash(), + out.i, + out.tx->vout[out.i].nValue, + out.tx->GetTxTime(), + out.nDepth + }; + mapCoins[key].emplace_back(value); } } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 366fa94eaba9..fe4135083efc 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -277,7 +277,16 @@ class WalletModel : public QObject } }; - void listCoins(std::map >& mapCoins) const; + class ListCoinsValue { + public: + const uint256& txhash; + int outIndex; + CAmount nValue; + int64_t nTime; + int nDepth; + }; + + void listCoins(std::map>& mapCoins) const; bool isLockedCoin(uint256 hash, unsigned int n) const; void lockCoin(COutPoint& output); From 71e1f9886cf54ecb014425b57eff2c4c13041f63 Mon Sep 17 00:00:00 2001 From: furszy Date: Thu, 19 Nov 2020 17:27:45 -0300 Subject: [PATCH 20/20] Wallet + GUI: IsChange function + coin control, setting change label for the change outputs --- src/qt/coincontroldialog.cpp | 16 +++++++++++----- src/qt/coincontroldialog.h | 3 ++- src/qt/walletmodel.cpp | 2 +- src/qt/walletmodel.h | 1 + src/wallet/wallet.cpp | 11 ++++++++--- src/wallet/wallet.h | 1 + 6 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 8e082cc100f7..25f3df0bb6c1 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -642,7 +642,8 @@ void CoinControlDialog::loadAvailableCoin(bool treeMode, const uint32_t outIndex, const CAmount nValue, const int64_t nTime, - const int nDepth) + const int nDepth, + const bool isChange) { CCoinControlWidgetItem* itemOutput; if (treeMode) @@ -660,7 +661,11 @@ void CoinControlDialog::loadAvailableCoin(bool treeMode, } // label - if (!treeMode) { + if (isChange) { + // tooltip stating where the change is being stored. + itemOutput->setToolTip(COLUMN_LABEL, tr("change in %1").arg(sWalletAddress)); + itemOutput->setText(COLUMN_LABEL, tr("(change)")); + } else if (!treeMode) { itemOutput->setText(COLUMN_LABEL, sWalletLabel); } @@ -729,7 +734,7 @@ void CoinControlDialog::updateView() itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); const WalletModel::ListCoinsKey& keys = coins.first; const QString& sWalletAddress = keys.address; - const Optional& stakerAddress = keys.stakerAddres; + const Optional& stakerAddress = keys.stakerAddress; QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); if (sWalletLabel.isEmpty()) sWalletLabel = tr("(no label)"); @@ -758,8 +763,9 @@ void CoinControlDialog::updateView() nChildren++; loadAvailableCoin(treeMode, itemWalletAddress, flgCheckbox, flgTristate, - nDisplayUnit, sWalletAddress, stakerAddress, sWalletLabel, - out.txhash, out.outIndex, out.nValue, out.nTime, out.nDepth); + nDisplayUnit, sWalletAddress, stakerAddress, sWalletLabel, + out.txhash, out.outIndex, out.nValue, out.nTime, out.nDepth, + keys.isChange); } // amount diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index 0193ab4e7fb8..131644483136 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -89,7 +89,8 @@ class CoinControlDialog : public QDialog const uint32_t outIndex, const CAmount nValue, const int64_t nTime, - const int nDepth); + const int nDepth, + const bool isChange); enum { COLUMN_CHECKBOX, diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 5b73f7d168ed..efa2baf5bb7e 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -885,7 +885,7 @@ void WalletModel::listCoins(std::map>& Optional(QString::fromStdString(EncodeDestination(outputAddressStaker, CChainParams::STAKING_ADDRESS))) : nullopt; - ListCoinsKey key{address, stakerAddr}; + ListCoinsKey key{address, wallet->IsChange(outputAddress), stakerAddr}; ListCoinsValue value{ out.tx->GetHash(), out.i, diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index fe4135083efc..e8704c23b9c1 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -266,6 +266,7 @@ class WalletModel : public QObject class ListCoinsKey { public: QString address; + bool isChange; Optional stakerAddress; // used only for P2CS utxo bool operator==(const ListCoinsKey& key2) const { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index aeb9aa91732f..2516d6b39a35 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1310,13 +1310,18 @@ bool CWallet::IsChange(const CTxOut& txout) const if (!ExtractDestination(txout.scriptPubKey, address)) return true; - LOCK(cs_wallet); - if (!HasAddressBook(address)) - return true; + return IsChange(address); } return false; } +bool CWallet::IsChange(const CTxDestination& address) const +{ + // Read the current assumptions in IsChange(const CTxOut&) + // this can definitely be different in the short future. + return WITH_LOCK(cs_wallet, return !HasAddressBook(address)); +} + int64_t CWalletTx::GetTxTime() const { int64_t n = nTimeSmart; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 7a35493d5d70..fcb392f2f50a 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -709,6 +709,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface isminetype IsMine(const CTxOut& txout) const; CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const; bool IsChange(const CTxOut& txout) const; + bool IsChange(const CTxDestination& address) const; CAmount GetChange(const CTxOut& txout) const; bool IsMine(const CTransaction& tx) const; /** should probably be renamed to IsRelevantToMe */