Skip to content

Commit 250e28a

Browse files
tcoratgerclaude
andauthored
refactor(forks): add Spec delegator surface (Stage 4A of leanEthereum#686) (leanEthereum#702)
* refactor(forks): add Spec delegator surface (Stage 4A of leanEthereum#686) Adds the LstarSpec method surface that mirrors the existing State / Store / SignedBlock methods one-for-one: - State transition: state_transition, process_slots, process_block, process_block_header, process_attestations, build_block. - Forkchoice: on_block, on_tick, on_gossip_attestation, on_gossip_aggregated_attestation, produce_attestation_data, produce_block_with_signatures, get_proposal_head. - Block signatures: verify_signatures. Each method is a pure delegator to the corresponding container method. No call sites change — the new surface is unused initially. Stage 4B will rewrite call sites to go through the spec; Stage 4C will move the bodies in and replace literal Block/State/Store references with self.*_class(...). Tests use unittest.mock.patch.object to verify each delegator forwards its arguments unchanged and returns the container method's result verbatim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: tighten delegator surface docstrings Rewrite the per-method docstrings on the fork-class delegators and the delegator test suite to follow the project documentation rules: - Each docstring describes the operation in plain English. - No explicit class or method names that rot when renamed. - No issue/stage references that belong in the PR description. - No banner-style separator comments inside the class body. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(forks): add inner-type class pointers and structural protocols Extend ForkProtocol with class pointers for the inner container types (BlockBody, BlockHeader, AggregatedAttestations, AttestationSignatures) and the matching structural protocols. Hook them up on LstarSpec. This is the scaffolding the next stages of leanEthereum#686 need so that moving container method bodies into the spec can replace literal Block(...), BlockBody(...), AggregatedAttestations(...) constructors with self.<name>_class(...) — keeping inheriting forks free to swap any single inner type without re-implementing the parent's logic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4d1816d commit 250e28a

3 files changed

Lines changed: 405 additions & 0 deletions

File tree

src/lean_spec/forks/lstar/spec.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
11
"""Lstar fork — identity and construction facade."""
22

3+
from collections.abc import Iterable
4+
from collections.abc import Set as AbstractSet
35
from typing import ClassVar
46

57
from lean_spec.forks.lstar.containers import (
68
AggregatedAttestation,
79
Attestation,
810
AttestationData,
911
Block,
12+
BlockBody,
13+
BlockHeader,
1014
Config,
1115
SignedAggregatedAttestation,
1216
SignedAttestation,
1317
SignedBlock,
1418
Validator,
1519
)
1620
from lean_spec.forks.lstar.containers.block.block import BlockSignatures
21+
from lean_spec.forks.lstar.containers.block.types import (
22+
AggregatedAttestations,
23+
AttestationSignatures,
24+
)
1725
from lean_spec.forks.lstar.containers.state import State
1826
from lean_spec.forks.lstar.containers.validator import Validators
27+
from lean_spec.subspecs.chain.clock import Interval
28+
from lean_spec.subspecs.xmss.aggregation import AggregatedSignatureProof
29+
from lean_spec.subspecs.xmss.interface import TARGET_SIGNATURE_SCHEME, GeneralizedXmssScheme
30+
from lean_spec.types import Bytes32, Slot, ValidatorIndex
1931

2032
from ..protocol import ForkProtocol, SpecStateType
2133
from .store import Store
@@ -32,8 +44,12 @@ class LstarSpec(ForkProtocol):
3244

3345
state_class: type[State] = State
3446
block_class: type[Block] = Block
47+
block_body_class: type[BlockBody] = BlockBody
48+
block_header_class: type[BlockHeader] = BlockHeader
3549
signed_block_class: type[SignedBlock] = SignedBlock
3650
block_signatures_class: type[BlockSignatures] = BlockSignatures
51+
aggregated_attestations_class: type[AggregatedAttestations] = AggregatedAttestations
52+
attestation_signatures_class: type[AttestationSignatures] = AttestationSignatures
3753
store_class: type[Store] = Store
3854

3955
attestation_data_class: type[AttestationData] = AttestationData
@@ -57,3 +73,113 @@ def upgrade_state(self, state: SpecStateType) -> State:
5773
"""
5874
assert isinstance(state, State)
5975
return state
76+
77+
def state_transition(
78+
self,
79+
state: State,
80+
block: Block,
81+
valid_signatures: bool = True,
82+
) -> State:
83+
"""Compute the post-state obtained by applying a block to a pre-state."""
84+
return state.state_transition(block, valid_signatures)
85+
86+
def process_slots(self, state: State, target_slot: Slot) -> State:
87+
"""Advance the state through empty slots up to a target slot."""
88+
return state.process_slots(target_slot)
89+
90+
def process_block(self, state: State, block: Block) -> State:
91+
"""Apply a full block (header and body) to the state."""
92+
return state.process_block(block)
93+
94+
def process_block_header(self, state: State, block: Block) -> State:
95+
"""Apply only the header portion of a block to the state."""
96+
return state.process_block_header(block)
97+
98+
def process_attestations(
99+
self,
100+
state: State,
101+
attestations: Iterable[AggregatedAttestation],
102+
) -> State:
103+
"""Fold attestations into the state and update justification and finalization."""
104+
return state.process_attestations(attestations)
105+
106+
def build_block(
107+
self,
108+
state: State,
109+
slot: Slot,
110+
proposer_index: ValidatorIndex,
111+
parent_root: Bytes32,
112+
known_block_roots: AbstractSet[Bytes32],
113+
aggregated_payloads: dict[AttestationData, set[AggregatedSignatureProof]] | None = None,
114+
) -> tuple[Block, State, list[AggregatedAttestation], list[AggregatedSignatureProof]]:
115+
"""Assemble a valid block on top of the given pre-state."""
116+
return state.build_block(
117+
slot=slot,
118+
proposer_index=proposer_index,
119+
parent_root=parent_root,
120+
known_block_roots=known_block_roots,
121+
aggregated_payloads=aggregated_payloads,
122+
)
123+
124+
def verify_signatures(
125+
self,
126+
signed_block: SignedBlock,
127+
validators: Validators,
128+
scheme: GeneralizedXmssScheme = TARGET_SIGNATURE_SCHEME,
129+
) -> bool:
130+
"""Check that every signature carried by a signed block is valid."""
131+
return signed_block.verify_signatures(validators, scheme)
132+
133+
def on_block(
134+
self,
135+
store: Store,
136+
signed_block: SignedBlock,
137+
scheme: GeneralizedXmssScheme = TARGET_SIGNATURE_SCHEME,
138+
) -> Store:
139+
"""Incorporate a newly received block into the forkchoice view."""
140+
return store.on_block(signed_block, scheme)
141+
142+
def on_tick(
143+
self,
144+
store: Store,
145+
target_interval: Interval,
146+
has_proposal: bool,
147+
is_aggregator: bool = False,
148+
) -> tuple[Store, list[SignedAggregatedAttestation]]:
149+
"""Advance forkchoice time to a target interval and emit any due aggregates."""
150+
return store.on_tick(target_interval, has_proposal, is_aggregator)
151+
152+
def on_gossip_attestation(
153+
self,
154+
store: Store,
155+
signed_attestation: SignedAttestation,
156+
scheme: GeneralizedXmssScheme = TARGET_SIGNATURE_SCHEME,
157+
is_aggregator: bool = False,
158+
) -> Store:
159+
"""Incorporate a single-validator attestation received from the network."""
160+
return store.on_gossip_attestation(signed_attestation, scheme, is_aggregator)
161+
162+
def on_gossip_aggregated_attestation(
163+
self,
164+
store: Store,
165+
signed_attestation: SignedAggregatedAttestation,
166+
) -> Store:
167+
"""Incorporate an aggregated attestation received from the network."""
168+
return store.on_gossip_aggregated_attestation(signed_attestation)
169+
170+
def produce_attestation_data(self, store: Store, slot: Slot) -> AttestationData:
171+
"""Build the attestation payload that a validator should sign at this slot."""
172+
return store.produce_attestation_data(slot)
173+
174+
def produce_block_with_signatures(
175+
self,
176+
store: Store,
177+
slot: Slot,
178+
validator_index: ValidatorIndex,
179+
) -> tuple[Store, Block, list[AggregatedSignatureProof]]:
180+
"""Produce a proposal block together with the aggregated signature proofs it needs."""
181+
return store.produce_block_with_signatures(slot, validator_index)
182+
183+
def get_proposal_head(self, store: Store, slot: Slot) -> tuple[Store, Bytes32]:
184+
"""Resolve the head root that a proposal at this slot should extend."""
185+
return store.get_proposal_head(slot)

src/lean_spec/forks/protocol.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,38 @@ def state_root(self) -> Bytes32:
7171
...
7272

7373

74+
class SpecBlockBodyType(SpecSSZType, Protocol):
75+
"""Structural contract: any fork's BlockBody container class.
76+
77+
Carries the variable-size payload attached to a block — typically
78+
aggregated attestations and any future operation lists.
79+
"""
80+
81+
82+
class SpecBlockHeaderType(SpecSSZType, Protocol):
83+
"""Structural contract: any fork's BlockHeader container class.
84+
85+
The fixed-shape summary of a block used in state-transition tracking
86+
and state-root caching. Carries slot, proposer, parent root, state
87+
root, and body root.
88+
"""
89+
90+
91+
class SpecAggregatedAttestationsType(SpecSSZType, Protocol):
92+
"""Structural contract: any fork's AggregatedAttestations list class.
93+
94+
Bounded SSZ list of aggregated attestations included in a block body.
95+
"""
96+
97+
98+
class SpecAttestationSignaturesType(SpecSSZType, Protocol):
99+
"""Structural contract: any fork's AttestationSignatures list class.
100+
101+
Bounded SSZ list of aggregated signature proofs aligned one-for-one
102+
with a block body's aggregated attestations.
103+
"""
104+
105+
74106
class SpecSignedBlockType(SpecSSZType, Protocol):
75107
"""Structural contract: any fork's SignedBlock container class.
76108
@@ -282,12 +314,24 @@ class ForkProtocol(ABC):
282314
block_class: type[SpecBlockType]
283315
"""Concrete Block container class owned by this fork."""
284316

317+
block_body_class: type[SpecBlockBodyType]
318+
"""Concrete BlockBody container class owned by this fork."""
319+
320+
block_header_class: type[SpecBlockHeaderType]
321+
"""Concrete BlockHeader container class owned by this fork."""
322+
285323
signed_block_class: type[SpecSignedBlockType]
286324
"""Concrete SignedBlock container class — block + signatures envelope."""
287325

288326
block_signatures_class: type[SpecBlockSignaturesType]
289327
"""Concrete BlockSignatures container class — proposer + attestation signatures."""
290328

329+
aggregated_attestations_class: type[SpecAggregatedAttestationsType]
330+
"""Concrete AggregatedAttestations list class — block-body aggregated votes."""
331+
332+
attestation_signatures_class: type[SpecAttestationSignaturesType]
333+
"""Concrete AttestationSignatures list class — signature group bundle."""
334+
291335
store_class: type[SpecStoreType]
292336
"""Concrete forkchoice Store class owned by this fork."""
293337

0 commit comments

Comments
 (0)