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#!/usr/bin/env python32# Copyright (c) 2016-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.5"""Test the SegWit changeover logic."""7from decimal import Decimal9from test_framework.address import (10script_to_p2sh_p2wsh,11script_to_p2wsh,12)13from test_framework.blocktools import (14NORMAL_GBT_REQUEST_PARAMS,15send_to_witness,16witness_script,17)18from test_framework.descriptors import descsum_create19from test_framework.messages import (20COIN,21COutPoint,22CTransaction,23CTxIn,24CTxOut,25tx_from_hex,26)27from test_framework.script import (28CScript,29OP_DROP,30OP_TRUE,31)32from test_framework.script_util import (33keys_to_multisig_script,34)35from test_framework.test_framework import BitcoinTestFramework36from test_framework.util import (37assert_equal,38assert_greater_than_or_equal,39assert_is_hex_string,40assert_raises_rpc_error,41)42from test_framework.wallet_util import (43get_generate_key,44)46NODE_0 = 047NODE_2 = 248P2WPKH = 049P2WSH = 152def getutxo(txid):53utxo = {}54utxo["vout"] = 055utxo["txid"] = txid56return utxo59def find_spendable_utxo(node, min_value):60for utxo in node.listunspent(query_options={'minimumAmount': min_value}):61if utxo['spendable']:62return utxo64raise AssertionError(f"Unspent output equal or higher than {min_value} not found")67class SegWitTest(BitcoinTestFramework):68def set_test_params(self):69self.setup_clean_chain = True70self.num_nodes = 371# This test tests SegWit both pre and post-activation, so use the normal BIP9 activation.72self.extra_args = [73[74"-acceptnonstdtxn=1",75"-testactivationheight=segwit@165",76"-addresstype=legacy",77],78[79"-acceptnonstdtxn=1",80"-testactivationheight=segwit@165",81"-addresstype=legacy",82],83[84"-acceptnonstdtxn=1",85"-testactivationheight=segwit@165",86"-addresstype=legacy",87],88]89self.rpc_timeout = 12091def skip_test_if_missing_module(self):92self.skip_if_no_wallet()94def setup_network(self):95super().setup_network()96self.connect_nodes(0, 2)97self.sync_all()99def success_mine(self, node, txid, sign, redeem_script=""):100send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script)101block = self.generate(node, 1)102assert_equal(len(node.getblock(block[0])["tx"]), 2)103self.sync_blocks()105def fail_accept(self, node, error_msg, txid, sign, redeem_script=""):106assert_raises_rpc_error(-26, error_msg, send_to_witness, use_p2wsh=1, node=node, utxo=getutxo(txid), pubkey=self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=sign, insert_redeem_script=redeem_script)108def run_test(self):109self.generate(self.nodes[0], 161) # block 161111self.log.info("Verify sigops are counted in GBT with pre-BIP141 rules before the fork")112txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)113tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)114assert_equal(tmpl['sizelimit'], 1000000)115assert 'weightlimit' not in tmpl116assert_equal(tmpl['sigoplimit'], 20000)117assert_equal(tmpl['transactions'][0]['hash'], txid)118assert_equal(tmpl['transactions'][0]['sigops'], 2)119assert '!segwit' not in tmpl['rules']120self.generate(self.nodes[0], 1) # block 162122balance_presetup = self.nodes[0].getbalance()123self.pubkey = []124p2sh_ids = [] # p2sh_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE embedded in p2sh125wit_ids = [] # wit_ids[NODE][TYPE] is an array of txids that spend to P2WPKH (TYPE=0) or P2WSH (TYPE=1) scripts to an address for NODE via bare witness126for i in range(3):127key = get_generate_key()128self.pubkey.append(key.pubkey)130multiscript = keys_to_multisig_script([self.pubkey[-1]])131p2sh_ms_addr = self.nodes[i].createmultisig(1, [self.pubkey[-1]], 'p2sh-segwit')['address']132bip173_ms_addr = self.nodes[i].createmultisig(1, [self.pubkey[-1]], 'bech32')['address']133assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript))134assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript))136p2sh_ms_desc = descsum_create(f"sh(wsh(multi(1,{key.privkey})))")137bip173_ms_desc = descsum_create(f"wsh(multi(1,{key.privkey}))")138assert_equal(self.nodes[i].deriveaddresses(p2sh_ms_desc)[0], p2sh_ms_addr)139assert_equal(self.nodes[i].deriveaddresses(bip173_ms_desc)[0], bip173_ms_addr)141sh_wpkh_desc = descsum_create(f"sh(wpkh({key.privkey}))")142wpkh_desc = descsum_create(f"wpkh({key.privkey})")143assert_equal(self.nodes[i].deriveaddresses(sh_wpkh_desc)[0], key.p2sh_p2wpkh_addr)144assert_equal(self.nodes[i].deriveaddresses(wpkh_desc)[0], key.p2wpkh_addr)146res = self.nodes[i].importdescriptors([147{"desc": p2sh_ms_desc, "timestamp": "now"},148{"desc": bip173_ms_desc, "timestamp": "now"},149{"desc": sh_wpkh_desc, "timestamp": "now"},150{"desc": wpkh_desc, "timestamp": "now"},151])152assert all([r["success"] for r in res])154p2sh_ids.append([])155wit_ids.append([])156for _ in range(2):157p2sh_ids[i].append([])158wit_ids[i].append([])160for _ in range(5):161for n in range(3):162for v in range(2):163wit_ids[n][v].append(send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999")))164p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999")))166self.generate(self.nodes[0], 1) # block 163168# Make sure all nodes recognize the transactions as theirs169assert_equal(self.nodes[0].getbalance(), balance_presetup - 60 * 50 + 20 * Decimal("49.999") + 50)170assert_equal(self.nodes[1].getbalance(), 20 * Decimal("49.999"))171assert_equal(self.nodes[2].getbalance(), 20 * Decimal("49.999"))173self.log.info("Verify unsigned p2sh witness txs without a redeem script are invalid")174self.fail_accept(self.nodes[2], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WPKH][1], sign=False)175self.fail_accept(self.nodes[2], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_2][P2WSH][1], sign=False)177self.generate(self.nodes[0], 1) # block 164179self.log.info("Verify witness txs are mined as soon as segwit activates")181send_to_witness(1, self.nodes[2], getutxo(wit_ids[NODE_2][P2WPKH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)182send_to_witness(1, self.nodes[2], getutxo(wit_ids[NODE_2][P2WSH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)183send_to_witness(1, self.nodes[2], getutxo(p2sh_ids[NODE_2][P2WPKH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)184send_to_witness(1, self.nodes[2], getutxo(p2sh_ids[NODE_2][P2WSH][0]), self.pubkey[0], encode_p2sh=False, amount=Decimal("49.998"), sign=True)186assert_equal(len(self.nodes[2].getrawmempool()), 4)187blockhash = self.generate(self.nodes[2], 1)[0] # block 165 (first block with new rules)188assert_equal(len(self.nodes[2].getrawmempool()), 0)189segwit_tx_list = self.nodes[2].getblock(blockhash)["tx"]190assert_equal(len(segwit_tx_list), 5)192self.log.info("Verify default node can't accept txs with missing witness")193# unsigned, no scriptsig194self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program hash mismatch)", wit_ids[NODE_0][P2WPKH][0], sign=False)195self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program was passed an empty witness)", wit_ids[NODE_0][P2WSH][0], sign=False)196self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WPKH][0], sign=False)197self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Operation not valid with the current stack size)", p2sh_ids[NODE_0][P2WSH][0], sign=False)198# unsigned with redeem script199self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program hash mismatch)", p2sh_ids[NODE_0][P2WPKH][0], sign=False, redeem_script=witness_script(False, self.pubkey[0]))200self.fail_accept(self.nodes[0], "mempool-script-verify-flag-failed (Witness program was passed an empty witness)", p2sh_ids[NODE_0][P2WSH][0], sign=False, redeem_script=witness_script(True, self.pubkey[0]))202# Coinbase contains the witness commitment nonce, check that RPC shows us203coinbase_txid = self.nodes[2].getblock(blockhash)['tx'][0]204coinbase_tx = self.nodes[2].gettransaction(txid=coinbase_txid, verbose=True)205witnesses = coinbase_tx["decoded"]["vin"][0]["txinwitness"]206assert_equal(len(witnesses), 1)207assert_is_hex_string(witnesses[0])208assert_equal(witnesses[0], '00' * 32)210self.log.info("Verify witness txs without witness data are invalid after the fork")211self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program hash mismatch)', wit_ids[NODE_2][P2WPKH][2], sign=False)212self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program was passed an empty witness)', wit_ids[NODE_2][P2WSH][2], sign=False)213self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program hash mismatch)', p2sh_ids[NODE_2][P2WPKH][2], sign=False, redeem_script=witness_script(False, self.pubkey[2]))214self.fail_accept(self.nodes[2], 'mempool-script-verify-flag-failed (Witness program was passed an empty witness)', p2sh_ids[NODE_2][P2WSH][2], sign=False, redeem_script=witness_script(True, self.pubkey[2]))216self.log.info("Verify default node can now use witness txs")217self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WPKH][0], True)218self.success_mine(self.nodes[0], wit_ids[NODE_0][P2WSH][0], True)219self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WPKH][0], True)220self.success_mine(self.nodes[0], p2sh_ids[NODE_0][P2WSH][0], True)222self.log.info("Verify sigops are counted in GBT with BIP141 rules after the fork")223txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1)224raw_tx = self.nodes[0].getrawtransaction(txid, True)225tmpl = self.nodes[0].getblocktemplate(NORMAL_GBT_REQUEST_PARAMS)226assert_greater_than_or_equal(tmpl['sizelimit'], 3999577) # actual maximum size is lower due to minimum mandatory non-witness data227assert_equal(tmpl['weightlimit'], 4000000)228assert_equal(tmpl['sigoplimit'], 80000)229assert_equal(tmpl['transactions'][0]['txid'], txid)230expected_sigops = 9 if 'txinwitness' in raw_tx["vin"][0] else 8231assert_equal(tmpl['transactions'][0]['sigops'], expected_sigops)232assert '!segwit' in tmpl['rules']234self.generate(self.nodes[0], 1) # Mine a block to clear the gbt cache236self.log.info("Non-segwit miners are able to use GBT response after activation.")237# Create a 3-tx chain: tx1 (non-segwit input, paying to a segwit output) ->238# tx2 (segwit input, paying to a non-segwit output) ->239# tx3 (non-segwit input, paying to a non-segwit output).240# tx1 is allowed to appear in the block, but no others.241txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996"))242assert txid1 in self.nodes[0].getrawmempool()244tx1_hex = self.nodes[0].gettransaction(txid1)['hex']245tx1 = tx_from_hex(tx1_hex)247# Check that wtxid is properly reported in mempool entry (txid1)248assert_equal(self.nodes[0].getmempoolentry(txid1)["wtxid"], tx1.wtxid_hex)250# Check that weight and vsize are properly reported in mempool entry (txid1)
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.