Repository: bitcoin/bitcoin
File: test/functional/feature_assumevalid.py (Python)

Code before:

        # Create a transaction spending the coinbase output with an invalid (null) signature
        tx = CTransaction()
        tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].txid_int, 0), scriptSig=b""))
        tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE])))

        block102 = create_block(self.tip, create_coinbase(height), self.block_time, txlist=[tx])
        self.block_time += 1
        block102.solve()
        self.blocks.append(block102)
        self.tip = block102.hash_int
        self.block_time += 1
        height += 1

        # Bury the assumed valid block 2100 deep
        for _ in range(2100):
            block = create_block(self.tip, create_coinbase(height), self.block_time)
            block.solve()
            self.blocks.append(block)
            self.tip = block.hash_int
            self.block_time += 1
            height += 1

        # Start node1 and node2 with assumevalid so they accept a block with a bad signature.
        self.start_node(1, extra_args=["-assumevalid=" + block102.hash_hex])
        self.start_node(2, extra_args=["-assumevalid=" + block102.hash_hex])

        p2p0 = self.nodes[0].add_p2p_connection(BaseNode())
        p2p0.send_header_for_blocks(self.blocks[0:2000])
        p2p0.send_header_for_blocks(self.blocks[2000:])

        # Send blocks to node0. Block 102 will be rejected.
        self.send_blocks_until_disconnected(p2p0)
        self.wait_until(lambda: self.nodes[0].getblockcount() >= COINBASE_MATURITY + 1)
        assert_equal(self.nodes[0].getblockcount(), COINBASE_MATURITY + 1)

        p2p1 = self.nodes[1].add_p2p_connection(BaseNode())
        p2p1.send_header_for_blocks(self.blocks[0:2000])
        p2p1.send_header_for_blocks(self.blocks[2000:])
        with self.nodes[1].assert_debug_log(expected_msgs=['Disabling signature validations at block #1', 'Enabling signature validations at block #103']):
            # Send all blocks to node1. All blocks will be accepted.
            for i in range(2202):
                p2p1.send_without_ping(msg_block(self.blocks[i]))
            # Syncing 2200 blocks can take a while on slow systems. Give it plenty of time to sync.
            p2p1.sync_with_ping(timeout=960)
        assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202)

        p2p2 = self.nodes[2].add_p2p_connection(BaseNode())
        p2p2.send_header_for_blocks(self.blocks[0:200])

        # Send blocks to node2. Block 102 will be rejected.

Review comment (performance):
nit in 9ecaadc94a5a4a61263aa17ddaba23ee5d155042: Think there is some value in benefit from nodes starting up in parallel with the python code running. Would prefer starting them all here or even sooner, before the loop generating 2100 blocks above.

Sorry if my suggestion in https://github.com/bitcoin/bitcoin/pull/33336#discussion_r2351892330 precipitated this, I was just trying to reduce complexity in the example.

Code after:
            block.solve()
            self.blocks.append(block)
            self.tip = block.hash_int
            self.block_time += 1
            height += 1

        # Create a transaction spending the coinbase output with an invalid (null) signature
        tx = CTransaction()
        tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].txid_int, 0), scriptSig=b""))
        tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE])))

        block102 = create_block(self.tip, create_coinbase(height), self.block_time, txlist=[tx])
        self.block_time += 1
        block102.solve()
        self.blocks.append(block102)
        self.tip = block102.hash_int
        self.block_time += 1
        height += 1

        # Bury the assumed valid block 2100 deep
        for _ in range(2100):
            block = create_block(self.tip, create_coinbase(height), self.block_time)
            block.solve()
            self.blocks.append(block)
            self.tip = block.hash_int
            self.block_time += 1
            height += 1
        block_1_hash = self.blocks[0].hash_hex

        self.start_node(1, extra_args=[f"-assumevalid={block102.hash_hex}"])
        self.start_node(2, extra_args=[f"-assumevalid={block102.hash_hex}"])
        self.start_node(3, extra_args=[f"-assumevalid={block102.hash_hex}"])
        self.start_node(4, extra_args=[f"-assumevalid={block102.hash_hex}"])
        self.start_node(5)


        # nodes[0]
        # Send blocks to node0. Block 102 will be rejected.
        with self.nodes[0].assert_debug_log(expected_msgs=[
            f"Enabling script verification at block #1 ({block_1_hash}): assumevalid=0 (always verify).",
            "Block validation error: block-script-verify-flag-failed",
        ]):
            p2p0 = self.nodes[0].add_p2p_connection(BaseNode())

            p2p0.send_header_for_blocks(self.blocks[0:2000])
            p2p0.send_header_for_blocks(self.blocks[2000:])

            self.send_blocks_until_disconnected(p2p0)
            self.wait_until(lambda: self.nodes[0].getblockcount() >= COINBASE_MATURITY + 1)
            assert_equal(self.nodes[0].getblockcount(), COINBASE_MATURITY + 1)