diff --git a/src/pubkey.h b/src/pubkey.h --- a/src/pubkey.h +++ b/src/pubkey.h @@ -33,10 +33,10 @@ /** * secp256k1: */ - static const unsigned int PUBLIC_KEY_SIZE = 65; - static const unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33; - static const unsigned int SIGNATURE_SIZE = 72; - static const unsigned int COMPACT_SIGNATURE_SIZE = 65; + static constexpr unsigned int PUBLIC_KEY_SIZE = 65; + static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33; + static constexpr unsigned int SIGNATURE_SIZE = 72; + static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65; /** * see www.keylength.com * script supports up to 75 for single byte push diff --git a/src/script/script.h b/src/script/script.h --- a/src/script/script.h +++ b/src/script/script.h @@ -189,12 +189,6 @@ OP_PREFIX_BEGIN = 0xf0, OP_PREFIX_END = 0xf7, - // template matching params - OP_SMALLINTEGER = 0xfa, - OP_PUBKEYS = 0xfb, - OP_PUBKEYHASH = 0xfd, - OP_PUBKEY = 0xfe, - OP_INVALIDOPCODE = 0xff, }; diff --git a/src/script/script.cpp b/src/script/script.cpp --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -258,12 +258,6 @@ case OP_INVALIDOPCODE: return "OP_INVALIDOPCODE"; - // Note: - // The template matching params OP_SMALLINTEGER/etc are defined in - // opcodetype enum as kind of implementation hack, they are *NOT* - // real opcodes. If found in real Script, just let the default: - // case deal with them. - default: return "OP_UNKNOWN"; } diff --git a/src/script/standard.cpp b/src/script/standard.cpp --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -35,27 +35,67 @@ return nullptr; } -bool Solver(const CScript &scriptPubKey, txnouttype &typeRet, - std::vector> &vSolutionsRet) { - // Templates - static std::multimap mTemplates; - if (mTemplates.empty()) { - // Standard tx, sender provides pubkey, receiver adds signature - mTemplates.insert( - std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); - - // 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)); - - // 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 MatchPayToPubkey(const CScript &script, valtype &pubkey) { + 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; +} + +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; +} + +static bool MatchMultisig(const CScript &script, unsigned int &required, + std::vector &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> &vSolutionsRet) { vSolutionsRet.clear(); // Shortcut for pay-to-script-hash, which are more constrained than the @@ -80,78 +120,29 @@ return true; } - // Scan templates - const CScript &script1 = scriptPubKey; - for (const std::pair &tplate : mTemplates) { - const CScript &script2 = tplate.second; - vSolutionsRet.clear(); - - opcodetype opcode1, opcode2; - std::vector vch1, vch2; - - // 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: - uint8_t m = vSolutionsRet.front()[0]; - uint8_t 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; - } + std::vector data; + if (MatchPayToPubkey(scriptPubKey, data)) { + typeRet = TX_PUBKEY; + vSolutionsRet.push_back(std::move(data)); + return true; + } - // 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 (MatchPayToPubkeyHash(scriptPubKey, data)) { + typeRet = TX_PUBKEYHASH; + vSolutionsRet.push_back(std::move(data)); + return true; + } - 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> keys; + if (MatchMultisig(scriptPubKey, required, keys)) { + typeRet = TX_MULTISIG; + // safe as required is in range 1..16 + vSolutionsRet.push_back({static_cast(required)}); + vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); + // safe as size is in range 1..16 + vSolutionsRet.push_back({static_cast(keys.size())}); + return true; } vSolutionsRet.clear();