Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions packages/testing/src/consensus_testing/test_fixtures/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@

from typing import Any, ClassVar

from lean_spec.subspecs.containers.slot import Slot
from lean_spec.subspecs.sync.checkpoint_sync import verify_checkpoint_state
from lean_spec.types import Uint64

from ..genesis import generate_pre_state
from ..genesis import build_anchor, generate_pre_state
from .base import BaseConsensusFixture


Expand Down Expand Up @@ -53,24 +54,38 @@ def make_fixture(self) -> "SyncTest":
return self.model_copy(update={"output": output})

def _make_verify_checkpoint(self) -> dict[str, Any]:
"""Build a genesis state for the given validator count and report the verdict.
"""Build a state for the given validator count and anchor slot and report the verdict.

Input keys:

- ``numValidators``: number of validators in the genesis state.
- ``numValidators``: number of validators in the state.
- ``anchorSlot``: optional slot to advance the chain through before
verifying. Zero (default) yields a genesis state; positive values
walk an empty-block chain through the slot so historical_block_hashes
reflects a real advanced anchor.

Output:

- ``valid``: result of verify_checkpoint_state on the built state.
- ``stateBytes``: SSZ-encoded state hex, so clients can deserialize
and run their own verify_checkpoint_state.
- ``validatorCount``: echoed for diagnostic clarity.
- ``anchorSlot``: echoed so consumers see exactly which state was verified.
"""
num_validators = int(self.input["numValidators"])
state = generate_pre_state(genesis_time=Uint64(0), num_validators=num_validators)
anchor_slot = int(self.input.get("anchorSlot", 0))
if anchor_slot == 0:
state = generate_pre_state(genesis_time=Uint64(0), num_validators=num_validators)
else:
state, _ = build_anchor(
num_validators=num_validators,
anchor_slot=Slot(anchor_slot),
genesis_time=Uint64(0),
)
valid = verify_checkpoint_state(state)
return {
"valid": valid,
"stateBytes": "0x" + state.encode_bytes().hex(),
"validatorCount": num_validators,
"anchorSlot": anchor_slot,
}
50 changes: 50 additions & 0 deletions tests/consensus/devnet/sync/test_checkpoint_verify_advanced.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Checkpoint-sync verification vectors at post-genesis anchor slots.

Existing verify_checkpoint vectors run against fresh genesis states.
These pin the verdict after the chain has advanced through empty
blocks, so clients' state deserialisation is exercised on the non-zero
historical_block_hashes path that real checkpoint-sync downloads hit.
"""

import pytest
from consensus_testing import SyncTestFiller

pytestmark = pytest.mark.valid_until("Devnet")


def test_checkpoint_verify_advanced_slot_three(sync: SyncTestFiller) -> None:
"""Advanced anchor state at slot 3 with four validators is accepted.

The chain walks through three empty blocks. The resulting state
carries non-empty historical_block_hashes and a non-zero latest
block header. Pins the accepted verdict plus the exact SSZ bytes.
"""
sync(
operation="verify_checkpoint",
input={"numValidators": 4, "anchorSlot": 3},
)


def test_checkpoint_verify_advanced_slot_ten(sync: SyncTestFiller) -> None:
"""Advanced anchor state at slot 10 with four validators is accepted.

Ten empty blocks populate historical_block_hashes and justified_slots
with longer lists. Pins the verdict and state bytes at a larger
history than the slot-three case.
"""
sync(
operation="verify_checkpoint",
input={"numValidators": 4, "anchorSlot": 10},
)


def test_checkpoint_verify_advanced_eight_validators(sync: SyncTestFiller) -> None:
"""Advanced anchor state at slot 5 with eight validators is accepted.

Exercises the combination of larger validator set with a non-zero
anchor slot so clients diff both axes in a single vector.
"""
sync(
operation="verify_checkpoint",
input={"numValidators": 8, "anchorSlot": 5},
)
Loading