@@ -28,6 +28,19 @@ enum class IsMineSigVersion
28
28
WITNESS_V0 = 2 // ! P2WSH witness script execution
29
29
};
30
30
31
+ /* *
32
+ * This is an internal representation of isminetype + invalidity.
33
+ * Its order is significant, as we return the max of all explored
34
+ * possibilities.
35
+ */
36
+ enum class IsMineResult
37
+ {
38
+ NO = 0 , // ! Not ours
39
+ WATCH_ONLY = 1 , // ! Included in watch-only balance
40
+ SPENDABLE = 2 , // ! Included in all balances
41
+ INVALID = 3 , // ! Not spendable by anyone
42
+ };
43
+
31
44
bool PermitsUncompressed (IsMineSigVersion sigversion)
32
45
{
33
46
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
@@ -42,17 +55,13 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore)
42
55
return true ;
43
56
}
44
57
45
- isminetype IsMineInner (const CKeyStore& keystore, const CScript& scriptPubKey, bool & isInvalid , IsMineSigVersion sigversion)
58
+ IsMineResult IsMineInner (const CKeyStore& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
46
59
{
47
- isInvalid = false ;
60
+ IsMineResult ret = IsMineResult::NO ;
48
61
49
62
std::vector<valtype> vSolutions;
50
63
txnouttype whichType;
51
- if (!Solver (scriptPubKey, whichType, vSolutions)) {
52
- if (keystore.HaveWatchOnly (scriptPubKey))
53
- return ISMINE_WATCH_UNSOLVABLE;
54
- return ISMINE_NO;
55
- }
64
+ Solver (scriptPubKey, whichType, vSolutions);
56
65
57
66
CKeyID keyID;
58
67
switch (whichType)
@@ -64,50 +73,58 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
64
73
case TX_PUBKEY:
65
74
keyID = CPubKey (vSolutions[0 ]).GetID ();
66
75
if (!PermitsUncompressed (sigversion) && vSolutions[0 ].size () != 33 ) {
67
- isInvalid = true ;
68
- return ISMINE_NO;
76
+ return IsMineResult::INVALID;
77
+ }
78
+ if (keystore.HaveKey (keyID)) {
79
+ ret = std::max (ret, IsMineResult::SPENDABLE);
69
80
}
70
- if (keystore.HaveKey (keyID))
71
- return ISMINE_SPENDABLE;
72
81
break ;
73
82
case TX_WITNESS_V0_KEYHASH:
74
83
{
84
+ if (sigversion == IsMineSigVersion::WITNESS_V0) {
85
+ // P2WPKH inside P2WSH is invalid.
86
+ return IsMineResult::INVALID;
87
+ }
75
88
if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript (CScriptID (CScript () << OP_0 << vSolutions[0 ]))) {
76
89
// We do not support bare witness outputs unless the P2SH version of it would be
77
90
// acceptable as well. This protects against matching before segwit activates.
78
91
// This also applies to the P2WSH case.
79
92
break ;
80
93
}
81
- isminetype ret = IsMineInner (keystore, GetScriptForDestination (CKeyID (uint160 (vSolutions[0 ]))), isInvalid, IsMineSigVersion::WITNESS_V0);
82
- if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
83
- return ret;
94
+ ret = std::max (ret, IsMineInner (keystore, GetScriptForDestination (CKeyID (uint160 (vSolutions[0 ]))), IsMineSigVersion::WITNESS_V0));
84
95
break ;
85
96
}
86
97
case TX_PUBKEYHASH:
87
98
keyID = CKeyID (uint160 (vSolutions[0 ]));
88
99
if (!PermitsUncompressed (sigversion)) {
89
100
CPubKey pubkey;
90
101
if (keystore.GetPubKey (keyID, pubkey) && !pubkey.IsCompressed ()) {
91
- isInvalid = true ;
92
- return ISMINE_NO;
102
+ return IsMineResult::INVALID;
93
103
}
94
104
}
95
- if (keystore.HaveKey (keyID))
96
- return ISMINE_SPENDABLE;
105
+ if (keystore.HaveKey (keyID)) {
106
+ ret = std::max (ret, IsMineResult::SPENDABLE);
107
+ }
97
108
break ;
98
109
case TX_SCRIPTHASH:
99
110
{
111
+ if (sigversion != IsMineSigVersion::TOP) {
112
+ // P2SH inside P2WSH or P2SH is invalid.
113
+ return IsMineResult::INVALID;
114
+ }
100
115
CScriptID scriptID = CScriptID (uint160 (vSolutions[0 ]));
101
116
CScript subscript;
102
117
if (keystore.GetCScript (scriptID, subscript)) {
103
- isminetype ret = IsMineInner (keystore, subscript, isInvalid, IsMineSigVersion::P2SH);
104
- if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
105
- return ret;
118
+ ret = std::max (ret, IsMineInner (keystore, subscript, IsMineSigVersion::P2SH));
106
119
}
107
120
break ;
108
121
}
109
122
case TX_WITNESS_V0_SCRIPTHASH:
110
123
{
124
+ if (sigversion == IsMineSigVersion::WITNESS_V0) {
125
+ // P2WSH inside P2WSH is invalid.
126
+ return IsMineResult::INVALID;
127
+ }
111
128
if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript (CScriptID (CScript () << OP_0 << vSolutions[0 ]))) {
112
129
break ;
113
130
}
@@ -116,17 +133,17 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
116
133
CScriptID scriptID = CScriptID (hash);
117
134
CScript subscript;
118
135
if (keystore.GetCScript (scriptID, subscript)) {
119
- isminetype ret = IsMineInner (keystore, subscript, isInvalid, IsMineSigVersion::WITNESS_V0);
120
- if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid))
121
- return ret;
136
+ ret = std::max (ret, IsMineInner (keystore, subscript, IsMineSigVersion::WITNESS_V0));
122
137
}
123
138
break ;
124
139
}
125
140
126
141
case TX_MULTISIG:
127
142
{
128
143
// Never treat bare multisig outputs as ours (they can still be made watchonly-though)
129
- if (sigversion == IsMineSigVersion::TOP) break ;
144
+ if (sigversion == IsMineSigVersion::TOP) {
145
+ break ;
146
+ }
130
147
131
148
// Only consider transactions "mine" if we own ALL the
132
149
// keys involved. Multi-signature transactions that are
@@ -137,30 +154,39 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b
137
154
if (!PermitsUncompressed (sigversion)) {
138
155
for (size_t i = 0 ; i < keys.size (); i++) {
139
156
if (keys[i].size () != 33 ) {
140
- isInvalid = true ;
141
- return ISMINE_NO;
157
+ return IsMineResult::INVALID;
142
158
}
143
159
}
144
160
}
145
- if (HaveKeys (keys, keystore))
146
- return ISMINE_SPENDABLE;
161
+ if (HaveKeys (keys, keystore)) {
162
+ ret = std::max (ret, IsMineResult::SPENDABLE);
163
+ }
147
164
break ;
148
165
}
149
166
}
150
167
151
- if (keystore.HaveWatchOnly (scriptPubKey)) {
152
- // TODO: This could be optimized some by doing some work after the above solver
153
- SignatureData sigs;
154
- return ProduceSignature (keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE;
168
+ if (ret == IsMineResult::NO && keystore.HaveWatchOnly (scriptPubKey)) {
169
+ ret = std::max (ret, IsMineResult::WATCH_ONLY);
155
170
}
156
- return ISMINE_NO ;
171
+ return ret ;
157
172
}
158
173
159
174
} // namespace
160
175
161
176
isminetype IsMine (const CKeyStore& keystore, const CScript& scriptPubKey, bool & isInvalid)
162
177
{
163
- return IsMineInner (keystore, scriptPubKey, isInvalid, IsMineSigVersion::TOP);
178
+ isInvalid = false ;
179
+ switch (IsMineInner (keystore, scriptPubKey, IsMineSigVersion::TOP)) {
180
+ case IsMineResult::INVALID:
181
+ isInvalid = true ;
182
+ case IsMineResult::NO:
183
+ return ISMINE_NO;
184
+ case IsMineResult::WATCH_ONLY:
185
+ return ISMINE_WATCH_ONLY;
186
+ case IsMineResult::SPENDABLE:
187
+ return ISMINE_SPENDABLE;
188
+ }
189
+ assert (false );
164
190
}
165
191
166
192
isminetype IsMine (const CKeyStore& keystore, const CScript& scriptPubKey)
0 commit comments