@@ -33,22 +33,54 @@ const char* GetTxnOutputType(txnouttype t)
3333 return nullptr ;
3434}
3535
36- bool Solver (const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector< unsigned char > >& vSolutionsRet )
36+ static bool MatchPayToPubkey (const CScript& script, valtype& pubkey )
3737{
38- // Templates
39- static std::multimap<txnouttype, CScript> mTemplates ;
40- if (mTemplates .empty ())
41- {
42- // Standard tx, sender provides pubkey, receiver adds signature
43- mTemplates .insert (std::make_pair (TX_PUBKEY, CScript () << OP_PUBKEY << OP_CHECKSIG));
38+ if (script.size () == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0 ] == CPubKey::PUBLIC_KEY_SIZE && script.back () == OP_CHECKSIG) {
39+ pubkey = valtype (script.begin () + 1 , script.begin () + CPubKey::PUBLIC_KEY_SIZE + 1 );
40+ return CPubKey::ValidSize (pubkey);
41+ }
42+ if (script.size () == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0 ] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back () == OP_CHECKSIG) {
43+ pubkey = valtype (script.begin () + 1 , script.begin () + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1 );
44+ return CPubKey::ValidSize (pubkey);
45+ }
46+ return false ;
47+ }
4448
45- // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
46- mTemplates .insert (std::make_pair (TX_PUBKEYHASH, CScript () << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
49+ static bool MatchPayToPubkeyHash (const CScript& script, valtype& pubkeyhash)
50+ {
51+ if (script.size () == 25 && script[0 ] == OP_DUP && script[1 ] == OP_HASH160 && script[2 ] == 20 && script[23 ] == OP_EQUALVERIFY && script[24 ] == OP_CHECKSIG) {
52+ pubkeyhash = valtype (script.begin () + 3 , script.begin () + 23 );
53+ return true ;
54+ }
55+ return false ;
56+ }
57+
58+ /* * Test for "small positive integer" script opcodes - OP_1 through OP_16. */
59+ static constexpr bool IsSmallInteger (opcodetype opcode)
60+ {
61+ return opcode >= OP_1 && opcode <= OP_16;
62+ }
4763
48- // Sender provides N pubkeys, receivers provides M signatures
49- mTemplates .insert (std::make_pair (TX_MULTISIG, CScript () << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
64+ static bool MatchMultisig (const CScript& script, unsigned int & required, std::vector<valtype>& pubkeys)
65+ {
66+ opcodetype opcode;
67+ valtype data;
68+ CScript::const_iterator it = script.begin ();
69+ if (script.size () < 1 || script.back () != OP_CHECKMULTISIG) return false ;
70+
71+ if (!script.GetOp (it, opcode, data) || !IsSmallInteger (opcode)) return false ;
72+ required = CScript::DecodeOP_N (opcode);
73+ while (script.GetOp (it, opcode, data) && CPubKey::ValidSize (data)) {
74+ pubkeys.emplace_back (std::move (data));
5075 }
76+ if (!IsSmallInteger (opcode)) return false ;
77+ unsigned int keys = CScript::DecodeOP_N (opcode);
78+ if (pubkeys.size () != keys || keys < required) return false ;
79+ return (it + 1 == script.end ());
80+ }
5181
82+ bool Solver (const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char > >& vSolutionsRet)
83+ {
5284 vSolutionsRet.clear ();
5385
5486 // Shortcut for pay-to-script-hash, which are more constrained than the other types:
@@ -71,84 +103,27 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v
71103 return true ;
72104 }
73105
74- // Scan templates
75- const CScript& script1 = scriptPubKey;
76- for ( const std::pair< const txnouttype, CScript>& tplate : mTemplates )
77- {
78- const CScript& script2 = tplate. second ;
79- vSolutionsRet. clear ();
106+ std::vector< unsigned char > data;
107+ if ( MatchPayToPubkey (scriptPubKey, data)) {
108+ typeRet = TX_PUBKEY;
109+ vSolutionsRet. push_back ( std::move (data));
110+ return true ;
111+ }
80112
81- opcodetype opcode1, opcode2;
82- std::vector<unsigned char > vch1, vch2;
113+ if (MatchPayToPubkeyHash (scriptPubKey, data)) {
114+ typeRet = TX_PUBKEYHASH;
115+ vSolutionsRet.push_back (std::move (data));
116+ return true ;
117+ }
83118
84- // Compare
85- CScript::const_iterator pc1 = script1.begin ();
86- CScript::const_iterator pc2 = script2.begin ();
87- while (true )
88- {
89- if (pc1 == script1.end () && pc2 == script2.end ())
90- {
91- // Found a match
92- typeRet = tplate.first ;
93- if (typeRet == TX_MULTISIG)
94- {
95- // Additional checks for TX_MULTISIG:
96- unsigned char m = vSolutionsRet.front ()[0 ];
97- unsigned char n = vSolutionsRet.back ()[0 ];
98- if (m < 1 || n < 1 || m > n || vSolutionsRet.size ()-2 != n)
99- return false ;
100- }
101- return true ;
102- }
103- if (!script1.GetOp (pc1, opcode1, vch1))
104- break ;
105- if (!script2.GetOp (pc2, opcode2, vch2))
106- break ;
107-
108- // Template matching opcodes:
109- if (opcode2 == OP_PUBKEYS)
110- {
111- while (CPubKey::ValidSize (vch1))
112- {
113- vSolutionsRet.push_back (vch1);
114- if (!script1.GetOp (pc1, opcode1, vch1))
115- break ;
116- }
117- if (!script2.GetOp (pc2, opcode2, vch2))
118- break ;
119- // Normal situation is to fall through
120- // to other if/else statements
121- }
122-
123- if (opcode2 == OP_PUBKEY)
124- {
125- if (!CPubKey::ValidSize (vch1))
126- break ;
127- vSolutionsRet.push_back (vch1);
128- }
129- else if (opcode2 == OP_PUBKEYHASH)
130- {
131- if (vch1.size () != sizeof (uint160))
132- break ;
133- vSolutionsRet.push_back (vch1);
134- }
135- else if (opcode2 == OP_SMALLINTEGER)
136- { // Single-byte small integer pushed onto vSolutions
137- if (opcode1 == OP_0 ||
138- (opcode1 >= OP_1 && opcode1 <= OP_16))
139- {
140- char n = (char )CScript::DecodeOP_N (opcode1);
141- vSolutionsRet.push_back (valtype (1 , n));
142- }
143- else
144- break ;
145- }
146- else if (opcode1 != opcode2 || vch1 != vch2)
147- {
148- // Others must match exactly
149- break ;
150- }
151- }
119+ unsigned int required;
120+ std::vector<std::vector<unsigned char >> keys;
121+ if (MatchMultisig (scriptPubKey, required, keys)) {
122+ typeRet = TX_MULTISIG;
123+ vSolutionsRet.push_back ({static_cast <unsigned char >(required)}); // safe as required is in range 1..16
124+ vSolutionsRet.insert (vSolutionsRet.end (), keys.begin (), keys.end ());
125+ vSolutionsRet.push_back ({static_cast <unsigned char >(keys.size ())}); // safe as size is in range 1..16
126+ return true ;
152127 }
153128
154129 vSolutionsRet.clear ();
0 commit comments