Inside Bitcoin
Opening the selected file…
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2009-2010 Satoshi Nakamoto2// Copyright (c) 2009-present The Bitcoin Core developers3// Distributed under the MIT software license, see the accompanying4// file COPYING or http://www.opensource.org/licenses/mit-license.php.6#include <script/solver.h>8#include <prevector.h>9#include <pubkey.h>10#include <script/interpreter.h>11#include <script/script.h>13#include <cassert>14#include <string>16typedef std::vector<unsigned char> valtype;18std::string GetTxnOutputType(TxoutType t)19{20switch (t) {21case TxoutType::NONSTANDARD: return "nonstandard";22case TxoutType::PUBKEY: return "pubkey";23case TxoutType::PUBKEYHASH: return "pubkeyhash";24case TxoutType::SCRIPTHASH: return "scripthash";25case TxoutType::MULTISIG: return "multisig";26case TxoutType::NULL_DATA: return "nulldata";27case TxoutType::ANCHOR: return "anchor";28case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";29case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";30case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";31case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";32} // no default case, so the compiler can warn about missing cases33assert(false);34}36static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)37{38if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {39pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);40return CPubKey::ValidSize(pubkey);41}42if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {43pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);44return CPubKey::ValidSize(pubkey);45}46return false;47}49static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)50{51if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {52pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);53return true;54}55return false;56}58/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */59static constexpr bool IsSmallInteger(opcodetype opcode)60{61return opcode >= OP_1 && opcode <= OP_16;62}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. */66static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max)67{68int count;69if (IsSmallInteger(opcode)) {70count = CScript::DecodeOP_N(opcode);71} else if (IsPushdataOp(opcode)) {72if (!CheckMinimalPush(data, opcode)) return {};73try {74count = CScriptNum(data, /* fRequireMinimal = */ true).getint();75} catch (const scriptnum_error&) {76return {};77}78} else {79return {};80}81if (count < min || count > max) return {};82return count;83}85static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)86{87opcodetype opcode;88valtype data;90CScript::const_iterator it = script.begin();91if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;93if (!script.GetOp(it, opcode, data)) return false;94auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);95if (!req_sigs) return false;96required_sigs = *req_sigs;97while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {98pubkeys.emplace_back(std::move(data));99}100auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);101if (!num_keys) return false;102if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;104return (it + 1 == script.end());105}107std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> MatchMultiA(const CScript& script)108{109std::vector<std::span<const unsigned char>> keyspans;111// Redundant, but very fast and selective test.112if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};114// Parse keys115auto it = script.begin();116while (script.end() - it >= 34) {117if (*it != 32) return {};118++it;119keyspans.emplace_back(&*it, 32);120it += 32;121if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};122++it;123}124if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};126// Parse threshold.127opcodetype opcode;128std::vector<unsigned char> data;129if (!script.GetOp(it, opcode, data)) return {};130if (it == script.end()) return {};131if (*it != OP_NUMEQUAL) return {};132++it;133if (it != script.end()) return {};134auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());135if (!threshold) return {};137// Construct result.138return std::pair{*threshold, std::move(keyspans)};139}141TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)142{143vSolutionsRet.clear();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_EQUAL147if (scriptPubKey.IsPayToScriptHash())148{149std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);150vSolutionsRet.push_back(hashBytes);151return TxoutType::SCRIPTHASH;152}154int witnessversion;155std::vector<unsigned char> witnessprogram;156if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {157if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {158vSolutionsRet.push_back(std::move(witnessprogram));159return TxoutType::WITNESS_V0_KEYHASH;160}161if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {162vSolutionsRet.push_back(std::move(witnessprogram));163return TxoutType::WITNESS_V0_SCRIPTHASH;164}165if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {166vSolutionsRet.push_back(std::move(witnessprogram));167return TxoutType::WITNESS_V1_TAPROOT;168}169if (scriptPubKey.IsPayToAnchor()) {170return TxoutType::ANCHOR;171}172if (witnessversion != 0) {173vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});174vSolutionsRet.push_back(std::move(witnessprogram));175return TxoutType::WITNESS_UNKNOWN;176}177return TxoutType::NONSTANDARD;178}180// Provably prunable, data-carrying output181//182// So long as script passes the IsUnspendable() test and all but the first183// byte passes the IsPushOnly() test we don't care what exactly is in the184// script.185if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {186return TxoutType::NULL_DATA;187}189std::vector<unsigned char> data;190if (MatchPayToPubkey(scriptPubKey, data)) {191vSolutionsRet.push_back(std::move(data));192return TxoutType::PUBKEY;193}195if (MatchPayToPubkeyHash(scriptPubKey, data)) {196vSolutionsRet.push_back(std::move(data));197return TxoutType::PUBKEYHASH;198}200int required;201std::vector<std::vector<unsigned char>> keys;202if (MatchMultisig(scriptPubKey, required, keys)) {203vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20204vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());205vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20206return TxoutType::MULTISIG;207}209vSolutionsRet.clear();210return TxoutType::NONSTANDARD;211}213CScript GetScriptForRawPubKey(const CPubKey& pubKey)214{215return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;216}218CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)219{220CScript script;222script << nRequired;223for (const CPubKey& key : keys)224script << ToByteVector(key);225script << keys.size() << OP_CHECKMULTISIG;227return script;228}