Skip to content

Commit 248f3a7

Browse files
sipajl2012
authored andcommitted
Fix ismine and addwitnessaddress: no uncompressed keys in segwit
1 parent b811124 commit 248f3a7

File tree

3 files changed

+75
-15
lines changed

3 files changed

+75
-15
lines changed

src/script/ismine.cpp

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,25 @@ unsigned int HaveKeys(const vector<valtype>& pubkeys, const CKeyStore& keystore)
2929
return nResult;
3030
}
3131

32-
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest)
32+
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion sigversion)
33+
{
34+
bool isInvalid = false;
35+
return IsMine(keystore, scriptPubKey, isInvalid, sigversion);
36+
}
37+
38+
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion sigversion)
39+
{
40+
bool isInvalid = false;
41+
return IsMine(keystore, dest, isInvalid, sigversion);
42+
}
43+
44+
isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& isInvalid, SigVersion sigversion)
3345
{
3446
CScript script = GetScriptForDestination(dest);
35-
return IsMine(keystore, script);
47+
return IsMine(keystore, script, isInvalid, sigversion);
3648
}
3749

38-
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
50+
isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion)
3951
{
4052
vector<valtype> vSolutions;
4153
txnouttype whichType;
@@ -53,12 +65,35 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
5365
break;
5466
case TX_PUBKEY:
5567
keyID = CPubKey(vSolutions[0]).GetID();
68+
if (sigversion != SIGVERSION_BASE && vSolutions[0].size() != 33) {
69+
isInvalid = true;
70+
return ISMINE_NO;
71+
}
5672
if (keystore.HaveKey(keyID))
5773
return ISMINE_SPENDABLE;
5874
break;
59-
case TX_PUBKEYHASH:
6075
case TX_WITNESS_V0_KEYHASH:
76+
{
77+
if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
78+
// We do not support bare witness outputs unless the P2SH version of it would be
79+
// acceptable as well. This protects against matching before segwit activates.
80+
// This also applies to the P2WSH case.
81+
break;
82+
}
83+
isminetype ret = ::IsMine(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, SIGVERSION_WITNESS_V0);
84+
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
85+
return ret;
86+
break;
87+
}
88+
case TX_PUBKEYHASH:
6189
keyID = CKeyID(uint160(vSolutions[0]));
90+
if (sigversion != SIGVERSION_BASE) {
91+
CPubKey pubkey;
92+
if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
93+
isInvalid = true;
94+
return ISMINE_NO;
95+
}
96+
}
6297
if (keystore.HaveKey(keyID))
6398
return ISMINE_SPENDABLE;
6499
break;
@@ -67,21 +102,24 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
67102
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
68103
CScript subscript;
69104
if (keystore.GetCScript(scriptID, subscript)) {
70-
isminetype ret = IsMine(keystore, subscript);
71-
if (ret == ISMINE_SPENDABLE)
105+
isminetype ret = IsMine(keystore, subscript, isInvalid);
106+
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
72107
return ret;
73108
}
74109
break;
75110
}
76111
case TX_WITNESS_V0_SCRIPTHASH:
77112
{
113+
if (!keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
114+
break;
115+
}
78116
uint160 hash;
79117
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
80118
CScriptID scriptID = CScriptID(hash);
81119
CScript subscript;
82120
if (keystore.GetCScript(scriptID, subscript)) {
83-
isminetype ret = IsMine(keystore, subscript);
84-
if (ret == ISMINE_SPENDABLE)
121+
isminetype ret = IsMine(keystore, subscript, isInvalid, SIGVERSION_WITNESS_V0);
122+
if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
85123
return ret;
86124
}
87125
break;
@@ -95,6 +133,14 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
95133
// them) enable spend-out-from-under-you attacks, especially
96134
// in shared-wallet situations.
97135
vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
136+
if (sigversion != SIGVERSION_BASE) {
137+
for (size_t i = 0; i < keys.size(); i++) {
138+
if (keys[i].size() != 33) {
139+
isInvalid = true;
140+
return ISMINE_NO;
141+
}
142+
}
143+
}
98144
if (HaveKeys(keys, keystore) == keys.size())
99145
return ISMINE_SPENDABLE;
100146
break;

src/script/ismine.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,14 @@ enum isminetype
2828
/** used for bitflags of isminetype */
2929
typedef uint8_t isminefilter;
3030

31-
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
32-
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest);
31+
/* isInvalid becomes true when the script is found invalid by consensus or policy. This will terminate the recursion
32+
* and return a ISMINE_NO immediately, as an invalid script should never be considered as "mine". This is needed as
33+
* different SIGVERSION may have different network rules. Currently the only use of isInvalid is indicate uncompressed
34+
* keys in SIGVERSION_WITNESS_V0 script, but could also be used in similar cases in the future
35+
*/
36+
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion = SIGVERSION_BASE);
37+
isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, SigVersion = SIGVERSION_BASE);
38+
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, bool& isInvalid, SigVersion = SIGVERSION_BASE);
39+
isminetype IsMine(const CKeyStore& keystore, const CTxDestination& dest, SigVersion = SIGVERSION_BASE);
3340

3441
#endif // BITCOIN_SCRIPT_ISMINE_H

src/wallet/rpcwallet.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2010 Satoshi Nakamoto
2-
// Copyright (c) 2009-2015 The Bitcoin Core developers
2+
// Copyright (c) 2009-2016 The Bitcoin Core developers
33
// Distributed under the MIT software license, see the accompanying
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

@@ -1025,9 +1025,12 @@ class Witnessifier : public boost::static_visitor<bool>
10251025

10261026
bool operator()(const CKeyID &keyID) {
10271027
CPubKey pubkey;
1028-
if (pwalletMain && pwalletMain->GetPubKey(keyID, pubkey)) {
1029-
CScript basescript;
1030-
basescript << ToByteVector(pubkey) << OP_CHECKSIG;
1028+
if (pwalletMain) {
1029+
CScript basescript = GetScriptForDestination(keyID);
1030+
isminetype typ;
1031+
typ = IsMine(*pwalletMain, basescript, SIGVERSION_WITNESS_V0);
1032+
if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE)
1033+
return false;
10311034
CScript witscript = GetScriptForWitness(basescript);
10321035
pwalletMain->AddCScript(witscript);
10331036
result = CScriptID(witscript);
@@ -1045,6 +1048,10 @@ class Witnessifier : public boost::static_visitor<bool>
10451048
result = scriptID;
10461049
return true;
10471050
}
1051+
isminetype typ;
1052+
typ = IsMine(*pwalletMain, subscript, SIGVERSION_WITNESS_V0);
1053+
if (typ != ISMINE_SPENDABLE && typ != ISMINE_WATCH_SOLVABLE)
1054+
return false;
10481055
CScript witscript = GetScriptForWitness(subscript);
10491056
pwalletMain->AddCScript(witscript);
10501057
result = CScriptID(witscript);
@@ -1090,7 +1097,7 @@ UniValue addwitnessaddress(const UniValue& params, bool fHelp)
10901097
CTxDestination dest = address.Get();
10911098
bool ret = boost::apply_visitor(w, dest);
10921099
if (!ret) {
1093-
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet");
1100+
throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed");
10941101
}
10951102

10961103
pwalletMain->SetAddressBook(w.result, "", "receive");

0 commit comments

Comments
 (0)