Skip to content

Commit 9c7a894

Browse files
laanwjgades
authored andcommitted
Merge bitcoin#13194: Remove template matching and pseudo opcodes
c814e2e Remove template matching and pseudo opcodes (Pieter Wuille) Pull request description: 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 easier to implement. As a side-effect, it also gets rid of the pseudo opcodes hack. Tree-SHA512: 643b409c5c36821519f613a43efd399af0ec99b6131f35cd4024decfb2d483d719e0e921cd088bc9832a7ac797cb4a6b1158b8574c82f7fbebb75f1b31b359df
1 parent 7b2755b commit 9c7a894

File tree

4 files changed

+66
-102
lines changed

4 files changed

+66
-102
lines changed

src/pubkey.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ class CPubKey
3434
/**
3535
* secp256k1:
3636
*/
37-
static const unsigned int PUBLIC_KEY_SIZE = 65;
38-
static const unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
39-
static const unsigned int SIGNATURE_SIZE = 72;
40-
static const unsigned int COMPACT_SIGNATURE_SIZE = 65;
37+
static constexpr unsigned int PUBLIC_KEY_SIZE = 65;
38+
static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
39+
static constexpr unsigned int SIGNATURE_SIZE = 72;
40+
static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
4141
/**
4242
* see www.keylength.com
4343
* 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
@@ -146,11 +146,6 @@ const char* GetOpName(opcodetype opcode)
146146

147147
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
148148

149-
// Note:
150-
// The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum
151-
// as kind of implementation hack, they are *NOT* real opcodes. If found in real
152-
// Script, just let the default: case deal with them.
153-
154149
default:
155150
return "OP_UNKNOWN";
156151
}

src/script/script.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,6 @@ enum opcodetype
188188
OP_CHECKDATASIG = 0xba,
189189
OP_CHECKDATASIGVERIFY = 0xbb,
190190

191-
// template matching params
192-
OP_SMALLINTEGER = 0xfa,
193-
OP_PUBKEYS = 0xfb,
194-
OP_PUBKEYHASH = 0xfd,
195-
OP_PUBKEY = 0xfe,
196-
197191
OP_INVALIDOPCODE = 0xff,
198192
};
199193

src/script/standard.cpp

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

Comments
 (0)