From 580fa137c61abe24c56b440e62fa21657227e9a2 Mon Sep 17 00:00:00 2001 From: CryptoManiac Date: Sat, 19 Jul 2014 05:05:51 +0400 Subject: [PATCH] Merge support for watch-only addresses Based on a patch by Eric Lombrozo. --- src/bitcoinrpc.cpp | 4 +- src/bitcoinrpc.h | 3 +- src/keystore.cpp | 13 +++++ src/keystore.h | 25 ++++++++++ src/qt/walletmodel.cpp | 12 +++-- src/rpcdump.cpp | 45 +++++++++++++++++ src/rpcrawtransaction.cpp | 1 + src/rpcwallet.cpp | 117 ++++++++++++--------------------------------- src/script.cpp | 52 +++++++++++++++----- src/script.h | 27 ++++------- src/wallet.cpp | 55 ++++++++++++++++----- src/wallet.h | 16 ++++-- src/walletdb.cpp | 13 +++++ src/walletdb.h | 7 +++ 14 files changed, 246 insertions(+), 144 deletions(-) diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index b9318f9..b8275cc 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -248,7 +248,6 @@ static const CRPCCommand vRPCCommands[] = { "getsubsidy", &getsubsidy, true, false }, { "getmininginfo", &getmininginfo, true, false }, { "getnewaddress", &getnewaddress, true, false }, - { "getnewpubkey", &getnewpubkey, true, false }, { "getaccountaddress", &getaccountaddress, true, false }, { "setaccount", &setaccount, true, false }, { "getaccount", &getaccount, false, false }, @@ -265,7 +264,6 @@ static const CRPCCommand vRPCCommands[] = { "walletlock", &walletlock, true, false }, { "encryptwallet", &encryptwallet, false, false }, { "validateaddress", &validateaddress, true, false }, - { "validatepubkey", &validatepubkey, true, false }, { "getbalance", &getbalance, false, false }, { "move", &movecmd, false, false }, { "sendfrom", &sendfrom, false, false }, @@ -292,6 +290,7 @@ static const CRPCCommand vRPCCommands[] = { "dumpwallet", &dumpwallet, true, false }, { "importwallet", &importwallet, false, false }, { "importprivkey", &importprivkey, false, false }, + { "importaddress", &importaddress, false, true }, { "listunspent", &listunspent, false, false }, { "getrawtransaction", &getrawtransaction, false, false }, { "createrawtransaction", &createrawtransaction, false, false }, @@ -1229,6 +1228,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 1) ConvertTo(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); if (strMethod == "keypoolrefill" && n > 0) ConvertTo(params[0]); + if (strMethod == "importaddress" && n > 2) ConvertTo(params[2]); return params; } diff --git a/src/bitcoinrpc.h b/src/bitcoinrpc.h index afb41f7..78d4608 100644 --- a/src/bitcoinrpc.h +++ b/src/bitcoinrpc.h @@ -150,6 +150,7 @@ extern json_spirit::Value dumpwallet(const json_spirit::Array& params, bool fHel extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp); 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 sendalert(const json_spirit::Array& params, bool fHelp); @@ -196,8 +197,6 @@ extern json_spirit::Value checkwallet(const json_spirit::Array& params, bool fHe extern json_spirit::Value repairwallet(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value resendtx(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value makekeypair(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value validatepubkey(const json_spirit::Array& params, bool fHelp); -extern json_spirit::Value getnewpubkey(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value getrawtransaction(const json_spirit::Array& params, bool fHelp); // in rcprawtransaction.cpp extern json_spirit::Value listunspent(const json_spirit::Array& params, bool fHelp); diff --git a/src/keystore.cpp b/src/keystore.cpp index 18a3942..564c49c 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -62,6 +62,19 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut) return false; } +bool CBasicKeyStore::AddWatchOnly(const CTxDestination &dest) +{ + LOCK(cs_KeyStore); + setWatchOnly.insert(dest); + return true; +} + +bool CBasicKeyStore::HaveWatchOnly(const CTxDestination &dest) const +{ + LOCK(cs_KeyStore); + return setWatchOnly.count(dest) > 0; +} + bool CCryptoKeyStore::SetCrypted() { { diff --git a/src/keystore.h b/src/keystore.h index ab369bb..9e5674f 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -8,9 +8,25 @@ #include "crypter.h" #include "sync.h" #include +#include class CScript; +class CNoDestination { +public: + friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } + friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } +}; + +/** 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 CTxDestination; + /** A virtual base class for key stores */ class CKeyStore { @@ -34,6 +50,10 @@ public: 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 CTxDestination &dest) =0; + virtual bool HaveWatchOnly(const CTxDestination &dest) const =0; + virtual bool GetSecret(const CKeyID &address, CSecret& vchSecret, bool &fCompressed) const { CKey key; @@ -46,6 +66,7 @@ public: typedef std::map > KeyMap; typedef std::map ScriptMap; +typedef std::set WatchOnlySet; /** Basic key store, that keeps keys in an address->secret map */ class CBasicKeyStore : public CKeyStore @@ -53,6 +74,7 @@ class CBasicKeyStore : public CKeyStore protected: KeyMap mapKeys; ScriptMap mapScripts; + WatchOnlySet setWatchOnly; public: bool AddKey(const CKey& key); @@ -95,6 +117,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 CTxDestination &dest); + virtual bool HaveWatchOnly(const CTxDestination &dest) const; }; typedef std::map > > CryptedKeyMap; diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d82e790..cafe9fc 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -166,7 +166,8 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QListAvailableCoins(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; if(total > nBalance) { @@ -427,7 +428,7 @@ void WalletModel::getOutputs(const std::vector& vOutpoints, std::vect BOOST_FOREACH(const COutPoint& outpoint, vOutpoints) { if (!wallet->mapWallet.count(outpoint.hash)) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain(), true); vOutputs.push_back(out); } } @@ -443,7 +444,7 @@ void WalletModel::listCoins(std::map >& mapCoins) BOOST_FOREACH(const COutPoint& outpoint, vLockedCoins) { if (!wallet->mapWallet.count(outpoint.hash)) continue; - COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain()); + COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, wallet->mapWallet[outpoint.hash].GetDepthInMainChain(), true); vCoins.push_back(out); } @@ -454,11 +455,12 @@ void WalletModel::listCoins(std::map >& 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); } } diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp index 1e31e73..fe9df92 100644 --- a/src/rpcdump.cpp +++ b/src/rpcdump.cpp @@ -71,6 +71,51 @@ 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
[label] [rescan=true]\n" + "Adds an address that can be watched as if it were in your wallet but cannot be used to spend."); + + CBitcoinAddress address(params[0].get_str()); + if (!address.IsValid()) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CTxDestination dest; + dest = address.Get(); + + 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); + + // Don't throw error in case an address is already there + if (pwalletMain->HaveWatchOnly(dest)) + return Value::null; + + pwalletMain->MarkDirty(); + pwalletMain->SetAddressBookName(dest, strLabel); + + if (!pwalletMain->AddWatchOnly(dest)) + throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); + + if (fRescan) + { + pwalletMain->ScanForWalletTransactions(pindexGenesisBlock, true); + pwalletMain->ReacceptWalletTransactions(); + } + } + + return Value::null; +} + Value importwallet(const Array& params, bool fHelp) { if (fHelp || params.size() != 1) diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 94db4ad..9b8eeab 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -217,6 +217,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); } diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index aa1747a..d64a4f8 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -98,35 +98,6 @@ Value getinfo(const Array& params, bool fHelp) return obj; } - -Value getnewpubkey(const Array& params, bool fHelp) -{ - if (fHelp || params.size() > 1) - throw runtime_error( - "getnewpubkey [account]\n" - "Returns new public key for coinbase generation."); - - // Parse the account first so we don't generate a key if there's an error - string strAccount; - if (params.size() > 0) - strAccount = AccountFromValue(params[0]); - - if (!pwalletMain->IsLocked()) - pwalletMain->TopUpKeyPool(); - - // Generate a new key that is added to wallet - CPubKey newKey; - if (!pwalletMain->GetKeyFromPool(newKey, false)) - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - CKeyID keyID = newKey.GetID(); - - pwalletMain->SetAddressBookName(keyID, strAccount); - vector vchPubKey = newKey.Raw(); - - return HexStr(vchPubKey.begin(), vchPubKey.end()); -} - - Value getnewaddress(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) @@ -1561,36 +1532,44 @@ Value encryptwallet(const Array& params, bool fHelp) class DescribeAddressVisitor : public boost::static_visitor { +private: + isminetype mine; public: - Object operator()(const CNoDestination &dest) const { return Object(); } + 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.Raw()))); - obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); + if (mine == MINE_SPENDABLE) { + pwalletMain->GetPubKey(keyID, vchPubKey); + obj.push_back(Pair("pubkey", HexStr(vchPubKey.Raw()))); + 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 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 == MINE_SPENDABLE) { + CScript subscript; + pwalletMain->GetCScript(scriptID, subscript); + std::vector 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; } }; @@ -1612,47 +1591,11 @@ Value validateaddress(const Array& params, bool fHelp) CTxDestination dest = address.Get(); string currentAddress = address.ToString(); ret.push_back(Pair("address", currentAddress)); - bool fMine = IsMine(*pwalletMain, dest); - ret.push_back(Pair("ismine", fMine)); - if (fMine) { - Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); - ret.insert(ret.end(), detail.begin(), detail.end()); - } - if (pwalletMain->mapAddressBook.count(dest)) - ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest])); - } - return ret; -} - -Value validatepubkey(const Array& params, bool fHelp) -{ - if (fHelp || !params.size() || params.size() > 2) - throw runtime_error( - "validatepubkey \n" - "Return information about ."); - - std::vector vchPubKey = ParseHex(params[0].get_str()); - CPubKey pubKey(vchPubKey); - - bool isValid = pubKey.IsValid(); - bool isCompressed = pubKey.IsCompressed(); - CKeyID keyID = pubKey.GetID(); - - CBitcoinAddress address; - address.Set(keyID); - - Object ret; - ret.push_back(Pair("isvalid", isValid)); - if (isValid) - { - CTxDestination dest = address.Get(); - string currentAddress = address.ToString(); - ret.push_back(Pair("address", currentAddress)); - bool fMine = IsMine(*pwalletMain, dest); - ret.push_back(Pair("ismine", fMine)); - ret.push_back(Pair("iscompressed", isCompressed)); - if (fMine) { - Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest); + isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO; + ret.push_back(Pair("ismine", mine != MINE_NO)); + if (mine != MINE_NO) { + ret.push_back(Pair("watchonly", mine == MINE_WATCH_ONLY)); + Object detail = boost::apply_visitor(DescribeAddressVisitor(mine), dest); ret.insert(ret.end(), detail.begin(), detail.end()); } if (pwalletMain->mapAddressBook.count(dest)) diff --git a/src/script.cpp b/src/script.cpp index c629438..e9cf81b 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1435,36 +1435,57 @@ 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); + if (boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest)) + return MINE_SPENDABLE; + if (keystore.HaveWatchOnly(dest)) + return MINE_WATCH_ONLY; + return MINE_NO; } -bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) +isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) { vector vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; + if (!Solver(scriptPubKey, whichType, vSolutions)) { + if (keystore.HaveWatchOnly(scriptPubKey.GetID())) + return MINE_WATCH_ONLY; + return MINE_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 MINE_SPENDABLE; + if (keystore.HaveWatchOnly(keyID)) + return MINE_WATCH_ONLY; + break; case TX_PUBKEYHASH: keyID = CKeyID(uint160(vSolutions[0])); - return keystore.HaveKey(keyID); + if (keystore.HaveKey(keyID)) + return MINE_SPENDABLE; + if (keystore.HaveWatchOnly(keyID)) + return MINE_WATCH_ONLY; + 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) + return ret; + } + if (keystore.HaveWatchOnly(scriptID)) + return MINE_WATCH_ONLY; + break; } case TX_MULTISIG: { @@ -1474,10 +1495,15 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) // them) enable spend-out-from-under-you attacks, especially // in shared-wallet situations. vector keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1); - return HaveKeys(keys, keystore) == keys.size(); + if (HaveKeys(keys, keystore) == keys.size()) + return MINE_SPENDABLE; + break; } } - return false; + + if (keystore.HaveWatchOnly(scriptPubKey.GetID())) + return MINE_WATCH_ONLY; + return MINE_NO; } bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) diff --git a/src/script.h b/src/script.h index 60ef0e4..4492f2a 100644 --- a/src/script.h +++ b/src/script.h @@ -9,7 +9,6 @@ #include #include -#include #include "keystore.h" #include "bignum.h" @@ -20,6 +19,14 @@ class CTransaction; static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes +/** IsMine() return codes */ +enum isminetype +{ + MINE_NO = 0, + MINE_WATCH_ONLY = 1, + MINE_SPENDABLE = 2, +}; + /** Signature hash types/flags */ enum { @@ -75,20 +82,6 @@ enum txnouttype TX_NULL_DATA, }; -class CNoDestination { -public: - friend bool operator==(const CNoDestination &a, const CNoDestination &b) { return true; } - friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } -}; - -/** 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 CTxDestination; - const char* GetTxnOutputType(txnouttype t); /** Script opcodes */ @@ -629,8 +622,8 @@ bool EvalScript(std::vector >& stack, const CScript& bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector >& 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 &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 ef77cd0..f53f344 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -102,6 +102,22 @@ bool CWallet::AddCScript(const CScript& redeemScript) return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), 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) +{ + printf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str()); + return CCryptoKeyStore::AddWatchOnly(dest); +} + // ppcoin: optional setting to unlock wallet for block minting only; // serves to disable the trivial sendmoney when OS account compromised bool fWalletUnlockMintOnly = false; @@ -572,7 +588,7 @@ bool CWallet::EraseFromWallet(uint256 hash) } -bool CWallet::IsMine(const CTxIn &txin) const +isminetype CWallet::IsMine(const CTxIn &txin) const { { LOCK(cs_wallet); @@ -581,11 +597,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 CWallet::GetDebit(const CTxIn &txin) const @@ -1079,11 +1094,15 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const if(pcoin->IsCoinStake() && pcoin->GetBlocksToMaturity() > 0) continue; - for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue && - (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) - vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); - + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + isminetype mine = IsMine(pcoin->vout[i]); + if (!(pcoin->IsSpent(i)) && mine != MINE_NO && + pcoin->vout[i].nValue >= nMinimumInputValue && + (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) + { + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE)); + } + } } } } @@ -1104,9 +1123,12 @@ void CWallet::AvailableCoinsMinConf(vector& vCoins, int nConf) const if(pcoin->GetDepthInMainChain() < nConf) continue; - for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue) - vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + isminetype mine = IsMine(pcoin->vout[i]); + + if (!(pcoin->IsSpent(i)) && mine != MINE_NO && pcoin->vout[i].nValue >= nMinimumInputValue) + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE)); + } } } } @@ -1190,8 +1212,11 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in 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)) @@ -1292,6 +1317,8 @@ bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, setvout[out.i].nValue; setCoinsRet.insert(make_pair(out.tx, out.i)); } @@ -1314,6 +1341,8 @@ bool CWallet::SelectCoinsSimple(int64 nTargetValue, unsigned int nSpendTime, int BOOST_FOREACH(COutput output, vCoins) { + if(!output.fSpendable) + continue; const CWalletTx *pcoin = output.tx; int i = output.i; diff --git a/src/wallet.h b/src/wallet.h index 56a60e1..964067f 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -149,6 +149,11 @@ public: bool AddCScript(const CScript& redeemScript); 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); + // Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) + bool LoadWatchOnly(const CTxDestination &dest); + bool Unlock(const SecureString& strWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); @@ -208,9 +213,9 @@ public: std::set< std::set > GetAddressGroupings(); std::map GetAddressBalances(); - bool IsMine(const CTxIn& txin) const; + isminetype IsMine(const CTxIn& txin) const; int64 GetDebit(const CTxIn& txin) const; - bool IsMine(const CTxOut& txout) const + isminetype IsMine(const CTxOut& txout) const { return ::IsMine(*this, txout.scriptPubKey); } @@ -710,15 +715,16 @@ 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 { - return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString().substr(0,10).c_str(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); + return strprintf("COutput(%s, %d, %d, %d) [%s]", tx->GetHash().ToString().substr(0,10).c_str(), i, fSpendable, nDepth, FormatMoney(tx->vout[i].nValue).c_str()); } void print() const diff --git a/src/walletdb.cpp b/src/walletdb.cpp index 556faf3..1519ff3 100644 --- a/src/walletdb.cpp +++ b/src/walletdb.cpp @@ -287,6 +287,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, wss.fAnyUnordered = true; } } + else if (strType == "watch") + { + std::string strAddress; + ssKey >> strAddress; + char fYes; + ssValue >> fYes; + if (fYes == '1') + pwallet->LoadWatchOnly(CBitcoinAddress(strAddress).Get()); + + // 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") { vector vchPubKey; diff --git a/src/walletdb.h b/src/walletdb.h index 7097e49..e9d6404 100644 --- a/src/walletdb.h +++ b/src/walletdb.h @@ -7,6 +7,7 @@ #include "db.h" #include "base58.h" +#include "keystore.h" class CKeyPool; class CAccount; @@ -122,6 +123,12 @@ public: return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false); } + bool WriteWatchOnly(const CTxDestination &dest) + { + nWalletDBUpdated++; + return Write(std::make_pair(std::string("watch"), CBitcoinAddress(dest).ToString()), '1'); + } + bool WriteBestBlock(const CBlockLocator& locator) { nWalletDBUpdated++; -- 1.7.1