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) 2012-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 <consensus/tx_verify.h>6#include <key.h>7#include <policy/policy.h>8#include <policy/settings.h>9#include <script/script.h>10#include <script/script_error.h>11#include <script/sign.h>12#include <script/signingprovider.h>13#include <test/util/setup_common.h>14#include <test/util/transaction_utils.h>15#include <validation.h>17#include <vector>19#include <boost/test/unit_test.hpp>21// Helpers:22static bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason)23{24return IsStandardTx(tx, std::nullopt, permit_bare_multisig, CFeeRate{DUST_RELAY_TX_FEE}, reason);25}27static bool IsStandardTx(const CTransaction& tx, std::string& reason)28{29return IsStandardTx(tx, std::nullopt, /*permit_bare_multisig=*/true, CFeeRate{DUST_RELAY_TX_FEE}, reason) &&30IsStandardTx(tx, std::nullopt, /*permit_bare_multisig=*/false, CFeeRate{DUST_RELAY_TX_FEE}, reason);31}33static std::vector<unsigned char> Serialize(const CScript& s)34{35std::vector<unsigned char> sSerialized(s.begin(), s.end());36return sSerialized;37}39static bool Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, ScriptError& err)40{41// Create dummy to/from transactions:42CMutableTransaction txFrom;43txFrom.vout.resize(1);44txFrom.vout[0].scriptPubKey = scriptPubKey;46CMutableTransaction txTo;47txTo.vin.resize(1);48txTo.vout.resize(1);49txTo.vin[0].prevout.n = 0;50txTo.vin[0].prevout.hash = txFrom.GetHash();51txTo.vin[0].scriptSig = scriptSig;52txTo.vout[0].nValue = 1;54return VerifyScript(scriptSig, scriptPubKey, nullptr, fStrict ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE, MutableTransactionSignatureChecker(&txTo, 0, txFrom.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &err);55}58BOOST_FIXTURE_TEST_SUITE(script_p2sh_tests, BasicTestingSetup)60BOOST_AUTO_TEST_CASE(sign)61{62// Pay-to-script-hash looks like this:63// scriptSig: <sig> <sig...> <serialized_script>64// scriptPubKey: HASH160 <hash> EQUAL66// Test SignSignature() (and therefore the version of Solver() that signs transactions)67FillableSigningProvider keystore;68CKey key[4];69for (int i = 0; i < 4; i++)70{71key[i].MakeNewKey(true);72BOOST_CHECK(keystore.AddKey(key[i]));73}75// 8 Scripts: checking all combinations of76// different keys, straight/P2SH, pubkey/pubkeyhash77CScript standardScripts[4];78standardScripts[0] << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;79standardScripts[1] = GetScriptForDestination(PKHash(key[1].GetPubKey()));80standardScripts[2] << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;81standardScripts[3] = GetScriptForDestination(PKHash(key[2].GetPubKey()));82CScript evalScripts[4];83for (int i = 0; i < 4; i++)84{85BOOST_CHECK(keystore.AddCScript(standardScripts[i]));86evalScripts[i] = GetScriptForDestination(ScriptHash(standardScripts[i]));87}89CMutableTransaction txFrom; // Funding transaction:90std::string reason;91txFrom.vout.resize(8);92for (int i = 0; i < 4; i++)93{94txFrom.vout[i].scriptPubKey = evalScripts[i];95txFrom.vout[i].nValue = COIN;96txFrom.vout[i+4].scriptPubKey = standardScripts[i];97txFrom.vout[i+4].nValue = COIN;98}99BOOST_CHECK(IsStandardTx(CTransaction(txFrom), reason));101CMutableTransaction txTo[8]; // Spending transactions102for (int i = 0; i < 8; i++)103{104txTo[i].vin.resize(1);105txTo[i].vout.resize(1);106txTo[i].vin[0].prevout.n = i;107txTo[i].vin[0].prevout.hash = txFrom.GetHash();108txTo[i].vout[0].nValue = 1;109}110for (int i = 0; i < 8; i++)111{112SignatureData empty;113BOOST_CHECK_MESSAGE(SignSignature(keystore, CTransaction(txFrom), txTo[i], 0, SIGHASH_ALL, empty), strprintf("SignSignature %d", i));114}115// All of the above should be OK, and the txTos have valid signatures116// Check to make sure signature verification fails if we use the wrong ScriptSig:117SignatureCache signature_cache{DEFAULT_SIGNATURE_CACHE_BYTES};118for (int i = 0; i < 8; i++) {119PrecomputedTransactionData txdata(txTo[i]);120for (int j = 0; j < 8; j++)121{122CScript sigSave = txTo[i].vin[0].scriptSig;123txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig;124bool sigOK = !CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], CTransaction(txTo[i]), signature_cache, 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)().has_value();125if (i == j)126BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j));127else128BOOST_CHECK_MESSAGE(!sigOK, strprintf("VerifySignature %d %d", i, j));129txTo[i].vin[0].scriptSig = sigSave;130}131}132}134BOOST_AUTO_TEST_CASE(norecurse)135{136ScriptError err;137// Make sure only the outer pay-to-script-hash does the138// extra-validation thing:139CScript invalidAsScript;140invalidAsScript << OP_INVALIDOPCODE << OP_INVALIDOPCODE;142CScript p2sh = GetScriptForDestination(ScriptHash(invalidAsScript));144CScript scriptSig;145scriptSig << Serialize(invalidAsScript);147// Should not verify, because it will try to execute OP_INVALIDOPCODE148BOOST_CHECK(!Verify(scriptSig, p2sh, true, err));149BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_BAD_OPCODE, ScriptErrorString(err));151// Try to recur, and verification should succeed because152// the inner HASH160 <> EQUAL should only check the hash:153CScript p2sh2 = GetScriptForDestination(ScriptHash(p2sh));154CScript scriptSig2;155scriptSig2 << Serialize(invalidAsScript) << Serialize(p2sh);157BOOST_CHECK(Verify(scriptSig2, p2sh2, true, err));158BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));159}161BOOST_AUTO_TEST_CASE(set)162{163// Test the CScript::Set* methods164FillableSigningProvider keystore;165CKey key[4];166std::vector<CPubKey> keys;167keys.reserve(4);168for (int i = 0; i < 4; i++)169{170key[i].MakeNewKey(true);171BOOST_CHECK(keystore.AddKey(key[i]));172keys.push_back(key[i].GetPubKey());173}175CScript inner[4];176inner[0] = GetScriptForDestination(PKHash(key[0].GetPubKey()));177inner[1] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+2));178inner[2] = GetScriptForMultisig(1, std::vector<CPubKey>(keys.begin(), keys.begin()+2));179inner[3] = GetScriptForMultisig(2, std::vector<CPubKey>(keys.begin(), keys.begin()+3));181CScript outer[4];182for (int i = 0; i < 4; i++)183{184outer[i] = GetScriptForDestination(ScriptHash(inner[i]));185BOOST_CHECK(keystore.AddCScript(inner[i]));186}188CMutableTransaction txFrom; // Funding transaction:189std::string reason;190txFrom.vout.resize(4);191for (int i = 0; i < 4; i++)192{193txFrom.vout[i].scriptPubKey = outer[i];194txFrom.vout[i].nValue = CENT;195}196BOOST_CHECK(IsStandardTx(CTransaction(txFrom), reason));198CMutableTransaction txTo[4]; // Spending transactions199for (int i = 0; i < 4; i++)200{201txTo[i].vin.resize(1);202txTo[i].vout.resize(1);203txTo[i].vin[0].prevout.n = i;204txTo[i].vin[0].prevout.hash = txFrom.GetHash();205txTo[i].vout[0].nValue = 1*CENT;206txTo[i].vout[0].scriptPubKey = inner[i];207}208for (int i = 0; i < 4; i++)209{210SignatureData empty;211BOOST_CHECK_MESSAGE(SignSignature(keystore, CTransaction(txFrom), txTo[i], 0, SIGHASH_ALL, empty), strprintf("SignSignature %d", i));212BOOST_CHECK_MESSAGE(IsStandardTx(CTransaction(txTo[i]), /*permit_bare_multisig=*/true, reason), strprintf("txTo[%d].IsStandard", i));213bool no_pbms_is_std = IsStandardTx(CTransaction(txTo[i]), /*permit_bare_multisig=*/false, reason);214BOOST_CHECK_MESSAGE((i == 0 ? no_pbms_is_std : !no_pbms_is_std), strprintf("txTo[%d].IsStandard(permbaremulti=false)", i));215}216}218BOOST_AUTO_TEST_CASE(is)219{220// Test CScript::IsPayToScriptHash()221uint160 dummy;222CScript p2sh;223p2sh << OP_HASH160 << ToByteVector(dummy) << OP_EQUAL;224BOOST_CHECK(p2sh.IsPayToScriptHash());226std::vector<unsigned char> direct = {OP_HASH160, 20};227direct.insert(direct.end(), 20, 0);228direct.push_back(OP_EQUAL);229BOOST_CHECK(CScript(direct.begin(), direct.end()).IsPayToScriptHash());231// Not considered pay-to-script-hash if using one of the OP_PUSHDATA opcodes:232std::vector<unsigned char> pushdata1 = {OP_HASH160, OP_PUSHDATA1, 20};233pushdata1.insert(pushdata1.end(), 20, 0);234pushdata1.push_back(OP_EQUAL);235BOOST_CHECK(!CScript(pushdata1.begin(), pushdata1.end()).IsPayToScriptHash());236std::vector<unsigned char> pushdata2 = {OP_HASH160, OP_PUSHDATA2, 20, 0};237pushdata2.insert(pushdata2.end(), 20, 0);238pushdata2.push_back(OP_EQUAL);239BOOST_CHECK(!CScript(pushdata2.begin(), pushdata2.end()).IsPayToScriptHash());240std::vector<unsigned char> pushdata4 = {OP_HASH160, OP_PUSHDATA4, 20, 0, 0, 0};241pushdata4.insert(pushdata4.end(), 20, 0);242pushdata4.push_back(OP_EQUAL);243BOOST_CHECK(!CScript(pushdata4.begin(), pushdata4.end()).IsPayToScriptHash());245CScript not_p2sh;246BOOST_CHECK(!not_p2sh.IsPayToScriptHash());248not_p2sh.clear(); not_p2sh << OP_HASH160 << ToByteVector(dummy) << ToByteVector(dummy) << OP_EQUAL;249BOOST_CHECK(!not_p2sh.IsPayToScriptHash());
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.