Skip to content

Commit b5b315c

Browse files
tcoratgerclaude
andauthored
framework: add sync fixture format with checkpoint verification KATs (leanEthereum#666)
Introduces a new consensus fixture format for sync-layer helpers clients must reproduce and lands the first batch of known-answer vectors for the checkpoint-state verifier. sync format - Dispatches by operation name. - Initial operation verify_checkpoint builds a genesis state for a given validator count and reports both the verdict and the SSZ-encoded state so clients can deserialize and run their own verify_checkpoint_state for bit-for-bit comparison. Initial vectors Three KATs under tests/consensus/devnet/sync covering the verdict branches of verify_checkpoint_state: - Zero validators -> rejected (state cannot produce blocks) - Four validators -> accepted (baseline suite size) - Eight validators -> accepted (upper end of test envelope) The verification check is defence-in-depth applied by a client after downloading an anchor state from a checkpoint provider; agreement on the verdict is required to catch misconfigured or corrupted anchors consistently across implementations. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e9d9f3b commit b5b315c

5 files changed

Lines changed: 139 additions & 0 deletions

File tree

packages/testing/src/consensus_testing/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
SlotClockTest,
1717
SSZTest,
1818
StateTransitionTest,
19+
SyncTest,
1920
VerifySignaturesTest,
2021
)
2122
from .test_types import (
@@ -46,6 +47,7 @@
4647
DiscoveryCryptoTestFiller = Type[DiscoveryCryptoTest]
4748
JustifiabilityTestFiller = Type[JustifiabilityTest]
4849
PoseidonPermutationTestFiller = Type[PoseidonPermutationTest]
50+
SyncTestFiller = Type[SyncTest]
4951

5052
__all__ = [
5153
# Public API
@@ -70,6 +72,7 @@
7072
"DiscoveryCryptoTest",
7173
"JustifiabilityTest",
7274
"PoseidonPermutationTest",
75+
"SyncTest",
7376
# Test types
7477
"BaseForkChoiceStep",
7578
"TickStep",
@@ -93,4 +96,5 @@
9396
"DiscoveryCryptoTestFiller",
9497
"JustifiabilityTestFiller",
9598
"PoseidonPermutationTestFiller",
99+
"SyncTestFiller",
96100
]

packages/testing/src/consensus_testing/test_fixtures/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .slot_clock import SlotClockTest
1212
from .ssz import SSZTest
1313
from .state_transition import StateTransitionTest
14+
from .sync import SyncTest
1415
from .verify_signatures import VerifySignaturesTest
1516

1617
__all__ = [
@@ -26,4 +27,5 @@
2627
"DiscoveryCryptoTest",
2728
"JustifiabilityTest",
2829
"PoseidonPermutationTest",
30+
"SyncTest",
2931
]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
"""Sync layer test fixture format.
2+
3+
Emits JSON vectors for the client-facing sync helpers. Each vector
4+
pins the expected verdict on a given input so clients can align their
5+
sync-layer decisions bit-for-bit.
6+
"""
7+
8+
from typing import Any, ClassVar
9+
10+
from lean_spec.subspecs.sync.checkpoint_sync import verify_checkpoint_state
11+
from lean_spec.types import Uint64
12+
13+
from ..genesis import generate_pre_state
14+
from .base import BaseConsensusFixture
15+
16+
17+
class SyncTest(BaseConsensusFixture):
18+
"""Fixture for sync-layer conformance.
19+
20+
Currently supports one operation:
21+
22+
- ``verify_checkpoint``: emits the SSZ-encoded anchor state plus the
23+
verification verdict a client must produce.
24+
25+
JSON output: operation, input, output.
26+
"""
27+
28+
format_name: ClassVar[str] = "sync"
29+
description: ClassVar[str] = "Tests sync-layer helpers clients must reproduce"
30+
31+
operation: str
32+
"""Sync operation: currently only verify_checkpoint."""
33+
34+
input: dict[str, Any]
35+
"""Operation-specific input. See per-handler docstrings."""
36+
37+
output: dict[str, Any] = {}
38+
"""Computed output. Filled by make_fixture."""
39+
40+
def make_fixture(self) -> "SyncTest":
41+
"""Dispatch to the operation handler.
42+
43+
Returns:
44+
A copy of this fixture with output populated.
45+
46+
Raises:
47+
ValueError: If the operation name is unknown.
48+
"""
49+
if self.operation == "verify_checkpoint":
50+
output = self._make_verify_checkpoint()
51+
else:
52+
raise ValueError(f"Unknown sync operation: {self.operation!r}")
53+
return self.model_copy(update={"output": output})
54+
55+
def _make_verify_checkpoint(self) -> dict[str, Any]:
56+
"""Build a genesis state for the given validator count and report the verdict.
57+
58+
Input keys:
59+
60+
- ``numValidators``: number of validators in the genesis state.
61+
62+
Output:
63+
64+
- ``valid``: result of verify_checkpoint_state on the built state.
65+
- ``stateBytes``: SSZ-encoded state hex, so clients can deserialize
66+
and run their own verify_checkpoint_state.
67+
- ``validatorCount``: echoed for diagnostic clarity.
68+
"""
69+
num_validators = int(self.input["numValidators"])
70+
state = generate_pre_state(genesis_time=Uint64(0), num_validators=num_validators)
71+
valid = verify_checkpoint_state(state)
72+
return {
73+
"valid": valid,
74+
"stateBytes": "0x" + state.encode_bytes().hex(),
75+
"validatorCount": num_validators,
76+
}

tests/consensus/devnet/sync/__init__.py

Whitespace-only changes.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Checkpoint-sync state verification: known-answer vectors.
2+
3+
Pins the structural-validity verdict each client must produce when
4+
fetching an anchor state from a checkpoint provider. The verdict is a
5+
defence-in-depth check applied before the state seeds a fork-choice
6+
store.
7+
"""
8+
9+
import pytest
10+
from consensus_testing import SyncTestFiller
11+
12+
pytestmark = pytest.mark.valid_until("Devnet")
13+
14+
15+
def test_checkpoint_verify_rejects_empty_validator_set(
16+
sync: SyncTestFiller,
17+
) -> None:
18+
"""A checkpoint state with zero validators is rejected.
19+
20+
A state without validators cannot produce blocks, so seeding a
21+
fork-choice store with it would be useless and mask configuration
22+
errors. Clients must refuse the anchor before any store setup.
23+
"""
24+
sync(
25+
operation="verify_checkpoint",
26+
input={"numValidators": 0},
27+
)
28+
29+
30+
def test_checkpoint_verify_accepts_small_validator_set(
31+
sync: SyncTestFiller,
32+
) -> None:
33+
"""A checkpoint state with a small in-range validator set is accepted.
34+
35+
Four validators is the baseline size used throughout the consensus
36+
test suite. Pins the happy path of the verifier so clients observe
37+
the accepted branch in addition to the rejection branch above.
38+
"""
39+
sync(
40+
operation="verify_checkpoint",
41+
input={"numValidators": 4},
42+
)
43+
44+
45+
def test_checkpoint_verify_accepts_eight_validator_set(
46+
sync: SyncTestFiller,
47+
) -> None:
48+
"""Eight-validator anchor state is accepted at the key-manager limit.
49+
50+
Matches the maximum-validator setup used by the existing fork-choice
51+
and signature-verification suites. Pins the verdict at the upper
52+
end of the practical test envelope.
53+
"""
54+
sync(
55+
operation="verify_checkpoint",
56+
input={"numValidators": 8},
57+
)

0 commit comments

Comments
 (0)