aboutsummaryrefslogtreecommitdiff
path: root/src/script/standard.cpp
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2018-05-08 20:24:06 -0700
committerPieter Wuille <pieter.wuille@gmail.com>2018-05-29 14:40:18 -0700
commitc814e2e7e81fd01fcb07f4a28435741bdc463801 (patch)
treecbbbdd22d0124e8988d8e6e0c4de6ab1dcb98925 /src/script/standard.cpp
parent6b824c090f53d0a56833930fd38c41bcaec8ff4a (diff)
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.
Diffstat (limited to 'src/script/standard.cpp')
-rw-r--r--src/script/standard.cpp149
1 files changed, 62 insertions, 87 deletions
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index 76778112aa..53fcbe37de 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -35,22 +35,54 @@ const char* GetTxnOutputType(txnouttype t)
return nullptr;
}
-bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
+static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
{
- // Templates
- static std::multimap<txnouttype, CScript> mTemplates;
- if (mTemplates.empty())
- {
- // Standard tx, sender provides pubkey, receiver adds signature
- mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG));
+ if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1);
+ return CPubKey::ValidSize(pubkey);
+ }
+ if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1);
+ return CPubKey::ValidSize(pubkey);
+ }
+ return false;
+}
- // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
- mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG));
+static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
+{
+ if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
+ pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
+ return true;
+ }
+ return false;
+}
+
+/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
+static constexpr bool IsSmallInteger(opcodetype opcode)
+{
+ return opcode >= OP_1 && opcode <= OP_16;
+}
- // Sender provides N pubkeys, receivers provides M signatures
- mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG));
+static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys)
+{
+ opcodetype opcode;
+ valtype data;
+ CScript::const_iterator it = script.begin();
+ if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
+
+ if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) return false;
+ required = CScript::DecodeOP_N(opcode);
+ while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
+ pubkeys.emplace_back(std::move(data));
}
+ if (!IsSmallInteger(opcode)) return false;
+ unsigned int keys = CScript::DecodeOP_N(opcode);
+ if (pubkeys.size() != keys || keys < required) return false;
+ return (it + 1 == script.end());
+}
+bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet)
+{
vSolutionsRet.clear();
// 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
return true;
}
- // Scan templates
- const CScript& script1 = scriptPubKey;
- for (const std::pair<txnouttype, CScript>& tplate : mTemplates)
- {
- const CScript& script2 = tplate.second;
- vSolutionsRet.clear();
+ std::vector<unsigned char> data;
+ if (MatchPayToPubkey(scriptPubKey, data)) {
+ typeRet = TX_PUBKEY;
+ vSolutionsRet.push_back(std::move(data));
+ return true;
+ }
- opcodetype opcode1, opcode2;
- std::vector<unsigned char> vch1, vch2;
+ if (MatchPayToPubkeyHash(scriptPubKey, data)) {
+ typeRet = TX_PUBKEYHASH;
+ vSolutionsRet.push_back(std::move(data));
+ return true;
+ }
- // Compare
- CScript::const_iterator pc1 = script1.begin();
- CScript::const_iterator pc2 = script2.begin();
- while (true)
- {
- if (pc1 == script1.end() && pc2 == script2.end())
- {
- // Found a match
- typeRet = tplate.first;
- if (typeRet == TX_MULTISIG)
- {
- // Additional checks for TX_MULTISIG:
- unsigned char m = vSolutionsRet.front()[0];
- unsigned char n = vSolutionsRet.back()[0];
- if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
- return false;
- }
- return true;
- }
- if (!script1.GetOp(pc1, opcode1, vch1))
- break;
- if (!script2.GetOp(pc2, opcode2, vch2))
- break;
-
- // Template matching opcodes:
- if (opcode2 == OP_PUBKEYS)
- {
- while (CPubKey::ValidSize(vch1))
- {
- vSolutionsRet.push_back(vch1);
- if (!script1.GetOp(pc1, opcode1, vch1))
- break;
- }
- if (!script2.GetOp(pc2, opcode2, vch2))
- break;
- // Normal situation is to fall through
- // to other if/else statements
- }
-
- if (opcode2 == OP_PUBKEY)
- {
- if (!CPubKey::ValidSize(vch1))
- break;
- vSolutionsRet.push_back(vch1);
- }
- else if (opcode2 == OP_PUBKEYHASH)
- {
- if (vch1.size() != sizeof(uint160))
- break;
- vSolutionsRet.push_back(vch1);
- }
- else if (opcode2 == OP_SMALLINTEGER)
- { // Single-byte small integer pushed onto vSolutions
- if (opcode1 == OP_0 ||
- (opcode1 >= OP_1 && opcode1 <= OP_16))
- {
- char n = (char)CScript::DecodeOP_N(opcode1);
- vSolutionsRet.push_back(valtype(1, n));
- }
- else
- break;
- }
- else if (opcode1 != opcode2 || vch1 != vch2)
- {
- // Others must match exactly
- break;
- }
- }
+ unsigned int required;
+ std::vector<std::vector<unsigned char>> keys;
+ if (MatchMultisig(scriptPubKey, required, keys)) {
+ typeRet = TX_MULTISIG;
+ vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16
+ vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
+ vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16
+ return true;
}
vSolutionsRet.clear();