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) 2015-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 BIP65 (CHECKLOCKTIMEVERIFY).7Test that the CHECKLOCKTIMEVERIFY soft-fork activates.8"""10from test_framework.blocktools import (11TIME_GENESIS_BLOCK,12create_block,13)14from test_framework.messages import (15CTransaction,16SEQUENCE_FINAL,17msg_block,18)19from test_framework.p2p import P2PInterface20from test_framework.script import (21CScript,22CScriptNum,23OP_1NEGATE,24OP_CHECKLOCKTIMEVERIFY,25OP_DROP,26)27from test_framework.test_framework import BitcoinTestFramework28from test_framework.util import assert_equal29from test_framework.wallet import (30MiniWallet,31MiniWalletMode,32)35# Helper function to modify a transaction by36# 1) prepending a given script to the scriptSig of vin 0 and37# 2) (optionally) modify the nSequence of vin 0 and the tx's nLockTime38def cltv_modify_tx(tx, prepend_scriptsig, nsequence=None, nlocktime=None):39assert_equal(len(tx.vin), 1)40if nsequence is not None:41tx.vin[0].nSequence = nsequence42tx.nLockTime = nlocktime44tx.vin[0].scriptSig = CScript(prepend_scriptsig + list(CScript(tx.vin[0].scriptSig)))47def cltv_invalidate(tx, failure_reason):48# Modify the signature in vin 0 and nSequence/nLockTime of the tx to fail CLTV49#50# According to BIP65, OP_CHECKLOCKTIMEVERIFY can fail due the following reasons:51# 1) the stack is empty52# 2) the top item on the stack is less than 053# 3) the lock-time type (height vs. timestamp) of the top stack item and the54# nLockTime field are not the same55# 4) the top stack item is greater than the transaction's nLockTime field56# 5) the nSequence field of the txin is 0xffffffff (SEQUENCE_FINAL)57assert failure_reason in range(5)58scheme = [59# | Script to prepend to scriptSig | nSequence | nLockTime |60# +-------------------------------------------------+------------+--------------+61[[OP_CHECKLOCKTIMEVERIFY], None, None],62[[OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP], None, None],63[[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, TIME_GENESIS_BLOCK],64[[CScriptNum(100), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, 50],65[[CScriptNum(50), OP_CHECKLOCKTIMEVERIFY, OP_DROP], SEQUENCE_FINAL, 50],66][failure_reason]68cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])71def cltv_validate(tx, height):72# Modify the signature in vin 0 and nSequence/nLockTime of the tx to pass CLTV73scheme = [[CScriptNum(height), OP_CHECKLOCKTIMEVERIFY, OP_DROP], 0, height]75cltv_modify_tx(tx, prepend_scriptsig=scheme[0], nsequence=scheme[1], nlocktime=scheme[2])78CLTV_HEIGHT = 11181class BIP65Test(BitcoinTestFramework):82def set_test_params(self):83self.num_nodes = 184# whitelist peers to speed up tx relay / mempool sync85self.noban_tx_relay = True86self.extra_args = [[87f'-testactivationheight=cltv@{CLTV_HEIGHT}',88'-acceptnonstdtxn=1', # cltv_invalidate is nonstandard89]]90self.setup_clean_chain = True91self.rpc_timeout = 48093def test_cltv_info(self, *, is_active):94assert_equal(self.nodes[0].getdeploymentinfo()['deployments']['bip65'], {95"active": is_active,96"height": CLTV_HEIGHT,97"type": "buried",98},99)101def run_test(self):102peer = self.nodes[0].add_p2p_connection(P2PInterface())103wallet = MiniWallet(self.nodes[0], mode=MiniWalletMode.RAW_OP_TRUE)105self.test_cltv_info(is_active=False)107self.log.info("Mining %d blocks", CLTV_HEIGHT - 2)108self.generate(wallet, 10)109self.generate(self.nodes[0], CLTV_HEIGHT - 2 - 10)110assert_equal(self.nodes[0].getblockcount(), CLTV_HEIGHT - 2)112self.log.info("Test that invalid-according-to-CLTV transactions can still appear in a block")114# create one invalid tx per CLTV failure reason (5 in total) and collect them115invalid_cltv_txs = []116for i in range(5):117spendtx = wallet.create_self_transfer()['tx']118cltv_invalidate(spendtx, i)119invalid_cltv_txs.append(spendtx)121tip = self.nodes[0].getbestblockhash()122block_time = self.nodes[0].getblockheader(tip)['mediantime'] + 1123block = create_block(int(tip, 16), height=CLTV_HEIGHT - 1, ntime=block_time, version=3, txlist=invalid_cltv_txs)124block.solve()126self.test_cltv_info(is_active=False) # Not active as of current tip and next block does not need to obey rules127peer.send_and_ping(msg_block(block))128self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules129assert_equal(self.nodes[0].getbestblockhash(), block.hash_hex)131self.log.info("Test that blocks must now be at least version 4")132tip = block.hash_int133block_time += 1134block = create_block(tip, height=CLTV_HEIGHT, ntime=block_time, version=3)135block.solve()137with self.nodes[0].assert_debug_log(expected_msgs=[f'{block.hash_hex}, bad-version(0x00000003)']):138peer.send_and_ping(msg_block(block))139assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)140peer.sync_with_ping()142self.log.info("Test that invalid-according-to-CLTV transactions cannot appear in a block")143block.nVersion = 4144block.vtx.append(CTransaction()) # dummy tx after coinbase that will be replaced later146# create and test one invalid tx per CLTV failure reason (5 in total)147for i in range(5):148spendtx = wallet.create_self_transfer()['tx']149assert_equal(len(spendtx.vin), 1)150coin = spendtx.vin[0]151coin_txid = format(coin.prevout.hash, '064x')152coin_vout = coin.prevout.n153cltv_invalidate(spendtx, i)155blk_rej = "block-script-verify-flag-failed"156tx_rej = "mempool-script-verify-flag-failed"157expected_cltv_reject_reason = [158" (Operation not valid with the current stack size)",159" (Negative locktime)",160" (Locktime requirement not satisfied)",161" (Locktime requirement not satisfied)",162" (Locktime requirement not satisfied)",163][i]164# First we show that this tx is valid except for CLTV by getting it165# rejected from the mempool for exactly that reason.166spendtx_txid = spendtx.txid_hex167spendtx_wtxid = spendtx.wtxid_hex168assert_equal(169[{170'txid': spendtx_txid,171'wtxid': spendtx_wtxid,172'allowed': False,173'reject-reason': tx_rej + expected_cltv_reject_reason,174'reject-details': tx_rej + expected_cltv_reject_reason + f", input 0 of {spendtx_txid} (wtxid {spendtx_wtxid}), spending {coin_txid}:{coin_vout}"175}],176self.nodes[0].testmempoolaccept(rawtxs=[spendtx.serialize().hex()], maxfeerate=0),177)179# Now we verify that a block with this transaction is also invalid.180block.vtx[1] = spendtx181block.hashMerkleRoot = block.calc_merkle_root()182block.solve()184with self.nodes[0].assert_debug_log(expected_msgs=[f'Block validation error: {blk_rej + expected_cltv_reject_reason}']):185peer.send_and_ping(msg_block(block))186assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip)187peer.sync_with_ping()189self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted")190cltv_validate(spendtx, CLTV_HEIGHT - 1)192block.vtx.pop(1)193block.vtx.append(spendtx)194block.hashMerkleRoot = block.calc_merkle_root()195block.solve()197self.test_cltv_info(is_active=True) # Not active as of current tip, but next block must obey rules198peer.send_and_ping(msg_block(block))199self.test_cltv_info(is_active=True) # Active as of current tip200assert_equal(self.nodes[0].getbestblockhash(), block.hash_hex)203if __name__ == '__main__':204BIP65Test(__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.