From: CryptoManiac Date: Fri, 25 Jul 2014 17:51:11 +0000 (+0400) Subject: Bugfix: Unspendable inputs handling X-Git-Tag: v0.4.4.6-nvc-update6^2~3 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=1c4fc9052a444c114d9c1501d2c6d1305de650d0 Bugfix: Unspendable inputs handling * Use script matching instead of destination matching to prevent possible compatibility issues; * Split GetBalance, GetAvailableCredit and GetCredit to regular and watch-only counterparts. --- diff --git a/src/keystore.cpp b/src/keystore.cpp index 089f2d5..b4783ec 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -63,20 +63,23 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) return false; } -bool CBasicKeyStore::AddWatchOnly(const CTxDestination &dest) +bool CBasicKeyStore::AddWatchOnly(const CScript &dest) { LOCK(cs_KeyStore); - CKeyID keyID; - CBitcoinAddress(dest).GetKeyID(keyID); - if (HaveKey(keyID)) - return false; + CTxDestination address; + if (ExtractDestination(dest, address)) { + CKeyID keyID; + CBitcoinAddress(address).GetKeyID(keyID); + if (HaveKey(keyID)) + return false; + } setWatchOnly.insert(dest); return true; } -bool CBasicKeyStore::HaveWatchOnly(const CTxDestination &dest) const +bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const { LOCK(cs_KeyStore); return setWatchOnly.count(dest) > 0; @@ -145,8 +148,10 @@ bool CCryptoKeyStore::AddKey(const CKey& key) { LOCK(cs_KeyStore); - CTxDestination address = key.GetPubKey().GetID(); - if (HaveWatchOnly(address)) + CScript script; + script.SetDestination(key.GetPubKey().GetID()); + + if (HaveWatchOnly(script)) return false; if (!IsCrypted()) diff --git a/src/keystore.h b/src/keystore.h index 9e5674f..34d94ef 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -51,8 +51,8 @@ public: virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0; // Support for Watch-only addresses - virtual bool AddWatchOnly(const CTxDestination &dest) =0; - virtual bool HaveWatchOnly(const CTxDestination &dest) const =0; + virtual bool AddWatchOnly(const CScript &dest) =0; + virtual bool HaveWatchOnly(const CScript &dest) const =0; virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const { @@ -66,7 +66,7 @@ public: typedef std::map > KeyMap; typedef std::map ScriptMap; -typedef std::set WatchOnlySet; +typedef std::set WatchOnlySet; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore @@ -118,8 +118,8 @@ public: virtual bool HaveCScript(const CScriptID &hash) const; virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const; - virtual bool AddWatchOnly(const CTxDestination &dest); - virtual bool HaveWatchOnly(const CTxDestination &dest) const; + virtual bool AddWatchOnly(const CScript &dest); + virtual bool HaveWatchOnly(const CScript &dest) const; }; typedef std::map > > CryptedKeyMap; diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index d2cd9ce..898f940 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -179,9 +179,7 @@ void OverviewPage::setModel(WalletModel *model) ui->listTransactions->setModelColumn(TransactionTableModel::ToAddress); // Keep up to date with wallet - qint64 nTotal=0, nWatchOnly=0; - model->getBalance(nTotal, nWatchOnly); - setBalance(nTotal, nWatchOnly, model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance()); + setBalance(model->getBalance(), model->getBalanceWatchOnly(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance()); connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64))); setNumTransactions(model->getNumTransactions()); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 34ad759..71ac191 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -93,9 +93,7 @@ void SendCoinsDialog::setModel(WalletModel *model) } if(model && model->getOptionsModel()) { - qint64 nTotal=0, nWatchOnly=0; - model->getBalance(nTotal, nWatchOnly); - setBalance(nTotal, nWatchOnly, model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance()); + setBalance(model->getBalance(), model->getBalanceWatchOnly(), model->getStake(), model->getUnconfirmedBalance(), model->getImmatureBalance()); connect(model, SIGNAL(balanceChanged(qint64, qint64, qint64, qint64, qint64)), this, SLOT(setBalance(qint64, qint64, qint64, qint64, qint64))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); @@ -377,9 +375,7 @@ void SendCoinsDialog::updateDisplayUnit() if(model && model->getOptionsModel()) { // Update labelBalance with the current balance and the current unit - qint64 total=0, watchOnly=0; - model->getBalance(total, watchOnly); - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), total - watchOnly)); + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), model->getBalance() - model->getBalanceWatchOnly())); } } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 4598e6e..8f1cc0f 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -40,7 +40,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += ""; int64 nTime = wtx.GetTxTime(); - int64 nCredit = wtx.GetCredit(false); + int64 nCredit = wtx.GetCredit(); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; @@ -60,7 +60,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // // From // - if (wtx.IsCoinBase()) + if (wtx.IsCoinBase() || wtx.IsCoinStake()) { strHTML += "" + tr("Source") + ": " + tr("Generated") + "
"; } @@ -124,7 +124,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) // int64 nUnmatured = 0; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - nUnmatured += wallet->GetCredit(txout, false); + nUnmatured += wallet->GetCredit(txout); strHTML += "" + tr("Credit") + ": "; if (wtx.IsInMainChain()) strHTML += BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, nUnmatured)+ " (" + tr("matures in %n more block(s)", "", wtx.GetBlocksToMaturity()) + ")"; @@ -199,7 +199,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if (wallet->IsMine(txout)) - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout, false)) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "
"; } } @@ -229,7 +229,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx) strHTML += "" + tr("Debit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, -wallet->GetDebit(txin)) + "
"; BOOST_FOREACH(const CTxOut& txout, wtx.vout) if(wallet->IsMine(txout)) - strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout, false)) + "
"; + strHTML += "" + tr("Credit") + ": " + BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, wallet->GetCredit(txout)) + "
"; strHTML += "
" + tr("Transaction") + ":
"; strHTML += GUIUtil::HtmlEscape(wtx.ToString(), true); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 20c263e..8962d18 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -25,7 +25,7 @@ QList TransactionRecord::decomposeTransaction(const CWallet * { QList parts; int64 nTime = wtx.GetTxTime(); - int64 nCredit = wtx.GetCredit(false,true); + int64 nCredit = wtx.GetCredit(true); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; uint256 hash = wtx.GetHash(), hashPrev = 0; @@ -205,7 +205,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) // For generated transactions, determine maturity if(type == TransactionRecord::Generated) { - int64 nCredit = wtx.GetCredit(false,true); + int64 nCredit = wtx.GetCredit(true); if (nCredit == 0) { status.maturity = TransactionStatus::Immature; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index b14388a..ee8affe 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -41,9 +41,9 @@ qint64 WalletModel::getBalance() const return wallet->GetBalance(); } -void WalletModel::getBalance(qint64 &nTotal, qint64 &nWatchOnly) const +qint64 WalletModel::getBalanceWatchOnly() const { - wallet->GetBalance(nTotal, nWatchOnly); + return wallet->GetBalanceWatchOnly(); } qint64 WalletModel::getUnconfirmedBalance() const @@ -91,9 +91,7 @@ void WalletModel::pollBalanceChanged() void WalletModel::checkBalanceChanged() { - qint64 newBalanceTotal=0, newBalanceWatchOnly=0; - getBalance(newBalanceTotal, newBalanceWatchOnly); - + qint64 newBalanceTotal=getBalance(), newBalanceWatchOnly=getBalanceWatchOnly(); qint64 newStake = getStake(); qint64 newUnconfirmedBalance = getUnconfirmedBalance(); qint64 newImmatureBalance = getImmatureBalance(); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 84be464..f155006 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -64,7 +64,7 @@ public: TransactionTableModel *getTransactionTableModel(); qint64 getBalance() const; - void getBalance(qint64 &nTotal, qint64 &nWatchOnly) const; + qint64 getBalanceWatchOnly() const; qint64 getStake() const; qint64 getUnconfirmedBalance() const; qint64 getImmatureBalance() const; diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index fe9df92..138fc00 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -76,13 +76,18 @@ Value importaddress(const Array& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 3) throw runtime_error( "importaddress
[label] [rescan=true]\n" - "Adds an address that can be watched as if it were in your wallet but cannot be used to spend."); + "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()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - CTxDestination dest; - dest = address.Get(); + if (address.IsValid()) { + script.SetDestination(address.Get()); + } else if (IsHex(params[0].get_str())) { + std::vector data(ParseHex(params[0].get_str())); + script = CScript(data.begin(), data.end()); + } else { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Novacoin address or script"); + } string strLabel = ""; if (params.size() > 1) @@ -97,13 +102,15 @@ Value importaddress(const Array& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); // Don't throw error in case an address is already there - if (pwalletMain->HaveWatchOnly(dest)) + if (pwalletMain->HaveWatchOnly(script)) return Value::null; pwalletMain->MarkDirty(); - pwalletMain->SetAddressBookName(dest, strLabel); - if (!pwalletMain->AddWatchOnly(dest)) + if (address.IsValid()) + pwalletMain->SetAddressBookName(address.Get(), strLabel); + + if (!pwalletMain->AddWatchOnly(script)) throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); if (fRescan) diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 5699c4f..3f03145 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -69,15 +69,12 @@ Value getinfo(const Array& params, bool fHelp) proxyType proxy; GetProxy(NET_IPV4, proxy); - int64 nTotal = 0, nWatchOnly = 0; - pwalletMain->GetBalance(nTotal, nWatchOnly); - Object obj, diff; obj.push_back(Pair("version", FormatFullVersion())); obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION)); obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(nTotal))); - obj.push_back(Pair("unspendable", ValueFromAmount(nWatchOnly))); + obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); + obj.push_back(Pair("unspendable", ValueFromAmount(pwalletMain->GetBalanceWatchOnly()))); obj.push_back(Pair("newmint", ValueFromAmount(pwalletMain->GetNewMint()))); obj.push_back(Pair("stake", ValueFromAmount(pwalletMain->GetStake()))); obj.push_back(Pair("blocks", (int)nBestHeight)); @@ -726,9 +723,7 @@ Value sendmany(const Array& params, bool fHelp) bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired); if (!fCreated) { - int64 nTotal = 0, nWatchOnly = 0; - pwalletMain->GetBalance(nTotal, nWatchOnly); - + int64 nTotal = pwalletMain->GetBalance(), nWatchOnly = pwalletMain->GetBalanceWatchOnly(); if (totalAmount + nFeeRequired > nTotal - nWatchOnly) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); throw JSONRPCError(RPC_WALLET_ERROR, "Transaction creation failed"); @@ -1265,7 +1260,7 @@ Value gettransaction(const Array& params, bool fHelp) TxToJSON(wtx, 0, entry); - int64 nCredit = wtx.GetCredit(false); + int64 nCredit = wtx.GetCredit(); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; int64 nFee = (wtx.IsFromMe() ? wtx.GetValueOut() - nDebit : 0); diff --git a/src/script.cpp b/src/script.cpp index e9cf81b..ea44c63 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1435,13 +1435,11 @@ public: bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); } }; -isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest) +isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest) { - if (boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest)) - return MINE_SPENDABLE; - if (keystore.HaveWatchOnly(dest)) - return MINE_WATCH_ONLY; - return MINE_NO; + CScript script; + script.SetDestination(dest); + return IsMine(keystore, script); } isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) @@ -1449,7 +1447,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) vector vSolutions; txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) { - if (keystore.HaveWatchOnly(scriptPubKey.GetID())) + if (keystore.HaveWatchOnly(scriptPubKey)) return MINE_WATCH_ONLY; return MINE_NO; } @@ -1464,15 +1462,11 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) keyID = CPubKey(vSolutions[0]).GetID(); if (keystore.HaveKey(keyID)) return MINE_SPENDABLE; - if (keystore.HaveWatchOnly(keyID)) - return MINE_WATCH_ONLY; break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); if (keystore.HaveKey(keyID)) return MINE_SPENDABLE; - if (keystore.HaveWatchOnly(keyID)) - return MINE_WATCH_ONLY; break; case TX_SCRIPTHASH: { @@ -1480,11 +1474,9 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { isminetype ret = IsMine(keystore, subscript); - if (ret) + if (ret == MINE_SPENDABLE) return ret; } - if (keystore.HaveWatchOnly(scriptID)) - return MINE_WATCH_ONLY; break; } case TX_MULTISIG: @@ -1501,7 +1493,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) } } - if (keystore.HaveWatchOnly(scriptPubKey.GetID())) + if (keystore.HaveWatchOnly(scriptPubKey)) return MINE_WATCH_ONLY; return MINE_NO; } diff --git a/src/script.h b/src/script.h index 4492f2a..b8cfbac 100644 --- a/src/script.h +++ b/src/script.h @@ -623,7 +623,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutions); bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); -isminetype IsMine(const CKeyStore& keystore, const CTxDestination &dest); +isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest); void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector &vKeys); bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector& addressRet, int& nRequiredRet); diff --git a/src/wallet.cpp b/src/wallet.cpp index fb12cb0..96e84b7 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -102,7 +102,7 @@ bool CWallet::AddCScript(const CScript& redeemScript) return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); } -bool CWallet::AddWatchOnly(const CTxDestination &dest) +bool CWallet::AddWatchOnly(const CScript &dest) { if (!CCryptoKeyStore::AddWatchOnly(dest)) return false; @@ -112,9 +112,8 @@ bool CWallet::AddWatchOnly(const CTxDestination &dest) return CWalletDB(strWalletFile).WriteWatchOnly(dest); } -bool CWallet::LoadWatchOnly(const CTxDestination &dest) +bool CWallet::LoadWatchOnly(const CScript &dest) { - printf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str()); return CCryptoKeyStore::AddWatchOnly(dest); } @@ -621,17 +620,19 @@ int64 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 isn't 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; @@ -1034,26 +1035,27 @@ int64 CWallet::GetBalance() const { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsTrusted()) - nTotal += pcoin->GetAvailableCredit(false); + nTotal += pcoin->GetAvailableCredit(); } } return nTotal; } -void CWallet::GetBalance(int64 &nTotal, int64 &nWatchOnly) const +int64 CWallet::GetBalanceWatchOnly() const { + int64 nTotal = 0; { LOCK(cs_wallet); for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; - if (pcoin->IsTrusted()) { - nWatchOnly += pcoin->GetAvailableCredit(true); - nTotal += pcoin->GetAvailableCredit(false); - } + if (pcoin->IsTrusted()) + nTotal += pcoin->GetAvailableCreditWatchOnly(); } } + + return nTotal; } int64 CWallet::GetUnconfirmedBalance() const diff --git a/src/wallet.h b/src/wallet.h index cb07d39..a2f9fca 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -33,7 +33,6 @@ enum WalletFeature FEATURE_WALLETCRYPT = 40000, // wallet encryption FEATURE_COMPRPUBKEY = 60000, // compressed public keys - FEATURE_LATEST = 60000 }; @@ -150,9 +149,9 @@ public: bool LoadCScript(const CScript& redeemScript) { return CCryptoKeyStore::AddCScript(redeemScript); } // Adds a watch-only address to the store, and saves it to disk. - bool AddWatchOnly(const CTxDestination &dest); + bool AddWatchOnly(const CScript &dest); // Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) - bool LoadWatchOnly(const CTxDestination &dest); + bool LoadWatchOnly(const CScript &dest); bool Unlock(const SecureString& strWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); @@ -185,7 +184,7 @@ public: void ReacceptWalletTransactions(); void ResendWalletTransactions(); int64 GetBalance() const; - void GetBalance(int64 &nTotal, int64 &nWatchOnly) const; + int64 GetBalanceWatchOnly() const; int64 GetUnconfirmedBalance() const; int64 GetImmatureBalance() const; int64 GetStake() const; @@ -220,12 +219,19 @@ public: { return ::IsMine(*this, txout.scriptPubKey); } - int64 GetCredit(const CTxOut& txout, bool fWatchOnly=false) const + int64 GetCredit(const CTxOut& txout) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); isminetype ismine = IsMine(txout); - if (fWatchOnly && ismine != MINE_WATCH_ONLY) + return (ismine != MINE_NO ? txout.nValue : 0); + } + int64 GetCreditWatchOnly(const CTxOut& txout) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetCreditWatchOnly() : value out of range"); + isminetype ismine = IsMine(txout); + if (ismine != MINE_WATCH_ONLY) return 0; return (ismine != MINE_NO ? txout.nValue : 0); } @@ -258,14 +264,23 @@ public: } return nDebit; } - int64 GetCredit(const CTransaction& tx, bool fWatchOnly=true) const + int64 GetCredit(const CTransaction& tx) const { int64 nCredit = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!fWatchOnly || (fWatchOnly && IsMine(txout) == MINE_WATCH_ONLY)) - nCredit += GetCredit(txout); - + nCredit += GetCredit(txout); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWallet::GetCredit() : value out of range"); + } + return nCredit; + } + int64 GetCreditWatchOnly(const CTransaction& tx) const + { + int64 nCredit = 0; + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + nCredit += GetCreditWatchOnly(txout); if (!MoneyRange(nCredit)) throw std::runtime_error("CWallet::GetCredit() : value out of range"); } @@ -601,7 +616,7 @@ public: return nDebitCached; } - int64 GetCredit(bool fWatchOnly, bool fUseCache=true) const + int64 GetCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) @@ -609,35 +624,23 @@ public: // GetBalance can assume transactions in mapWallet won't change if (fUseCache) { - if (fWatchOnly && fCreditCached) - return nWatchOnlyCreditCached; if (fCreditCached) return nCreditCached; } - if (fWatchOnly) { - nWatchOnlyCreditCached = pwallet->GetCredit(*this, true); - fWatchOnlyCreditCached = true; - - return nWatchOnlyCreditCached; - } - nCreditCached = pwallet->GetCredit(*this); fCreditCached = true; return nCreditCached; } - int64 GetAvailableCredit(bool fWatchOnly, bool fUseCache=true) const + int64 GetAvailableCredit(bool fUseCache=true) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) return 0; if (fUseCache) { - if (fWatchOnly && fAvailableWatchOnlyCreditCached) - return nAvailableWatchOnlyCreditCached; - if (fAvailableCreditCached) return nAvailableCreditCached; } @@ -648,19 +651,62 @@ public: if (!IsSpent(i)) { const CTxOut &txout = vout[i]; - nCredit += pwallet->GetCredit(txout, fWatchOnly); + nCredit += pwallet->GetCredit(txout); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } - if (fWatchOnly) { - nAvailableWatchOnlyCreditCached = nCredit; - fAvailableWatchOnlyCreditCached = true; - } else { - nAvailableCreditCached = nCredit; - fAvailableCreditCached = true; + nAvailableCreditCached = nCredit; + fAvailableCreditCached = true; + + return nCredit; + } + + int64 GetCreditWatchOnly(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) + return 0; + + // GetBalance can assume transactions in mapWallet won't change + if (fUseCache) { + if (fWatchOnlyCreditCached) + return nWatchOnlyCreditCached; + } + + nWatchOnlyCreditCached = pwallet->GetCreditWatchOnly(*this); + fWatchOnlyCreditCached = true; + + return nWatchOnlyCreditCached; + } + + int64 GetAvailableCreditWatchOnly(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if ((IsCoinBase() || IsCoinStake()) && GetBlocksToMaturity() > 0) + return 0; + + if (fUseCache) { + if (fAvailableWatchOnlyCreditCached) + return nAvailableWatchOnlyCreditCached; + } + + int64 nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetCreditWatchOnly(txout); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetAvailableCreditWatchOnly() : value out of range"); + } } + + nAvailableWatchOnlyCreditCached = nCredit; + fAvailableWatchOnlyCreditCached = true; + return nCredit; } diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 1519ff3..32dc595 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -287,14 +287,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, wss.fAnyUnordered = true; } } - else if (strType == "watch") + else if (strType == "watchs") { - std::string strAddress; - ssKey >> strAddress; + CScript script; + ssKey >> script; char fYes; ssValue >> fYes; if (fYes == '1') - pwallet->LoadWatchOnly(CBitcoinAddress(strAddress).Get()); + pwallet->LoadWatchOnly(script); // Watch-only addresses have no birthday information for now, // so set the wallet birthday to the beginning of time. diff --git a/src/walletdb.h b/src/walletdb.h index e9d6404..31d6463 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -123,10 +123,10 @@ public: return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); } - bool WriteWatchOnly(const CTxDestination &dest) + bool WriteWatchOnly(const CScript &dest) { nWalletDBUpdated++; - return Write(std::make_pair(std::string("watch"), CBitcoinAddress(dest).ToString()), '1'); + return Write(std::make_pair(std::string("watchs"), dest), '1'); } bool WriteBestBlock(const CBlockLocator& locator)