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-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <core_io.h>7#include <addresstype.h>8#include <coins.h>9#include <consensus/amount.h>10#include <consensus/consensus.h>11#include <consensus/validation.h>12#include <crypto/hex_base.h>13#include <key_io.h>14#include <prevector.h>15#include <primitives/block.h>16#include <primitives/transaction.h>17#include <script/descriptor.h>18#include <script/interpreter.h>19#include <script/script.h>20#include <script/signingprovider.h>21#include <script/solver.h>22#include <serialize.h>23#include <streams.h>24#include <tinyformat.h>25#include <uint256.h>26#include <undo.h>27#include <univalue.h>28#include <util/check.h>29#include <util/result.h>30#include <util/strencodings.h>31#include <util/string.h>32#include <util/translation.h>34#include <algorithm>35#include <compare>36#include <cstdint>37#include <exception>38#include <functional>39#include <map>40#include <memory>41#include <optional>42#include <span>43#include <stdexcept>44#include <string>45#include <utility>46#include <vector>48using util::SplitString;50namespace {51class OpCodeParser52{53private:54std::map<std::string, opcodetype> mapOpNames;56public:57OpCodeParser()58{59for (unsigned int op = 0; op <= MAX_OPCODE; ++op) {60// Allow OP_RESERVED to get into mapOpNames61if (op < OP_NOP && op != OP_RESERVED) {62continue;63}65std::string strName = GetOpName(static_cast<opcodetype>(op));66if (strName == "OP_UNKNOWN") {67continue;68}69mapOpNames[strName] = static_cast<opcodetype>(op);70// Convenience: OP_ADD and just ADD are both recognized:71if (strName.starts_with("OP_")) {72mapOpNames[strName.substr(3)] = static_cast<opcodetype>(op);73}74}75}76opcodetype Parse(const std::string& s) const77{78auto it = mapOpNames.find(s);79if (it == mapOpNames.end()) throw std::runtime_error("script parse error: unknown opcode");80return it->second;81}82};84opcodetype ParseOpCode(const std::string& s)85{86static const OpCodeParser ocp;87return ocp.Parse(s);88}90} // namespace92CScript ParseScript(const std::string& s)93{94CScript result;96std::vector<std::string> words = SplitString(s, " \t\n");98for (const std::string& w : words) {99if (w.empty()) {100// Empty string, ignore. (SplitString doesn't combine multiple separators)101} else if (std::all_of(w.begin(), w.end(), ::IsDigit) ||102(w.front() == '-' && w.size() > 1 && std::all_of(w.begin() + 1, w.end(), ::IsDigit)))103{104// Number105const auto num{ToIntegral<int64_t>(w)};107// limit the range of numbers ParseScript accepts in decimal108// since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts109if (!num.has_value() || num > int64_t{0xffffffff} || num < -1 * int64_t{0xffffffff}) {110throw std::runtime_error("script parse error: decimal numeric value only allowed in the "111"range -0xFFFFFFFF...0xFFFFFFFF");112}114result << num.value();115} else if (w.starts_with("0x") && w.size() > 2 && IsHex(std::string(w.begin() + 2, w.end()))) {116// Raw hex data, inserted NOT pushed onto stack:117std::vector<unsigned char> raw = ParseHex(std::string(w.begin() + 2, w.end()));118result.insert(result.end(), raw.begin(), raw.end());119} else if (w.size() >= 2 && w.front() == '\'' && w.back() == '\'') {120// Single-quoted string, pushed as data. NOTE: this is poor-man's121// parsing, spaces/tabs/newlines in single-quoted strings won't work.122std::vector<unsigned char> value(w.begin() + 1, w.end() - 1);123result << value;124} else {125// opcode, e.g. OP_ADD or ADD:126result << ParseOpCode(w);127}128}130return result;131}133/// Check that all of the input and output scripts of a transaction contain valid opcodes134static bool CheckTxScriptsSanity(const CMutableTransaction& tx)135{136// Check input scripts for non-coinbase txs137if (!CTransaction(tx).IsCoinBase()) {138for (unsigned int i = 0; i < tx.vin.size(); i++) {139if (!tx.vin[i].scriptSig.HasValidOps() || tx.vin[i].scriptSig.size() > MAX_SCRIPT_SIZE) {140return false;141}142}143}144// Check output scripts145for (unsigned int i = 0; i < tx.vout.size(); i++) {146if (!tx.vout[i].scriptPubKey.HasValidOps() || tx.vout[i].scriptPubKey.size() > MAX_SCRIPT_SIZE) {147return false;148}149}151return true;152}154static bool DecodeTx(CMutableTransaction& tx, const std::vector<unsigned char>& tx_data, bool try_no_witness, bool try_witness)155{156// General strategy:157// - Decode both with extended serialization (which interprets the 0x0001 tag as a marker for158// the presence of witnesses) and with legacy serialization (which interprets the tag as a159// 0-input 1-output incomplete transaction).160// - Restricted by try_no_witness (which disables legacy if false) and try_witness (which161// disables extended if false).162// - Ignore serializations that do not fully consume the hex string.163// - If neither succeeds, fail.164// - If only one succeeds, return that one.165// - If both decode attempts succeed:166// - If only one passes the CheckTxScriptsSanity check, return that one.167// - If neither or both pass CheckTxScriptsSanity, return the extended one.169CMutableTransaction tx_extended, tx_legacy;170bool ok_extended = false, ok_legacy = false;172// Try decoding with extended serialization support, and remember if the result successfully173// consumes the entire input.174if (try_witness) {175SpanReader ssData{tx_data};176try {177ssData >> TX_WITH_WITNESS(tx_extended);178if (ssData.empty()) ok_extended = true;179} catch (const std::exception&) {180// Fall through.181}182}184// Optimization: if extended decoding succeeded and the result passes CheckTxScriptsSanity,185// don't bother decoding the other way.186if (ok_extended && CheckTxScriptsSanity(tx_extended)) {187tx = std::move(tx_extended);188return true;189}191// Try decoding with legacy serialization, and remember if the result successfully consumes the entire input.192if (try_no_witness) {193SpanReader ssData{tx_data};194try {195ssData >> TX_NO_WITNESS(tx_legacy);196if (ssData.empty()) ok_legacy = true;197} catch (const std::exception&) {198// Fall through.199}200}202// If legacy decoding succeeded and passes CheckTxScriptsSanity, that's our answer, as we know203// at this point that extended decoding either failed or doesn't pass the sanity check.204if (ok_legacy && CheckTxScriptsSanity(tx_legacy)) {205tx = std::move(tx_legacy);206return true;207}209// If extended decoding succeeded, and neither decoding passes sanity, return the extended one.210if (ok_extended) {211tx = std::move(tx_extended);212return true;213}215// If legacy decoding succeeded and extended didn't, return the legacy one.216if (ok_legacy) {217tx = std::move(tx_legacy);218return true;219}221// If none succeeded, we failed.222return false;223}225bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness, bool try_witness)226{227if (!IsHex(hex_tx)) {228return false;229}231std::vector<unsigned char> txData(ParseHex(hex_tx));232return DecodeTx(tx, txData, try_no_witness, try_witness);233}235bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header)236{237if (!IsHex(hex_header)) return false;239const std::vector<unsigned char> header_data{ParseHex(hex_header)};240try {241SpanReader{header_data} >> header;242} catch (const std::exception&) {243return false;244}245return true;246}248bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)249{250if (!IsHex(strHexBlk))
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.