Skip to content

Commit c814e2e

Browse files
committed
Remove template matching and pseudo opcodes
The current code contains a rather complex script template matching engine, which is only used for 3 particular script types (P2PK, P2PKH, multisig). The first two of these are trivial to match for otherwise, and a specialized matcher for multisig is both more compact and more efficient than a generic one. The goal is being more flexible, so that for example larger standard multisigs inside SegWit outputs are more easy to implement. As a side-effect, it also gets rid of the pseudo opcodes hack.
1 parent 6b824c0 commit c814e2e

File tree

4 files changed

+66
-103
lines changed

4 files changed

+66
-103
lines changed

src/pubkey.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ class CPubKey
3333
/**
3434
* secp256k1:
3535
*/
36-
static const unsigned int PUBLIC_KEY_SIZE = 65;
37-
static const unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
38-
static const unsigned int SIGNATURE_SIZE = 72;
39-
static const unsigned int COMPACT_SIGNATURE_SIZE = 65;
36+
static constexpr unsigned int PUBLIC_KEY_SIZE = 65;
37+
static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
38+
static constexpr unsigned int SIGNATURE_SIZE = 72;
39+
static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
4040
/**
4141
* see www.keylength.com
4242
* script supports up to 75 for single byte push

src/script/script.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,11 +141,6 @@ const char* GetOpName(opcodetype opcode)
141141

142142
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
143143

144-
// Note:
145-
// The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum
146-
// as kind of implementation hack, they are *NOT* real opcodes. If found in real
147-
// Script, just let the default: case deal with them.
148-
149144
default:
150145
return "OP_UNKNOWN";
151146
}

src/script/script.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,6 @@ enum opcodetype
181181
OP_NOP9 = 0xb8,
182182
OP_NOP10 = 0xb9,
183183

184-
185-
// template matching params
186-
OP_SMALLINTEGER = 0xfa,
187-
OP_PUBKEYS = 0xfb,
188-
OP_PUBKEYHASH = 0xfd,
189-
OP_PUBKEY = 0xfe,
190-
191184
OP_INVALIDOPCODE = 0xff,
192185
};
193186

src/script/standard.cpp

Lines changed: 62 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)