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 <addresstype.h>6#include <coins.h>7#include <consensus/consensus.h>8#include <consensus/tx_verify.h>9#include <key.h>10#include <pubkey.h>11#include <script/interpreter.h>12#include <script/script.h>13#include <script/solver.h>14#include <test/util/setup_common.h>15#include <uint256.h>17#include <vector>19#include <boost/test/unit_test.hpp>21// Helpers:22static std::vector<unsigned char>23Serialize(const CScript& s)24{25std::vector<unsigned char> sSerialized(s.begin(), s.end());26return sSerialized;27}29BOOST_FIXTURE_TEST_SUITE(sigopcount_tests, BasicTestingSetup)31BOOST_AUTO_TEST_CASE(GetSigOpCount)32{33// Test CScript::GetSigOpCount()34CScript s1;35BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 0U);36BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 0U);38uint160 dummy;39s1 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << OP_2 << OP_CHECKMULTISIG;40BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 2U);41s1 << OP_IF << OP_CHECKSIG << OP_ENDIF;42BOOST_CHECK_EQUAL(s1.GetSigOpCount(true), 3U);43BOOST_CHECK_EQUAL(s1.GetSigOpCount(false), 21U);45CScript p2sh = GetScriptForDestination(ScriptHash(s1));46CScript scriptSig;47scriptSig << OP_0 << Serialize(s1);48BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig), 3U);50std::vector<CPubKey> keys;51for (int i = 0; i < 3; i++)52{53CKey k = GenerateRandomKey();54keys.push_back(k.GetPubKey());55}56CScript s2 = GetScriptForMultisig(1, keys);57BOOST_CHECK_EQUAL(s2.GetSigOpCount(true), 3U);58BOOST_CHECK_EQUAL(s2.GetSigOpCount(false), 20U);60p2sh = GetScriptForDestination(ScriptHash(s2));61BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(true), 0U);62BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(false), 0U);63CScript scriptSig2;64scriptSig2 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << Serialize(s2);65BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(scriptSig2), 3U);66}68/**69* Verifies script execution of the zeroth scriptPubKey of tx output and70* zeroth scriptSig and witness of tx input.71*/72static ScriptError VerifyWithFlag(const CTransaction& output, const CMutableTransaction& input, script_verify_flags flags)73{74ScriptError error;75CTransaction inputi(input);76bool ret = VerifyScript(inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &error);77BOOST_CHECK((ret == true) == (error == SCRIPT_ERR_OK));79return error;80}82/**83* Builds a creationTx from scriptPubKey and a spendingTx from scriptSig84* and witness such that spendingTx spends output zero of creationTx.85* Also inserts creationTx's output into the coins view.86*/87static void BuildTxs(CMutableTransaction& spendingTx, CCoinsViewCache& coins, CMutableTransaction& creationTx, const CScript& scriptPubKey, const CScript& scriptSig, const CScriptWitness& witness)88{89creationTx.version = 1;90creationTx.vin.resize(1);91creationTx.vin[0].prevout.SetNull();92creationTx.vin[0].scriptSig = CScript();93creationTx.vout.resize(1);94creationTx.vout[0].nValue = 1;95creationTx.vout[0].scriptPubKey = scriptPubKey;97spendingTx.version = 1;98spendingTx.vin.resize(1);99spendingTx.vin[0].prevout.hash = creationTx.GetHash();100spendingTx.vin[0].prevout.n = 0;101spendingTx.vin[0].scriptSig = scriptSig;102spendingTx.vin[0].scriptWitness = witness;103spendingTx.vout.resize(1);104spendingTx.vout[0].nValue = 1;105spendingTx.vout[0].scriptPubKey = CScript();107AddCoins(coins, CTransaction(creationTx), 0);108}110BOOST_AUTO_TEST_CASE(GetTxSigOpCost)111{112// Transaction creates outputs113CMutableTransaction creationTx;114// Transaction that spends outputs and whose115// sig op cost is going to be tested116CMutableTransaction spendingTx;118// Create utxo set119CCoinsViewCache coins{&CoinsViewEmpty::Get()};120// Create key121CKey key = GenerateRandomKey();122CPubKey pubkey = key.GetPubKey();123// Default flags124const script_verify_flags flags{SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH};126// Multisig script (legacy counting)127{128CScript scriptPubKey = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;129// Do not use a valid signature to avoid using wallet operations.130CScript scriptSig = CScript() << OP_0 << OP_0;132BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness());133// Legacy counting only includes signature operations in scriptSigs and scriptPubKeys134// of a transaction and does not take the actual executed sig operations into account.135// spendingTx in itself does not contain a signature operation.136assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0);137// creationTx contains two signature operations in its scriptPubKey, but legacy counting138// is not accurate.139assert(GetTransactionSigOpCost(CTransaction(creationTx), coins, flags) == MAX_PUBKEYS_PER_MULTISIG * WITNESS_SCALE_FACTOR);140// Sanity check: script verification fails because of an invalid signature.141assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);142}144// Multisig nested in P2SH145{146CScript redeemScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;147CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));148CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript);150BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, CScriptWitness());151assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2 * WITNESS_SCALE_FACTOR);152assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);154// P2SH sigops are not counted if we don't set the SCRIPT_VERIFY_P2SH flag155assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, /*flags=*/0) == 0);156}158// P2WPKH witness program159{160CScript scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkey));161CScript scriptSig = CScript();162CScriptWitness scriptWitness;163scriptWitness.stack.emplace_back(0);164scriptWitness.stack.emplace_back(0);167BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);168assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);169// No signature operations if we don't verify the witness.170assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);171assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);173// The sig op cost for witness version != 0 is zero.174assert(scriptPubKey[0] == 0x00);175scriptPubKey[0] = 0x51;176BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);177assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0);178scriptPubKey[0] = 0x00;179BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);181// The witness of a coinbase transaction is not taken into account.182spendingTx.vin[0].prevout.SetNull();183assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 0);184}186// P2WPKH nested in P2SH187{188CScript scriptSig = GetScriptForDestination(WitnessV0KeyHash(pubkey));189CScript scriptPubKey = GetScriptForDestination(ScriptHash(scriptSig));190scriptSig = CScript() << ToByteVector(scriptSig);191CScriptWitness scriptWitness;192scriptWitness.stack.emplace_back(0);193scriptWitness.stack.emplace_back(0);195BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);196assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 1);197assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_EQUALVERIFY);198}200// P2WSH witness program201{202CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;203CScript scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));204CScript scriptSig = CScript();205CScriptWitness scriptWitness;206scriptWitness.stack.emplace_back(0);207scriptWitness.stack.emplace_back(0);208scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end());210BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);211assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);212assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags & ~SCRIPT_VERIFY_WITNESS) == 0);213assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);214}216// P2WSH nested in P2SH217{218CScript witnessScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY;219CScript redeemScript = GetScriptForDestination(WitnessV0ScriptHash(witnessScript));220CScript scriptPubKey = GetScriptForDestination(ScriptHash(redeemScript));221CScript scriptSig = CScript() << ToByteVector(redeemScript);222CScriptWitness scriptWitness;223scriptWitness.stack.emplace_back(0);224scriptWitness.stack.emplace_back(0);225scriptWitness.stack.emplace_back(witnessScript.begin(), witnessScript.end());227BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig, scriptWitness);228assert(GetTransactionSigOpCost(CTransaction(spendingTx), coins, flags) == 2);229assert(VerifyWithFlag(CTransaction(creationTx), spendingTx, flags) == SCRIPT_ERR_CHECKMULTISIGVERIFY);230}231}233BOOST_AUTO_TEST_SUITE_END()
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.