From ffd40da361639faeef405c7e4a504a340d77aa5b Mon Sep 17 00:00:00 2001 From: JaSK Date: Sat, 29 Mar 2014 05:15:28 +0100 Subject: [PATCH] Watchonly balances are shown separately in gui. --- src/qt/forms/overviewpage.ui | 525 +++++++++++++++++++++++------------ src/qt/overviewpage.cpp | 34 ++- src/qt/overviewpage.h | 6 +- src/qt/sendcoinsdialog.cpp | 13 +- src/qt/sendcoinsdialog.h | 3 +- src/qt/transactiondesc.cpp | 64 +++-- src/qt/transactionrecord.cpp | 17 +- src/qt/walletmodel.cpp | 27 +- src/qt/walletmodel.h | 9 +- src/rpcdump.cpp | 4 +- src/rpcmisc.cpp | 2 +- src/script.h | 2 + src/wallet.cpp | 53 +++- src/wallet.h | 71 ++++- 14 files changed, 597 insertions(+), 233 deletions(-) diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index e66291278..8784da5f3 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -6,7 +6,7 @@ 0 0 - 573 + 596 342 @@ -46,204 +46,369 @@ - - - - 75 - true - - - - Wallet - - + + + + + + 75 + true + + + + Wallet + + + + + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet. + + + QLabel { color: red; } + + + (out of sync) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + - - - The displayed information may be out of date. Your wallet automatically synchronizes with the Bitcoin network after a connection is established, but this process has not completed yet. - - - QLabel { color: red; } - - - (out of sync) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - + + + + + + 75 + true + + + + Watchonly: + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 40 + 20 + + + + + - - - - - QFormLayout::AllNonFixedFieldsGrow - - - 12 - - - 12 - - - - - Available: + + + + + QFormLayout::AllNonFixedFieldsGrow - + + 12 + + + 12 + + + + + Available: + + + + + + + + 75 + true + + + + IBeamCursor + + + Your current spendable balance + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Pending: + + + + + + + + 75 + true + + + + IBeamCursor + + + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Immature: + + + + + + + + 75 + true + + + + IBeamCursor + + + Mined balance that has not yet matured + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + + + + Total: + + + + + + + + 75 + true + + + + IBeamCursor + + + Your current total balance + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + - - - - - 75 - true - + + + + QFormLayout::AllNonFixedFieldsGrow - - IBeamCursor + + 12 - - Your current spendable balance + + 12 - - 0 BTC - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - + + + + + 75 + true + + + + IBeamCursor + + + Your current balance in watchonly addresses + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + IBeamCursor + + + Unconfirmed transactions to watchonly addresses + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + IBeamCursor + + + Mined balance in watchonly addresses that has not yet matured + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + + 140 + 0 + + + + Qt::Horizontal + + + + + + + + 75 + true + + + + IBeamCursor + + + Current total balance in watchonly addresses + + + 0 BTC + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + - - - - Pending: - - - - - - - - 75 - true - - - - IBeamCursor - - - Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance - - - 0 BTC - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Immature: - - - - - - - - 75 - true - - - - Mined balance that has not yet matured - - - 0 BTC - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Total: - - - - - - - - 75 - true - - - - IBeamCursor - - - Your current total balance - - - 0 BTC - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - - Qt::Horizontal + + QSizePolicy::Expanding + - 40 + 20 20 diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 1a9d1de57..1278f368c 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -103,6 +103,9 @@ OverviewPage::OverviewPage(QWidget *parent) : currentBalance(-1), currentUnconfirmedBalance(-1), currentImmatureBalance(-1), + currentWatchOnlyBalance(-1), + currentWatchUnconfBalance(-1), + currentWatchImmatureBalance(-1), txdelegate(new TxViewDelegate()), filter(0) { @@ -135,22 +138,39 @@ OverviewPage::~OverviewPage() delete ui; } -void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) +void OverviewPage::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance) { int unit = walletModel->getOptionsModel()->getDisplayUnit(); currentBalance = balance; currentUnconfirmedBalance = unconfirmedBalance; currentImmatureBalance = immatureBalance; + currentWatchOnlyBalance = watchOnlyBalance; + currentWatchUnconfBalance = watchUnconfBalance; + currentWatchImmatureBalance = watchImmatureBalance; ui->labelBalance->setText(BitcoinUnits::formatWithUnit(unit, balance)); ui->labelUnconfirmed->setText(BitcoinUnits::formatWithUnit(unit, unconfirmedBalance)); ui->labelImmature->setText(BitcoinUnits::formatWithUnit(unit, immatureBalance)); ui->labelTotal->setText(BitcoinUnits::formatWithUnit(unit, balance + unconfirmedBalance + immatureBalance)); + ui->labelWatchAvailable->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance)); + ui->labelWatchPending->setText(BitcoinUnits::formatWithUnit(unit, watchUnconfBalance)); + ui->labelWatchImmature->setText(BitcoinUnits::formatWithUnit(unit, watchImmatureBalance)); + ui->labelWatchTotal->setText(BitcoinUnits::formatWithUnit(unit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance)); // only show immature (newly mined) balance if it's non-zero, so as not to complicate things // for the non-mining users bool showImmature = immatureBalance != 0; - ui->labelImmature->setVisible(showImmature); - ui->labelImmatureText->setVisible(showImmature); + bool showWatchOnlyImmature = watchImmatureBalance != 0; + bool showWatchOnly = (watchOnlyBalance != 0 || watchUnconfBalance != 0 || showWatchOnlyImmature); + + // for symmetry reasons also show immature label when the watchonly one is shown + ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); + ui->labelImmatureText->setVisible(showImmature || showWatchOnlyImmature); + ui->labelWatchonly->setVisible(showWatchOnly); // show Watchonly label + ui->lineWatchBalance->setVisible(showWatchOnly); // show watchonly balance separator line + ui->labelWatchAvailable->setVisible(showWatchOnly); // show watchonly available balance + ui->labelWatchImmature->setVisible(showWatchOnlyImmature); // show watchonly immature balance + ui->labelWatchPending->setVisible(showWatchOnly); // show watchonly pending balance + ui->labelWatchTotal->setVisible(showWatchOnly); // show watchonly total balance } void OverviewPage::setClientModel(ClientModel *model) @@ -182,8 +202,9 @@ void OverviewPage::setWalletModel(WalletModel *model) ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance()); - connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64))); + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), + model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); } @@ -197,7 +218,8 @@ void OverviewPage::updateDisplayUnit() if(walletModel && walletModel->getOptionsModel()) { if(currentBalance != -1) - setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance); + setBalance(currentBalance, currentUnconfirmedBalance, currentImmatureBalance, + currentWatchOnlyBalance, currentWatchUnconfBalance, currentWatchImmatureBalance); // Update txdelegate->unit with the current unit txdelegate->unit = walletModel->getOptionsModel()->getDisplayUnit(); diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 2507a3fb3..fe0010677 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -34,7 +34,8 @@ public: void showOutOfSyncWarning(bool fShow); public slots: - void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance); signals: void transactionClicked(const QModelIndex &index); @@ -46,6 +47,9 @@ private: qint64 currentBalance; qint64 currentUnconfirmedBalance; qint64 currentImmatureBalance; + qint64 currentWatchOnlyBalance; + qint64 currentWatchUnconfBalance; + qint64 currentWatchImmatureBalance; TxViewDelegate *txdelegate; TransactionFilterProxy *filter; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index b7d74d703..6f10ed5b0 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -90,8 +90,9 @@ void SendCoinsDialog::setModel(WalletModel *model) } } - setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance()); - connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64))); + setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), + model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); + connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); // Coin Control @@ -383,10 +384,14 @@ bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv) return true; } -void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance) +void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchBalance, qint64 watchUnconfirmedBalance, qint64 watchImmatureBalance) { Q_UNUSED(unconfirmedBalance); Q_UNUSED(immatureBalance); + Q_UNUSED(watchBalance); + Q_UNUSED(watchUnconfirmedBalance); + Q_UNUSED(watchImmatureBalance); if(model && model->getOptionsModel()) { @@ -396,7 +401,7 @@ void SendCoinsDialog::setBalance(qint64 balance, qint64 unconfirmedBalance, qint void SendCoinsDialog::updateDisplayUnit() { - setBalance(model->getBalance(), 0, 0); + setBalance(model->getBalance(), 0, 0, 0, 0, 0); } void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg) diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index fcae26c72..6cdf4a00c 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -47,7 +47,8 @@ public slots: void accept(); SendCoinsEntry *addEntry(); void updateTabsAndLabels(); - void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void setBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance); private: Ui::SendCoinsDialog *ui; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index e48dbcac9..76dc47318 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -89,19 +89,27 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (nNet > 0) { // Credit - if (CBitcoinAddress(rec->address).IsValid()) + BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - CTxDestination address = CBitcoinAddress(rec->address).Get(); - if (wallet->mapAddressBook.count(address)) + if (wallet->IsMine(txout)) { - strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; - strHTML += "" + tr("To") + ": "; - strHTML += GUIUtil::HtmlEscape(rec->address); - if (!wallet->mapAddressBook[address].name.empty()) - strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; - else - strHTML += " (" + tr("own address") + ")"; - strHTML += "
"; + if (CBitcoinAddress(rec->address).IsValid()) + { + CTxDestination address = CBitcoinAddress(rec->address).Get(); + if (wallet->mapAddressBook.count(address)) + { + strHTML += "" + tr("From") + ": " + tr("unknown") + "
"; + strHTML += "" + tr("To") + ": "; + strHTML += GUIUtil::HtmlEscape(rec->address); + std::string addressOwned = wallet->IsMine(txout) == MINE_SPENDABLE ? "own address" : "watch-only"; + if (!wallet->mapAddressBook[address].name.empty()) + strHTML += " (" + tr(addressOwned.c_str()) + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")"; + else + strHTML += " (" + tr(addressOwned.c_str()) + ")"; + strHTML += "
"; + } + } + break; } } } @@ -148,22 +156,33 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco } else { - bool fAllFromMe = true; + isminetype fAllFromMe = MINE_SPENDABLE; BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && wallet->IsMine(txin); + { + isminetype mine = wallet->IsMine(txin); + if(fAllFromMe > mine) fAllFromMe = mine; + } - bool fAllToMe = true; + isminetype fAllToMe = MINE_SPENDABLE; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && wallet->IsMine(txout); + { + isminetype mine = wallet->IsMine(txout); + if(fAllToMe > mine) fAllToMe = mine; + } if (fAllFromMe) { + if(fAllFromMe == MINE_WATCH_ONLY) + strHTML += "" + tr("From") + ": " + tr("watch-only") + "
"; + // // Debit // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - if (wallet->IsMine(txout)) + // Ignore change + isminetype toSelf = wallet->IsMine(txout); + if ((toSelf == MINE_SPENDABLE) && (fAllFromMe == MINE_SPENDABLE)) continue; if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty()) @@ -176,11 +195,17 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + if(toSelf == MINE_SPENDABLE) + strHTML += " (own address)"; + else if(toSelf == MINE_WATCH_ONLY) + strHTML += " (watch-only)"; strHTML += "
"; } } strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "
"; + if(toSelf) + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(unit, txout.nValue) + "
"; } if (fAllToMe) @@ -188,8 +213,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // Payment to self int64_t nChange = wtx.GetChange(); int64_t nValue = nCredit - nChange; - strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(unit, -nValue) + "
"; - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(unit, nValue) + "
"; + strHTML += "" + tr("Total debit") + ": " + BitcoinUnits::formatWithUnit(unit, -nValue) + "
"; + strHTML += "" + tr("Total credit") + ": " + BitcoinUnits::formatWithUnit(unit, nValue) + "
"; } int64_t nTxFee = nDebit - wtx.GetValueOut(); @@ -286,7 +311,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatWithUnit(unit, vout.nValue); - strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) ? tr("true") : tr("false")) + ""; + strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & MINE_SPENDABLE ? tr("true") : tr("false")) + ""; + strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & MINE_WATCH_ONLY ? tr("true") : tr("false")) + ""; } } } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 21f1b7356..3d77d3989 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -45,7 +45,8 @@ QList TransactionRecord::decomposeTransaction(const CWallet * // BOOST_FOREACH(const CTxOut& txout, wtx.vout) { - if(wallet->IsMine(txout)) + isminetype mine = wallet->IsMine(txout); + if(mine) { TransactionRecord sub(hash, nTime); CTxDestination address; @@ -75,13 +76,19 @@ QList TransactionRecord::decomposeTransaction(const CWallet * } else { - bool fAllFromMe = true; + isminetype fAllFromMe = MINE_SPENDABLE; BOOST_FOREACH(const CTxIn& txin, wtx.vin) - fAllFromMe = fAllFromMe && wallet->IsMine(txin); + { + isminetype mine = wallet->IsMine(txin); + if(fAllFromMe > mine) fAllFromMe = mine; + } - bool fAllToMe = true; + isminetype fAllToMe = MINE_SPENDABLE; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - fAllToMe = fAllToMe && wallet->IsMine(txout); + { + isminetype mine = wallet->IsMine(txout); + if(fAllToMe > mine) fAllToMe = mine; + } if (fAllFromMe && fAllToMe) { diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 098d39e8a..7317c3276 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -79,6 +79,21 @@ qint64 WalletModel::getImmatureBalance() const return wallet->GetImmatureBalance(); } +qint64 WalletModel::getWatchBalance() const +{ + return wallet->GetWatchOnlyBalance(); +} + +qint64 WalletModel::getWatchUnconfirmedBalance() const +{ + return wallet->GetUnconfirmedWatchOnlyBalance(); +} + +qint64 WalletModel::getWatchImmatureBalance() const +{ + return wallet->GetImmatureWatchOnlyBalance(); +} + int WalletModel::getNumTransactions() const { int numTransactions = 0; @@ -127,13 +142,21 @@ void WalletModel::checkBalanceChanged() qint64 newBalance = getBalance(); qint64 newUnconfirmedBalance = getUnconfirmedBalance(); qint64 newImmatureBalance = getImmatureBalance(); + qint64 newWatchOnlyBalance = getWatchBalance(); + qint64 newWatchUnconfBalance = getWatchUnconfirmedBalance(); + qint64 newWatchImmatureBalance = getWatchImmatureBalance(); - if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance) + if(cachedBalance != newBalance || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance || + cachedWatchOnlyBalance != newWatchOnlyBalance || cachedWatchUnconfBalance != newWatchUnconfBalance || cachedWatchImmatureBalance != newWatchImmatureBalance) { cachedBalance = newBalance; cachedUnconfirmedBalance = newUnconfirmedBalance; cachedImmatureBalance = newImmatureBalance; - emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance); + cachedWatchOnlyBalance = newWatchOnlyBalance; + cachedWatchUnconfBalance = newWatchUnconfBalance; + cachedWatchImmatureBalance = newWatchImmatureBalance; + emit balanceChanged(newBalance, newUnconfirmedBalance, newImmatureBalance, + newWatchOnlyBalance, newWatchUnconfBalance, newWatchImmatureBalance); } } diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index ccf590aae..7ad54ff8e 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -128,6 +128,9 @@ public: qint64 getBalance(const CCoinControl *coinControl = NULL) const; qint64 getUnconfirmedBalance() const; qint64 getImmatureBalance() const; + qint64 getWatchBalance() const; + qint64 getWatchUnconfirmedBalance() const; + qint64 getWatchImmatureBalance() const; int getNumTransactions() const; EncryptionStatus getEncryptionStatus() const; @@ -206,6 +209,9 @@ private: qint64 cachedBalance; qint64 cachedUnconfirmedBalance; qint64 cachedImmatureBalance; + qint64 cachedWatchOnlyBalance; + qint64 cachedWatchUnconfBalance; + qint64 cachedWatchImmatureBalance; qint64 cachedNumTransactions; EncryptionStatus cachedEncryptionStatus; int cachedNumBlocks; @@ -218,7 +224,8 @@ private: signals: // Signal that balance in wallet changed - void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance); + void balanceChanged(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance, + qint64 watchOnlyBalance, qint64 watchUnconfBalance, qint64 watchImmatureBalance); // Number of transactions in wallet changed void numTransactionsChanged(int count); diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 5b325c46e..98af4695d 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -158,12 +158,14 @@ Value importaddress(const Array& params, bool fHelp) { LOCK2(cs_main, pwalletMain->cs_wallet); + // add to address book or update label + pwalletMain->SetAddressBook(dest, strLabel, "receive"); + // Don't throw error in case an address is already there if (pwalletMain->HaveWatchOnly(dest)) return Value::null; pwalletMain->MarkDirty(); - pwalletMain->SetAddressBook(dest, strLabel, "receive"); if (!pwalletMain->AddWatchOnly(dest)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index e2de2dbcd..3245f7d71 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -171,7 +171,7 @@ Value validateaddress(const Array& params, bool fHelp) ret.push_back(Pair("address", currentAddress)); #ifdef ENABLE_WALLET isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO; - ret.push_back(Pair("ismine", mine != MINE_NO)); + ret.push_back(Pair("ismine", mine == MINE_SPENDABLE)); if (mine != MINE_NO) { ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY)); Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); diff --git a/src/script.h b/src/script.h index 1db758094..56c7d01d4 100644 --- a/src/script.h +++ b/src/script.h @@ -201,6 +201,8 @@ enum isminetype MINE_WATCH_ONLY = 1, MINE_SPENDABLE = 2, }; +/** used for bitflags of isminetype */ +typedef uint8_t isminefilter; // Mandatory script verification flags that all new blocks must comply with for // them to be valid. (but old blocks may not comply with) Currently just P2SH, diff --git a/src/wallet.cpp b/src/wallet.cpp index 40ace9c40..d4e9fe9d1 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -738,7 +738,7 @@ bool CWallet::IsChange(const CTxOut& txout) const // a better way of identifying which outputs are 'the send' and which are // 'the change' will need to be implemented (maybe extend CWalletTx to remember // which output, if any, was change). - if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address)) + if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address) == MINE_SPENDABLE) { LOCK(cs_wallet); if (!mapAddressBook.count(address)) @@ -793,7 +793,7 @@ int CWalletTx::GetRequestCount() const } void CWalletTx::GetAmounts(list >& listReceived, - list >& listSent, int64_t& nFee, string& strSentAccount) const + list >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const { nFee = 0; listReceived.clear(); @@ -820,9 +820,9 @@ void CWalletTx::GetAmounts(list >& listReceived, // Don't report 'change' txouts if (pwallet->IsChange(txout)) continue; - fIsMine = pwallet->IsMine(txout); + fIsMine = (pwallet->IsMine(txout) & filter); } - else if (!(fIsMine = pwallet->IsMine(txout))) + else if (!(fIsMine = (pwallet->IsMine(txout) & filter))) continue; // In either case, we need to get the destination address @@ -1066,6 +1066,51 @@ int64_t CWallet::GetImmatureBalance() const return nTotal; } +int64_t CWallet::GetWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + + return nTotal; +} + +int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + return nTotal; +} + +int64_t CWallet::GetImmatureWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); + } + } + return nTotal; +} + // populate vCoins with vector of available COutputs. void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { diff --git a/src/wallet.h b/src/wallet.h index ff6af3a6a..355aa3697 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -262,6 +262,9 @@ public: int64_t GetBalance() const; int64_t GetUnconfirmedBalance() const; int64_t GetImmatureBalance() const; + int64_t GetWatchOnlyBalance() const; + int64_t GetUnconfirmedWatchOnlyBalance() const; + int64_t GetImmatureWatchOnlyBalance() const; bool CreateTransaction(const std::vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL); bool CreateTransaction(CScript scriptPubKey, int64_t nValue, @@ -290,11 +293,11 @@ public: { return ::IsMine(*this, txout.scriptPubKey); } - int64_t GetCredit(const CTxOut& txout) const + int64_t GetCredit(const CTxOut& txout, const isminefilter& filter = (MINE_WATCH_ONLY | MINE_SPENDABLE)) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); - return (IsMine(txout) ? txout.nValue : 0); + return ((IsMine(txout) & filter) ? txout.nValue : 0); } bool IsChange(const CTxOut& txout) const; int64_t GetChange(const CTxOut& txout) const @@ -332,12 +335,12 @@ public: } return nDebit; } - int64_t GetCredit(const CTransaction& tx) const + int64_t GetCredit(const CTransaction& tx, const isminefilter& filter=(MINE_SPENDABLE|MINE_WATCH_ONLY)) const { int64_t nCredit = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { - nCredit += GetCredit(txout); + nCredit += GetCredit(txout, filter); if (!MoneyRange(nCredit)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); } @@ -483,11 +486,15 @@ public: mutable bool fCreditCached; mutable bool fImmatureCreditCached; mutable bool fAvailableCreditCached; + mutable bool fImmatureWatchCreditCached; + mutable bool fAvailableWatchCreditCached; mutable bool fChangeCached; mutable int64_t nDebitCached; mutable int64_t nCreditCached; mutable int64_t nImmatureCreditCached; mutable int64_t nAvailableCreditCached; + mutable int64_t nImmatureWatchCreditCached; + mutable int64_t nAvailableWatchCreditCached; mutable int64_t nChangeCached; CWalletTx() @@ -524,11 +531,15 @@ public: fCreditCached = false; fImmatureCreditCached = false; fAvailableCreditCached = false; + fImmatureWatchCreditCached = false; + fAvailableWatchCreditCached = false; fChangeCached = false; nDebitCached = 0; nCreditCached = 0; nImmatureCreditCached = 0; nAvailableCreditCached = 0; + nAvailableWatchCreditCached = 0; + nImmatureWatchCreditCached = 0; nChangeCached = 0; nOrderPos = -1; } @@ -581,6 +592,8 @@ public: { fCreditCached = false; fAvailableCreditCached = false; + fAvailableWatchCreditCached = false; + fImmatureWatchCreditCached = false; fDebitCached = false; fChangeCached = false; } @@ -622,7 +635,7 @@ public: { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; - nImmatureCreditCached = pwallet->GetCredit(*this); + nImmatureCreditCached = pwallet->GetCredit(*this, MINE_SPENDABLE); fImmatureCreditCached = true; return nImmatureCreditCached; } @@ -649,7 +662,7 @@ public: if (!pwallet->IsSpent(hashTx, i)) { const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout); + nCredit += pwallet->GetCredit(txout, MINE_SPENDABLE); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } @@ -660,6 +673,48 @@ public: return nCredit; } + int64_t GetImmatureWatchOnlyCredit(const bool& fUseCache=true) const + { + if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) + { + if (fUseCache && fImmatureWatchCreditCached) + return nImmatureWatchCreditCached; + nImmatureWatchCreditCached = pwallet->GetCredit(*this, MINE_WATCH_ONLY); + fImmatureWatchCreditCached = true; + return nImmatureWatchCreditCached; + } + + return 0; + } + + int64_t GetAvailableWatchOnlyCredit(const bool& fUseCache=true) const + { + if (pwallet == 0) + return 0; + + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache && fAvailableWatchCreditCached) + return nAvailableWatchCreditCached; + + int64_t nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!pwallet->IsSpent(GetHash(), i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCredit(txout, MINE_WATCH_ONLY); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); + } + } + + nAvailableWatchCreditCached = nCredit; + fAvailableWatchCreditCached = true; + return nCredit; + } int64_t GetChange() const { @@ -671,7 +726,7 @@ public: } void GetAmounts(std::list >& listReceived, - std::list >& listSent, int64_t& nFee, std::string& strSentAccount) const; + std::list >& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter=(MINE_SPENDABLE|MINE_WATCH_ONLY)) const; void GetAccountAmounts(const std::string& strAccount, int64_t& nReceived, int64_t& nSent, int64_t& nFee) const; @@ -702,7 +757,7 @@ public: if (parent == NULL) return false; const CTxOut& parentOut = parent->vout[txin.prevout.n]; - if (!pwallet->IsMine(parentOut)) + if (pwallet->IsMine(parentOut) != MINE_SPENDABLE) return false; } return true;