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

Code before:

        current_height = node.getblock(node.getbestblockhash())['height']

        # Create a fork somewhere below our current height, invalidate the tip
        # of that fork, and then ensure that waitforblockheight still
        # works as expected.
        #
        # (Previously this was broken based on setting
        # `rpc/blockchain.cpp:latestblock` incorrectly.)
        #
        fork_height = current_height - 100 # choose something vaguely near our tip
        fork_hash = node.getblockhash(fork_height)
        fork_block = node.getblock(fork_hash)

        def solve_and_send_block(prevhash, height, time):
            b = create_block(prevhash, create_coinbase(height), time)
            b.solve()
            peer.send_and_ping(msg_block(b))
            return b

        b1 = solve_and_send_block(int(fork_hash, 16), fork_height+1, fork_block['time'] + 1)
        b2 = solve_and_send_block(b1.sha256, fork_height+2, b1.nTime + 1)

        node.invalidateblock(b2.hash)

        def assert_waitforheight(height, timeout=2):
            assert_equal(
                node.waitforblockheight(height=height, timeout=timeout)['height'],
                current_height)

        assert_waitforheight(0)
        assert_waitforheight(current_height - 1)
        assert_waitforheight(current_height)
        assert_waitforheight(current_height + 1)

    def _test_getblock(self):
        node = self.nodes[0]
        fee_per_byte = Decimal('0.00000010')
        fee_per_kb = 1000 * fee_per_byte

        self.wallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
        blockhash = self.generate(node, 1)[0]

        def assert_hexblock_hashes(verbosity):
            block = node.getblock(blockhash, verbosity)
            assert_equal(blockhash, hash256(bytes.fromhex(block[:160]))[::-1].hex())

        def assert_fee_not_in_block(hash, verbosity):
            block = node.getblock(hash, verbosity)
            assert 'fee' not in block['tx'][1]


Review comment (bug):
In commit "rpc: add test for waitforblock and waitfornewblock" (a18cb7adf192bae481c879fe865f1c159b2b8622)

I don't think I understand what this test is checking for. Maybe there could be a comment that describes what the test setup is doing what a failed result might look like. It seems like it is just creating a small fork and then invalidating the fork tip, so it shouldn't affect the most work tip or current chain. I would expect to see wait_until call after the invalidateblock call which verifies it has the intended effect. Also the 2 millisecond timeout seems small so if this is supposed to wait for something it doesn't seem like it waiting very long.

Same comments also apply to the  _test_waitforblock test below, and maybe it would be good to dedupe the test setup shared by these functions, moving it into a helper method they both could call.

Code after:

        current_height = node.getblock(node.getbestblockhash())['height']
        current_hash = node.getblock(node.getbestblockhash())['hash']

        self.log.debug("Roll the chain back a few blocks and then reconsider it")
        rollback_height = current_height - 100
        rollback_hash = node.getblockhash(rollback_height)
        rollback_header = node.getblockheader(rollback_hash)

        node.invalidateblock(rollback_hash)
        assert_equal(node.getblockcount(), rollback_height - 1)

        self.log.debug("waitforblock should return the same block after its timeout")
        assert_equal(node.waitforblock(blockhash=current_hash, timeout=1)['hash'], rollback_header['previousblockhash'])

        node.reconsiderblock(rollback_hash)
        # The chain has probably already been restored by the time reconsiderblock returns,
        # but poll anyway.
        self.wait_until(lambda: node.waitforblock(blockhash=current_hash, timeout=100)['hash'] == current_hash)

        # roll back again
        node.invalidateblock(rollback_hash)
        assert_equal(node.getblockcount(), rollback_height - 1)

        node.reconsiderblock(rollback_hash)
        # The chain has probably already been restored by the time reconsiderblock returns,
        # but poll anyway.
        self.wait_until(lambda: node.waitfornewblock(timeout=100)['hash'] == current_hash)

    def _test_waitforblockheight(self):
        self.log.info("Test waitforblockheight")
        node = self.nodes[0]
        peer = node.add_p2p_connection(P2PInterface())

        current_height = node.getblock(node.getbestblockhash())['height']

        # Create a fork somewhere below our current height, invalidate the tip
        # of that fork, and then ensure that waitforblockheight still
        # works as expected.
        #
        # (Previously this was broken based on setting
        # `rpc/blockchain.cpp:latestblock` incorrectly.)
        #
        fork_height = current_height - 100 # choose something vaguely near our tip
        fork_hash = node.getblockhash(fork_height)
        fork_block = node.getblock(fork_hash)

        def solve_and_send_block(prevhash, height, time):
            b = create_block(prevhash, create_coinbase(height), time)
            b.solve()
            peer.send_and_ping(msg_block(b))