Skip to content

test_blockhash_zero_out_of_window()

Documentation for tests/frontier/opcodes/test_blockhash_state_test_recency.py::test_blockhash_zero_out_of_window@2867859a.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/frontier/opcodes/test_blockhash_state_test_recency.py::test_blockhash_zero_out_of_window --fork Amsterdam

BLOCKHASH(0) must be 0 when block 0 is outside the recency window.

At current_number well past the window, block 0 is ancient, so every spec-conformant client returns 0. Nethermind's state-test runner instead returns keccak256("0") because TestBlockhashProvider skips the window check, which is the bug this test guards against.

Marked state_test_only: the bug lives solely in the state-test code path (the production blockhash provider enforces the window), and a state test with such a high env.number cannot be converted into a real blockchain-test fixture (no genesis chain up to that height).

Source code in tests/frontier/opcodes/test_blockhash_state_test_recency.py
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
@pytest.mark.valid_from("Frontier")
@pytest.mark.state_test_only
@pytest.mark.parametrize(
    "current_number",
    [
        pytest.param(0x101, id="just_past_256_window"),
        pytest.param(0x2001, id="just_past_8191_window"),
        pytest.param(0x100000, id="far_out_of_window"),
        pytest.param(0xA63CCE, id="fuzzer_discovered_number"),
    ],
)
def test_blockhash_zero_out_of_window(
    state_test: StateTestFiller,
    pre: Alloc,
    current_number: int,
) -> None:
    """
    BLOCKHASH(0) must be 0 when block 0 is outside the recency window.

    At ``current_number`` well past the window, block 0 is ancient, so every
    spec-conformant client returns 0. Nethermind's state-test runner instead
    returns ``keccak256("0")`` because ``TestBlockhashProvider`` skips the
    window check, which is the bug this test guards against.

    Marked ``state_test_only``: the bug lives solely in the state-test code
    path (the production blockhash provider enforces the window), and a state
    test with such a high ``env.number`` cannot be converted into a real
    blockchain-test fixture (no genesis chain up to that height).
    """
    storage = Storage()
    contract = pre.deploy_contract(
        code=(
            # slot 0: the raw hash -- must be zero
            Op.SSTORE(
                storage.store_next(0, "blockhash_0_value"),
                Op.BLOCKHASH(0),
            )
            # slot 1: ISZERO of the hash -- must be 1 (written, so the
            # divergence lands in a non-default slot too)
            + Op.SSTORE(
                storage.store_next(1, "blockhash_0_is_zero"),
                Op.ISZERO(Op.BLOCKHASH(0)),
            )
        )
    )
    sender = pre.fund_eoa()

    tx = Transaction(
        sender=sender,
        to=contract,
        protected=False,  # legacy tx so it fills on pre-EIP-155 forks too
    )

    state_test(
        env=Environment(number=current_number),
        pre=pre,
        post={contract: Account(storage=storage)},
        tx=tx,
    )

Parametrized Test Cases

This test generates 4 parametrized test cases across 16 forks.