@@ -35,22 +35,54 @@ const char* GetTxnOutputType(txnouttype t)
3535 return nullptr ;
3636}
3737
38- bool Solver (const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector< unsigned char > >& vSolutionsRet )
38+ static bool MatchPayToPubkey (const CScript& script, valtype& pubkey )
3939{
40- // Templates
41- static std::multimap<txnouttype, CScript> mTemplates ;
42- if (mTemplates .empty ())
43- {
44- // Standard tx, sender provides pubkey, receiver adds signature
45- mTemplates .insert (std::make_pair (TX_PUBKEY, CScript () << OP_PUBKEY << OP_CHECKSIG));
40+ if (script.size () == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0 ] == CPubKey::PUBLIC_KEY_SIZE && script.back () == OP_CHECKSIG) {
41+ pubkey = valtype (script.begin () + 1 , script.begin () + CPubKey::PUBLIC_KEY_SIZE + 1 );
42+ return CPubKey::ValidSize (pubkey);
43+ }
44+ if (script.size () == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0 ] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back () == OP_CHECKSIG) {
45+ pubkey = valtype (script.begin () + 1 , script.begin () + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1 );
46+ return CPubKey::ValidSize (pubkey);
47+ }
48+ return false ;
49+ }
4650
47- // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
48- mTemplates .insert (std::make_pair (TX_PUBKEYHASH, CScript () << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
51+ static bool MatchPayToPubkeyHash (const CScript& script, valtype& pubkeyhash)
52+ {
53+ if (script.size () == 25 && script[0 ] == OP_DUP && script[1 ] == OP_HASH160 && script[2 ] == 20 && script[23 ] == OP_EQUALVERIFY && script[24 ] == OP_CHECKSIG) {
54+ pubkeyhash = valtype (script.begin () + 3 , script.begin () + 23 );
55+ return true ;
56+ }
57+ return false ;
58+ }
59+
60+ /* * Test for "small positive integer" script opcodes - OP_1 through OP_16. */
61+ static constexpr bool IsSmallInteger (opcodetype opcode)
62+ {
63+ return opcode >= OP_1 && opcode <= OP_16;
64+ }
4965
50- // Sender provides N pubkeys, receivers provides M signatures
51- mTemplates .insert (std::make_pair (TX_MULTISIG, CScript () << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
66+ static bool MatchMultisig (const CScript& script, unsigned int & required, std::vector<valtype>& pubkeys)
67+ {
68+ opcodetype opcode;
69+ valtype data;
70+ CScript::const_iterator it = script.begin ();
71+ if (script.size () < 1 || script.back () != OP_CHECKMULTISIG) return false ;
72+
73+ if (!script.GetOp (it, opcode, data) || !IsSmallInteger (opcode)) return false ;
74+ required = CScript::DecodeOP_N (opcode);
75+ while (script.GetOp (it, opcode, data) && CPubKey::ValidSize (data)) {
76+ pubkeys.emplace_back (std::move (data));
5277 }
78+ if (!IsSmallInteger (opcode)) return false ;
79+ unsigned int keys = CScript::DecodeOP_N (opcode);
80+ if (pubkeys.size () != keys || keys < required) return false ;
81+ return (it + 1 == script.end ());
82+ }
5383
84+ bool Solver (const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char > >& vSolutionsRet)
85+ {
5486 vSolutionsRet.clear ();
5587
5688 // Shortcut for pay-to-script-hash, which are more constrained than the other types:
@@ -95,84 +127,27 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
95127 return true ;
96128 }
97129
98- // Scan templates
99- const CScript& script1 = scriptPubKey;
100- for ( const std::pair<txnouttype, CScript>& tplate : mTemplates )
101- {
102- const CScript& script2 = tplate. second ;
103- vSolutionsRet. clear ();
130+ std::vector< unsigned char > data;
131+ if ( MatchPayToPubkey (scriptPubKey, data)) {
132+ typeRet = TX_PUBKEY;
133+ vSolutionsRet. push_back ( std::move (data));
134+ return true ;
135+ }
104136
105- opcodetype opcode1, opcode2;
106- std::vector<unsigned char > vch1, vch2;
137+ if (MatchPayToPubkeyHash (scriptPubKey, data)) {
138+ typeRet = TX_PUBKEYHASH;
139+ vSolutionsRet.push_back (std::move (data));
140+ return true ;
141+ }
107142
108- // Compare
109- CScript::const_iterator pc1 = script1.begin ();
110- CScript::const_iterator pc2 = script2.begin ();
111- while (true )
112- {
113- if (pc1 == script1.end () && pc2 == script2.end ())
114- {
115- // Found a match
116- typeRet = tplate.first ;
117- if (typeRet == TX_MULTISIG)
118- {
119- // Additional checks for TX_MULTISIG:
120- unsigned char m = vSolutionsRet.front ()[0 ];
121- unsigned char n = vSolutionsRet.back ()[0 ];
122- if (m < 1 || n < 1 || m > n || vSolutionsRet.size ()-2 != n)
123- return false ;
124- }
125- return true ;
126- }
127- if (!script1.GetOp (pc1, opcode1, vch1))
128- break ;
129- if (!script2.GetOp (pc2, opcode2, vch2))
130- break ;
131-
132- // Template matching opcodes:
133- if (opcode2 == OP_PUBKEYS)
134- {
135- while (CPubKey::ValidSize (vch1))
136- {
137- vSolutionsRet.push_back (vch1);
138- if (!script1.GetOp (pc1, opcode1, vch1))
139- break ;
140- }
141- if (!script2.GetOp (pc2, opcode2, vch2))
142- break ;
143- // Normal situation is to fall through
144- // to other if/else statements
145- }
146-
147- if (opcode2 == OP_PUBKEY)
148- {
149- if (!CPubKey::ValidSize (vch1))
150- break ;
151- vSolutionsRet.push_back (vch1);
152- }
153- else if (opcode2 == OP_PUBKEYHASH)
154- {
155- if (vch1.size () != sizeof (uint160))
156- break ;
157- vSolutionsRet.push_back (vch1);
158- }
159- else if (opcode2 == OP_SMALLINTEGER)
160- { // Single-byte small integer pushed onto vSolutions
161- if (opcode1 == OP_0 ||
162- (opcode1 >= OP_1 && opcode1 <= OP_16))
163- {
164- char n = (char )CScript::DecodeOP_N (opcode1);
165- vSolutionsRet.push_back (valtype (1 , n));
166- }
167- else
168- break ;
169- }
170- else if (opcode1 != opcode2 || vch1 != vch2)
171- {
172- // Others must match exactly
173- break ;
174- }
175- }
143+ unsigned int required;
144+ std::vector<std::vector<unsigned char >> keys;
145+ if (MatchMultisig (scriptPubKey, required, keys)) {
146+ typeRet = TX_MULTISIG;
147+ vSolutionsRet.push_back ({static_cast <unsigned char >(required)}); // safe as required is in range 1..16
148+ vSolutionsRet.insert (vSolutionsRet.end (), keys.begin (), keys.end ());
149+ vSolutionsRet.push_back ({static_cast <unsigned char >(keys.size ())}); // safe as size is in range 1..16
150+ return true ;
176151 }
177152
178153 vSolutionsRet.clear ();
0 commit comments