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

Code before:

        assert self.nodes[0].getbestblockhash() == block.hash_hex

        # Now make sure that malleating the witness reserved value doesn't
        # result in a block permanently marked bad.
        block = self.build_next_block()
        add_witness_commitment(block)
        block.solve()

        # Change the nonce -- should not cause the block to be permanently
        # failed
        block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(1)]
        test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-witness-merkle-match')

        # Changing the witness reserved value doesn't change the block hash
        block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
        test_witness_block(self.nodes[0], self.test_node, block, accepted=True)

    @subtest
    def test_witness_block_size(self):
        # TODO: Test that non-witness carrying blocks can't exceed 1MB
        # Skipping this test for now; this is covered in feature_block.py

        # Test that witness-bearing blocks are limited at ceil(base + wit/4) <= 1MB.
        block = self.build_next_block()

        assert len(self.utxo) > 0

        # Create a P2WSH transaction.
        # The witness script will be a bunch of OP_2DROP's, followed by OP_TRUE.
        # This should give us plenty of room to tweak the spending tx's
        # virtual size.
        NUM_DROPS = 200  # 201 max ops per script!
        NUM_OUTPUTS = 50

        witness_script = CScript([OP_2DROP] * NUM_DROPS + [OP_TRUE])
        script_pubkey = script_to_p2wsh_script(witness_script)

        prevout = COutPoint(self.utxo[0].sha256, self.utxo[0].n)
        value = self.utxo[0].nValue

        parent_tx = CTransaction()
        parent_tx.vin.append(CTxIn(prevout, b""))
        child_value = int(value / NUM_OUTPUTS)
        for _ in range(NUM_OUTPUTS):
            parent_tx.vout.append(CTxOut(child_value, script_pubkey))
        parent_tx.vout[0].nValue -= 50000
        assert parent_tx.vout[0].nValue > 0

        child_tx = CTransaction()
        for i in range(NUM_OUTPUTS):

Review comment (suggestion):
nit: you can change this comment to `Confirm the block is still relayable by weight`, As this is not checking whether a block is relayable its just checking weight.

Code after:

        assert self.nodes[0].getbestblockhash() == block.hash_hex

        # Build a relayable-but-invalid block
        relayable_block = self.build_next_block()
        add_witness_commitment(relayable_block)
        relayable_block.solve()

        # Append extra witness data to the coinbase input that triggers the
        # same validation rejection but keeps the block under the weight limit
        # so it can be sent via P2P.
        relayable_block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.append(b'a' * 100_000)

        # Ensure it's relayable by weight
        assert_greater_than_or_equal(MAX_BLOCK_WEIGHT, relayable_block.get_weight())

        # Send over P2P and expect rejection for the same reason
        test_witness_block(self.nodes[0], self.test_node, relayable_block,
                           accepted=False, reason='bad-witness-nonce-size')

        # Node should still be on the previous tip
        assert_not_equal(self.nodes[0].getbestblockhash(), relayable_block.hash_hex)

        # Now fix the block by removing the extra witness data
        relayable_block.vtx[0].wit.vtxinwit[0].scriptWitness.stack.pop()

        # Confirm the block is still relayable by weight
        assert relayable_block.get_weight() <= MAX_BLOCK_WEIGHT

        # Send the corrected block and expect acceptance
        test_witness_block(self.nodes[0], self.test_node, relayable_block, accepted=True)
        assert_equal(self.nodes[0].getbestblockhash(), relayable_block.hash_hex)

        # Now make sure that malleating the witness reserved value doesn't
        # result in a block permanently marked bad.
        block = self.build_next_block()
        add_witness_commitment(block)
        block.solve()

        # Change the nonce -- should not cause the block to be permanently
        # failed
        block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(1)]
        test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='bad-witness-merkle-match')

        # Changing the witness reserved value doesn't change the block hash
        block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)]
        test_witness_block(self.nodes[0], self.test_node, block, accepted=True)

    @subtest
    def test_witness_block_size(self):
        # TODO: Test that non-witness carrying blocks can't exceed 1MB