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.
1251# == sighash caching tests ==1253# Sighash caching in legacy.1254for p2sh in [False, True]:1255for witv0 in [False, True]:1256eckey1, pubkey1 = generate_keypair(compressed=compressed)1257for _ in range(10):1258# Construct a script with 20 checksig operations (10 sighash types, each 2 times),1259# randomly ordered and interleaved with 4 OP_CODESEPARATORS.1260ops = [1, 2, 3, 0x21, 0x42, 0x63, 0x81, 0x83, 0xe1, 0xc2, -1, -1] * 21261# Make sure no OP_CODESEPARATOR appears last.1262while True:1263random.shuffle(ops)1264if ops[-1] != -1:1265break1266script = [pubkey1]1267inputs = []1268codeseps = -11269for pos, op in enumerate(ops):1270if op == -1:1271codeseps += 11272script.append(OP_CODESEPARATOR)1273elif pos + 1 != len(ops):1274script += [OP_TUCK, OP_CHECKSIGVERIFY]1275inputs.append(getter("sign", codesepnum=codeseps, hashtype=op))1276else:1277script += [OP_CHECKSIG]1278inputs.append(getter("sign", codesepnum=codeseps, hashtype=op))1279inputs.reverse()1280script = CScript(script)1281add_spender(spenders, "sighashcache/legacy", p2sh=p2sh, witv0=witv0, standard=False, script=script, inputs=inputs, key=eckey1, sigops_weight=12*8*(4-3*witv0), no_fail=True)1283# Sighash caching in tapscript.1284for _ in range(10):1285# Construct a script with 700 checksig operations (7 sighash types, each 100 times),1286# randomly ordered and interleaved with 100 OP_CODESEPARATORS.1287ops = [0, 1, 2, 3, 0x81, 0x82, 0x83, -1] * 1001288# Make sure no OP_CODESEPARATOR appears last.1289while True:1290random.shuffle(ops)1291if ops[-1] != -1:1292break1293script = [pubs[1]]1294inputs = []1295opcount = 11296codeseppos = 0xffffffff1297for pos, op in enumerate(ops):1298if op == -1:1299codeseppos = opcount1300opcount += 11301script.append(OP_CODESEPARATOR)1302elif pos + 1 != len(ops):1303opcount += 21304script += [OP_TUCK, OP_CHECKSIGVERIFY]1305inputs.append(getter("sign", codeseppos=codeseppos, hashtype=op))1306else:1307opcount += 11308script += [OP_CHECKSIG]1309inputs.append(getter("sign", codeseppos=codeseppos, hashtype=op))1310inputs.reverse()1311script = CScript(script)1312tap = taproot_construct(pubs[0], [("leaf", script)])1313add_spender(spenders, "sighashcache/taproot", tap=tap, leaf="leaf", inputs=inputs, standard=True, key=secs[1], no_fail=True)1315return spenders1318def spenders_taproot_nonstandard():1319"""Spenders for testing that post-activation Taproot rules may be nonstandard."""1321spenders = []1323sec = generate_privkey()1324pub, _ = compute_xonly_pubkey(sec)1325scripts = [1326("future_leaf", CScript([pub, OP_CHECKSIG]), 0xc2),1327("op_success", CScript([pub, OP_CHECKSIG, OP_0, OP_IF, CScriptOp(0x50), OP_ENDIF])),1328]1329tap = taproot_construct(pub, scripts)1331# Test that features like annex, leaf versions, or OP_SUCCESS are valid but non-standard1332add_spender(spenders, "inactive/scriptpath_valid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")])1333add_spender(spenders, "inactive/scriptpath_invalid_unkleaf", key=sec, tap=tap, leaf="future_leaf", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))1334add_spender(spenders, "inactive/scriptpath_valid_opsuccess", key=sec, tap=tap, leaf="op_success", standard=False, inputs=[getter("sign")])1335add_spender(spenders, "inactive/scriptpath_valid_opsuccess", key=sec, tap=tap, leaf="op_success", standard=False, inputs=[getter("sign")], sighash=bitflipper(default_sighash))1337return spenders1339def sample_spenders():1341# Create key(s) for output creation, as well as key and script-spends1342secs = [generate_privkey() for _ in range(2)]1343pubs = [compute_xonly_pubkey(sec)[0] for sec in secs]1345# Create a list of scripts which will be built into a taptree1346scripts = [1347# leaf label, followed by CScript1348("2byte_push", CScript([OP_DROP, b'\xaa\xaa'])),1349("nonstd_2byte_push", CScript.fromhex("4c02aaaa")),1350("dummyleaf", CScript([])),1351]1353# Build TaprootInfo using scripts and appropriate pubkey for output creation1354tap = taproot_construct(pubs[0], scripts)1356# Finally, add spender(s).1357# Each spender embodies a test with an optional failure condition.1358# These failure conditions allow for fine-grained success/failure1359# conditions that are tested randomly.1360spenders = []1362# Named comment, using first leaf from scripts, with empty string as witness data, no optional fail condition1363add_spender(spenders, comment="tutorial/push", tap=tap, leaf="2byte_push", inputs=[b'\x00'], no_fail=True)1365# Spender with alternative failure tapscript via over-riding "failure" dictionary, along with the failure's expected err_msg / ERR_*1366add_spender(spenders, comment="tutorial/pushredux", tap=tap, leaf="2byte_push", inputs=[b'\x00'], failure={"leaf": "dummyleaf"}, **ERR_EVAL_FALSE)1368# Spender that is non-standard but otherwise valid, with extraneous signature data from inner key for optional failure condition1369add_spender(spenders, comment="tutorial/nonminpush", tap=tap, leaf="nonstd_2byte_push", key=secs[0], standard=False, failure={"inputs": [getter("sign")]}, **ERR_CLEANSTACK)1371# New scripts=[] can be defined, and rinse-repeated as necessary until the spenders list is returned for execution1372return spenders1374# Consensus validation flags to use in dumps for tests with "legacy/" or "inactive/" prefix.1375LEGACY_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY"1376# Consensus validation flags to use in dumps for all other tests.1377TAPROOT_FLAGS = "P2SH,DERSIG,CHECKLOCKTIMEVERIFY,CHECKSEQUENCEVERIFY,WITNESS,NULLDUMMY,TAPROOT"1379def dump_json_test(tx, input_utxos, idx, success, failure):1380spender = input_utxos[idx].spender1381# Determine flags to dump1382flags = LEGACY_FLAGS if spender.comment.startswith("legacy/") or spender.comment.startswith("inactive/") else TAPROOT_FLAGS1384fields = [1385("tx", tx.serialize().hex()),1386("prevouts", [x.output.serialize().hex() for x in input_utxos]),1387("index", idx),1388("flags", flags),1389("comment", spender.comment)1390]1392# The "final" field indicates that a spend should be always valid, even with more validation flags enabled1393# than the listed ones. Use standardness as a proxy for this (which gives a conservative underestimate).1394if spender.is_standard:1395fields.append(("final", True))1397def dump_witness(wit):1398return OrderedDict([("scriptSig", wit[0].hex()), ("witness", [x.hex() for x in wit[1]])])1399if success is not None:1400fields.append(("success", dump_witness(success)))1401if failure is not None:1402fields.append(("failure", dump_witness(failure)))1404# Write the dump to $TEST_DUMP_DIR/x/xyz... where x,y,z,... are the SHA1 sum of the dump (which makes the1405# file naming scheme compatible with fuzzing infrastructure).1406dump = json.dumps(OrderedDict(fields)) + ",\n"1407sha1 = hashlib.sha1(dump.encode("utf-8")).hexdigest()1408dirname = os.environ.get("TEST_DUMP_DIR", ".") + ("/%s" % sha1[0])1409os.makedirs(dirname, exist_ok=True)1410with open(dirname + ("/%s" % sha1), 'w') as f:1411f.write(dump)1413# Data type to keep track of UTXOs, where they were created, and how to spend them.1414UTXOData = namedtuple('UTXOData', 'outpoint,output,spender')1417class TaprootTest(BitcoinTestFramework):1418def add_options(self, parser):1419parser.add_argument("--dumptests", dest="dump_tests", default=False, action="store_true",1420help="Dump generated test cases to directory set by TEST_DUMP_DIR environment variable")1422def set_test_params(self):1423self.num_nodes = 11424self.setup_clean_chain = True1426def block_submit(self, node, txs, msg, err_msg, cb_pubkey=None, fees=0, sigops_weight=0, witness=False, accept=False):1428# Deplete block of any non-tapscript sigops using a single additional 0-value coinbase output.1429# It is not impossible to fit enough tapscript sigops to hit the old 80k limit without1430# busting txin-level limits. We simply have to account for the p2pk outputs in all1431# transactions.1432extra_output_script = CScript(bytes([OP_CHECKSIG]*((MAX_BLOCK_SIGOPS_WEIGHT - sigops_weight) // WITNESS_SCALE_FACTOR)))1434coinbase_tx = create_coinbase(self.lastblockheight + 1, pubkey=cb_pubkey, extra_output_script=extra_output_script, fees=fees)1435block = create_block(self.tip, coinbase_tx, ntime=self.lastblocktime + 1, txlist=txs)1436witness and add_witness_commitment(block)1437block.solve()1438block_response = node.submitblock(block.serialize().hex())1439if err_msg is not None:1440assert block_response is not None and err_msg in block_response, "Missing error message '%s' from block response '%s': %s" % (err_msg, "(None)" if block_response is None else block_response, msg)1441if accept:1442assert node.getbestblockhash() == block.hash_hex, "Failed to accept: %s (response: %s)" % (msg, block_response)1443self.tip = block.hash_int1444self.lastblockhash = block.hash_hex1445self.lastblocktime += 11446self.lastblockheight += 11447else:1448assert node.getbestblockhash() == self.lastblockhash, "Failed to reject: " + msg1450def init_blockinfo(self, node):1451# Initialize variables used by block_submit().1452self.lastblockhash = node.getbestblockhash()1453self.tip = int(self.lastblockhash, 16)1454block = node.getblock(self.lastblockhash)1455self.lastblockheight = block['height']1456self.lastblocktime = block['time']1458def test_spenders(self, node, spenders, input_counts):1459"""Run randomized tests with a number of "spenders".1461Steps:14621) Generate an appropriate UTXO for each spender to test spend conditions14632) Generate 100 random addresses of all wallet types: pkh/sh_wpkh/wpkh14643) Select random number of inputs from (1)14654) Select random number of addresses from (2) as outputs1467Each spender embodies a test; in a large randomized test, it is verified1468that toggling the valid argument to each lambda toggles the validity of1469the transaction. This is accomplished by constructing transactions consisting1470of all valid inputs, except one invalid one.1471"""1473# Construct a bunch of sPKs that send coins back to the host wallet1474self.log.info("- Constructing addresses for returning coins")1475host_spks = []1476host_pubkeys = []1477for i in range(16):1478pubkey, spk, _ = self.nodesigner.getnewaddress(address_type=random.choice(["legacy", "p2sh-segwit", "bech32"]))1479host_spks.append(spk)1480host_pubkeys.append(pubkey)1482self.init_blockinfo(node)1484# Create transactions spending up to 50 of the wallet's inputs, with one output for each spender, and1485# one change output at the end. The transaction is constructed on the Python side to enable1486# having multiple outputs to the same address and outputs with no assigned address. The node1487# is then asked to sign it through signrawtransactionwithkey, and then added to a block on the1488# Python side (to bypass standardness rules).1489self.log.info("- Creating test UTXOs...")1490random.shuffle(spenders)1491normal_utxos = []1492mismatching_utxos = [] # UTXOs with input that requires mismatching output position1493done = 01494while done < len(spenders):1495# Compute how many UTXOs to create with this transaction1496count_this_tx = min(len(spenders) - done, (len(spenders) + 4) // 5, 10000)1498fund_tx = CTransaction()1499# Add the 50 highest-value inputs1500unspents = self.nodesigner.listunspent()
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.