Created
December 25, 2013 07:22
-
-
Save BlinkyStitt/8120948 to your computer and use it in GitHub Desktop.
https://github.com/laanwj/bitcoin/commit/a3bfbf95bf204f9b2b8b7cd844c9aab267f34e82 rebased off 6e77920
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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