Skip to content

Instantly share code, notes, and snippets.

@BlinkyStitt
Created December 25, 2013 07:22
Show Gist options
  • Save BlinkyStitt/8120948 to your computer and use it in GitHub Desktop.
Save BlinkyStitt/8120948 to your computer and use it in GitHub Desktop.
diff --git a/src/keystore.cpp b/src/keystore.cpp
index 46402ea..b5657c8 100644
--- a/src/keystore.cpp
+++ b/src/keystore.cpp
@@ -56,3 +56,16 @@ 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;
+}
+
diff --git a/src/keystore.h b/src/keystore.h
index 0d55e6c..3d1f42b 100644
--- a/src/keystore.h
+++ b/src/keystore.h
@@ -10,9 +10,24 @@
#include "sync.h"
#include <boost/signals2/signal.hpp>
+#include <boost/variant.hpp>
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<CNoDestination, CKeyID, CScriptID> CTxDestination;
+
/** A virtual base class for key stores */
class CKeyStore
{
@@ -36,10 +51,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 CTxDestination &dest) =0;
+ virtual bool HaveWatchOnly(const CTxDestination &dest) const =0;
};
typedef std::map<CKeyID, CKey> KeyMap;
typedef std::map<CScriptID, CScript > ScriptMap;
+typedef std::set<CTxDestination> WatchOnlySet;
/** Basic key store, that keeps keys in an address->secret map */
class CBasicKeyStore : public CKeyStore
@@ -47,6 +67,7 @@ class CBasicKeyStore : public CKeyStore
protected:
KeyMap mapKeys;
ScriptMap mapScripts;
+ WatchOnlySet setWatchOnly;
public:
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
@@ -88,6 +109,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::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index f08342b..5282a02 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -491,7 +491,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& 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);
}
}
@@ -509,7 +509,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& 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);
}
@@ -520,7 +520,7 @@ 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;
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
index c404ac2..42aa1ae 100644
--- a/src/rpcclient.cpp
+++ b/src/rpcclient.cpp
@@ -177,6 +177,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "getrawmempool" && n > 0) ConvertTo<bool>(params[0]);
+ if (strMethod == "importaddress" && n > 2) ConvertTo<bool>(params[2]);
return params;
}
diff --git a/src/rpcdump.cpp b/src/rpcdump.cpp
index c801b28..b5c2279 100644
--- a/src/rpcdump.cpp
+++ b/src/rpcdump.cpp
@@ -128,6 +128,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 <address> [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->SetAddressBook(dest, strLabel, "receive");
+
+ if (!pwalletMain->AddWatchOnly(dest))
+ 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)
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index 8602591..91ca01f 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -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);
}
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp
index 9f2100a..d9d8b5a 100644
--- a/src/rpcserver.cpp
+++ b/src/rpcserver.cpp
@@ -291,6 +291,7 @@ static const CRPCCommand vRPCCommands[] =
{ "dumpwallet", &dumpwallet, true, false, true },
{ "importprivkey", &importprivkey, false, false, true },
{ "importwallet", &importwallet, false, false, true },
+ { "importaddress", &importaddress, false, false, true },
{ "listunspent", &listunspent, false, false, true },
{ "lockunspent", &lockunspent, false, false, true },
{ "listlockunspent", &listlockunspent, false, false, true },
diff --git a/src/rpcserver.h b/src/rpcserver.h
index 9087be9..5eb67a2 100644
--- a/src/rpcserver.h
+++ b/src/rpcserver.h
@@ -111,6 +111,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);
diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp
index 8ad5c9c..9af948c 100644
--- a/src/rpcwallet.cpp
+++ b/src/rpcwallet.cpp
@@ -1722,6 +1722,97 @@ Value encryptwallet(const Array& params, bool fHelp)
return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup.";
}
+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;
+ obj.push_back(Pair("isscript", false));
+ if (mine == MINE_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));
+ if (mine == MINE_SPENDABLE) {
+ 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;
+ }
+};
+
+Value validateaddress(const Array& params, bool fHelp)
+{
+ if (fHelp || params.size() != 1)
+ throw runtime_error(
+ "validateaddress \"bitcoinaddress\"\n"
+ "\nReturn information about the given bitcoin address.\n"
+ "\nArguments:\n"
+ "1. \"bitcoinaddress\" (string, required) The bitcoin address to validate\n"
+ "\nResult:\n"
+ "{\n"
+ " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n"
+ " \"address\" : \"bitcoinaddress\", (string) The bitcoin address validated\n"
+ " \"ismine\" : true|false, (boolean) If the address is yours or not\n"
+ " \"isscript\" : true|false, (boolean) If the key is a script\n"
+ " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n"
+ " \"iscompressed\" : true|false, (boolean) If the address is compressed\n"
+ " \"account\" : \"account\" (string) The account associated with the address, \"\" is the default account\n"
+ "}\n"
+ "\nExamples:\n"
+ + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ );
+
+ CBitcoinAddress address(params[0].get_str());
+ bool isValid = address.IsValid();
+
+ Object ret;
+ ret.push_back(Pair("isvalid", isValid));
+ if (isValid)
+ {
+ CTxDestination dest = address.Get();
+ string currentAddress = address.ToString();
+ ret.push_back(Pair("address", currentAddress));
+ 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 && pwalletMain->mapAddressBook.count(dest))
+ ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name));
+ }
+ return ret;
+}
+
Value lockunspent(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 1 || params.size() > 2)
diff --git a/src/script.cpp b/src/script.cpp
index 2b66bc7..7f3860b 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -1456,36 +1456,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<valtype> 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:
{
@@ -1495,10 +1516,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 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 bd120cc..d4648b5 100644
--- a/src/script.h
+++ b/src/script.h
@@ -9,6 +9,7 @@
#include "bignum.h"
#include "key.h"
#include "util.h"
+#include "keystore.h"
#include <stdexcept>
#include <stdint.h>
@@ -16,7 +17,6 @@
#include <vector>
#include <boost/foreach.hpp>
-#include <boost/variant.hpp>
class CCoins;
class CKeyStore;
@@ -43,6 +43,14 @@ enum
SCRIPT_VERIFY_NOCACHE = (1U << 3), // do not store results in signature cache (but do query it)
};
+/** IsMine() return codes */
+enum isminetype
+{
+ MINE_NO = 0,
+ MINE_WATCH_ONLY = 1,
+ MINE_SPENDABLE = 2,
+};
+
enum txnouttype
{
TX_NONSTANDARD,
@@ -54,20 +62,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<CNoDestination, CKeyID, CScriptID> CTxDestination;
-
const char* GetTxnOutputType(txnouttype t);
/** Script opcodes */
@@ -691,8 +685,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);
diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp
index 0acd94e..e0033c0 100644
--- a/src/test/wallet_tests.cpp
+++ b/src/test/wallet_tests.cpp
@@ -40,7 +40,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);
}
diff --git a/src/wallet.cpp b/src/wallet.cpp
index 76a8308..06417f7 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -116,6 +116,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)
+{
+ LogPrintf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str());
+ return CCryptoKeyStore::AddWatchOnly(dest);
+}
+
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{
CCrypter crypter;
@@ -548,7 +564,7 @@ void CWallet::EraseFromWallet(const uint256 &hash)
}
-bool CWallet::IsMine(const CTxIn &txin) const
+isminetype CWallet::IsMine(const CTxIn &txin) const
{
{
LOCK(cs_wallet);
@@ -557,11 +573,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
@@ -1012,7 +1027,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<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const
{
vCoins.clear();
@@ -1033,10 +1048,13 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
continue;
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
- if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) &&
+ isminetype mine = IsMine(pcoin->vout[i]);
+ if (!(pcoin->IsSpent(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, pcoin->GetDepthInMainChain()));
+ {
+ vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE));
+ }
}
}
}
@@ -1103,8 +1121,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))
diff --git a/src/wallet.h b/src/wallet.h
index 99f6293..957392a 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -183,6 +183,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);
@@ -238,9 +243,9 @@ public:
std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const;
- bool IsMine(const CTxIn& txin) const;
+ isminetype IsMine(const CTxIn& txin) const;
int64_t GetDebit(const CTxIn& txin) const;
- bool IsMine(const CTxOut& txout) const
+ isminetype IsMine(const CTxOut& txout) const
{
return ::IsMine(*this, txout.scriptPubKey);
}
@@ -737,10 +742,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
diff --git a/src/walletdb.cpp b/src/walletdb.cpp
index 2dc6594..c059752 100644
--- a/src/walletdb.cpp
+++ b/src/walletdb.cpp
@@ -114,6 +114,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 CTxDestination &dest)
+{
+ nWalletDBUpdated++;
+ return Write(std::make_pair(std::string("watch"), CBitcoinAddress(dest).ToString()), '1');
+}
+
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
{
nWalletDBUpdated++;
@@ -416,6 +422,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")
{
CPubKey vchPubKey;
@@ -867,3 +886,4 @@ bool CWalletDB::Recover(CDBEnv& dbenv, std::string filename)
{
return CWalletDB::Recover(dbenv, filename, false);
}
+
diff --git a/src/walletdb.h b/src/walletdb.h
index 88ba89f..318ecda 100644
--- a/src/walletdb.h
+++ b/src/walletdb.h
@@ -7,6 +7,7 @@
#include "db.h"
#include "key.h"
+#include "keystore.h"
#include <list>
#include <stdint.h>
@@ -93,6 +94,8 @@ public:
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
+ bool WriteWatchOnly(const CTxDestination &dest);
+
bool WriteBestBlock(const CBlockLocator& locator);
bool ReadBestBlock(CBlockLocator& locator);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment