Skip to content

Commit d5087d1

Browse files
sipasdkfjlsfjlskdfjlsdjflsjf
authored andcommitted
Use script matching rather than destination matching for watch-only.
This changes the keystore data format, wallet format and IsMine logic to detect watch-only outputs based on direct script matching rather than first trying to convert outputs to destinations (addresses). The reason is that we don't know how the software that has the spending keys works. It may support the same types of scripts as us, but that is not guaranteed. Furthermore, it removes the ambiguity between addresses used as identifiers for output scripts or identifiers for public keys. One practical implication is that adding a normal pay-to-pubkey-hash address via importaddress will not cause payments to the corresponding full public key to be detected as IsMine. If that is wanted, add those scripts directly (importaddress now also accepts any hex-encoded script). Conflicts: src/wallet.cpp
1 parent 0fa2f88 commit d5087d1

File tree

9 files changed

+47
-47
lines changed

9 files changed

+47
-47
lines changed

src/keystore.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ bool CBasicKeyStore::GetCScript(const CScriptID &hash, CScript& redeemScriptOut)
5959
return false;
6060
}
6161

62-
bool CBasicKeyStore::AddWatchOnly(const CTxDestination &dest)
62+
bool CBasicKeyStore::AddWatchOnly(const CScript &dest)
6363
{
6464
LOCK(cs_KeyStore);
6565
setWatchOnly.insert(dest);
6666
return true;
6767
}
6868

69-
bool CBasicKeyStore::HaveWatchOnly(const CTxDestination &dest) const
69+
bool CBasicKeyStore::HaveWatchOnly(const CScript &dest) const
7070
{
7171
LOCK(cs_KeyStore);
7272
return setWatchOnly.count(dest) > 0;

src/keystore.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,13 @@ class CKeyStore
4848
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const =0;
4949

5050
// Support for Watch-only addresses
51-
virtual bool AddWatchOnly(const CTxDestination &dest) =0;
52-
virtual bool HaveWatchOnly(const CTxDestination &dest) const =0;
51+
virtual bool AddWatchOnly(const CScript &dest) =0;
52+
virtual bool HaveWatchOnly(const CScript &dest) const =0;
5353
};
5454

5555
typedef std::map<CKeyID, CKey> KeyMap;
5656
typedef std::map<CScriptID, CScript > ScriptMap;
57-
typedef std::set<CTxDestination> WatchOnlySet;
57+
typedef std::set<CScript> WatchOnlySet;
5858

5959
/** Basic key store, that keeps keys in an address->secret map */
6060
class CBasicKeyStore : public CKeyStore
@@ -105,8 +105,8 @@ class CBasicKeyStore : public CKeyStore
105105
virtual bool HaveCScript(const CScriptID &hash) const;
106106
virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const;
107107

108-
virtual bool AddWatchOnly(const CTxDestination &dest);
109-
virtual bool HaveWatchOnly(const CTxDestination &dest) const;
108+
virtual bool AddWatchOnly(const CScript &dest);
109+
virtual bool HaveWatchOnly(const CScript &dest) const;
110110
};
111111

112112
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;

src/rpcdump.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,19 @@ Value importaddress(const Array& params, bool fHelp)
138138
if (fHelp || params.size() < 1 || params.size() > 3)
139139
throw runtime_error(
140140
"importaddress <address> [label] [rescan=true]\n"
141-
"Adds an address that can be watched as if it were in your wallet but cannot be used to spend.");
141+
"Adds an address or script (in hex) that can be watched as if it were in your wallet but cannot be used to spend.");
142+
143+
CScript script;
142144

143145
CBitcoinAddress address(params[0].get_str());
144-
if (!address.IsValid())
145-
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
146-
CTxDestination dest;
147-
dest = address.Get();
146+
if (address.IsValid()) {
147+
script.SetDestination(address.Get());
148+
} else if (IsHex(params[0].get_str())) {
149+
std::vector<unsigned char> data(ParseHex(params[0].get_str()));
150+
script = CScript(data.begin(), data.end());
151+
} else {
152+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address or script");
153+
}
148154

149155
string strLabel = "";
150156
if (params.size() > 1)
@@ -159,15 +165,16 @@ Value importaddress(const Array& params, bool fHelp)
159165
LOCK2(cs_main, pwalletMain->cs_wallet);
160166

161167
// add to address book or update label
162-
pwalletMain->SetAddressBook(dest, strLabel, "receive");
168+
if (address.IsValid())
169+
pwalletMain->SetAddressBook(address.Get(), strLabel, "receive");
163170

164171
// Don't throw error in case an address is already there
165-
if (pwalletMain->HaveWatchOnly(dest))
172+
if (pwalletMain->HaveWatchOnly(script))
166173
return Value::null;
167174

168175
pwalletMain->MarkDirty();
169176

170-
if (!pwalletMain->AddWatchOnly(dest))
177+
if (!pwalletMain->AddWatchOnly(script))
171178
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet");
172179

173180
if (fRescan)

src/script.cpp

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,21 +1456,19 @@ class CKeyStoreIsMineVisitor : public boost::static_visitor<bool>
14561456
bool operator()(const CScriptID &scriptID) const { return keystore->HaveCScript(scriptID); }
14571457
};
14581458

1459-
isminetype IsMine(const CKeyStore &keystore, const CTxDestination &dest)
1459+
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest)
14601460
{
1461-
if (boost::apply_visitor(CKeyStoreIsMineVisitor(&keystore), dest))
1462-
return MINE_SPENDABLE;
1463-
if (keystore.HaveWatchOnly(dest))
1464-
return MINE_WATCH_ONLY;
1465-
return MINE_NO;
1461+
CScript script;
1462+
script.SetDestination(dest);
1463+
return IsMine(keystore, script);
14661464
}
14671465

14681466
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
14691467
{
14701468
vector<valtype> vSolutions;
14711469
txnouttype whichType;
14721470
if (!Solver(scriptPubKey, whichType, vSolutions)) {
1473-
if (keystore.HaveWatchOnly(scriptPubKey.GetID()))
1471+
if (keystore.HaveWatchOnly(scriptPubKey))
14741472
return MINE_WATCH_ONLY;
14751473
return MINE_NO;
14761474
}
@@ -1485,27 +1483,21 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
14851483
keyID = CPubKey(vSolutions[0]).GetID();
14861484
if (keystore.HaveKey(keyID))
14871485
return MINE_SPENDABLE;
1488-
if (keystore.HaveWatchOnly(keyID))
1489-
return MINE_WATCH_ONLY;
14901486
break;
14911487
case TX_PUBKEYHASH:
14921488
keyID = CKeyID(uint160(vSolutions[0]));
14931489
if (keystore.HaveKey(keyID))
14941490
return MINE_SPENDABLE;
1495-
if (keystore.HaveWatchOnly(keyID))
1496-
return MINE_WATCH_ONLY;
14971491
break;
14981492
case TX_SCRIPTHASH:
14991493
{
15001494
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
15011495
CScript subscript;
15021496
if (keystore.GetCScript(scriptID, subscript)) {
15031497
isminetype ret = IsMine(keystore, subscript);
1504-
if (ret)
1498+
if (ret == MINE_SPENDABLE)
15051499
return ret;
15061500
}
1507-
if (keystore.HaveWatchOnly(scriptID))
1508-
return MINE_WATCH_ONLY;
15091501
break;
15101502
}
15111503
case TX_MULTISIG:
@@ -1522,7 +1514,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
15221514
}
15231515
}
15241516

1525-
if (keystore.HaveWatchOnly(scriptPubKey.GetID()))
1517+
if (keystore.HaveWatchOnly(scriptPubKey))
15261518
return MINE_WATCH_ONLY;
15271519
return MINE_NO;
15281520
}

src/script.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
812812
int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
813813
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
814814
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
815-
isminetype IsMine(const CKeyStore& keystore, const CTxDestination &dest);
815+
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
816816
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);
817817
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);
818818
bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);

src/wallet.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript)
145145
return CCryptoKeyStore::AddCScript(redeemScript);
146146
}
147147

148-
bool CWallet::AddWatchOnly(const CTxDestination &dest)
148+
bool CWallet::AddWatchOnly(const CScript &dest)
149149
{
150150
if (!CCryptoKeyStore::AddWatchOnly(dest))
151151
return false;
@@ -155,9 +155,8 @@ bool CWallet::AddWatchOnly(const CTxDestination &dest)
155155
return CWalletDB(strWalletFile).WriteWatchOnly(dest);
156156
}
157157

158-
bool CWallet::LoadWatchOnly(const CTxDestination &dest)
158+
bool CWallet::LoadWatchOnly(const CScript &dest)
159159
{
160-
LogPrintf("Loaded %s!\n", CBitcoinAddress(dest).ToString().c_str());
161160
return CCryptoKeyStore::AddWatchOnly(dest);
162161
}
163162

@@ -729,17 +728,19 @@ int64_t CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
729728

730729
bool CWallet::IsChange(const CTxOut& txout) const
731730
{
732-
CTxDestination address;
733-
734731
// TODO: fix handling of 'change' outputs. The assumption is that any
735-
// payment to a TX_PUBKEYHASH that is mine but isn't in the address book
732+
// payment to a script that is ours, but is not in the address book
736733
// is change. That assumption is likely to break when we implement multisignature
737734
// wallets that return change back into a multi-signature-protected address;
738735
// a better way of identifying which outputs are 'the send' and which are
739736
// 'the change' will need to be implemented (maybe extend CWalletTx to remember
740737
// which output, if any, was change).
741-
if (ExtractDestination(txout.scriptPubKey, address) && ::IsMine(*this, address) == MINE_SPENDABLE)
738+
if (::IsMine(*this, txout.scriptPubKey))
742739
{
740+
CTxDestination address;
741+
if (!ExtractDestination(txout.scriptPubKey, address))
742+
return true;
743+
743744
LOCK(cs_wallet);
744745
if (!mapAddressBook.count(address))
745746
return true;

src/wallet.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,9 @@ class CWallet : public CCryptoKeyStore, public CWalletInterface
227227
bool GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const;
228228

229229
// Adds a watch-only address to the store, and saves it to disk.
230-
bool AddWatchOnly(const CTxDestination &dest);
230+
bool AddWatchOnly(const CScript &dest);
231231
// Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
232-
bool LoadWatchOnly(const CTxDestination &dest);
232+
bool LoadWatchOnly(const CScript &dest);
233233

234234
bool Unlock(const SecureString& strWalletPassphrase);
235235
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);

src/walletdb.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,10 @@ bool CWalletDB::WriteCScript(const uint160& hash, const CScript& redeemScript)
112112
return Write(std::make_pair(std::string("cscript"), hash), redeemScript, false);
113113
}
114114

115-
bool CWalletDB::WriteWatchOnly(const CTxDestination &dest)
115+
bool CWalletDB::WriteWatchOnly(const CScript &dest)
116116
{
117117
nWalletDBUpdated++;
118-
return Write(std::make_pair(std::string("watch"), CBitcoinAddress(dest).ToString()), '1');
118+
return Write(std::make_pair(std::string("watchs"), dest), '1');
119119
}
120120

121121
bool CWalletDB::WriteBestBlock(const CBlockLocator& locator)
@@ -410,14 +410,14 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
410410
wss.fAnyUnordered = true;
411411
}
412412
}
413-
else if (strType == "watch")
413+
else if (strType == "watchs")
414414
{
415-
std::string strAddress;
416-
ssKey >> strAddress;
415+
CScript script;
416+
ssKey >> script;
417417
char fYes;
418418
ssValue >> fYes;
419419
if (fYes == '1')
420-
pwallet->LoadWatchOnly(CBitcoinAddress(strAddress).Get());
420+
pwallet->LoadWatchOnly(script);
421421

422422
// Watch-only addresses have no birthday information for now,
423423
// so set the wallet birthday to the beginning of time.

src/walletdb.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class CWalletDB : public CDB
9494

9595
bool WriteCScript(const uint160& hash, const CScript& redeemScript);
9696

97-
bool WriteWatchOnly(const CTxDestination &dest);
97+
bool WriteWatchOnly(const CScript &script);
9898

9999
bool WriteBestBlock(const CBlockLocator& locator);
100100
bool ReadBestBlock(CBlockLocator& locator);

0 commit comments

Comments
 (0)