Changeset View
Changeset View
Standalone View
Standalone View
src/qt/coincontroldialog.cpp
// Copyright (c) 2011-2016 The Bitcoin Core developers | // Copyright (c) 2011-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "coincontroldialog.h" | #include "coincontroldialog.h" | ||||
#include "ui_coincontroldialog.h" | #include "ui_coincontroldialog.h" | ||||
#include "addresstablemodel.h" | #include "addresstablemodel.h" | ||||
#include "bitcoinunits.h" | #include "bitcoinunits.h" | ||||
#include "guiutil.h" | #include "guiutil.h" | ||||
#include "optionsmodel.h" | #include "optionsmodel.h" | ||||
#include "platformstyle.h" | #include "platformstyle.h" | ||||
#include "txmempool.h" | #include "txmempool.h" | ||||
#include "walletmodel.h" | #include "walletmodel.h" | ||||
#include "dstencode.h" | #include "dstencode.h" | ||||
#include "init.h" | #include "interface/node.h" | ||||
#include "policy/policy.h" | #include "policy/policy.h" | ||||
#include "validation.h" // For mempool | #include "validation.h" // For mempool | ||||
#include "wallet/coincontrol.h" | #include "wallet/coincontrol.h" | ||||
#include "wallet/fees.h" | #include "wallet/fees.h" | ||||
#include "wallet/wallet.h" | #include "wallet/wallet.h" | ||||
#include <QApplication> | #include <QApplication> | ||||
#include <QCheckBox> | #include <QCheckBox> | ||||
▲ Show 20 Lines • Show All 204 Lines • ▼ Show 20 Lines | if (item) { | ||||
// roots in context menu | // roots in context menu | ||||
if (item->text(COLUMN_TXHASH).length() == 64) { | if (item->text(COLUMN_TXHASH).length() == 64) { | ||||
TxId txid; | TxId txid; | ||||
txid.SetHex(item->text(COLUMN_TXHASH).toStdString()); | txid.SetHex(item->text(COLUMN_TXHASH).toStdString()); | ||||
// transaction hash is 64 characters (this means its a child node, | // transaction hash is 64 characters (this means its a child node, | ||||
// so its not a parent node in tree mode) | // so its not a parent node in tree mode) | ||||
copyTransactionHashAction->setEnabled(true); | copyTransactionHashAction->setEnabled(true); | ||||
if (model->isLockedCoin(txid, | if (model->wallet().isLockedCoin( | ||||
item->text(COLUMN_VOUT_INDEX).toUInt())) { | COutPoint(txid, item->text(COLUMN_VOUT_INDEX).toUInt()))) { | ||||
lockAction->setEnabled(false); | lockAction->setEnabled(false); | ||||
unlockAction->setEnabled(true); | unlockAction->setEnabled(true); | ||||
} else { | } else { | ||||
lockAction->setEnabled(true); | lockAction->setEnabled(true); | ||||
unlockAction->setEnabled(false); | unlockAction->setEnabled(false); | ||||
} | } | ||||
} else { | } else { | ||||
// this means click on parent node in tree mode -> disable all | // this means click on parent node in tree mode -> disable all | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
void CoinControlDialog::lockCoin() { | void CoinControlDialog::lockCoin() { | ||||
if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked) { | if (contextMenuItem->checkState(COLUMN_CHECKBOX) == Qt::Checked) { | ||||
contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | contextMenuItem->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | ||||
} | } | ||||
COutPoint outpt( | COutPoint outpt( | ||||
uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), | uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), | ||||
contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); | contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); | ||||
model->lockCoin(outpt); | model->wallet().lockCoin(outpt); | ||||
contextMenuItem->setDisabled(true); | contextMenuItem->setDisabled(true); | ||||
contextMenuItem->setIcon( | contextMenuItem->setIcon( | ||||
COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); | COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); | ||||
updateLabelLocked(); | updateLabelLocked(); | ||||
} | } | ||||
// context menu action: unlock coin | // context menu action: unlock coin | ||||
void CoinControlDialog::unlockCoin() { | void CoinControlDialog::unlockCoin() { | ||||
COutPoint outpt( | COutPoint outpt( | ||||
uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), | uint256S(contextMenuItem->text(COLUMN_TXHASH).toStdString()), | ||||
contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); | contextMenuItem->text(COLUMN_VOUT_INDEX).toUInt()); | ||||
model->unlockCoin(outpt); | model->wallet().unlockCoin(outpt); | ||||
contextMenuItem->setDisabled(false); | contextMenuItem->setDisabled(false); | ||||
contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon()); | contextMenuItem->setIcon(COLUMN_CHECKBOX, QIcon()); | ||||
updateLabelLocked(); | updateLabelLocked(); | ||||
} | } | ||||
// copy label "Quantity" to clipboard | // copy label "Quantity" to clipboard | ||||
void CoinControlDialog::clipboardQuantity() { | void CoinControlDialog::clipboardQuantity() { | ||||
GUIUtil::setClipboard(ui->labelCoinControlQuantity->text()); | GUIUtil::setClipboard(ui->labelCoinControlQuantity->text()); | ||||
▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | else if (column == COLUMN_CHECKBOX && item->childCount() > 0) { | ||||
item->setCheckState(COLUMN_CHECKBOX, Qt::Checked); | item->setCheckState(COLUMN_CHECKBOX, Qt::Checked); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
// shows count of locked unspent outputs | // shows count of locked unspent outputs | ||||
void CoinControlDialog::updateLabelLocked() { | void CoinControlDialog::updateLabelLocked() { | ||||
std::vector<COutPoint> vOutpts; | std::vector<COutPoint> vOutpts; | ||||
model->listLockedCoins(vOutpts); | model->wallet().listLockedCoins(vOutpts); | ||||
if (vOutpts.size() > 0) { | if (vOutpts.size() > 0) { | ||||
ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); | ui->labelLocked->setText(tr("(%1 locked)").arg(vOutpts.size())); | ||||
ui->labelLocked->setVisible(true); | ui->labelLocked->setVisible(true); | ||||
} else { | } else { | ||||
ui->labelLocked->setVisible(false); | ui->labelLocked->setVisible(false); | ||||
} | } | ||||
} | } | ||||
void CoinControlDialog::updateLabels(WalletModel *model, QDialog *dialog) { | void CoinControlDialog::updateLabels(WalletModel *model, QDialog *dialog) { | ||||
if (!model) { | if (!model) { | ||||
return; | return; | ||||
} | } | ||||
// nPayAmount | // nPayAmount | ||||
Amount nPayAmount = Amount::zero(); | Amount nPayAmount = Amount::zero(); | ||||
bool fDust = false; | bool fDust = false; | ||||
CMutableTransaction txDummy; | CMutableTransaction txDummy; | ||||
for (const Amount amount : CoinControlDialog::payAmounts) { | for (const Amount amount : CoinControlDialog::payAmounts) { | ||||
nPayAmount += amount; | nPayAmount += amount; | ||||
if (amount > Amount::zero()) { | if (amount > Amount::zero()) { | ||||
CTxOut txout(amount, | CTxOut txout(amount, | ||||
static_cast<CScript>(std::vector<uint8_t>(24, 0))); | static_cast<CScript>(std::vector<uint8_t>(24, 0))); | ||||
txDummy.vout.push_back(txout); | txDummy.vout.push_back(txout); | ||||
if (txout.IsDust(dustRelayFee)) { | if (txout.IsDust(model->node().getDustRelayFee())) { | ||||
fDust = true; | fDust = true; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Amount nAmount = Amount::zero(); | Amount nAmount = Amount::zero(); | ||||
Amount nPayFee = Amount::zero(); | Amount nPayFee = Amount::zero(); | ||||
Amount nAfterFee = Amount::zero(); | Amount nAfterFee = Amount::zero(); | ||||
Amount nChange = Amount::zero(); | Amount nChange = Amount::zero(); | ||||
unsigned int nBytes = 0; | unsigned int nBytes = 0; | ||||
unsigned int nBytesInputs = 0; | unsigned int nBytesInputs = 0; | ||||
unsigned int nQuantity = 0; | unsigned int nQuantity = 0; | ||||
int nQuantityUncompressed = 0; | int nQuantityUncompressed = 0; | ||||
std::vector<COutPoint> vCoinControl; | std::vector<COutPoint> vCoinControl; | ||||
std::vector<COutput> vOutputs; | |||||
coinControl()->ListSelected(vCoinControl); | 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 | // unselect already spent, very unlikely scenario, this could happen | ||||
// when selected are spent elsewhere, like rpc or another computer | // when selected are spent elsewhere, like rpc or another computer | ||||
uint256 txhash = out.tx->GetId(); | const COutPoint &outpt = vCoinControl[i++]; | ||||
COutPoint outpt(txhash, out.i); | if (out.is_spent) { | ||||
if (model->isSpent(outpt)) { | |||||
coinControl()->UnSelect(outpt); | coinControl()->UnSelect(outpt); | ||||
continue; | continue; | ||||
} | } | ||||
// Quantity | // Quantity | ||||
nQuantity++; | nQuantity++; | ||||
// Amount | // Amount | ||||
nAmount += out.tx->tx->vout[out.i].nValue; | nAmount += out.txout.nValue; | ||||
// Bytes | // Bytes | ||||
CTxDestination address; | CTxDestination address; | ||||
if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, address)) { | if (ExtractDestination(out.txout.scriptPubKey, address)) { | ||||
CPubKey pubkey; | CPubKey pubkey; | ||||
CKeyID *keyid = boost::get<CKeyID>(&address); | CKeyID *keyid = boost::get<CKeyID>(&address); | ||||
if (keyid && model->getPubKey(*keyid, pubkey)) { | if (keyid && model->wallet().getPubKey(*keyid, pubkey)) { | ||||
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); | nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); | ||||
if (!pubkey.IsCompressed()) { | if (!pubkey.IsCompressed()) { | ||||
nQuantityUncompressed++; | nQuantityUncompressed++; | ||||
} | } | ||||
} else { | } else { | ||||
// in all error cases, simply assume 148 here | // in all error cases, simply assume 148 here | ||||
nBytesInputs += 148; | nBytesInputs += 148; | ||||
} | } | ||||
Show All 18 Lines | if (nQuantity > 0) { | ||||
// accurate | // accurate | ||||
if (CoinControlDialog::fSubtractFeeFromAmount) { | if (CoinControlDialog::fSubtractFeeFromAmount) { | ||||
if (nAmount - nPayAmount == Amount::zero()) { | if (nAmount - nPayAmount == Amount::zero()) { | ||||
nBytes -= 34; | nBytes -= 34; | ||||
} | } | ||||
} | } | ||||
// Fee | // Fee | ||||
nPayFee = GetMinimumFee(nBytes, g_mempool, *coinControl()); | nPayFee = model->node().getMinimumFee(nBytes, *coinControl()); | ||||
if (nPayAmount > Amount::zero()) { | if (nPayAmount > Amount::zero()) { | ||||
nChange = nAmount - nPayAmount; | nChange = nAmount - nPayAmount; | ||||
if (!CoinControlDialog::fSubtractFeeFromAmount) { | if (!CoinControlDialog::fSubtractFeeFromAmount) { | ||||
nChange -= nPayFee; | nChange -= nPayFee; | ||||
} | } | ||||
// Never create dust outputs; if we would, just add the dust to the | // Never create dust outputs; if we would, just add the dust to the | ||||
// fee. | // fee. | ||||
if (nChange > Amount::zero() && nChange < MIN_CHANGE) { | if (nChange > Amount::zero() && nChange < MIN_CHANGE) { | ||||
CTxOut txout(nChange, | CTxOut txout(nChange, | ||||
static_cast<CScript>(std::vector<uint8_t>(24, 0))); | static_cast<CScript>(std::vector<uint8_t>(24, 0))); | ||||
if (txout.IsDust(dustRelayFee)) { | if (txout.IsDust(model->node().getDustRelayFee())) { | ||||
// dust-change will be raised until no dust | // dust-change will be raised until no dust | ||||
if (CoinControlDialog::fSubtractFeeFromAmount) { | if (CoinControlDialog::fSubtractFeeFromAmount) { | ||||
nChange = txout.GetDustThreshold(dustRelayFee); | nChange = txout.GetDustThreshold(dustRelayFee); | ||||
} else { | } else { | ||||
nPayFee += nChange; | nPayFee += nChange; | ||||
nChange = Amount::zero(); | nChange = Amount::zero(); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | void CoinControlDialog::updateView() { | ||||
QFlags<Qt::ItemFlag> flgCheckbox = | QFlags<Qt::ItemFlag> flgCheckbox = | ||||
Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable; | ||||
QFlags<Qt::ItemFlag> flgTristate = | QFlags<Qt::ItemFlag> flgTristate = | ||||
Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | | Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | | ||||
Qt::ItemIsTristate; | Qt::ItemIsTristate; | ||||
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); | int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); | ||||
std::map<QString, std::vector<COutput>> mapCoins; | for (const auto &coins : model->wallet().listCoins()) { | ||||
model->listCoins(mapCoins); | |||||
for (const std::pair<QString, std::vector<COutput>> &coins : mapCoins) { | |||||
CCoinControlWidgetItem *itemWalletAddress = | CCoinControlWidgetItem *itemWalletAddress = | ||||
new CCoinControlWidgetItem(); | new CCoinControlWidgetItem(); | ||||
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | ||||
QString sWalletAddress = coins.first; | QString sWalletAddress = | ||||
QString::fromStdString(EncodeDestination(coins.first)); | |||||
QString sWalletLabel = | QString sWalletLabel = | ||||
model->getAddressTableModel()->labelForAddress(sWalletAddress); | model->getAddressTableModel()->labelForAddress(sWalletAddress); | ||||
if (sWalletLabel.isEmpty()) { | if (sWalletLabel.isEmpty()) { | ||||
sWalletLabel = tr("(no label)"); | sWalletLabel = tr("(no label)"); | ||||
} | } | ||||
if (treeMode) { | if (treeMode) { | ||||
// wallet address | // wallet address | ||||
ui->treeWidget->addTopLevelItem(itemWalletAddress); | ui->treeWidget->addTopLevelItem(itemWalletAddress); | ||||
itemWalletAddress->setFlags(flgTristate); | itemWalletAddress->setFlags(flgTristate); | ||||
itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | ||||
// label | // label | ||||
itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); | itemWalletAddress->setText(COLUMN_LABEL, sWalletLabel); | ||||
// address | // address | ||||
itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); | itemWalletAddress->setText(COLUMN_ADDRESS, sWalletAddress); | ||||
} | } | ||||
Amount nSum = Amount::zero(); | Amount nSum = Amount::zero(); | ||||
int nChildren = 0; | int nChildren = 0; | ||||
for (const COutput &out : coins.second) { | for (const auto &outpair : coins.second) { | ||||
nSum += out.tx->tx->vout[out.i].nValue; | const COutPoint &output = std::get<0>(outpair); | ||||
const interface::WalletTxOut &out = std::get<1>(outpair); | |||||
nSum += out.txout.nValue; | |||||
nChildren++; | nChildren++; | ||||
CCoinControlWidgetItem *itemOutput; | CCoinControlWidgetItem *itemOutput; | ||||
if (treeMode) { | if (treeMode) { | ||||
itemOutput = new CCoinControlWidgetItem(itemWalletAddress); | itemOutput = new CCoinControlWidgetItem(itemWalletAddress); | ||||
} else { | } else { | ||||
itemOutput = new CCoinControlWidgetItem(ui->treeWidget); | itemOutput = new CCoinControlWidgetItem(ui->treeWidget); | ||||
} | } | ||||
itemOutput->setFlags(flgCheckbox); | itemOutput->setFlags(flgCheckbox); | ||||
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); | ||||
// address | // address | ||||
CTxDestination outputAddress; | CTxDestination outputAddress; | ||||
QString sAddress = ""; | QString sAddress = ""; | ||||
if (ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, | if (ExtractDestination(out.txout.scriptPubKey, outputAddress)) { | ||||
outputAddress)) { | |||||
sAddress = | sAddress = | ||||
QString::fromStdString(EncodeDestination(outputAddress)); | QString::fromStdString(EncodeDestination(outputAddress)); | ||||
// if listMode or change => show bitcoin address. In tree mode, | // if listMode or change => show bitcoin address. In tree mode, | ||||
// address is not shown again for direct wallet address outputs | // address is not shown again for direct wallet address outputs | ||||
if (!treeMode || (!(sAddress == sWalletAddress))) { | if (!treeMode || (!(sAddress == sWalletAddress))) { | ||||
itemOutput->setText(COLUMN_ADDRESS, sAddress); | itemOutput->setText(COLUMN_ADDRESS, sAddress); | ||||
} | } | ||||
Show All 13 Lines | for (const auto &coins : model->wallet().listCoins()) { | ||||
sLabel = tr("(no label)"); | sLabel = tr("(no label)"); | ||||
} | } | ||||
itemOutput->setText(COLUMN_LABEL, sLabel); | itemOutput->setText(COLUMN_LABEL, sLabel); | ||||
} | } | ||||
// amount | // amount | ||||
itemOutput->setText( | itemOutput->setText( | ||||
COLUMN_AMOUNT, | COLUMN_AMOUNT, | ||||
BitcoinUnits::format(nDisplayUnit, | BitcoinUnits::format(nDisplayUnit, out.txout.nValue)); | ||||
out.tx->tx->vout[out.i].nValue)); | |||||
// padding so that sorting works correctly | // padding so that sorting works correctly | ||||
itemOutput->setData( | itemOutput->setData( | ||||
COLUMN_AMOUNT, Qt::UserRole, | COLUMN_AMOUNT, Qt::UserRole, | ||||
QVariant(qlonglong(out.tx->tx->vout[out.i].nValue / SATOSHI))); | QVariant(qlonglong(out.txout.nValue / SATOSHI))); | ||||
// date | // date | ||||
itemOutput->setText(COLUMN_DATE, | itemOutput->setText(COLUMN_DATE, GUIUtil::dateTimeStr(out.time)); | ||||
GUIUtil::dateTimeStr(out.tx->GetTxTime())); | |||||
itemOutput->setData(COLUMN_DATE, Qt::UserRole, | itemOutput->setData(COLUMN_DATE, Qt::UserRole, | ||||
QVariant((qlonglong)out.tx->GetTxTime())); | QVariant(qlonglong(out.time))); | ||||
// confirmations | // confirmations | ||||
itemOutput->setText(COLUMN_CONFIRMATIONS, | itemOutput->setText(COLUMN_CONFIRMATIONS, | ||||
QString::number(out.nDepth)); | QString::number(out.depth_in_main_chain)); | ||||
itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, | itemOutput->setData(COLUMN_CONFIRMATIONS, Qt::UserRole, | ||||
QVariant((qlonglong)out.nDepth)); | QVariant(qlonglong(out.depth_in_main_chain))); | ||||
// transaction id | // transaction hash | ||||
const TxId txid = out.tx->GetId(); | itemOutput->setText(COLUMN_TXHASH, QString::fromStdString( | ||||
itemOutput->setText(COLUMN_TXHASH, | output.GetTxId().GetHex())); | ||||
QString::fromStdString(txid.GetHex())); | |||||
// vout index | // vout index | ||||
itemOutput->setText(COLUMN_VOUT_INDEX, QString::number(out.i)); | itemOutput->setText(COLUMN_VOUT_INDEX, | ||||
QString::number(output.GetN())); | |||||
// disable locked coins | // disable locked coins | ||||
if (model->isLockedCoin(txid, out.i)) { | if (model->wallet().isLockedCoin(output)) { | ||||
COutPoint outpt(txid, out.i); | |||||
// just to be sure | // just to be sure | ||||
coinControl()->UnSelect(outpt); | coinControl()->UnSelect(output); | ||||
itemOutput->setDisabled(true); | itemOutput->setDisabled(true); | ||||
itemOutput->setIcon( | itemOutput->setIcon( | ||||
COLUMN_CHECKBOX, | COLUMN_CHECKBOX, | ||||
platformStyle->SingleColorIcon(":/icons/lock_closed")); | platformStyle->SingleColorIcon(":/icons/lock_closed")); | ||||
} | } | ||||
// set checkbox | // set checkbox | ||||
if (coinControl()->IsSelected(COutPoint(txid, out.i))) { | if (coinControl()->IsSelected(output)) { | ||||
itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); | itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); | ||||
} | } | ||||
} | } | ||||
// amount | // amount | ||||
if (treeMode) { | if (treeMode) { | ||||
itemWalletAddress->setText(COLUMN_CHECKBOX, | itemWalletAddress->setText(COLUMN_CHECKBOX, | ||||
"(" + QString::number(nChildren) + ")"); | "(" + QString::number(nChildren) + ")"); | ||||
Show All 20 Lines |