Add support for watch-only addresses
Changes: * Add Add/Have WatchOnly methods to CKeyStore, and implementations in CBasicKeyStore. * Add similar methods to CWallet, and support entries for it in CWalletDB. * Make IsMine in script/wallet return a new enum 'isminetype', rather than a boolean. This allows distinguishing between spendable and unspendable coins. * Add a field fSpendable to COutput (GetAvailableCoins' return type). * Mark watchonly coins in listunspent as 'watchonly': true. * Add 'watchonly' to validateaddress, suppressing script/pubkey/... in this case. Based on a patch by Eric Lombrozo. Conflicts: src/qt/walletmodel.cpp src/rpcserver.cpp src/wallet.cpp
This commit is contained in:
parent
dd49e92fb0
commit
c8988460a2
16 changed files with 223 additions and 52 deletions
|
@ -59,3 +59,15 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut)
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -8,11 +8,21 @@
|
||||||
|
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
|
#include "script.h" // for CNoDestination
|
||||||
|
|
||||||
#include <boost/signals2/signal.hpp>
|
#include <boost/signals2/signal.hpp>
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
class CScript;
|
class CScript;
|
||||||
|
|
||||||
|
/** 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 */
|
/** A virtual base class for key stores */
|
||||||
class CKeyStore
|
class CKeyStore
|
||||||
{
|
{
|
||||||
|
@ -36,10 +46,15 @@ public:
|
||||||
virtual bool AddCScript(const CScript& redeemScript) =0;
|
virtual bool AddCScript(const CScript& redeemScript) =0;
|
||||||
virtual bool HaveCScript(const CScriptID &hash) const =0;
|
virtual bool HaveCScript(const CScriptID &hash) const =0;
|
||||||
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) 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<CKeyID, CKey> KeyMap;
|
||||||
typedef std::map<CScriptID, CScript > ScriptMap;
|
typedef std::map<CScriptID, CScript > ScriptMap;
|
||||||
|
typedef std::set<CTxDestination> WatchOnlySet;
|
||||||
|
|
||||||
/** Basic key store, that keeps keys in an address->secret map */
|
/** Basic key store, that keeps keys in an address->secret map */
|
||||||
class CBasicKeyStore : public CKeyStore
|
class CBasicKeyStore : public CKeyStore
|
||||||
|
@ -47,6 +62,7 @@ class CBasicKeyStore : public CKeyStore
|
||||||
protected:
|
protected:
|
||||||
KeyMap mapKeys;
|
KeyMap mapKeys;
|
||||||
ScriptMap mapScripts;
|
ScriptMap mapScripts;
|
||||||
|
WatchOnlySet setWatchOnly;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
|
bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
|
||||||
|
@ -88,6 +104,9 @@ public:
|
||||||
virtual bool AddCScript(const CScript& redeemScript);
|
virtual bool AddCScript(const CScript& redeemScript);
|
||||||
virtual bool HaveCScript(const CScriptID &hash) const;
|
virtual bool HaveCScript(const CScriptID &hash) const;
|
||||||
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) 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;
|
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
|
||||||
|
|
|
@ -543,7 +543,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect
|
||||||
if (!wallet->mapWallet.count(outpoint.hash)) continue;
|
if (!wallet->mapWallet.count(outpoint.hash)) continue;
|
||||||
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
|
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
|
||||||
if (nDepth < 0) continue;
|
if (nDepth < 0) continue;
|
||||||
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth);
|
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
|
||||||
vOutputs.push_back(out);
|
vOutputs.push_back(out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -570,7 +570,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
|
||||||
if (!wallet->mapWallet.count(outpoint.hash)) continue;
|
if (!wallet->mapWallet.count(outpoint.hash)) continue;
|
||||||
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
|
int nDepth = wallet->mapWallet[outpoint.hash].GetDepthInMainChain();
|
||||||
if (nDepth < 0) continue;
|
if (nDepth < 0) continue;
|
||||||
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth);
|
COutput out(&wallet->mapWallet[outpoint.hash], outpoint.n, nDepth, true);
|
||||||
vCoins.push_back(out);
|
vCoins.push_back(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -581,7 +581,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]))
|
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;
|
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;
|
CTxDestination address;
|
||||||
|
|
|
@ -72,6 +72,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||||
{ "lockunspent", 0 },
|
{ "lockunspent", 0 },
|
||||||
{ "lockunspent", 1 },
|
{ "lockunspent", 1 },
|
||||||
{ "importprivkey", 2 },
|
{ "importprivkey", 2 },
|
||||||
|
{ "importaddress", 2 },
|
||||||
{ "verifychain", 0 },
|
{ "verifychain", 0 },
|
||||||
{ "verifychain", 1 },
|
{ "verifychain", 1 },
|
||||||
{ "keypoolrefill", 0 },
|
{ "keypoolrefill", 0 },
|
||||||
|
|
|
@ -133,6 +133,51 @@ Value importprivkey(const Array& params, bool fHelp)
|
||||||
return Value::null;
|
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)
|
Value importwallet(const Array& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (fHelp || params.size() != 1)
|
if (fHelp || params.size() != 1)
|
||||||
|
|
|
@ -92,36 +92,45 @@ Value getinfo(const Array& params, bool fHelp)
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
class DescribeAddressVisitor : public boost::static_visitor<Object>
|
class DescribeAddressVisitor : public boost::static_visitor<Object>
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
isminetype mine;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
DescribeAddressVisitor(isminetype mineIn) : mine(mineIn) {}
|
||||||
|
|
||||||
Object operator()(const CNoDestination &dest) const { return Object(); }
|
Object operator()(const CNoDestination &dest) const { return Object(); }
|
||||||
|
|
||||||
Object operator()(const CKeyID &keyID) const {
|
Object operator()(const CKeyID &keyID) const {
|
||||||
Object obj;
|
Object obj;
|
||||||
CPubKey vchPubKey;
|
CPubKey vchPubKey;
|
||||||
pwalletMain->GetPubKey(keyID, vchPubKey);
|
|
||||||
obj.push_back(Pair("isscript", false));
|
obj.push_back(Pair("isscript", false));
|
||||||
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
|
if (mine == MINE_SPENDABLE) {
|
||||||
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
pwalletMain->GetPubKey(keyID, vchPubKey);
|
||||||
|
obj.push_back(Pair("pubkey", HexStr(vchPubKey)));
|
||||||
|
obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed()));
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object operator()(const CScriptID &scriptID) const {
|
Object operator()(const CScriptID &scriptID) const {
|
||||||
Object obj;
|
Object obj;
|
||||||
obj.push_back(Pair("isscript", true));
|
obj.push_back(Pair("isscript", true));
|
||||||
CScript subscript;
|
if (mine == MINE_SPENDABLE) {
|
||||||
pwalletMain->GetCScript(scriptID, subscript);
|
CScript subscript;
|
||||||
std::vector<CTxDestination> addresses;
|
pwalletMain->GetCScript(scriptID, subscript);
|
||||||
txnouttype whichType;
|
std::vector<CTxDestination> addresses;
|
||||||
int nRequired;
|
txnouttype whichType;
|
||||||
ExtractDestinations(subscript, whichType, addresses, nRequired);
|
int nRequired;
|
||||||
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
|
ExtractDestinations(subscript, whichType, addresses, nRequired);
|
||||||
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
|
obj.push_back(Pair("script", GetTxnOutputType(whichType)));
|
||||||
Array a;
|
obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end())));
|
||||||
BOOST_FOREACH(const CTxDestination& addr, addresses)
|
Array a;
|
||||||
a.push_back(CBitcoinAddress(addr).ToString());
|
BOOST_FOREACH(const CTxDestination& addr, addresses)
|
||||||
obj.push_back(Pair("addresses", a));
|
a.push_back(CBitcoinAddress(addr).ToString());
|
||||||
if (whichType == TX_MULTISIG)
|
obj.push_back(Pair("addresses", a));
|
||||||
obj.push_back(Pair("sigsrequired", nRequired));
|
if (whichType == TX_MULTISIG)
|
||||||
|
obj.push_back(Pair("sigsrequired", nRequired));
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -161,10 +170,11 @@ Value validateaddress(const Array& params, bool fHelp)
|
||||||
string currentAddress = address.ToString();
|
string currentAddress = address.ToString();
|
||||||
ret.push_back(Pair("address", currentAddress));
|
ret.push_back(Pair("address", currentAddress));
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
bool fMine = pwalletMain ? IsMine(*pwalletMain, dest) : false;
|
isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : MINE_NO;
|
||||||
ret.push_back(Pair("ismine", fMine));
|
ret.push_back(Pair("ismine", mine != MINE_NO));
|
||||||
if (fMine) {
|
if (mine != MINE_NO) {
|
||||||
Object detail = boost::apply_visitor(DescribeAddressVisitor(), dest);
|
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());
|
ret.insert(ret.end(), detail.begin(), detail.end());
|
||||||
}
|
}
|
||||||
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
|
if (pwalletMain && pwalletMain->mapAddressBook.count(dest))
|
||||||
|
|
|
@ -304,6 +304,7 @@ Value listunspent(const Array& params, bool fHelp)
|
||||||
}
|
}
|
||||||
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
|
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
|
||||||
entry.push_back(Pair("confirmations",out.nDepth));
|
entry.push_back(Pair("confirmations",out.nDepth));
|
||||||
|
entry.push_back(Pair("spendable", out.fSpendable));
|
||||||
results.push_back(entry);
|
results.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,6 +282,7 @@ static const CRPCCommand vRPCCommands[] =
|
||||||
{ "getwalletinfo", &getwalletinfo, true, false, true },
|
{ "getwalletinfo", &getwalletinfo, true, false, true },
|
||||||
{ "importprivkey", &importprivkey, false, false, true },
|
{ "importprivkey", &importprivkey, false, false, true },
|
||||||
{ "importwallet", &importwallet, false, false, true },
|
{ "importwallet", &importwallet, false, false, true },
|
||||||
|
{ "importaddress", &importaddress, false, false, true },
|
||||||
{ "keypoolrefill", &keypoolrefill, true, false, true },
|
{ "keypoolrefill", &keypoolrefill, true, false, true },
|
||||||
{ "listaccounts", &listaccounts, false, false, true },
|
{ "listaccounts", &listaccounts, false, false, true },
|
||||||
{ "listaddressgroupings", &listaddressgroupings, false, false, true },
|
{ "listaddressgroupings", &listaddressgroupings, false, false, true },
|
||||||
|
|
|
@ -131,6 +131,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 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 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 dumpwallet(const json_spirit::Array& params, bool fHelp);
|
||||||
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
|
extern json_spirit::Value importwallet(const json_spirit::Array& params, bool fHelp);
|
||||||
|
|
||||||
|
|
|
@ -1456,36 +1456,57 @@ public:
|
||||||
bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); }
|
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;
|
vector<valtype> vSolutions;
|
||||||
txnouttype whichType;
|
txnouttype whichType;
|
||||||
if (!Solver(scriptPubKey, whichType, vSolutions))
|
if (!Solver(scriptPubKey, whichType, vSolutions)) {
|
||||||
return false;
|
if (keystore.HaveWatchOnly(scriptPubKey.GetID()))
|
||||||
|
return MINE_WATCH_ONLY;
|
||||||
|
return MINE_NO;
|
||||||
|
}
|
||||||
|
|
||||||
CKeyID keyID;
|
CKeyID keyID;
|
||||||
switch (whichType)
|
switch (whichType)
|
||||||
{
|
{
|
||||||
case TX_NONSTANDARD:
|
case TX_NONSTANDARD:
|
||||||
case TX_NULL_DATA:
|
case TX_NULL_DATA:
|
||||||
return false;
|
break;
|
||||||
case TX_PUBKEY:
|
case TX_PUBKEY:
|
||||||
keyID = CPubKey(vSolutions[0]).GetID();
|
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:
|
case TX_PUBKEYHASH:
|
||||||
keyID = CKeyID(uint160(vSolutions[0]));
|
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:
|
case TX_SCRIPTHASH:
|
||||||
{
|
{
|
||||||
|
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
|
||||||
CScript subscript;
|
CScript subscript;
|
||||||
if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript))
|
if (keystore.GetCScript(scriptID, subscript)) {
|
||||||
return false;
|
isminetype ret = IsMine(keystore, subscript);
|
||||||
return IsMine(keystore, subscript);
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (keystore.HaveWatchOnly(scriptID))
|
||||||
|
return MINE_WATCH_ONLY;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case TX_MULTISIG:
|
case TX_MULTISIG:
|
||||||
{
|
{
|
||||||
|
@ -1495,10 +1516,15 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
|
||||||
// them) enable spend-out-from-under-you attacks, especially
|
// them) enable spend-out-from-under-you attacks, especially
|
||||||
// in shared-wallet situations.
|
// in shared-wallet situations.
|
||||||
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
|
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)
|
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
|
||||||
|
|
12
src/script.h
12
src/script.h
|
@ -194,6 +194,14 @@ enum
|
||||||
SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
|
SCRIPT_VERIFY_NULLDUMMY = (1U << 4), // verify dummy stack item consumed by CHECKMULTISIG is of zero-length
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** IsMine() return codes */
|
||||||
|
enum isminetype
|
||||||
|
{
|
||||||
|
MINE_NO = 0,
|
||||||
|
MINE_WATCH_ONLY = 1,
|
||||||
|
MINE_SPENDABLE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
// Mandatory script verification flags that all new blocks must comply with for
|
// Mandatory script verification flags that all new blocks must comply with for
|
||||||
// them to be valid. (but old blocks may not comply with) Currently just P2SH,
|
// them to be valid. (but old blocks may not comply with) Currently just P2SH,
|
||||||
// but in the future other flags may be added, such as a soft-fork to enforce
|
// but in the future other flags may be added, such as a soft-fork to enforce
|
||||||
|
@ -801,8 +809,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);
|
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);
|
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
|
||||||
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
|
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
|
||||||
bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
|
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
|
||||||
bool 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<CKeyID> &vKeys);
|
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);
|
||||||
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
|
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
|
||||||
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
|
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
|
||||||
|
|
|
@ -46,7 +46,7 @@ static void add_coin(int64_t nValue, int nAge = 6*24, bool fIsFromMe = false, in
|
||||||
wtx->fDebitCached = true;
|
wtx->fDebitCached = true;
|
||||||
wtx->nDebitCached = 1;
|
wtx->nDebitCached = 1;
|
||||||
}
|
}
|
||||||
COutput output(wtx, nInput, nAge);
|
COutput output(wtx, nInput, nAge, true);
|
||||||
vCoins.push_back(output);
|
vCoins.push_back(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,22 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
|
||||||
return CCryptoKeyStore::AddCScript(redeemScript);
|
return CCryptoKeyStore::AddCScript(redeemScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CWallet::AddWatchOnly(const CTxDestination &dest)
|
||||||
|
{
|
||||||
|
if (!CCryptoKeyStore::AddWatchOnly(dest))
|
||||||
|
return false;
|
||||||
|
nTimeFirstKey = 1; // No birthday information for watch-only keys.
|
||||||
|
if (!fFileBacked)
|
||||||
|
return true;
|
||||||
|
return CWalletDB(strWalletFile).WriteWatchOnly(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWallet::LoadWatchOnly(const CTxDestination &dest)
|
||||||
|
{
|
||||||
|
LogPrintf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str());
|
||||||
|
return CCryptoKeyStore::AddWatchOnly(dest);
|
||||||
|
}
|
||||||
|
|
||||||
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
|
bool CWallet::Unlock(const SecureString& strWalletPassphrase)
|
||||||
{
|
{
|
||||||
CCrypter crypter;
|
CCrypter crypter;
|
||||||
|
@ -680,7 +696,7 @@ void CWallet::EraseFromWallet(const uint256 &hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CWallet::IsMine(const CTxIn &txin) const
|
isminetype CWallet::IsMine(const CTxIn &txin) const
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
LOCK(cs_wallet);
|
LOCK(cs_wallet);
|
||||||
|
@ -689,11 +705,10 @@ bool CWallet::IsMine(const CTxIn &txin) const
|
||||||
{
|
{
|
||||||
const CWalletTx& prev = (*mi).second;
|
const CWalletTx& prev = (*mi).second;
|
||||||
if (txin.prevout.n < prev.vout.size())
|
if (txin.prevout.n < prev.vout.size())
|
||||||
if (IsMine(prev.vout[txin.prevout.n]))
|
return IsMine(prev.vout[txin.prevout.n]);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return MINE_NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t CWallet::GetDebit(const CTxIn &txin) const
|
int64_t CWallet::GetDebit(const CTxIn &txin) const
|
||||||
|
@ -1051,7 +1066,7 @@ int64_t CWallet::GetImmatureBalance() const
|
||||||
return nTotal;
|
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
|
void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const
|
||||||
{
|
{
|
||||||
vCoins.clear();
|
vCoins.clear();
|
||||||
|
@ -1077,10 +1092,11 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
|
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
|
||||||
if (!(IsSpent(wtxid, i)) && IsMine(pcoin->vout[i]) &&
|
isminetype mine = IsMine(pcoin->vout[i]);
|
||||||
|
if (!(IsSpent(wtxid, i)) && mine != MINE_NO &&
|
||||||
!IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 &&
|
!IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue > 0 &&
|
||||||
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
|
(!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i)))
|
||||||
vCoins.push_back(COutput(pcoin, i, nDepth));
|
vCoins.push_back(COutput(pcoin, i, nDepth, mine & MINE_SPENDABLE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1147,8 +1163,11 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT
|
||||||
|
|
||||||
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
|
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;
|
const CWalletTx *pcoin = output.tx;
|
||||||
|
|
||||||
if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
|
if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
|
||||||
|
|
14
src/wallet.h
14
src/wallet.h
|
@ -226,6 +226,11 @@ public:
|
||||||
/// Look up a destination data tuple in the store, return true if found false otherwise
|
/// Look up a destination data tuple in the store, return true if found false otherwise
|
||||||
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
|
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
|
||||||
|
|
||||||
|
// 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 Unlock(const SecureString& strWalletPassphrase);
|
||||||
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
||||||
bool EncryptWallet(const SecureString& strWalletPassphrase);
|
bool EncryptWallet(const SecureString& strWalletPassphrase);
|
||||||
|
@ -279,9 +284,9 @@ public:
|
||||||
|
|
||||||
std::set<CTxDestination> GetAccountAddresses(std::string strAccount) const;
|
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;
|
int64_t GetDebit(const CTxIn& txin) const;
|
||||||
bool IsMine(const CTxOut& txout) const
|
isminetype IsMine(const CTxOut& txout) const
|
||||||
{
|
{
|
||||||
return ::IsMine(*this, txout.scriptPubKey);
|
return ::IsMine(*this, txout.scriptPubKey);
|
||||||
}
|
}
|
||||||
|
@ -722,10 +727,11 @@ public:
|
||||||
const CWalletTx *tx;
|
const CWalletTx *tx;
|
||||||
int i;
|
int i;
|
||||||
int nDepth;
|
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
|
std::string ToString() const
|
||||||
|
|
|
@ -112,6 +112,12 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
|
||||||
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
|
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)
|
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
|
||||||
{
|
{
|
||||||
nWalletDBUpdated++;
|
nWalletDBUpdated++;
|
||||||
|
@ -404,6 +410,19 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
|
||||||
wss.fAnyUnordered = true;
|
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")
|
else if (strType == "key" || strType == "wkey")
|
||||||
{
|
{
|
||||||
CPubKey vchPubKey;
|
CPubKey vchPubKey;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
|
#include "keystore.h"
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -93,6 +94,8 @@ public:
|
||||||
|
|
||||||
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
|
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
|
||||||
|
|
||||||
|
bool WriteWatchOnly(const CTxDestination &dest);
|
||||||
|
|
||||||
bool WriteBestBlock(const CBlockLocator& locator);
|
bool WriteBestBlock(const CBlockLocator& locator);
|
||||||
bool ReadBestBlock(CBlockLocator& locator);
|
bool ReadBestBlock(CBlockLocator& locator);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue