Inside Bitcoin
Opening the selected file…
Inside Bitcoin
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) 2013-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 <common/system.h>6#include <consensus/tx_check.h>7#include <consensus/validation.h>8#include <hash.h>9#include <script/interpreter.h>10#include <script/script.h>11#include <serialize.h>12#include <streams.h>13#include <test/data/sighash.json.h>14#include <test/util/common.h>15#include <test/util/json.h>16#include <test/util/random.h>17#include <test/util/setup_common.h>18#include <util/strencodings.h>20#include <iostream>22#include <boost/test/unit_test.hpp>24#include <univalue.h>26// Old script.cpp SignatureHash function27uint256 static SignatureHashOld(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType)28{29if (nIn >= txTo.vin.size())30{31return uint256::ONE;32}33CMutableTransaction txTmp(txTo);35// In case concatenating two scripts ends up with two codeseparators,36// or an extra one at the end, this prevents all those possible incompatibilities.37FindAndDelete(scriptCode, CScript(OP_CODESEPARATOR));39// Blank out other inputs' signatures40for (unsigned int i = 0; i < txTmp.vin.size(); i++)41txTmp.vin[i].scriptSig = CScript();42txTmp.vin[nIn].scriptSig = scriptCode;44// Blank out some of the outputs45if ((nHashType & 0x1f) == SIGHASH_NONE)46{47// Wildcard payee48txTmp.vout.clear();50// Let the others update at will51for (unsigned int i = 0; i < txTmp.vin.size(); i++)52if (i != nIn)53txTmp.vin[i].nSequence = 0;54}55else if ((nHashType & 0x1f) == SIGHASH_SINGLE)56{57// Only lock-in the txout payee at same index as txin58unsigned int nOut = nIn;59if (nOut >= txTmp.vout.size())60{61return uint256::ONE;62}63txTmp.vout.resize(nOut+1);64for (unsigned int i = 0; i < nOut; i++)65txTmp.vout[i].SetNull();67// Let the others update at will68for (unsigned int i = 0; i < txTmp.vin.size(); i++)69if (i != nIn)70txTmp.vin[i].nSequence = 0;71}73// Blank out other inputs completely, not recommended for open transactions74if (nHashType & SIGHASH_ANYONECANPAY)75{76txTmp.vin[0] = txTmp.vin[nIn];77txTmp.vin.resize(1);78}80// Serialize and hash81HashWriter ss{};82ss << TX_NO_WITNESS(txTmp) << nHashType;83return ss.GetHash();84}86struct SigHashTest : BasicTestingSetup {87void RandomScript(CScript &script) {88static const opcodetype oplist[] = {OP_FALSE, OP_1, OP_2, OP_3, OP_CHECKSIG, OP_IF, OP_VERIF, OP_RETURN, OP_CODESEPARATOR};89script = CScript();90int ops = (m_rng.randrange(10));91for (int i=0; i<ops; i++)92script << oplist[m_rng.randrange(std::size(oplist))];93}95void RandomTransaction(CMutableTransaction& tx, bool fSingle)96{97tx.version = m_rng.rand32();98tx.vin.clear();99tx.vout.clear();100tx.nLockTime = (m_rng.randbool()) ? m_rng.rand32() : 0;101int ins = (m_rng.randbits(2)) + 1;102int outs = fSingle ? ins : (m_rng.randbits(2)) + 1;103for (int in = 0; in < ins; in++) {104tx.vin.emplace_back();105CTxIn &txin = tx.vin.back();106txin.prevout.hash = Txid::FromUint256(m_rng.rand256());107txin.prevout.n = m_rng.randbits(2);108RandomScript(txin.scriptSig);109txin.nSequence = (m_rng.randbool()) ? m_rng.rand32() : std::numeric_limits<uint32_t>::max();110}111for (int out = 0; out < outs; out++) {112tx.vout.emplace_back();113CTxOut &txout = tx.vout.back();114txout.nValue = RandMoney(m_rng);115RandomScript(txout.scriptPubKey);116}117}118}; // struct SigHashTest120BOOST_FIXTURE_TEST_SUITE(sighash_tests, SigHashTest)122BOOST_AUTO_TEST_CASE(sighash_test)123{124#if defined(PRINT_SIGHASH_JSON)125std::cout << "[\n";126std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n";127int nRandomTests = 500;128#else129int nRandomTests = 50000;130#endif131for (int i=0; i<nRandomTests; i++) {132int nHashType{int(m_rng.rand32())};133CMutableTransaction txTo;134RandomTransaction(txTo, (nHashType & 0x1f) == SIGHASH_SINGLE);135CScript scriptCode;136RandomScript(scriptCode);137int nIn = m_rng.randrange(txTo.vin.size());139uint256 sh, sho;140sho = SignatureHashOld(scriptCode, CTransaction(txTo), nIn, nHashType);141sh = SignatureHash(scriptCode, txTo, nIn, nHashType, 0, SigVersion::BASE);142#if defined(PRINT_SIGHASH_JSON)143DataStream ss;144ss << TX_WITH_WITNESS(txTo);146std::cout << "\t[\"" ;147std::cout << HexStr(ss) << "\", \"";148std::cout << HexStr(scriptCode) << "\", ";149std::cout << nIn << ", ";150std::cout << nHashType << ", \"";151std::cout << sho.GetHex() << "\"]";152if (i+1 != nRandomTests) {153std::cout << ",";154}155std::cout << "\n";156#endif157BOOST_CHECK(sh == sho);158}159#if defined(PRINT_SIGHASH_JSON)160std::cout << "]\n";161#endif162}164// Goal: check that SignatureHash generates correct hash165BOOST_AUTO_TEST_CASE(sighash_from_data)166{167UniValue tests = read_json(json_tests::sighash);169for (unsigned int idx = 0; idx < tests.size(); idx++) {170const UniValue& test = tests[idx];171std::string strTest = test.write();172if (test.size() < 1) // Allow for extra stuff (useful for comments)173{174BOOST_ERROR("Bad test: " << strTest);175continue;176}177if (test.size() == 1) continue; // comment179std::string raw_tx, raw_script, sigHashHex;180int nIn, nHashType;181uint256 sh;182CTransactionRef tx;183CScript scriptCode = CScript();185try {186// deserialize test data187raw_tx = test[0].get_str();188raw_script = test[1].get_str();189nIn = test[2].getInt<int>();190nHashType = test[3].getInt<int>();191sigHashHex = test[4].get_str();193SpanReader{ParseHex(raw_tx)} >> TX_WITH_WITNESS(tx);195TxValidationState state;196BOOST_CHECK_MESSAGE(CheckTransaction(*tx, state), strTest);197BOOST_CHECK(state.IsValid());199std::vector<unsigned char> raw = ParseHex(raw_script);200scriptCode.insert(scriptCode.end(), raw.begin(), raw.end());201} catch (...) {202BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest);203continue;204}206sh = SignatureHash(scriptCode, *tx, nIn, nHashType, 0, SigVersion::BASE);207BOOST_CHECK_MESSAGE(sh.GetHex() == sigHashHex, strTest);208}209}211BOOST_AUTO_TEST_CASE(sighash_caching)212{213// Get a script, transaction and parameters as inputs to the sighash function.214CScript scriptcode;215RandomScript(scriptcode);216CScript diff_scriptcode{scriptcode};217diff_scriptcode << OP_1;218CMutableTransaction tx;219RandomTransaction(tx, /*fSingle=*/false);220const auto in_index{static_cast<uint32_t>(m_rng.randrange(tx.vin.size()))};221const auto amount{m_rng.rand<CAmount>()};223// Exercise the sighash function under both legacy and segwit v0.224for (const auto sigversion: {SigVersion::BASE, SigVersion::WITNESS_V0}) {225// For each, run it against all the 6 standard hash types and a few additional random ones.226std::vector<int32_t> hash_types{{SIGHASH_ALL, SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ALL | SIGHASH_ANYONECANPAY,227SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, SIGHASH_NONE | SIGHASH_ANYONECANPAY,228SIGHASH_ANYONECANPAY, 0, std::numeric_limits<int32_t>::max()}};229for (int i{0}; i < 10; ++i) {230hash_types.push_back(i % 2 == 0 ? m_rng.rand<int8_t>() : m_rng.rand<int32_t>());231}233// Reuse the same cache across script types. This must not cause any issue as the cached value for one hash type must never234// be confused for another (instantiating the cache within the loop instead would prevent testing this).235SigHashCache cache;236for (const auto hash_type: hash_types) {237const bool expect_one{sigversion == SigVersion::BASE && ((hash_type & 0x1f) == SIGHASH_SINGLE) && in_index >= tx.vout.size()};239// The result of computing the sighash should be the same with or without cache.240const auto sighash_with_cache{SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache)};241const auto sighash_no_cache{SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, nullptr)};242BOOST_CHECK_EQUAL(sighash_with_cache, sighash_no_cache);244// Calling the cached version again should return the same value again.245BOOST_CHECK_EQUAL(sighash_with_cache, SignatureHash(scriptcode, tx, in_index, hash_type, amount, sigversion, nullptr, &cache));247// While here we might as well also check that the result for legacy is the same as for the old SignatureHash() function.248if (sigversion == SigVersion::BASE) {249BOOST_CHECK_EQUAL(sighash_with_cache, SignatureHashOld(scriptcode, CTransaction(tx), in_index, hash_type));250}
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.