Browse Source

Merge pull request #4045

a3e192a replaced MINE_ with ISMINE_ (JaSK)
53a2148 fixed bug where validateaddress doesn't display information (JaSK)
f28707a fixed bug in ListReceived() (JaSK)
519dd1c Added MINE_ALL = (spendable|watchonly) (JaSK)
23b0506 Fixed some stuff in TransactionDesc (JaSK)
80dda36 removed default argument values for ismine filter (JaSK)
d5087d1 Use script matching rather than destination matching for watch-only. (Pieter Wuille)
0fa2f88 added includedWatchonly argument to listreceivedbyaddress/...account (JaSK)
f87ba3d added includeWatchonly argument to 'gettransaction' because it affects balance calculation (JaSK)
a5c6c5d fixed tiny glitch and improved readability like laanwj suggested (JaSK)
d7d5d23 Added argument to listtransactions and listsinceblock to include watchonly addresses (JaSK)
952877e Showing 'involvesWatchonly' property for transactions returned by 'listtransactions' and 'listsinceblock'. It is only appended when the transaction involves a watchonly address. (JaSK)
83f3543 Added argument to listaccounts to include watchonly addresses (JaSK)
d4640d7 Added argument to getbalance to include watchonly addresses and fixed errors in balance calculation. (JaSK)
d2692f6 Watchonly transactions are marked in transaction history (JaSK)
ffd40da Watchonly balances are shown separately in gui. (JaSK)
2935b21 qt: Hide unspendable outputs in coin control (Wladimir J. van der Laan)
c898846 Add support for watch-only addresses (Pieter Wuille)
0.10
Wladimir J. van der Laan 11 years ago
parent
commit
f748ff730b
No known key found for this signature in database
GPG Key ID: 74810B012346C9A6
  1. 12
      src/keystore.cpp
  2. 19
      src/keystore.h
  3. 533
      src/qt/forms/overviewpage.ui
  4. 34
      src/qt/overviewpage.cpp
  5. 6
      src/qt/overviewpage.h
  6. 13
      src/qt/sendcoinsdialog.cpp
  7. 3
      src/qt/sendcoinsdialog.h
  8. 54
      src/qt/transactiondesc.cpp
  9. 26
      src/qt/transactionrecord.cpp
  10. 3
      src/qt/transactionrecord.h
  11. 11
      src/qt/transactiontablemodel.cpp
  12. 39
      src/qt/walletmodel.cpp
  13. 9
      src/qt/walletmodel.h
  14. 8
      src/rpcclient.cpp
  15. 54
      src/rpcdump.cpp
  16. 52
      src/rpcmisc.cpp
  17. 1
      src/rpcrawtransaction.cpp
  18. 1
      src/rpcserver.cpp
  19. 1
      src/rpcserver.h
  20. 127
      src/rpcwallet.cpp
  21. 44
      src/script.cpp
  22. 15
      src/script.h
  23. 2
      src/test/wallet_tests.cpp
  24. 113
      src/wallet.cpp
  25. 148
      src/wallet.h
  26. 19
      src/walletdb.cpp
  27. 3
      src/walletdb.h

12
src/keystore.cpp

@ -59,3 +59,15 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) @@ -59,3 +59,15 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut)
return false;
}
bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
{
LOCK(cs_KeyStore);
setWatchOnly.insert(dest);
return true;
}
bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const
{
LOCK(cs_KeyStore);
return setWatchOnly.count(dest) > 0;
}

19
src/keystore.h

@ -8,11 +8,21 @@ @@ -8,11 +8,21 @@
#include "key.h"
#include "sync.h"
#include "script.h" // for CNoDestination
#include <boost/signals2/signal.hpp>
#include <boost/variant.hpp>
class CScript;
/** A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
* * CKeyID: TX_PUBKEYHASH destination
* * CScriptID: TX_SCRIPTHASH destination
* A CTxDestination is the internal data type encoded in a CBitcoinAddress
*/
typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination;
/** A virtual base class for key stores */
class CKeyStore
{
@ -36,10 +46,15 @@ public: @@ -36,10 +46,15 @@ public:
virtual bool AddCScript(const CScript& redeemScript) =0;
virtual bool HaveCScript(const CScriptID &hash) const =0;
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0;
// Support for Watch-only addresses
virtual bool AddWatchOnly(const CScript &dest) =0;
virtual bool HaveWatchOnly(const CScript &dest) const =0;
};
typedef std::map<CKeyID, CKey> KeyMap;
typedef std::map<CScriptID, CScript > ScriptMap;
typedef std::set<CScript> WatchOnlySet;
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
@ -47,6 +62,7 @@ class CBasicKeyStore : public CKeyStore @@ -47,6 +62,7 @@ class CBasicKeyStore : public CKeyStore
protected:
KeyMap mapKeys;
ScriptMap mapScripts;
WatchOnlySet setWatchOnly;
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
@ -88,6 +104,9 @@ public: @@ -88,6 +104,9 @@ public:
virtual bool AddCScript(const CScript& redeemScript);
virtual bool HaveCScript(const CScriptID &hash) const;
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const;
virtual bool AddWatchOnly(const CScript &dest);
virtual bool HaveWatchOnly(const CScript &dest) const;
};
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;

533
src/qt/forms/overviewpage.ui

@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>573</width>
<width>596</width>
<height>342</height>
</rect>
</property>
@ -46,204 +46,369 @@ @@ -46,204 +46,369 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Wallet</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelWalletStatus">
<property name="toolTip">
<string>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.</string>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color: red; }</string>
</property>
<property name="text">
<string notr="true">(out of sync)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Wallet</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelWalletStatus">
<property name="toolTip">
<string>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.</string>
</property>
<property name="styleSheet">
<string notr="true">QLabel { color: red; }</string>
</property>
<property name="text">
<string notr="true">(out of sync)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="labelWatchonly">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Watchonly:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Preferred</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>12</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Available:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelBalance">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current spendable balance</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Pending:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelUnconfirmed">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelImmatureText">
<property name="text">
<string>Immature:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="labelImmature">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="toolTip">
<string>Mined balance that has not yet matured</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelTotalText">
<property name="text">
<string>Total:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="labelTotal">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current total balance</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>12</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Available:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="labelBalance">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current spendable balance</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Pending:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelUnconfirmed">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelImmatureText">
<property name="text">
<string>Immature:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="labelImmature">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Mined balance that has not yet matured</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelTotalText">
<property name="text">
<string>Total:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="labelTotal">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current total balance</string>
</property>
<property name="text">
<string notr="true">0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="horizontalSpacing">
<number>12</number>
</property>
<property name="verticalSpacing">
<number>12</number>
</property>
<item row="0" column="1">
<widget class="QLabel" name="labelWatchAvailable">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Your current balance in watchonly addresses</string>
</property>
<property name="text">
<string>0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="labelWatchPending">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Unconfirmed transactions to watchonly addresses</string>
</property>
<property name="text">
<string>0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="labelWatchImmature">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Mined balance in watchonly addresses that has not yet matured</string>
</property>
<property name="text">
<string>0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="Line" name="lineWatchBalance">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>140</width>
<height>0</height>
</size>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="labelWatchTotal">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="cursor">
<cursorShape>IBeamCursor</cursorShape>
</property>
<property name="toolTip">
<string>Current total balance in watchonly addresses</string>
</property>
<property name="text">
<string>0 BTC</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<width>20</width>
<height>20</height>
</size>
</property>

34
src/qt/overviewpage.cpp

@ -103,6 +103,9 @@ OverviewPage::OverviewPage(QWidget *parent) : @@ -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() @@ -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) @@ -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() @@ -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();

6
src/qt/overviewpage.h

@ -34,7 +34,8 @@ public: @@ -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: @@ -46,6 +47,9 @@ private:
qint64 currentBalance;
qint64 currentUnconfirmedBalance;
qint64 currentImmatureBalance;
qint64 currentWatchOnlyBalance;
qint64 currentWatchUnconfBalance;
qint64 currentWatchImmatureBalance;
TxViewDelegate *txdelegate;
TransactionFilterProxy *filter;

13
src/qt/sendcoinsdialog.cpp

@ -90,8 +90,9 @@ void SendCoinsDialog::setModel(WalletModel *model) @@ -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) @@ -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 @@ -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)

3
src/qt/sendcoinsdialog.h

@ -47,7 +47,8 @@ public slots: @@ -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;

54
src/qt/transactiondesc.cpp

@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
#include "timedata.h"
#include "ui_interface.h"
#include "wallet.h"
#include "script.h"
#include <stdint.h>
#include <string>
@ -54,8 +55,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -54,8 +55,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
int64_t nTime = wtx.GetTxTime();
int64_t nCredit = wtx.GetCredit();
int64_t nDebit = wtx.GetDebit();
int64_t nCredit = wtx.GetCredit(ISMINE_ALL);
int64_t nDebit = wtx.GetDebit(ISMINE_ALL);
int64_t nNet = nCredit - nDebit;
strHTML += "<b>" + tr("Status") + ":</b> " + FormatTxStatus(wtx);
@ -97,10 +98,11 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -97,10 +98,11 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>";
strHTML += "<b>" + tr("To") + ":</b> ";
strHTML += GUIUtil::HtmlEscape(rec->address);
QString addressOwned = (::IsMine(*wallet, address) == ISMINE_SPENDABLE) ? tr("own address") : tr("watch-only");
if (!wallet->mapAddressBook[address].name.empty())
strHTML += " (" + tr("own address") + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")";
strHTML += " (" + addressOwned + ", " + tr("label") + ": " + GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + ")";
else
strHTML += " (" + tr("own address") + ")";
strHTML += " (" + addressOwned + ")";
strHTML += "<br>";
}
}
@ -131,7 +133,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -131,7 +133,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
int64_t nUnmatured = 0;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
nUnmatured += wallet->GetCredit(txout);
nUnmatured += wallet->GetCredit(txout, ISMINE_ALL);
strHTML += "<b>" + tr("Credit") + ":</b> ";
if (wtx.IsInMainChain())
strHTML += BitcoinUnits::formatWithUnit(unit, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")";
@ -148,22 +150,33 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -148,22 +150,33 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
}
else
{
bool fAllFromMe = true;
isminetype fAllFromMe = ISMINE_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 = ISMINE_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 == ISMINE_WATCH_ONLY)
strHTML += "<b>" + tr("From") + ":</b> " + tr("watch-only") + "<br>";
//
// Debit
//
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
if (wallet->IsMine(txout))
// Ignore change
isminetype toSelf = wallet->IsMine(txout);
if ((toSelf == ISMINE_SPENDABLE) && (fAllFromMe == ISMINE_SPENDABLE))
continue;
if (!wtx.mapValue.count("to") || wtx.mapValue["to"].empty())
@ -176,11 +189,17 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -176,11 +189,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 == ISMINE_SPENDABLE)
strHTML += " (own address)";
else if(toSelf == ISMINE_WATCH_ONLY)
strHTML += " (watch-only)";
strHTML += "<br>";
}
}
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -txout.nValue) + "<br>";
if(toSelf)
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, txout.nValue) + "<br>";
}
if (fAllToMe)
@ -188,8 +207,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -188,8 +207,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
// Payment to self
int64_t nChange = wtx.GetChange();
int64_t nValue = nCredit - nChange;
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>";
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>";
strHTML += "<b>" + tr("Total debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -nValue) + "<br>";
strHTML += "<b>" + tr("Total credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, nValue) + "<br>";
}
int64_t nTxFee = nDebit - wtx.GetValueOut();
@ -203,10 +222,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -203,10 +222,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
//
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if (wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>";
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if (wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>";
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
}
}
@ -256,10 +275,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -256,10 +275,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco
strHTML += "<hr><br>" + tr("Debug information") + "<br><br>";
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
if(wallet->IsMine(txin))
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin)) + "<br>";
strHTML += "<b>" + tr("Debit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, -wallet->GetDebit(txin, ISMINE_ALL)) + "<br>";
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
if(wallet->IsMine(txout))
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout)) + "<br>";
strHTML += "<b>" + tr("Credit") + ":</b> " + BitcoinUnits::formatWithUnit(unit, wallet->GetCredit(txout, ISMINE_ALL)) + "<br>";
strHTML += "<br><b>" + tr("Transaction") + ":</b><br>";
strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true);
@ -286,7 +305,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco @@ -286,7 +305,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")) + "</li>";
strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>";
strHTML = strHTML + " IsWatchOnly=" + (wallet->IsMine(vout) & ISMINE_WATCH_ONLY ? tr("true") : tr("false")) + "</li>";
}
}
}

26
src/qt/transactionrecord.cpp

@ -33,7 +33,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * @@ -33,7 +33,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
QList<TransactionRecord> parts;
int64_t nTime = wtx.GetTxTime();
int64_t nCredit = wtx.GetCredit(true);
int64_t nDebit = wtx.GetDebit();
int64_t nDebit = wtx.GetDebit(ISMINE_ALL);
int64_t nNet = nCredit - nDebit;
uint256 hash = wtx.GetHash();
std::map<std::string, std::string> mapValue = wtx.mapValue;
@ -45,12 +45,14 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * @@ -45,12 +45,14 @@ QList<TransactionRecord> 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;
sub.idx = parts.size(); // sequence number
sub.credit = txout.nValue;
sub.involvesWatchAddress = mine == ISMINE_WATCH_ONLY;
if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*wallet, address))
{
// Received by Bitcoin Address
@ -75,13 +77,22 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * @@ -75,13 +77,22 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
}
else
{
bool fAllFromMe = true;
bool involvesWatchAddress = false;
isminetype fAllFromMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
fAllFromMe = fAllFromMe && wallet->IsMine(txin);
{
isminetype mine = wallet->IsMine(txin);
if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllFromMe > mine) fAllFromMe = mine;
}
bool fAllToMe = true;
isminetype fAllToMe = ISMINE_SPENDABLE;
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
fAllToMe = fAllToMe && wallet->IsMine(txout);
{
isminetype mine = wallet->IsMine(txout);
if(mine == ISMINE_WATCH_ONLY) involvesWatchAddress = true;
if(fAllToMe > mine) fAllToMe = mine;
}
if (fAllFromMe && fAllToMe)
{
@ -90,6 +101,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * @@ -90,6 +101,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
parts.append(TransactionRecord(hash, nTime, TransactionRecord::SendToSelf, "",
-(nDebit - nChange), nCredit - nChange));
parts.last().involvesWatchAddress = involvesWatchAddress; // maybe pass to TransactionRecord as constructor argument
}
else if (fAllFromMe)
{
@ -103,6 +115,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * @@ -103,6 +115,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
const CTxOut& txout = wtx.vout[nOut];
TransactionRecord sub(hash, nTime);
sub.idx = parts.size();
sub.involvesWatchAddress = involvesWatchAddress;
if(wallet->IsMine(txout))
{
@ -143,6 +156,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * @@ -143,6 +156,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet *
// Mixed debit transaction, can't break down payees
//
parts.append(TransactionRecord(hash, nTime, TransactionRecord::Other, "", nNet, 0));
parts.last().involvesWatchAddress = involvesWatchAddress;
}
}

3
src/qt/transactionrecord.h

@ -137,6 +137,9 @@ public: @@ -137,6 +137,9 @@ public:
/** Status: can change with block chain update */
TransactionStatus status;
/** Whether the transaction was sent/received with a watch-only address */
bool involvesWatchAddress;
/** Return the unique identifier for this transaction (part) */
QString getTxID() const;

11
src/qt/transactiontablemodel.cpp

@ -396,19 +396,22 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx @@ -396,19 +396,22 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx
QString TransactionTableModel::formatTxToAddress(const TransactionRecord *wtx, bool tooltip) const
{
// mark transactions involving watch-only addresses:
QString watchAddress = wtx->involvesWatchAddress ? " (w) " : "";
switch(wtx->type)
{
case TransactionRecord::RecvFromOther:
return QString::fromStdString(wtx->address);
return QString::fromStdString(wtx->address) + watchAddress;
case TransactionRecord::RecvWithAddress:
case TransactionRecord::SendToAddress:
case TransactionRecord::Generated:
return lookupAddress(wtx->address, tooltip);
return lookupAddress(wtx->address, tooltip) + watchAddress;
case TransactionRecord::SendToOther:
return QString::fromStdString(wtx->address);
return QString::fromStdString(wtx->address) + watchAddress;
case TransactionRecord::SendToSelf:
default:
return tr("(n/a)");
return tr("(n/a)") + watchAddress;
}
}

39
src/qt/walletmodel.cpp

@ -60,7 +60,8 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const @@ -60,7 +60,8 @@ qint64 WalletModel::getBalance(const CCoinControl *coinControl) const
std::vector<COutput> vCoins;
wallet->AvailableCoins(vCoins, true, coinControl);
BOOST_FOREACH(const COutput& out, vCoins)
nBalance += out.tx->vout[out.i].nValue;
if(out.fSpendable)
nBalance += out.tx->vout[out.i].nValue;
return nBalance;
}
@ -78,6 +79,21 @@ qint64 WalletModel::getImmatureBalance() const @@ -78,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;
@ -126,13 +142,21 @@ void WalletModel::checkBalanceChanged() @@ -126,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);
}
}
@ -543,7 +567,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect @@ -543,7 +567,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect
if (!wallet->mapWallet.count(outpoint.hash)) continue;
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
if (nDepth < 0) continue;
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth);
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
vOutputs.push_back(out);
}
}
@ -570,7 +594,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) @@ -570,7 +594,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
if (!wallet->mapWallet.count(outpoint.hash)) continue;
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
if (nDepth < 0) continue;
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth);
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
vCoins.push_back(out);
}
@ -581,11 +605,12 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) @@ -581,11 +605,12 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
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);
cout = COutput(&wallet->mapWallet[cout.tx->vin[0].prevout.hash], cout.tx->vin[0].prevout.n, 0, true);
}
CTxDestination address;
if(!ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address)) continue;
if(!out.fSpendable || !ExtractDestination(cout.tx->vout[cout.i].scriptPubKey, address))
continue;
mapCoins[CBitcoinAddress(address).ToString().c_str()].push_back(out);
}
}

9
src/qt/walletmodel.h

@ -128,6 +128,9 @@ public: @@ -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: @@ -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: @@ -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);

8
src/rpcclient.cpp

@ -37,9 +37,12 @@ static const CRPCConvertParam vRPCConvertParams[] = @@ -37,9 +37,12 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getreceivedbyaccount", 1 },
{ "listreceivedbyaddress", 0 },
{ "listreceivedbyaddress", 1 },
{ "listreceivedbyaddress", 2 },
{ "listreceivedbyaccount", 0 },
{ "listreceivedbyaccount", 1 },
{ "listreceivedbyaccount", 2 },
{ "getbalance", 1 },
{ "getbalance", 2 },
{ "getblockhash", 0 },
{ "move", 2 },
{ "move", 3 },
@ -47,10 +50,13 @@ static const CRPCConvertParam vRPCConvertParams[] = @@ -47,10 +50,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendfrom", 3 },
{ "listtransactions", 1 },
{ "listtransactions", 2 },
{ "listtransactions", 3 },
{ "listaccounts", 0 },
{ "listaccounts", 1 },
{ "walletpassphrase", 1 },
{ "getblocktemplate", 0 },
{ "listsinceblock", 1 },
{ "listsinceblock", 2 },
{ "sendmany", 1 },
{ "sendmany", 2 },
{ "addmultisigaddress", 0 },
@ -72,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] = @@ -72,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "lockunspent", 0 },
{ "lockunspent", 1 },
{ "importprivkey", 2 },
{ "importaddress", 2 },
{ "verifychain", 0 },
{ "verifychain", 1 },
{ "keypoolrefill", 0 },
@ -126,7 +133,6 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri @@ -126,7 +133,6 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
throw runtime_error(string("Error parsing JSON:")+strVal);
params.push_back(jVal);
}
}
return params;

54
src/rpcdump.cpp

@ -133,6 +133,60 @@ Value importprivkey(const Array& params, bool fHelp) @@ -133,6 +133,60 @@ Value importprivkey(const Array& params, bool fHelp)
return Value::null;
}
Value importaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 3)
throw runtime_error(
"importaddress <address> [label] [rescan=true]\n"
"Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.");
CScript script;
CBitcoinAddress address(params[0].get_str());
if (address.IsValid()) {
script.SetDestination(address.Get());
} else if (IsHex(params[0].get_str())) {
std::vector<unsigned char> data(ParseHex(params[0].get_str()));
script = CScript(data.begin(), data.end());
} else {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
}
string strLabel = "";
if (params.size() > 1)
strLabel = params[1].get_str();
// Whether to perform rescan after import
bool fRescan = true;
if (params.size() > 2)
fRescan = params[2].get_bool();
{
LOCK2(cs_main, pwalletMain->cs_wallet);
// add to address book or update label
if (address.IsValid())
pwalletMain->SetAddressBook(address.Get(), strLabel, "receive");
// Don't throw error in case an address is already there
if (pwalletMain->HaveWatchOnly(script))
return Value::null;
pwalletMain->MarkDirty();
if (!pwalletMain->AddWatchOnly(script))
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
if (fRescan)
{
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
pwalletMain->ReacceptWalletTransactions();
}
}
return Value::null;
}
Value importwallet(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)

52
src/rpcmisc.cpp

@ -92,36 +92,45 @@ Value getinfo(const Array& params, bool fHelp) @@ -92,36 +92,45 @@ Value getinfo(const Array& params, bool fHelp)
#ifdef ENABLE_WALLET
class DescribeAddressVisitor : public boost::static_visitor<Object>
{
private:
isminetype mine;
public:
DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
Object operator()(const CNoDestination &dest) const { return Object(); }
Object operator()(const CKeyID &keyID) const {
Object obj;
CPubKey vchPubKey;
pwalletMain->GetPubKey(keyID, vchPubKey);
obj.push_back(Pair("isscript", false));
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
if (mine == ISMINE_SPENDABLE) {
pwalletMain->GetPubKey(keyID, vchPubKey);
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
}
return obj;
}
Object operator()(const CScriptID &scriptID) const {
Object obj;
obj.push_back(Pair("isscript", true));
CScript subscript;
pwalletMain->GetCScript(scriptID, subscript);
std::vector<CTxDestination> addresses;
txnouttype whichType;
int nRequired;
ExtractDestinations(subscript, whichType, addresses, nRequired);
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
Array a;
BOOST_FOREACH(const CTxDestination& addr, addresses)
a.push_back(CBitcoinAddress(addr).ToString());
obj.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG)
obj.push_back(Pair("sigsrequired", nRequired));
if (mine != ISMINE_NO) {
CScript subscript;
pwalletMain->GetCScript(scriptID, subscript);
std::vector<CTxDestination> addresses;
txnouttype whichType;
int nRequired;
ExtractDestinations(subscript, whichType, addresses, nRequired);
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
Array a;
BOOST_FOREACH(const CTxDestination& addr, addresses)
a.push_back(CBitcoinAddress(addr).ToString());
obj.push_back(Pair("addresses", a));
if (whichType == TX_MULTISIG)
obj.push_back(Pair("sigsrequired", nRequired));
}
return obj;
}
};
@ -161,10 +170,11 @@ Value validateaddress(const Array& params, bool fHelp) @@ -161,10 +170,11 @@ Value validateaddress(const Array& params, bool fHelp)
string currentAddress = address.ToString();
ret.push_back(Pair("address", currentAddress));
#ifdef ENABLE_WALLET
bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false;
ret.push_back(Pair("ismine", fMine));
if (fMine) {
Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO;
ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false));
if (mine != ISMINE_NO) {
ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false));
Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest);
ret.insert(ret.end(), detail.begin(), detail.end());
}
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))

1
src/rpcrawtransaction.cpp

@ -304,6 +304,7 @@ Value listunspent(const Array& params, bool fHelp) @@ -304,6 +304,7 @@ Value listunspent(const Array& params, bool fHelp)
}
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
entry.push_back(Pair("confirmations",out.nDepth));
entry.push_back(Pair("spendable", out.fSpendable));
results.push_back(entry);
}

1
src/rpcserver.cpp

@ -282,6 +282,7 @@ static const CRPCCommand vRPCCommands[] = @@ -282,6 +282,7 @@ static const CRPCCommand vRPCCommands[] =
{ "getwalletinfo", &getwalletinfo, true, false, true },
{ "importprivkey", &importprivkey, false, false, true },
{ "importwallet", &importwallet, false, false, true },
{ "importaddress", &importaddress, false, false, true },
{ "keypoolrefill", &keypoolrefill, true, false, true },
{ "listaccounts", &listaccounts, false, false, true },
{ "listaddressgroupings", &listaddressgroupings, false, false, true },

1
src/rpcserver.h

@ -131,6 +131,7 @@ extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fH @@ -131,6 +131,7 @@ extern json_spirit::Value getnettotals(const json_spirit::Array& params, bool fH
extern json_spirit::Value dumpprivkey(const json_spirit::Array& params, bool fHelp); // in rpcdump.cpp
extern json_spirit::Value importprivkey(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);

127
src/rpcwallet.cpp

@ -557,7 +557,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp) @@ -557,7 +557,7 @@ Value getreceivedbyaccount(const Array& params, bool fHelp)
}
int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth)
int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter)
{
int64_t nBalance = 0;
@ -569,7 +569,7 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi @@ -569,7 +569,7 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi
continue;
int64_t nReceived, nSent, nFee;
wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee);
wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter);
if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth)
nBalance += nReceived;
@ -582,18 +582,18 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi @@ -582,18 +582,18 @@ int64_t GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi
return nBalance;
}
int64_t GetAccountBalance(const string& strAccount, int nMinDepth)
int64_t GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter)
{
CWalletDB walletdb(pwalletMain->strWalletFile);
return GetAccountBalance(walletdb, strAccount, nMinDepth);
return GetAccountBalance(walletdb, strAccount, nMinDepth, filter);
}
Value getbalance(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 2)
if (fHelp || params.size() > 3)
throw runtime_error(
"getbalance ( \"account\" minconf )\n"
"getbalance ( \"account\" minconf includeWatchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"If account is specified, returns the balance in the account.\n"
"Note that the account \"\" is not the same as leaving the parameter out.\n"
@ -601,6 +601,7 @@ Value getbalance(const Array& params, bool fHelp) @@ -601,6 +601,7 @@ Value getbalance(const Array& params, bool fHelp)
"\nArguments:\n"
"1. \"account\" (string, optional) The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
"3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in btc received for this account.\n"
"\nExamples:\n"
@ -622,6 +623,10 @@ Value getbalance(const Array& params, bool fHelp) @@ -622,6 +623,10 @@ Value getbalance(const Array& params, bool fHelp)
int nMinDepth = 1;
if (params.size() > 1)
nMinDepth = params[1].get_int();
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 2)
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
if (params[0].get_str() == "*") {
// Calculate total balance a different way from GetBalance()
@ -638,7 +643,7 @@ Value getbalance(const Array& params, bool fHelp) @@ -638,7 +643,7 @@ Value getbalance(const Array& params, bool fHelp)
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount);
wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (wtx.GetDepthInMainChain() >= nMinDepth)
{
BOOST_FOREACH(const PAIRTYPE(CTxDestination,int64_t)& r, listReceived)
@ -653,7 +658,7 @@ Value getbalance(const Array& params, bool fHelp) @@ -653,7 +658,7 @@ Value getbalance(const Array& params, bool fHelp)
string strAccount = AccountFromValue(params[0]);
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, filter);
return ValueFromAmount(nBalance);
}
@ -781,7 +786,7 @@ Value sendfrom(const Array& params, bool fHelp) @@ -781,7 +786,7 @@ Value sendfrom(const Array& params, bool fHelp)
EnsureWalletIsUnlocked();
// Check funds
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (nAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@ -858,7 +863,7 @@ Value sendmany(const Array& params, bool fHelp) @@ -858,7 +863,7 @@ Value sendmany(const Array& params, bool fHelp)
EnsureWalletIsUnlocked();
// Check funds
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth);
int64_t nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE);
if (totalAmount > nBalance)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds");
@ -927,10 +932,12 @@ struct tallyitem @@ -927,10 +932,12 @@ struct tallyitem
int64_t nAmount;
int nConf;
vector<uint256> txids;
bool fIsWatchonly;
tallyitem()
{
nAmount = 0;
nConf = std::numeric_limits<int>::max();
fIsWatchonly = false;
}
};
@ -946,6 +953,11 @@ Value ListReceived(const Array& params, bool fByAccounts) @@ -946,6 +953,11 @@ Value ListReceived(const Array& params, bool fByAccounts)
if (params.size() > 1)
fIncludeEmpty = params[1].get_bool();
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 2)
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
// Tally
map<CBitcoinAddress, tallyitem> mapTally;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
@ -962,13 +974,19 @@ Value ListReceived(const Array& params, bool fByAccounts) @@ -962,13 +974,19 @@ Value ListReceived(const Array& params, bool fByAccounts)
BOOST_FOREACH(const CTxOut& txout, wtx.vout)
{
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address) || !IsMine(*pwalletMain, address))
if (!ExtractDestination(txout.scriptPubKey, address))
continue;
isminefilter mine = IsMine(*pwalletMain, address);
if(!(mine & filter))
continue;
tallyitem& item = mapTally[address];
item.nAmount += txout.nValue;
item.nConf = min(item.nConf, nDepth);
item.txids.push_back(wtx.GetHash());
if (mine & ISMINE_WATCH_ONLY)
item.fIsWatchonly = true;
}
}
@ -985,10 +1003,12 @@ Value ListReceived(const Array& params, bool fByAccounts) @@ -985,10 +1003,12 @@ Value ListReceived(const Array& params, bool fByAccounts)
int64_t nAmount = 0;
int nConf = std::numeric_limits<int>::max();
bool fIsWatchonly = false;
if (it != mapTally.end())
{
nAmount = (*it).second.nAmount;
nConf = (*it).second.nConf;
fIsWatchonly = (*it).second.fIsWatchonly;
}
if (fByAccounts)
@ -996,10 +1016,13 @@ Value ListReceived(const Array& params, bool fByAccounts) @@ -996,10 +1016,13 @@ Value ListReceived(const Array& params, bool fByAccounts)
tallyitem& item = mapAccountTally[strAccount];
item.nAmount += nAmount;
item.nConf = min(item.nConf, nConf);
item.fIsWatchonly = fIsWatchonly;
}
else
{
Object obj;
if(fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true));
obj.push_back(Pair("address", address.ToString()));
obj.push_back(Pair("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
@ -1024,6 +1047,8 @@ Value ListReceived(const Array& params, bool fByAccounts) @@ -1024,6 +1047,8 @@ Value ListReceived(const Array& params, bool fByAccounts)
int64_t nAmount = (*it).second.nAmount;
int nConf = (*it).second.nConf;
Object obj;
if((*it).second.fIsWatchonly)
obj.push_back(Pair("involvesWatchonly", true));
obj.push_back(Pair("account", (*it).first));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
@ -1036,17 +1061,19 @@ Value ListReceived(const Array& params, bool fByAccounts) @@ -1036,17 +1061,19 @@ Value ListReceived(const Array& params, bool fByAccounts)
Value listreceivedbyaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 2)
if (fHelp || params.size() > 3)
throw runtime_error(
"listreceivedbyaddress ( minconf includeempty )\n"
"listreceivedbyaddress ( minconf includeempty includeWatchonly)\n"
"\nList balances by receiving address.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. includeempty (numeric, optional, dafault=false) Whether to include addresses that haven't received any payments.\n"
"3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
" \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n"
" \"address\" : \"receivingaddress\", (string) The receiving address\n"
" \"account\" : \"accountname\", (string) The account of the receiving address. The default account is \"\".\n"
" \"amount\" : x.xxx, (numeric) The total amount in btc received by the address\n"
@ -1058,7 +1085,7 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) @@ -1058,7 +1085,7 @@ Value listreceivedbyaddress(const Array& params, bool fHelp)
"\nExamples:\n"
+ HelpExampleCli("listreceivedbyaddress", "")
+ HelpExampleCli("listreceivedbyaddress", "6 true")
+ HelpExampleRpc("listreceivedbyaddress", "6, true")
+ HelpExampleRpc("listreceivedbyaddress", "6, true, true")
);
return ListReceived(params, false);
@ -1066,17 +1093,19 @@ Value listreceivedbyaddress(const Array& params, bool fHelp) @@ -1066,17 +1093,19 @@ Value listreceivedbyaddress(const Array& params, bool fHelp)
Value listreceivedbyaccount(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 2)
if (fHelp || params.size() > 3)
throw runtime_error(
"listreceivedbyaccount ( minconf includeempty )\n"
"listreceivedbyaccount ( minconf includeempty includeWatchonly)\n"
"\nList balances by account.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
"2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n"
"3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
" {\n"
" \"involvesWatchonly\" : \"true\", (bool) Only returned if imported addresses were involved in transaction\n"
" \"account\" : \"accountname\", (string) The account name of the receiving account\n"
" \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n"
" \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n"
@ -1087,7 +1116,7 @@ Value listreceivedbyaccount(const Array& params, bool fHelp) @@ -1087,7 +1116,7 @@ Value listreceivedbyaccount(const Array& params, bool fHelp)
"\nExamples:\n"
+ HelpExampleCli("listreceivedbyaccount", "")
+ HelpExampleCli("listreceivedbyaccount", "6 true")
+ HelpExampleRpc("listreceivedbyaccount", "6, true")
+ HelpExampleRpc("listreceivedbyaccount", "6, true, true")
);
return ListReceived(params, true);
@ -1100,16 +1129,17 @@ static void MaybePushAddress(Object & entry, const CTxDestination &dest) @@ -1100,16 +1129,17 @@ static void MaybePushAddress(Object & entry, const CTxDestination &dest)
entry.push_back(Pair("address", addr.ToString()));
}
void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret)
void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter)
{
int64_t nFee;
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter);
bool fAllAccounts = (strAccount == string("*"));
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
// Sent
if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount))
@ -1117,6 +1147,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe @@ -1117,6 +1147,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
{
Object entry;
if(involvesWatchonly || (::IsMine(*pwalletMain, s.first) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", strSentAccount));
MaybePushAddress(entry, s.first);
entry.push_back(Pair("category", "send"));
@ -1139,6 +1171,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe @@ -1139,6 +1171,8 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
if (fAllAccounts || (account == strAccount))
{
Object entry;
if(involvesWatchonly || (::IsMine(*pwalletMain, r.first) & ISMINE_WATCH_ONLY))
entry.push_back(Pair("involvesWatchonly", true));
entry.push_back(Pair("account", account));
MaybePushAddress(entry, r.first);
if (wtx.IsCoinBase())
@ -1182,16 +1216,16 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar @@ -1182,16 +1216,16 @@ void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Ar
Value listtransactions(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 3)
if (fHelp || params.size() > 4)
throw runtime_error(
"listtransactions ( \"account\" count from )\n"
"listtransactions ( \"account\" count from includeWatchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) The account name. If not included, it will list all transactions for all accounts.\n"
" If \"\" is set, it will list transactions for the default account.\n"
"2. count (numeric, optional, default=10) The number of transactions to return\n"
"3. from (numeric, optional, default=0) The number of transactions to skip\n"
"4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"[\n"
" {\n"
@ -1251,6 +1285,10 @@ Value listtransactions(const Array& params, bool fHelp) @@ -1251,6 +1285,10 @@ Value listtransactions(const Array& params, bool fHelp)
int nFrom = 0;
if (params.size() > 2)
nFrom = params[2].get_int();
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 3)
if(params[3].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
if (nCount < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
@ -1267,7 +1305,7 @@ Value listtransactions(const Array& params, bool fHelp) @@ -1267,7 +1305,7 @@ Value listtransactions(const Array& params, bool fHelp)
{
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != 0)
ListTransactions(*pwtx, strAccount, 0, true, ret);
ListTransactions(*pwtx, strAccount, 0, true, ret, filter);
CAccountingEntry *const pacentry = (*it).second.second;
if (pacentry != 0)
AcentryToJSON(*pacentry, strAccount, ret);
@ -1295,12 +1333,13 @@ Value listtransactions(const Array& params, bool fHelp) @@ -1295,12 +1333,13 @@ Value listtransactions(const Array& params, bool fHelp)
Value listaccounts(const Array& params, bool fHelp)
{
if (fHelp || params.size() > 1)
if (fHelp || params.size() > 2)
throw runtime_error(
"listaccounts ( minconf )\n"
"listaccounts ( minconf includeWatchonly)\n"
"\nReturns Object that has account names as keys, account balances as values.\n"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) Only onclude transactions with at least this many confirmations\n"
"1. minconf (numeric, optional, default=1) Only onclude transactions with at least this many confirmations\n"
"2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n"
"\nResult:\n"
"{ (json object where keys are account names, and values are numeric balances\n"
" \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n"
@ -1320,10 +1359,14 @@ Value listaccounts(const Array& params, bool fHelp) @@ -1320,10 +1359,14 @@ Value listaccounts(const Array& params, bool fHelp)
int nMinDepth = 1;
if (params.size() > 0)
nMinDepth = params[0].get_int();
isminefilter includeWatchonly = ISMINE_SPENDABLE;
if(params.size() > 1)
if(params[1].get_bool())
includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;
map<string, int64_t> mapAccountBalances;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) {
if (IsMine(*pwalletMain, entry.first)) // This address belongs to me
if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me
mapAccountBalances[entry.second.name] = 0;
}
@ -1337,7 +1380,7 @@ Value listaccounts(const Array& params, bool fHelp) @@ -1337,7 +1380,7 @@ Value listaccounts(const Array& params, bool fHelp)
int nDepth = wtx.GetDepthInMainChain();
if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0)
continue;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount);
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly);
mapAccountBalances[strSentAccount] -= nFee;
BOOST_FOREACH(const PAIRTYPE(CTxDestination, int64_t)& s, listSent)
mapAccountBalances[strSentAccount] -= s.second;
@ -1367,11 +1410,12 @@ Value listsinceblock(const Array& params, bool fHelp) @@ -1367,11 +1410,12 @@ Value listsinceblock(const Array& params, bool fHelp)
{
if (fHelp)
throw runtime_error(
"listsinceblock ( \"blockhash\" target-confirmations )\n"
"listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n"
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
"\nArguments:\n"
"1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
"2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
"3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
@ -1407,6 +1451,7 @@ Value listsinceblock(const Array& params, bool fHelp) @@ -1407,6 +1451,7 @@ Value listsinceblock(const Array& params, bool fHelp)
CBlockIndex *pindex = NULL;
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
if (params.size() > 0)
{
@ -1426,6 +1471,10 @@ Value listsinceblock(const Array& params, bool fHelp) @@ -1426,6 +1471,10 @@ Value listsinceblock(const Array& params, bool fHelp)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
if(params.size() > 2)
if(params[2].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
Array transactions;
@ -1435,7 +1484,7 @@ Value listsinceblock(const Array& params, bool fHelp) @@ -1435,7 +1484,7 @@ Value listsinceblock(const Array& params, bool fHelp)
CWalletTx tx = (*it).second;
if (depth == -1 || tx.GetDepthInMainChain() < depth)
ListTransactions(tx, "*", 0, true, transactions);
ListTransactions(tx, "*", 0, true, transactions, filter);
}
CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms];
@ -1450,12 +1499,13 @@ Value listsinceblock(const Array& params, bool fHelp) @@ -1450,12 +1499,13 @@ Value listsinceblock(const Array& params, bool fHelp)
Value gettransaction(const Array& params, bool fHelp)
{
if (fHelp || params.size() != 1)
if (fHelp || params.size() < 1 || params.size() > 2)
throw runtime_error(
"gettransaction \"txid\"\n"
"\nGet detailed information about in-wallet transaction <txid>\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n"
"\nResult:\n"
"{\n"
" \"amount\" : x.xxx, (numeric) The transaction amount in btc\n"
@ -1492,24 +1542,29 @@ Value gettransaction(const Array& params, bool fHelp) @@ -1492,24 +1542,29 @@ Value gettransaction(const Array& params, bool fHelp)
uint256 hash;
hash.SetHex(params[0].get_str());
isminefilter filter = ISMINE_SPENDABLE;
if(params.size() > 1)
if(params[1].get_bool())
filter = filter | ISMINE_WATCH_ONLY;
Object entry;
if (!pwalletMain->mapWallet.count(hash))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
const CWalletTx& wtx = pwalletMain->mapWallet[hash];
int64_t nCredit = wtx.GetCredit();
int64_t nDebit = wtx.GetDebit();
int64_t nCredit = wtx.GetCredit(filter);
int64_t nDebit = wtx.GetDebit(filter);
int64_t nNet = nCredit - nDebit;
int64_t nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0);
int64_t nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0);
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
if (wtx.IsFromMe())
if (wtx.IsFromMe(filter))
entry.push_back(Pair("fee", ValueFromAmount(nFee)));
WalletTxToJSON(wtx, entry);
Array details;
ListTransactions(wtx, "*", 0, false, details);
ListTransactions(wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);

44
src/script.cpp

@ -1456,36 +1456,49 @@ public: @@ -1456,36 +1456,49 @@ public:
bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); }
};
bool IsMine(const CKeyStore &keystore, const CTxDestination &dest)
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest)
{
return boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest);
CScript script;
script.SetDestination(dest);
return IsMine(keystore, script);
}
bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
{
vector<valtype> vSolutions;
txnouttype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions))
return false;
if (!Solver(scriptPubKey, whichType, vSolutions)) {
if (keystore.HaveWatchOnly(scriptPubKey))
return ISMINE_WATCH_ONLY;
return ISMINE_NO;
}
CKeyID keyID;
switch (whichType)
{
case TX_NONSTANDARD:
case TX_NULL_DATA:
return false;
break;
case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
return keystore.HaveKey(keyID);
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
case TX_PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
return keystore.HaveKey(keyID);
if (keystore.HaveKey(keyID))
return ISMINE_SPENDABLE;
break;
case TX_SCRIPTHASH:
{
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript))
return false;
return IsMine(keystore, subscript);
if (keystore.GetCScript(scriptID, subscript)) {
isminetype ret = IsMine(keystore, subscript);
if (ret == ISMINE_SPENDABLE)
return ret;
}
break;
}
case TX_MULTISIG:
{
@ -1495,10 +1508,15 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) @@ -1495,10 +1508,15 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
// them) enable spend-out-from-under-you attacks, especially
// in shared-wallet situations.
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
return HaveKeys(keys, keystore) == keys.size();
if (HaveKeys(keys, keystore) == keys.size())
return ISMINE_SPENDABLE;
break;
}
}
return false;
if (keystore.HaveWatchOnly(scriptPubKey))
return ISMINE_WATCH_ONLY;
return ISMINE_NO;
}
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)

15
src/script.h

@ -194,6 +194,17 @@ enum @@ -194,6 +194,17 @@ enum
SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
};
/** IsMine() return codes */
enum isminetype
{
ISMINE_NO = 0,
ISMINE_WATCH_ONLY = 1,
ISMINE_SPENDABLE = 2,
ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE
};
/** 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,
// but in the future other flags may be added, such as a soft-fork to enforce
@ -801,8 +812,8 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& @@ -801,8 +812,8 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CTxDestination &dest);
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);

2
src/test/wallet_tests.cpp

@ -46,7 +46,7 @@ static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, in @@ -46,7 +46,7 @@ static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, in
wtx->fDebitCached = true;
wtx->nDebitCached = 1;
}
COutput output(wtx, nInput, nAge);
COutput output(wtx, nInput, nAge, true);
vCoins.push_back(output);
}

113
src/wallet.cpp

@ -149,6 +149,21 @@ bool CWallet::LoadCScript(const CScript& redeemScript) @@ -149,6 +149,21 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
return CCryptoKeyStore::AddCScript(redeemScript);
}
bool CWallet::AddWatchOnly(const CScript &dest)
{
if (!CCryptoKeyStore::AddWatchOnly(dest))
return false;
nTimeFirstKey = 1; // No birthday information for watch-only keys.
if (!fFileBacked)
return true;
return CWalletDB(strWalletFile).WriteWatchOnly(dest);
}
bool CWallet::LoadWatchOnly(const CScript &dest)
{
return CCryptoKeyStore::AddWatchOnly(dest);
}
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
CCrypter crypter;
@ -684,7 +699,7 @@ void CWallet::EraseFromWallet(const uint256 &hash) @@ -684,7 +699,7 @@ void CWallet::EraseFromWallet(const uint256 &hash)
}
bool CWallet::IsMine(const CTxIn &txin) const
isminetype CWallet::IsMine(const CTxIn &txin) const
{
{
LOCK(cs_wallet);
@ -693,14 +708,13 @@ bool CWallet::IsMine(const CTxIn &txin) const @@ -693,14 +708,13 @@ bool CWallet::IsMine(const CTxIn &txin) const
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]))
return true;
return IsMine(prev.vout[txin.prevout.n]);
}
}
return false;
return ISMINE_NO;
}
int64_t CWallet::GetDebit(const CTxIn &txin) const
int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{
{
LOCK(cs_wallet);
@ -709,7 +723,7 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const @@ -709,7 +723,7 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const
{
const CWalletTx& prev = (*mi).second;
if (txin.prevout.n < prev.vout.size())
if (IsMine(prev.vout[txin.prevout.n]))
if (IsMine(prev.vout[txin.prevout.n]) & filter)
return prev.vout[txin.prevout.n].nValue;
}
}
@ -718,17 +732,19 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const @@ -718,17 +732,19 @@ int64_t CWallet::GetDebit(const CTxIn &txin) const
bool CWallet::IsChange(const CTxOut& txout) const
{
CTxDestination address;
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a TX_PUBKEYHASH that is mine but isn't in the address book
// payment to a script that is ours, but is not in the address book
// is change. That assumption is likely to break when we implement multisignature
// wallets that return change back into a multi-signature-protected address;
// 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 (::IsMine(*this, txout.scriptPubKey))
{
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address))
return true;
LOCK(cs_wallet);
if (!mapAddressBook.count(address))
return true;
@ -782,7 +798,7 @@ int CWalletTx::GetRequestCount() const @@ -782,7 +798,7 @@ int CWalletTx::GetRequestCount() const
}
void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount) const
list<pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const
{
nFee = 0;
listReceived.clear();
@ -790,7 +806,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, @@ -790,7 +806,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
strSentAccount = strFromAccount;
// Compute fee:
int64_t nDebit = GetDebit();
int64_t nDebit = GetDebit(filter);
if (nDebit > 0) // debit>0 means we signed/sent this transaction
{
int64_t nValueOut = GetValueOut();
@ -800,7 +816,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, @@ -800,7 +816,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
// Sent/received.
BOOST_FOREACH(const CTxOut& txout, vout)
{
bool fIsMine;
isminetype fIsMine = pwallet->IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
// 2) the output is to us (received)
@ -809,9 +826,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, @@ -809,9 +826,8 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
// Don't report 'change' txouts
if (pwallet->IsChange(txout))
continue;
fIsMine = pwallet->IsMine(txout);
}
else if (!(fIsMine = pwallet->IsMine(txout)))
else if (!(fIsMine & filter))
continue;
// In either case, we need to get the destination address
@ -835,7 +851,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived, @@ -835,7 +851,7 @@ void CWalletTx::GetAmounts(list<pair<CTxDestination, int64_t> >& listReceived,
}
void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived,
int64_t& nSent, int64_t& nFee) const
int64_t& nSent, int64_t& nFee, const isminefilter& filter) const
{
nReceived = nSent = nFee = 0;
@ -843,7 +859,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, @@ -843,7 +859,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived,
string strSentAccount;
list<pair<CTxDestination, int64_t> > listReceived;
list<pair<CTxDestination, int64_t> > listSent;
GetAmounts(listReceived, listSent, allFee, strSentAccount);
GetAmounts(listReceived, listSent, allFee, strSentAccount, filter);
if (strAccount == strSentAccount)
{
@ -1055,7 +1071,52 @@ int64_t CWallet::GetImmatureBalance() const @@ -1055,7 +1071,52 @@ int64_t CWallet::GetImmatureBalance() const
return nTotal;
}
// populate vCoins with vector of spendable COutputs
int64_t CWallet::GetWatchOnlyBalance() const
{
int64_t nTotal = 0;
{
LOCK(cs_wallet);
for (map<uint256, CWalletTx>::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<uint256, CWalletTx>::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<uint256, CWalletTx>::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<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const
{
vCoins.clear();
@ -1081,10 +1142,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const @@ -1081,10 +1142,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
if (!(IsSpent(wtxid, i)) && IsMine(pcoin->vout[i]) &&
isminetype mine = IsMine(pcoin->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
!IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 &&
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
vCoins.push_back(COutput(pcoin, i, nDepth));
vCoins.push_back(COutput(pcoin, i, nDepth, mine & ISMINE_SPENDABLE));
}
}
}
@ -1151,11 +1213,14 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT @@ -1151,11 +1213,14 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
BOOST_FOREACH(COutput output, vCoins)
BOOST_FOREACH(const COutput &output, vCoins)
{
if (!output.fSpendable)
continue;
const CWalletTx *pcoin = output.tx;
if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
continue;
int i = output.i;
@ -1244,6 +1309,8 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsign @@ -1244,6 +1309,8 @@ bool CWallet::SelectCoins(int64_t nTargetValue, set<pair<const CWalletTx*,unsign
{
BOOST_FOREACH(const COutput& out, vCoins)
{
if(!out.fSpendable)
continue;
nValueRet += out.tx->vout[out.i].nValue;
setCoinsRet.insert(make_pair(out.tx, out.i));
}
@ -1804,7 +1871,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances() @@ -1804,7 +1871,7 @@ std::map<CTxDestination, int64_t> CWallet::GetAddressBalances()
continue;
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < (pcoin->IsFromMe() ? 0 : 1))
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1))
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++)

148
src/wallet.h

@ -229,6 +229,11 @@ public: @@ -229,6 +229,11 @@ public:
/// Look up a destination data tuple in the store, return true if found false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
// Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript &dest);
// Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const SecureString& strWalletPassphrase);
@ -260,6 +265,9 @@ public: @@ -260,6 +265,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<std::pair<CScript, int64_t> >& vecSend,
CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, std::string& strFailReason, const CCoinControl *coinControl = NULL);
bool CreateTransaction(CScript scriptPubKey, int64_t nValue,
@ -284,17 +292,17 @@ public: @@ -284,17 +292,17 @@ public:
std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const;
bool IsMine(const CTxIn& txin) const;
int64_t GetDebit(const CTxIn& txin) const;
bool IsMine(const CTxOut& txout) const
isminetype IsMine(const CTxIn& txin) const;
int64_t GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const
{
return ::IsMine(*this, txout.scriptPubKey);
}
int64_t GetCredit(const CTxOut& txout) const
int64_t GetCredit(const CTxOut& txout, const isminefilter& filter) 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
@ -310,9 +318,9 @@ public: @@ -310,9 +318,9 @@ public:
return true;
return false;
}
bool IsFromMe(const CTransaction& tx) const
bool IsFromMe(const CTransaction& tx) const // should probably be renamed to IsRelevantToMe
{
return (GetDebit(tx) > 0);
return (GetDebit(tx, ISMINE_ALL) > 0);
}
bool IsConflicting(const CTransaction& tx) const
{
@ -321,23 +329,23 @@ public: @@ -321,23 +329,23 @@ public:
return true;
return false;
}
int64_t GetDebit(const CTransaction& tx) const
int64_t GetDebit(const CTransaction& tx, const isminefilter& filter) const
{
int64_t nDebit = 0;
BOOST_FOREACH(const CTxIn& txin, tx.vin)
{
nDebit += GetDebit(txin);
nDebit += GetDebit(txin, filter);
if (!MoneyRange(nDebit))
throw std::runtime_error("CWallet::GetDebit() : value out of range");
}
return nDebit;
}
int64_t GetCredit(const CTransaction& tx) const
int64_t GetCredit(const CTransaction& tx, const isminefilter& filter) 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 +491,19 @@ public: @@ -483,11 +491,19 @@ public:
mutable bool fCreditCached;
mutable bool fImmatureCreditCached;
mutable bool fAvailableCreditCached;
mutable bool fWatchDebitCached;
mutable bool fWatchCreditCached;
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 nWatchDebitCached;
mutable int64_t nWatchCreditCached;
mutable int64_t nImmatureWatchCreditCached;
mutable int64_t nAvailableWatchCreditCached;
mutable int64_t nChangeCached;
CWalletTx()
@ -524,11 +540,19 @@ public: @@ -524,11 +540,19 @@ public:
fCreditCached = false;
fImmatureCreditCached = false;
fAvailableCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fChangeCached = false;
nDebitCached = 0;
nCreditCached = 0;
nImmatureCreditCached = 0;
nAvailableCreditCached = 0;
nWatchDebitCached = 0;
nWatchCreditCached = 0;
nAvailableWatchCreditCached = 0;
nImmatureWatchCreditCached = 0;
nChangeCached = 0;
nOrderPos = -1;
}
@ -581,6 +605,10 @@ public: @@ -581,6 +605,10 @@ public:
{
fCreditCached = false;
fAvailableCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fDebitCached = false;
fChangeCached = false;
}
@ -591,15 +619,36 @@ public: @@ -591,15 +619,36 @@ public:
MarkDirty();
}
int64_t GetDebit() const
// filter decides which addresses will count towards the debit
int64_t GetDebit(const isminefilter& filter) const
{
if (vin.empty())
return 0;
if (fDebitCached)
return nDebitCached;
nDebitCached = pwallet->GetDebit(*this);
fDebitCached = true;
return nDebitCached;
int64_t debit = 0;
if(filter & ISMINE_SPENDABLE)
{
if (fDebitCached)
debit += nDebitCached;
else
{
nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
}
if(filter & ISMINE_WATCH_ONLY)
{
if(fWatchDebitCached)
debit += nWatchDebitCached;
else
{
nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);
fWatchDebitCached = true;
debit += nWatchDebitCached;
}
}
return debit;
}
int64_t GetCredit(bool fUseCache=true) const
@ -611,7 +660,7 @@ public: @@ -611,7 +660,7 @@ public:
// GetBalance can assume transactions in mapWallet won't change
if (fUseCache && fCreditCached)
return nCreditCached;
nCreditCached = pwallet->GetCredit(*this);
nCreditCached = pwallet->GetCredit(*this, ISMINE_ALL);
fCreditCached = true;
return nCreditCached;
}
@ -622,7 +671,7 @@ public: @@ -622,7 +671,7 @@ public:
{
if (fUseCache && fImmatureCreditCached)
return nImmatureCreditCached;
nImmatureCreditCached = pwallet->GetCredit(*this);
nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
}
@ -649,7 +698,7 @@ public: @@ -649,7 +698,7 @@ public:
if (!pwallet->IsSpent(hashTx, i))
{
const CTxOut &txout = vout[i];
nCredit += pwallet->GetCredit(txout);
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit))
throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");
}
@ -660,6 +709,48 @@ public: @@ -660,6 +709,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, ISMINE_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, ISMINE_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,14 +762,14 @@ public: @@ -671,14 +762,14 @@ public:
}
void GetAmounts(std::list<std::pair<CTxDestination, int64_t> >& listReceived,
std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount) const;
std::list<std::pair<CTxDestination, int64_t> >& listSent, int64_t& nFee, std::string& strSentAccount, const isminefilter& filter) const;
void GetAccountAmounts(const std::string& strAccount, int64_t& nReceived,
int64_t& nSent, int64_t& nFee) const;
int64_t& nSent, int64_t& nFee, const isminefilter& filter) const;
bool IsFromMe() const
bool IsFromMe(const isminefilter& filter) const
{
return (GetDebit() > 0);
return (GetDebit(filter) > 0);
}
bool IsTrusted() const
@ -691,7 +782,7 @@ public: @@ -691,7 +782,7 @@ public:
return true;
if (nDepth < 0)
return false;
if (!bSpendZeroConfChange || !IsFromMe()) // using wtx's cached debit
if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit
return false;
// Trusted if all inputs are from us and are in the mempool:
@ -702,7 +793,7 @@ public: @@ -702,7 +793,7 @@ public:
if (parent == NULL)
return false;
const CTxOut& parentOut = parent->vout[txin.prevout.n];
if (!pwallet->IsMine(parentOut))
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)
return false;
}
return true;
@ -727,10 +818,11 @@ public: @@ -727,10 +818,11 @@ public:
const CWalletTx *tx;
int i;
int nDepth;
bool fSpendable;
COutput(const CWalletTx *txIn, int iIn, int nDepthIn)
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn)
{
tx = txIn; i = iIn; nDepth = nDepthIn;
tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn;
}
std::string ToString() const

19
src/walletdb.cpp

@ -112,6 +112,12 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript) @@ -112,6 +112,12 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
}
bool CWalletDB::WriteWatchOnly(const CScript &dest)
{
nWalletDBUpdated++;
return Write(std::make_pair(std::string("watchs"), dest), '1');
}
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
@ -404,6 +410,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, @@ -404,6 +410,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
wss.fAnyUnordered = true;
}
}
else if (strType == "watchs")
{
CScript script;
ssKey >> script;
char fYes;
ssValue >> fYes;
if (fYes == '1')
pwallet->LoadWatchOnly(script);
// Watch-only addresses have no birthday information for now,
// so set the wallet birthday to the beginning of time.
pwallet->nTimeFirstKey = 1;
}
else if (strType == "key" || strType == "wkey")
{
CPubKey vchPubKey;

3
src/walletdb.h

@ -7,6 +7,7 @@ @@ -7,6 +7,7 @@
#include "db.h"
#include "key.h"
#include "keystore.h"
#include <list>
#include <stdint.h>
@ -93,6 +94,8 @@ public: @@ -93,6 +94,8 @@ public:
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
bool WriteWatchOnly(const CScript &script);
bool WriteBestBlock(const CBlockLocator& locator);
bool ReadBestBlock(CBlockLocator& locator);

Loading…
Cancel
Save