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) 2022-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 signet miner tool"""7import json8import os.path9import shlex10import subprocess11import sys12import time14from test_framework.blocktools import DIFF_1_N_BITS, SIGNET_HEADER15from test_framework.key import ECKey16from test_framework.script_util import CScript, key_to_p2wpkh_script17from test_framework.test_framework import BitcoinTestFramework18from test_framework.util import (19assert_equal,20wallet_importprivkey,21)22from test_framework.wallet_util import bytes_to_wif25CHALLENGE_PRIVATE_KEY = (42).to_bytes(32, 'big')27def get_segwit_commitment(node):28coinbase = node.getblock(node.getbestblockhash(), 2)['tx'][0]29commitment = coinbase['vout'][1]['scriptPubKey']['hex']30assert_equal(commitment[0:12], '6a24aa21a9ed')31return commitment33def get_signet_commitment(segwit_commitment):34for el in CScript.fromhex(segwit_commitment):35if isinstance(el, bytes) and el[0:4] == SIGNET_HEADER:36return el[4:].hex()37return None39class SignetMinerTest(BitcoinTestFramework):40def set_test_params(self):41self.chain = "signet"42self.setup_clean_chain = True43self.num_nodes = 445# generate and specify signet challenge (simple p2wpkh script)46privkey = ECKey()47privkey.set(CHALLENGE_PRIVATE_KEY, True)48pubkey = privkey.get_pubkey().get_bytes()49challenge = key_to_p2wpkh_script(pubkey)51self.extra_args = [52[f'-signetchallenge={challenge.hex()}'],53["-signetchallenge=51"], # OP_TRUE54["-signetchallenge=60"], # OP_1655["-signetchallenge=202cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"], # sha256("hello")56]58def skip_test_if_missing_module(self):59self.skip_if_no_cli()60self.skip_if_no_wallet()61self.skip_if_no_bitcoin_util()63def setup_network(self):64self.setup_nodes()65# Nodes with different signet networks are not connected67# generate block with signet miner tool68def mine_block(self, node):69n_blocks = node.getblockcount()70base_dir = self.config["environment"]["SRCDIR"]71signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")72rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"]73util_argv = node.binaries.util_argv() + ["grind"]74subprocess.run([75sys.executable,76signet_miner_path,77f'--cli={shlex.join(rpc_argv)}',78'generate',79f'--address={node.getnewaddress()}',80f'--grind-cmd={shlex.join(util_argv)}',81f'--nbits={DIFF_1_N_BITS:08x}',82f'--set-block-time={int(time.time())}',83'--poolnum=99',84], check=True, stderr=subprocess.STDOUT)85assert_equal(node.getblockcount(), n_blocks + 1)87# generate block using the signet miner tool genpsbt and solvepsbt commands88def mine_block_manual(self, node, *, sign):89n_blocks = node.getblockcount()90base_dir = self.config["environment"]["SRCDIR"]91signet_miner_path = os.path.join(base_dir, "contrib", "signet", "miner")92rpc_argv = node.binaries.rpc_argv() + [f"-datadir={node.datadir_path}"]93util_argv = node.binaries.util_argv() + ["grind"]94base_cmd = [95sys.executable,96signet_miner_path,97f'--cli={shlex.join(rpc_argv)}',98]100template = node.getblocktemplate(dict(rules=["signet","segwit"]))101genpsbt = subprocess.run(base_cmd + [102'genpsbt',103f'--address={node.getnewaddress()}',104'--poolnum=98',105], check=True, text=True, input=json.dumps(template), capture_output=True)106psbt = genpsbt.stdout.strip()107if sign:108self.log.debug("Sign the PSBT")109res = node.walletprocesspsbt(psbt=psbt, sign=True, sighashtype='ALL')110assert res['complete']111psbt = res['psbt']112solvepsbt = subprocess.run(base_cmd + [113'solvepsbt',114f'--grind-cmd={shlex.join(util_argv)}',115], check=True, text=True, input=psbt, capture_output=True)116node.submitblock(solvepsbt.stdout.strip())117assert_equal(node.getblockcount(), n_blocks + 1)119def run_test(self):120self.log.info("Signet node with single signature challenge")121node = self.nodes[0]122# import private key needed for signing block123wallet_importprivkey(node, bytes_to_wif(CHALLENGE_PRIVATE_KEY), 0)124self.mine_block(node)125# MUST include signet commitment126assert get_signet_commitment(get_segwit_commitment(node))128self.log.info("Mine manually using genpsbt and solvepsbt")129self.mine_block_manual(node, sign=True)130assert get_signet_commitment(get_segwit_commitment(node))132node = self.nodes[1]133self.log.info("Signet node with trivial challenge (OP_TRUE)")134self.mine_block(node)135# MAY omit signet commitment (BIP 325). Do so for better compatibility136# with signet unaware mining software and hardware.137assert get_signet_commitment(get_segwit_commitment(node)) is None139node = self.nodes[2]140self.log.info("Signet node with trivial challenge (OP_16)")141self.mine_block(node)142assert get_signet_commitment(get_segwit_commitment(node)) is None144node = self.nodes[3]145self.log.info("Signet node with trivial challenge (push sha256 hash)")146self.mine_block(node)147assert get_signet_commitment(get_segwit_commitment(node)) is None149self.log.info("Manual mining with a trivial challenge doesn't require a PSBT")150self.mine_block_manual(node, sign=False)151assert get_signet_commitment(get_segwit_commitment(node)) is None154if __name__ == "__main__":155SignetMinerTest(__file__).main()
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.