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
2 changes: 1 addition & 1 deletion .claude/agents/code-tester.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The fork specs under `src/lean_spec/spec/forks/` are tested EXCLUSIVELY through

- There is NO `tests/spec/forks/` tree, and you must never create one.
- For ANY fork behavior — fork choice, state transition, block production, validator duties, aggregation, the containers, slot/interval math, the fork registry or protocol — write or update a consensus test-vector fixture (`state_transition`, `fork_choice`, `ssz`, `slot_clock`, `verify_signatures`, etc.), never a pytest.
- Mirrored pytest unit tests apply only to NON-fork modules (`node/`, `spec/crypto/`, `spec/ssz/`, and similar).
- Mirrored pytest unit tests apply only to NON-fork modules (`node/`, `spec/crypto/`, and similar).
- If asked to "add tests" for a fork container or function (for example a new container under `spec/forks/lstar/containers/`), produce a consensus vector fixture, not a pytest under `tests/`.

## Auto-Invoke Skills
Expand Down
4 changes: 2 additions & 2 deletions .claude/rules/code-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ paths:
Bad:
```python
def process(data):
from lean_spec.spec.crypto.merkleization import hash_tree_root
from ssz import hash_tree_root
return hash_tree_root(data)
```

Good:
```python
from lean_spec.spec.crypto.merkleization import hash_tree_root
from ssz import hash_tree_root

def process(data):
return hash_tree_root(data)
Expand Down
1 change: 0 additions & 1 deletion .claude/skills/audit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ orchestrator sharded the tree only so the work parallelizes.
2. **Shard.** Split the in-scope tree into coherent subsystems so agents run in parallel.
The natural shards:
- `src/lean_spec/spec/crypto/` (XMSS, hashing, signatures, aggregation)
- `src/lean_spec/spec/ssz/`
- `src/lean_spec/spec/forks/` (state transition, fork choice, containers, validator
duties, aggregation)
- `src/lean_spec/node/networking/` (gossipsub, reqresp, quic, discovery)
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Pass additional arguments after `--`:

- `/test -- -v` - Verbose output
- `/test -- -k "test_serialize"` - Run matching tests
- `/test -- tests/spec/ssz/` - Run specific test directory
- `/test -- tests/spec/crypto/` - Run specific test directory

## Examples

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ subspecifications that the Lean Ethereum protocol relies on.
- A test file must never test a type that lives in a different source module. For example, tests
for `SlotClock` (in `node/chain/clock.py`) belong in `tests/node/chain/test_clock.py`,
never in an unrelated test module.
- This mirroring covers non-fork modules only (`node/`, `spec/crypto/`, `spec/ssz/`, etc.). The
- This mirroring covers non-fork modules only (`node/`, `spec/crypto/`, etc.). The
fork specs under `src/lean_spec/spec/forks/` are exempt — see the forks-are-vectors rule below.
- **CRITICAL - FORKS ARE TESTED BY VECTORS, NOT PYTESTS**: This is a STRICT requirement. The fork
specs under `src/lean_spec/spec/forks/` are tested exclusively through consensus test vectors
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ just test
│ └── spec/ # Protocol specifications
│ ├── crypto/ # Cryptographic subspecs (poseidon, koalabear, xmss, ...)
│ ├── forks/ # Fork specifications (tested via consensus vectors)
│ ├── ssz/ # SSZ serialization
│ ├── ssz_types.py # The SSZ shapes leanSpec declares itself
│ └── observability/ # Observability spec
├── tests/ # Test suite
│ ├── consensus/ # Consensus test vectors
Expand Down
5 changes: 3 additions & 2 deletions packages/testing/src/consensus_testing/genesis.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Consensus layer genesis state, block, and anchor construction for tests."""

from ssz import Uint64, hash_tree_root

from consensus_testing.keys import XmssKeyManager
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.forks import Checkpoint, Interval, Slot, ValidatorIndex
from lean_spec.spec.forks.lstar import Store
from lean_spec.spec.forks.lstar.containers import (
Expand All @@ -16,7 +17,7 @@
Validators,
)
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import Bytes52, Uint64
from lean_spec.spec.ssz_types import Bytes52


def build_genesis_state(
Expand Down
5 changes: 3 additions & 2 deletions packages/testing/src/consensus_testing/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
from pathlib import Path
from typing import ClassVar, Literal

from ssz import hash_tree_root

from lean_spec.config import LEAN_ENV
from lean_spec.spec.crypto.koalabear import Fp
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.crypto.xmss.constants import TARGET_CONFIG
from lean_spec.spec.crypto.xmss.containers import (
PublicKey,
Expand All @@ -39,7 +40,7 @@
AttestationData,
SingleMessageAggregate,
)
from lean_spec.spec.ssz import Bytes32
from lean_spec.spec.ssz_types import Bytes32

KeyRole = Literal["attestation", "proposal"]
"""Discriminator for which signing role's key to load from a validator key pair."""
Expand Down
2 changes: 1 addition & 1 deletion packages/testing/src/consensus_testing/keys_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pathlib import Path

import click
from ssz import Uint64

from consensus_testing.keys import (
LEAN_ENV_TO_SCHEMES,
Expand All @@ -24,7 +25,6 @@
from lean_spec.spec.crypto.xmss.containers import ValidatorKeyPair
from lean_spec.spec.crypto.xmss.interface import GeneralizedXmssScheme
from lean_spec.spec.forks import Slot
from lean_spec.spec.ssz import Uint64

KEY_DOWNLOAD_URLS = {
"test": "https://github.com/leanEthereum/leansig-test-keys/releases/download/latest/test_scheme.tar.gz",
Expand Down
5 changes: 3 additions & 2 deletions packages/testing/src/consensus_testing/mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from types import MappingProxyType
from typing import cast

from ssz import Uint64, hash_tree_root

from lean_spec.node.chain.clock import SlotClock
from lean_spec.node.networking import PeerId
from lean_spec.node.networking.peer import PeerInfo
Expand All @@ -17,7 +19,6 @@
from lean_spec.node.sync.block_cache import BlockCache
from lean_spec.node.sync.peer_manager import PeerManager
from lean_spec.node.sync.service import SyncService
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.forks import (
Checkpoint,
RejectionReason,
Expand All @@ -34,7 +35,7 @@
State,
)
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import Bytes32, Uint64
from lean_spec.spec.ssz_types import Bytes32


@dataclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
ProofSetting,
)
from lean_spec.spec.forks import Slot, ValidatorIndex
from lean_spec.spec.ssz import Bytes32
from lean_spec.spec.ssz_types import Bytes32


class FixtureCollector:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from typing import Any, ClassVar

from ssz import Uint64

from consensus_testing.genesis import build_anchor
from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from consensus_testing.test_fixtures.hex_codec import to_hex
Expand All @@ -10,7 +12,6 @@
from lean_spec.spec.forks import Slot
from lean_spec.spec.forks.lstar import Store
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import Uint64

REQUIRED_METRIC_NAMES = [
"lean_node_info",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import ClassVar

from pydantic import Field
from ssz import hash_tree_root

from consensus_testing.genesis import build_genesis_state, reconstruct_block_from_header
from consensus_testing.keys import XmssKeyManager
Expand All @@ -23,7 +24,6 @@
TickStep,
)
from lean_spec.node.chain.clock import SlotClock
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.forks import (
Interval,
RejectionReason,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

from typing import ClassVar

from ssz import hash_tree_root

from consensus_testing.keys import XmssKeyManager
from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from consensus_testing.test_fixtures.hex_codec import to_hex
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.forks import AggregationBits, Checkpoint, Slot, ValidatorIndex
from lean_spec.spec.forks.lstar.containers import (
AggregatedAttestation,
Expand All @@ -18,7 +19,7 @@
MultiMessageAggregate,
SingleMessageAggregate,
)
from lean_spec.spec.ssz import Bytes32
from lean_spec.spec.ssz_types import Bytes32

ATTESTATION_SLOT: Slot = Slot(1)
"""Attestation slot, one before the block that carries it."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Annotated, ClassVar, Literal

from pydantic import AfterValidator, Field
from ssz import Uint64

from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from lean_spec.base import StrictBaseModel
Expand All @@ -14,7 +15,6 @@
MILLISECONDS_PER_INTERVAL,
SECONDS_PER_SLOT,
)
from lean_spec.spec.ssz import Uint64


def _reject_non_integral_timestamp(timestamp: float) -> float:
Expand Down
13 changes: 5 additions & 8 deletions packages/testing/src/consensus_testing/test_fixtures/ssz.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

from typing import Any, ClassVar

from pydantic import field_serializer
from pydantic import ValidationError, field_serializer
from ssz import Boolean, SSZError, SSZType, hash_tree_root

from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from consensus_testing.test_fixtures.hex_codec import from_hex, to_hex
from lean_spec.base import CamelModel
from lean_spec.spec.crypto.koalabear import Fp
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.ssz.boolean import Boolean
from lean_spec.spec.ssz.ssz_base import SSZType
from lean_spec.spec.ssz_types import ContainerInvariantError


class SSZFixture(BaseConsensusFixture):
Expand Down Expand Up @@ -43,8 +41,6 @@ def serialize_value(self, ssz_value: SSZType) -> Any:
return to_hex(ssz_value)
if isinstance(ssz_value, int):
return str(ssz_value)
if isinstance(ssz_value, Fp):
return str(ssz_value.value)
return str(ssz_value)


Expand Down Expand Up @@ -104,7 +100,8 @@ def _generate_decode_failure(self) -> SSZFixture:
exception_raised: Exception | None = None
try:
decoder.decode_bytes(raw)
except Exception as exception:
except (SSZError, ValidationError, ContainerInvariantError) as exception:
# Anything else is a bug in the filler, not an input every client must reject.
exception_raised = exception

return SSZFixture(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from typing import ClassVar

from pydantic import Field, model_validator
from ssz import hash_tree_root

from consensus_testing.genesis import build_genesis_state
from consensus_testing.keys import XmssKeyManager
from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from consensus_testing.test_types import AggregatedAttestationSpec, BlockSpec, StateExpectation
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.forks import SpecRejectionError
from lean_spec.spec.forks.lstar.containers import (
AggregatedAttestation,
Expand All @@ -20,7 +20,7 @@
State,
)
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import Bytes32
from lean_spec.spec.ssz_types import Bytes32


class StateTransitionFixture(BaseConsensusFixture):
Expand Down
3 changes: 2 additions & 1 deletion packages/testing/src/consensus_testing/test_fixtures/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

from typing import ClassVar, Literal

from ssz import Uint64

from consensus_testing.genesis import build_anchor
from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from consensus_testing.test_fixtures.hex_codec import to_hex
from lean_spec.base import StrictBaseModel
from lean_spec.node.sync.checkpoint_sync import verify_checkpoint_state
from lean_spec.spec.forks import Slot
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import Uint64


class VerifyCheckpointOutput(StrictBaseModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

from typing import ClassVar

from ssz import hash_tree_root

from consensus_testing.keys import XmssKeyManager
from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from lean_spec.base import StrictBaseModel
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.crypto.xmss.containers import PublicKey
from lean_spec.spec.forks import (
AggregationBits,
Expand All @@ -21,7 +22,7 @@
MultiMessageAggregate,
SingleMessageAggregate,
)
from lean_spec.spec.ssz import ByteList512KiB, Bytes32
from lean_spec.spec.ssz_types import ByteList512KiB, Bytes32

ALTERNATE_HEAD_ROOT: Bytes32 = Bytes32(b"\xee" * 32)
"""Sentinel head root used by the rebind tamper to bind one component off-target."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
from typing import ClassVar

from pydantic import Field
from ssz import Boolean, hash_tree_root

from consensus_testing.genesis import build_genesis_state
from consensus_testing.keys import XmssKeyManager
from consensus_testing.test_fixtures.base import BaseConsensusFixture, BaseTestSpec
from consensus_testing.test_types import BlockSpec
from lean_spec.base import StrictBaseModel
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.forks import (
AggregationBits,
Checkpoint,
Expand All @@ -28,7 +28,7 @@
State,
)
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import Boolean, ByteList512KiB, Bytes32
from lean_spec.spec.ssz_types import ByteList512KiB, Bytes32


class SetProposerIndex(StrictBaseModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from __future__ import annotations

from ssz import hash_tree_root

from consensus_testing.keys import XmssKeyManager, create_dummy_signature
from consensus_testing.test_types.utils import resolve_checkpoint
from lean_spec.base import CamelModel
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.forks import AggregationBits, Checkpoint, Slot, ValidatorIndex
from lean_spec.spec.forks.lstar.containers import (
AggregatedAttestation,
Expand All @@ -19,7 +20,7 @@
Store,
)
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import ByteList512KiB, Bytes32
from lean_spec.spec.ssz_types import ByteList512KiB, Bytes32


class AttestationSpec(CamelModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
import copy
from collections import defaultdict

from ssz import Uint64, hash_tree_root

from consensus_testing.genesis import reconstruct_block_from_header
from consensus_testing.keys import XmssKeyManager, create_dummy_signature
from consensus_testing.test_types.attestation_specs import AggregatedAttestationSpec
from lean_spec.base import CamelModel
from lean_spec.spec.crypto.merkleization import hash_tree_root
from lean_spec.spec.crypto.xmss.containers import PublicKey, Signature
from lean_spec.spec.forks import AggregationBits, Slot, ValidatorIndex
from lean_spec.spec.forks.lstar.containers import (
Expand All @@ -27,7 +28,7 @@
Store,
)
from lean_spec.spec.forks.lstar.spec import LstarSpec
from lean_spec.spec.ssz import ByteList512KiB, Bytes32, Uint64
from lean_spec.spec.ssz_types import ByteList512KiB, Bytes32


class BlockSpec(CamelModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
State,
Validators,
)
from lean_spec.spec.ssz import Bytes32
from lean_spec.spec.ssz_types import Bytes32


class StateExpectation(SelectiveCheck):
Expand Down
Loading
Loading