From c8988460a2865b99ee96da6799d37ac6ccb79d4d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 26 Jul 2013 01:06:01 +0200 Subject: Add support for watch-only addresses Changes: * Add Add/Have WatchOnly methods to CKeyStore, and implementations in CBasicKeyStore. * Add similar methods to CWallet, and support entries for it in CWalletDB. * Make IsMine in script/wallet return a new enum 'isminetype', rather than a boolean. This allows distinguishing between spendable and unspendable coins. * Add a field fSpendable to COutput (GetAvailableCoins' return type). * Mark watchonly coins in listunspent as 'watchonly': true. * Add 'watchonly' to validateaddress, suppressing script/pubkey/... in this case. Based on a patch by Eric Lombrozo. Conflicts: src/qt/walletmodel.cpp src/rpcserver.cpp src/wallet.cpp --- src/wallet.cpp | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index daca7ac04..3d679f4c5 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -145,6 +145,22 @@ bool CWallet::LoadCScript(const CScript& redeemScript) return CCryptoKeyStore::AddCScript(redeemScript); } +bool CWallet::AddWatchOnly(const CTxDestination &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 CTxDestination &dest) +{ + LogPrintf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str()); + return CCryptoKeyStore::AddWatchOnly(dest); +} + bool CWallet::Unlock(const SecureString& strWalletPassphrase) { CCrypter crypter; @@ -680,7 +696,7 @@ void CWallet::EraseFromWallet(const uint256 &hash) } -bool CWallet::IsMine(const CTxIn &txin) const +isminetype CWallet::IsMine(const CTxIn &txin) const { { LOCK(cs_wallet); @@ -689,11 +705,10 @@ 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 MINE_NO; } int64_t CWallet::GetDebit(const CTxIn &txin) const @@ -1051,7 +1066,7 @@ int64_t CWallet::GetImmatureBalance() const return nTotal; } -// populate vCoins with vector of spendable COutputs +// populate vCoins with vector of available COutputs. void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { vCoins.clear(); @@ -1077,10 +1092,11 @@ void CWallet::AvailableCoins(vector& 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 != MINE_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 & MINE_SPENDABLE)); } } } @@ -1147,8 +1163,11 @@ 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)) -- cgit v1.2.3 From 2935b211033610d7ef0deef9bf1b344a5bac029f Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 10 Dec 2013 15:27:53 +0100 Subject: qt: Hide unspendable outputs in coin control --- src/wallet.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 3d679f4c5..40ace9c40 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1259,6 +1259,8 @@ bool CWallet::SelectCoins(int64_t nTargetValue, setvout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } -- cgit v1.2.3 From ffd40da361639faeef405c7e4a504a340d77aa5b Mon Sep 17 00:00:00 2001 From: JaSK Date: Sat, 29 Mar 2014 05:15:28 +0100 Subject: Watchonly balances are shown separately in gui. --- src/wallet.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 4 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 40ace9c40..d4e9fe9d1 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -738,7 +738,7 @@ bool CWallet::IsChange(const CTxOut& txout) const // a better way of identifying which outputs are 'the send' and which are // 'the change' will need to be implemented (maybe extend CWalletTx to remember // which output, if any, was change). - if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address)) + if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address) == MINE_SPENDABLE) { LOCK(cs_wallet); if (!mapAddressBook.count(address)) @@ -793,7 +793,7 @@ int CWalletTx::GetRequestCount() const } void CWalletTx::GetAmounts(list >& listReceived, - list >& listSent, int64_t& nFee, string& strSentAccount) const + list >& listSent, int64_t& nFee, string& strSentAccount, const isminefilter& filter) const { nFee = 0; listReceived.clear(); @@ -820,9 +820,9 @@ void CWalletTx::GetAmounts(list >& listReceived, // Don't report 'change' txouts if (pwallet->IsChange(txout)) continue; - fIsMine = pwallet->IsMine(txout); + fIsMine = (pwallet->IsMine(txout) & filter); } - else if (!(fIsMine = pwallet->IsMine(txout))) + else if (!(fIsMine = (pwallet->IsMine(txout) & filter))) continue; // In either case, we need to get the destination address @@ -1066,6 +1066,51 @@ int64_t CWallet::GetImmatureBalance() const return nTotal; } +int64_t CWallet::GetWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + + return nTotal; +} + +int64_t CWallet::GetUnconfirmedWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!IsFinalTx(*pcoin) || (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0)) + nTotal += pcoin->GetAvailableWatchOnlyCredit(); + } + } + return nTotal; +} + +int64_t CWallet::GetImmatureWatchOnlyBalance() const +{ + int64_t nTotal = 0; + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + nTotal += pcoin->GetImmatureWatchOnlyCredit(); + } + } + return nTotal; +} + // populate vCoins with vector of available COutputs. void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { -- cgit v1.2.3 From d4640d7d8c3ba373195d33ab75db9c8cb43f8806 Mon Sep 17 00:00:00 2001 From: JaSK Date: Tue, 8 Apr 2014 15:23:50 +0200 Subject: Added argument to getbalance to include watchonly addresses and fixed errors in balance calculation. --- src/wallet.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index d4e9fe9d1..3c9aa3306 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -711,7 +711,7 @@ isminetype CWallet::IsMine(const CTxIn &txin) const return MINE_NO; } -int64_t CWallet::GetDebit(const CTxIn &txin) const +int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const { { LOCK(cs_wallet); @@ -720,7 +720,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; } } @@ -801,7 +801,7 @@ void CWalletTx::GetAmounts(list >& 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(); @@ -846,7 +846,7 @@ void CWalletTx::GetAmounts(list >& 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; @@ -854,7 +854,7 @@ void CWalletTx::GetAccountAmounts(const string& strAccount, int64_t& nReceived, string strSentAccount; list > listReceived; list > listSent; - GetAmounts(listReceived, listSent, allFee, strSentAccount); + GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); if (strAccount == strSentAccount) { -- cgit v1.2.3 From a5c6c5d6df887764ac07664aae842234c19bbf8d Mon Sep 17 00:00:00 2001 From: JaSK Date: Tue, 29 Apr 2014 19:39:01 +0200 Subject: fixed tiny glitch and improved readability like laanwj suggested --- src/wallet.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 3c9aa3306..4b480321a 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -811,7 +811,8 @@ void CWalletTx::GetAmounts(list >& 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) @@ -820,9 +821,8 @@ void CWalletTx::GetAmounts(list >& listReceived, // Don't report 'change' txouts if (pwallet->IsChange(txout)) continue; - fIsMine = (pwallet->IsMine(txout) & filter); } - else if (!(fIsMine = (pwallet->IsMine(txout) & filter))) + else if (!(fIsMine & filter)) continue; // In either case, we need to get the destination address -- cgit v1.2.3 From d5087d1ba08142bdf135333a0da08ef0f5fc7ef0 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Mon, 9 Jun 2014 21:11:59 +0200 Subject: Use script matching rather than destination matching for watch-only. This changes the keystore data format, wallet format and IsMine logic to detect watch-only outputs based on direct script matching rather than first trying to convert outputs to destinations (addresses). The reason is that we don't know how the software that has the spending keys works. It may support the same types of scripts as us, but that is not guaranteed. Furthermore, it removes the ambiguity between addresses used as identifiers for output scripts or identifiers for public keys. One practical implication is that adding a normal pay-to-pubkey-hash address via importaddress will not cause payments to the corresponding full public key to be detected as IsMine. If that is wanted, add those scripts directly (importaddress now also accepts any hex-encoded script). Conflicts: src/wallet.cpp --- src/wallet.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 4b480321a..445d98b90 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -145,7 +145,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript) return CCryptoKeyStore::AddCScript(redeemScript); } -bool CWallet::AddWatchOnly(const CTxDestination &dest) +bool CWallet::AddWatchOnly(const CScript &dest) { if (!CCryptoKeyStore::AddWatchOnly(dest)) return false; @@ -155,9 +155,8 @@ bool CWallet::AddWatchOnly(const CTxDestination &dest) return CWalletDB(strWalletFile).WriteWatchOnly(dest); } -bool CWallet::LoadWatchOnly(const CTxDestination &dest) +bool CWallet::LoadWatchOnly(const CScript &dest) { - LogPrintf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str()); return CCryptoKeyStore::AddWatchOnly(dest); } @@ -729,17 +728,19 @@ int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) 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) == MINE_SPENDABLE) + if (::IsMine(*this, txout.scriptPubKey)) { + CTxDestination address; + if (!ExtractDestination(txout.scriptPubKey, address)) + return true; + LOCK(cs_wallet); if (!mapAddressBook.count(address)) return true; -- cgit v1.2.3 From 80dda36a07d09f99be861fa5271d0da5bd4f07dc Mon Sep 17 00:00:00 2001 From: JaSK Date: Thu, 19 Jun 2014 15:24:17 +0200 Subject: removed default argument values for ismine filter --- src/wallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 445d98b90..e996fca3d 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1216,7 +1216,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT const CWalletTx *pcoin = output.tx; - if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs)) + if (output.nDepth < (pcoin->IsFromMe(MINE_SPENDABLE|MINE_WATCH_ONLY) ? nConfMine : nConfTheirs)) continue; int i = output.i; @@ -1845,7 +1845,7 @@ std::map CWallet::GetAddressBalances() continue; int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe() ? 0 : 1)) + if (nDepth < (pcoin->IsFromMe(MINE_SPENDABLE|MINE_WATCH_ONLY) ? 0 : 1)) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) -- cgit v1.2.3 From 519dd1c89afa2b7d0f2720eb70cd11de23d61006 Mon Sep 17 00:00:00 2001 From: JaSK Date: Fri, 20 Jun 2014 05:02:14 +0200 Subject: Added MINE_ALL = (spendable|watchonly) --- src/wallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index e996fca3d..2823e7fa3 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1216,7 +1216,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT const CWalletTx *pcoin = output.tx; - if (output.nDepth < (pcoin->IsFromMe(MINE_SPENDABLE|MINE_WATCH_ONLY) ? nConfMine : nConfTheirs)) + if (output.nDepth < (pcoin->IsFromMe(MINE_ALL) ? nConfMine : nConfTheirs)) continue; int i = output.i; @@ -1845,7 +1845,7 @@ std::map CWallet::GetAddressBalances() continue; int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe(MINE_SPENDABLE|MINE_WATCH_ONLY) ? 0 : 1)) + if (nDepth < (pcoin->IsFromMe(MINE_ALL) ? 0 : 1)) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) -- cgit v1.2.3 From a3e192a3274817517671f624d5744297905e20d2 Mon Sep 17 00:00:00 2001 From: JaSK Date: Tue, 1 Jul 2014 11:00:22 +0200 Subject: replaced MINE_ with ISMINE_ --- src/wallet.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/wallet.cpp') diff --git a/src/wallet.cpp b/src/wallet.cpp index 2823e7fa3..84b50c0f8 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -707,7 +707,7 @@ isminetype CWallet::IsMine(const CTxIn &txin) const return IsMine(prev.vout[txin.prevout.n]); } } - return MINE_NO; + return ISMINE_NO; } int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const @@ -1139,10 +1139,10 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const for (unsigned int i = 0; i < pcoin->vout.size(); i++) { isminetype mine = IsMine(pcoin->vout[i]); - if (!(IsSpent(wtxid, i)) && mine != MINE_NO && + 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, mine & MINE_SPENDABLE)); + vCoins.push_back(COutput(pcoin, i, nDepth, mine & ISMINE_SPENDABLE)); } } } @@ -1216,7 +1216,7 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT const CWalletTx *pcoin = output.tx; - if (output.nDepth < (pcoin->IsFromMe(MINE_ALL) ? nConfMine : nConfTheirs)) + if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) continue; int i = output.i; @@ -1845,7 +1845,7 @@ std::map CWallet::GetAddressBalances() continue; int nDepth = pcoin->GetDepthInMainChain(); - if (nDepth < (pcoin->IsFromMe(MINE_ALL) ? 0 : 1)) + if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) -- cgit v1.2.3