solver.cpp
1 // Copyright (c) 2009-2010 Satoshi Nakamoto 2 // Copyright (c) 2009-present The Bitcoin Core developers 3 // Distributed under the MIT software license, see the accompanying 4 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 6 #include <pubkey.h> 7 #include <script/interpreter.h> 8 #include <script/script.h> 9 #include <script/solver.h> 10 #include <span.h> 11 12 #include <algorithm> 13 #include <cassert> 14 #include <string> 15 16 typedef std::vector<unsigned char> valtype; 17 18 std::string GetTxnOutputType(TxoutType t) 19 { 20 switch (t) { 21 case TxoutType::NONSTANDARD: return "nonstandard"; 22 case TxoutType::PUBKEY: return "pubkey"; 23 case TxoutType::PUBKEYHASH: return "pubkeyhash"; 24 case TxoutType::SCRIPTHASH: return "scripthash"; 25 case TxoutType::MULTISIG: return "multisig"; 26 case TxoutType::NULL_DATA: return "nulldata"; 27 case TxoutType::ANCHOR: return "anchor"; 28 case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; 29 case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; 30 case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot"; 31 case TxoutType::WITNESS_UNKNOWN: return "witness_unknown"; 32 } // no default case, so the compiler can warn about missing cases 33 assert(false); 34 } 35 36 static bool MatchPayToPubkey(const CScript& script, valtype& pubkey) 37 { 38 if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) { 39 pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1); 40 return CPubKey::ValidSize(pubkey); 41 } 42 if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) { 43 pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1); 44 return CPubKey::ValidSize(pubkey); 45 } 46 return false; 47 } 48 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 } 63 64 /** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair, 65 * whether it's OP_n or through a push. */ 66 static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max) 67 { 68 int count; 69 if (IsSmallInteger(opcode)) { 70 count = CScript::DecodeOP_N(opcode); 71 } else if (IsPushdataOp(opcode)) { 72 if (!CheckMinimalPush(data, opcode)) return {}; 73 try { 74 count = CScriptNum(data, /* fRequireMinimal = */ true).getint(); 75 } catch (const scriptnum_error&) { 76 return {}; 77 } 78 } else { 79 return {}; 80 } 81 if (count < min || count > max) return {}; 82 return count; 83 } 84 85 static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys) 86 { 87 opcodetype opcode; 88 valtype data; 89 90 CScript::const_iterator it = script.begin(); 91 if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false; 92 93 if (!script.GetOp(it, opcode, data)) return false; 94 auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG); 95 if (!req_sigs) return false; 96 required_sigs = *req_sigs; 97 while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) { 98 pubkeys.emplace_back(std::move(data)); 99 } 100 auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG); 101 if (!num_keys) return false; 102 if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false; 103 104 return (it + 1 == script.end()); 105 } 106 107 std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> MatchMultiA(const CScript& script) 108 { 109 std::vector<std::span<const unsigned char>> keyspans; 110 111 // Redundant, but very fast and selective test. 112 if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {}; 113 114 // Parse keys 115 auto it = script.begin(); 116 while (script.end() - it >= 34) { 117 if (*it != 32) return {}; 118 ++it; 119 keyspans.emplace_back(&*it, 32); 120 it += 32; 121 if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {}; 122 ++it; 123 } 124 if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {}; 125 126 // Parse threshold. 127 opcodetype opcode; 128 std::vector<unsigned char> data; 129 if (!script.GetOp(it, opcode, data)) return {}; 130 if (it == script.end()) return {}; 131 if (*it != OP_NUMEQUAL) return {}; 132 ++it; 133 if (it != script.end()) return {}; 134 auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size()); 135 if (!threshold) return {}; 136 137 // Construct result. 138 return std::pair{*threshold, std::move(keyspans)}; 139 } 140 141 TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet) 142 { 143 vSolutionsRet.clear(); 144 145 // Shortcut for pay-to-script-hash, which are more constrained than the other types: 146 // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL 147 if (scriptPubKey.IsPayToScriptHash()) 148 { 149 std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); 150 vSolutionsRet.push_back(hashBytes); 151 return TxoutType::SCRIPTHASH; 152 } 153 154 int witnessversion; 155 std::vector<unsigned char> witnessprogram; 156 if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { 157 if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { 158 vSolutionsRet.push_back(std::move(witnessprogram)); 159 return TxoutType::WITNESS_V0_KEYHASH; 160 } 161 if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { 162 vSolutionsRet.push_back(std::move(witnessprogram)); 163 return TxoutType::WITNESS_V0_SCRIPTHASH; 164 } 165 if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) { 166 vSolutionsRet.push_back(std::move(witnessprogram)); 167 return TxoutType::WITNESS_V1_TAPROOT; 168 } 169 if (scriptPubKey.IsPayToAnchor()) { 170 return TxoutType::ANCHOR; 171 } 172 if (witnessversion != 0) { 173 vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); 174 vSolutionsRet.push_back(std::move(witnessprogram)); 175 return TxoutType::WITNESS_UNKNOWN; 176 } 177 return TxoutType::NONSTANDARD; 178 } 179 180 // Provably prunable, data-carrying output 181 // 182 // So long as script passes the IsUnspendable() test and all but the first 183 // byte passes the IsPushOnly() test we don't care what exactly is in the 184 // script. 185 if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) { 186 return TxoutType::NULL_DATA; 187 } 188 189 std::vector<unsigned char> data; 190 if (MatchPayToPubkey(scriptPubKey, data)) { 191 vSolutionsRet.push_back(std::move(data)); 192 return TxoutType::PUBKEY; 193 } 194 195 if (MatchPayToPubkeyHash(scriptPubKey, data)) { 196 vSolutionsRet.push_back(std::move(data)); 197 return TxoutType::PUBKEYHASH; 198 } 199 200 int required; 201 std::vector<std::vector<unsigned char>> keys; 202 if (MatchMultisig(scriptPubKey, required, keys)) { 203 vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20 204 vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); 205 vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20 206 return TxoutType::MULTISIG; 207 } 208 209 vSolutionsRet.clear(); 210 return TxoutType::NONSTANDARD; 211 } 212 213 CScript GetScriptForRawPubKey(const CPubKey& pubKey) 214 { 215 return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG; 216 } 217 218 CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) 219 { 220 CScript script; 221 222 script << nRequired; 223 for (const CPubKey& key : keys) 224 script << ToByteVector(key); 225 script << keys.size() << OP_CHECKMULTISIG; 226 227 return script; 228 }