Skip to content

Commit 45ba21e

Browse files
tcoratgerclaude
andauthored
refactor(forks): move verify_signatures body into the spec (Stage 4C, part 1 of leanEthereum#686) (leanEthereum#704)
* refactor(forks): move verify_signatures body into the spec class Moves the full XMSS signature verification logic from SignedBlock.verify_signatures into LstarSpec.verify_signatures. SignedBlock becomes a pure SSZ data container. Internal callers that still hold a Store (notably Store.on_block, which itself moves to the spec class in a follow-up) reach the verification path via a deferred import of LstarSpec to sidestep the spec ↔ store module-load cycle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(forks): move State and Store bodies into the spec class Migrates every State and Store method body into LstarSpec. Containers (State, Store, SignedBlock) become thin Pydantic data classes whose methods are one-line forwarders to the active fork spec, reached via a deferred import that breaks the spec ↔ container module-load cycle. Inside the moved bodies, every literal Block(...), BlockBody(...), BlockHeader(...), Config(...), AggregatedAttestations(...), and other container constructor is now self.<name>_class(...). An inheriting fork that swaps a single container type therefore receives the parent fork's logic for free. Observability hooks (observe_state_transition, observe_on_block, observe_on_attestation) ride along with the bodies, preserving the metrics surface. The obsolete delegator-forwarding test file is removed; behavioural coverage now lives in the existing state-transition, fork-choice, and block-production test suites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(forks): delete the trivial container forwarders (Stage 4D of leanEthereum#686) State, Store, and SignedBlock become pure Pydantic data containers. All forwarder methods that delegated through the lazy spec singleton are removed; the lazy singleton helpers are removed alongside them. Every remaining call site that used to go through a container method now goes through the active fork spec: - Tests use the session-scoped `spec` fixture from `tests/lean_spec/conftest.py`. - Subspec services (`sync`, `validator`, `chain`, plus the fork-choice API endpoint) carry a module-level `_SPEC = LstarSpec()` constant. - Library helpers (`tests/lean_spec/helpers/builders.py`, `packages/testing/...`) follow the same `_SPEC` pattern. `ForkProtocol.generate_genesis` and `ForkProtocol.create_store` become abstract; the previous default implementations referenced container methods that no longer exist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(forks): resolve Block forward reference in BlockLookup The Store.blocks field is annotated as `BlockLookup`, which was defined as `dict[Bytes32, "Block"]` — a string forward reference. After the container methods moved off Store, Pydantic's model rebuild could no longer resolve `Block` because it was never imported into store.py alongside the alias. Drop the forward-reference quoting: `BlockLookup` lives in the same module as `Block`, so the type can refer to the class directly. Pydantic then resolves `Store.blocks` correctly through the alias re-export. Without this, every consensus filler that constructed a Store via `spec.create_store(...)` raised `PydanticUserError: 'Store' is not fully defined`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(forks): route mock-store tests through autouse spec patches The forwarders that used to live on `Store` (and were patched in `tests/lean_spec/subspecs/{sync,chain,networking,validator}/`) are gone after Stage 4D. The mocks (`MockStore`, `MockForkchoiceStore`) still implement the same method surface, but the service code now calls the real spec, which expects a Pydantic Store. Add an autouse fixture per affected subspec that patches the active spec's methods to delegate back to `store.method(...)`. The mocks intercept calls in-place, preserving every test's recording semantics without touching service code. The validator service tests that previously patched `Store.produce_block_with_signatures` and `Store.on_gossip_attestation` now patch the matching methods on the validator service's `_SPEC` — same intent, current attribute path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(forks): drop cast(Store, ...) at call sites Casts were a workaround for Liskov violations on LstarSpec.create_store when it returned the SpecStoreType protocol while concretely producing Store. cast had no runtime effect and pushed type-checker noise into fixtures and tests. Move the imprecision into the fork itself: create_store now declares its concrete Store return and suppresses the override warning at the single definition site. Callers receive Store directly and need no cast. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(forks): replace 17-site _SPEC duplication with DI Each module declared its own _SPEC = LstarSpec(), creating value-equal but identity-distinct instances. The pattern also baked LstarSpec into 17 import sites; a future fork would have to grep-and-replace them all. Production services (chain, sync, validator, api) now take spec as a dataclass field with default_factory=LstarSpec — explicit at the composition root in node.py, optional in tests. node.py narrows config.fork (ForkProtocol) to LstarSpec once with isinstance, which also lets the cast(State, ...) and cast(Store, ...) at the genesis construction sites drop. Test conftests that intercept spec calls now monkey-patch LstarSpec at the class level (not the deleted module-level _SPEC instance). Test types and fixtures instantiate LstarSpec at call time — no module-level cache, no shared mutable state to alias. ForkProtocol still declares only the three abstract construction methods (generate_genesis, create_store, upgrade_state). Services and tests that drive consensus methods (process_slots, build_block, tick_interval, ...) keep the concrete LstarSpec type until the protocol surface is widened in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(tests): replace conftest monkey-patches with injected spec Three autouse fixtures in chain/sync/networking conftests patched LstarSpec class methods so MockStore / MockForkchoiceStore could intercept consensus calls in place. Class-level patching mutates shared state and runs against every test in the directory whether needed or not. Now that services accept a spec field, tests inject a small StoreInterceptingSpec subclass that forwards each spec call back to the store argument. make_store() (used by sync/networking tests) hands the intercepting spec to the real SyncService transparently. Chain test_service.py threads it through ChainService directly. Two conftests delete entirely; the sync conftest keeps only its sample_checkpoint / sample_status fixtures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(tests): patch validator spec on the instance, not the dropped module Two validator tests still resolved `lean_spec.subspecs.validator.service._SPEC`, which was removed when the spec moved onto the service as a field. Patching now targets `service.spec` directly via `patch.object`, which also exercises the single instance the test actually calls into. 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 22afd98 commit 45ba21e

38 files changed

Lines changed: 2183 additions & 2582 deletions

packages/testing/src/consensus_testing/genesis.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,12 @@
44
from lean_spec.forks.lstar.containers.state import State, Validators
55
from lean_spec.forks.lstar.containers.validator import Validator
66
from lean_spec.forks.lstar.spec import LstarSpec
7-
from lean_spec.forks.protocol import ForkProtocol
87
from lean_spec.subspecs.ssz.hash import hash_tree_root
98
from lean_spec.types import Bytes52, Slot, Uint64, ValidatorIndex
109

1110
from .keys import XmssKeyManager
1211

1312
_DEFAULT_GENESIS_TIME = Uint64(0)
14-
_SPEC = LstarSpec()
15-
"""Active fork spec — stateless, safe to share across all helper invocations."""
16-
17-
_DEFAULT_FORK: ForkProtocol = _SPEC
18-
"""Stateless fork instance used when callers do not pass one explicitly."""
1913

2014

2115
def _build_validators(num_validators: int) -> Validators:
@@ -44,30 +38,30 @@ def _build_validators(num_validators: int) -> Validators:
4438

4539

4640
def generate_pre_state(
47-
fork: ForkProtocol = _DEFAULT_FORK,
41+
fork: LstarSpec | None = None,
4842
genesis_time: Uint64 = _DEFAULT_GENESIS_TIME,
4943
num_validators: int = 4,
5044
) -> State:
5145
"""Generate a default pre-state for consensus tests.
5246
5347
Args:
54-
fork: Fork dispatching genesis construction.
48+
fork: Fork dispatching genesis construction. Defaults to a fresh
49+
LstarSpec instance.
5550
genesis_time: The genesis timestamp.
5651
num_validators: Number of validators to include.
5752
5853
Returns:
5954
A properly initialized consensus state.
6055
"""
56+
fork = fork or LstarSpec()
6157
validators = _build_validators(num_validators)
62-
state = fork.generate_genesis(genesis_time=genesis_time, validators=validators)
63-
assert isinstance(state, State)
64-
return state
58+
return fork.generate_genesis(genesis_time=genesis_time, validators=validators)
6559

6660

6761
def build_anchor(
6862
num_validators: int,
6963
anchor_slot: Slot,
70-
fork: ForkProtocol = _DEFAULT_FORK,
64+
fork: LstarSpec | None = None,
7165
genesis_time: Uint64 = _DEFAULT_GENESIS_TIME,
7266
) -> tuple[State, Block]:
7367
"""Build a consistent non-genesis anchor by advancing the genesis state.
@@ -101,6 +95,7 @@ def build_anchor(
10195
"For a genesis anchor use generate_pre_state instead."
10296
)
10397

98+
fork = fork or LstarSpec()
10499
state = generate_pre_state(fork=fork, genesis_time=genesis_time, num_validators=num_validators)
105100

106101
# Reconstruct the genesis block from the state's latest header.
@@ -124,7 +119,7 @@ def build_anchor(
124119
for next_slot in range(1, int(anchor_slot) + 1):
125120
slot = Slot(next_slot)
126121
proposer_index = ValidatorIndex(int(slot) % int(num_validators_u64))
127-
current_block, state, _, _ = _SPEC.build_block(
122+
current_block, state, _, _ = fork.build_block(
128123
state,
129124
slot=slot,
130125
proposer_index=proposer_index,

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def _build_store(num_validators: int, genesis_time: int, anchor_slot: int = 0) -
5353
)
5454
block = _make_genesis_block(state)
5555
# No validator identity — fixture only reads store data, never signs.
56-
return Store.from_anchor(state, block, validator_id=None)
56+
return fork.create_store(state, block, validator_id=None)
5757

5858
# Walk the chain from genesis through anchor_slot using empty blocks.
5959
# The returned pair (state, block) is internally consistent with the
@@ -64,7 +64,7 @@ def _build_store(num_validators: int, genesis_time: int, anchor_slot: int = 0) -
6464
anchor_slot=Slot(anchor_slot),
6565
genesis_time=Uint64(genesis_time),
6666
)
67-
return Store.from_anchor(state, block, validator_id=None)
67+
return fork.create_store(state, block, validator_id=None)
6868

6969

7070
def _health_response(_store: Store, _fixture: "ApiEndpointTest") -> dict[str, Any]:
@@ -100,7 +100,7 @@ def _finalized_state_response(store: Store, _fixture: "ApiEndpointTest") -> dict
100100

101101
def _fork_choice_response(store: Store, _fixture: "ApiEndpointTest") -> dict[str, Any]:
102102
"""Fork choice tree: blocks with weights, head, checkpoints, validator count."""
103-
weights = store.compute_block_weights()
103+
weights = LstarSpec().compute_block_weights(store)
104104

105105
# Only post-finalization blocks are relevant to head selection.
106106
nodes = [

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from pydantic import Field, model_validator
1313

14-
from lean_spec.forks.lstar import Store
1514
from lean_spec.forks.lstar.containers.block import (
1615
Block,
1716
BlockBody,
@@ -38,9 +37,6 @@
3837
)
3938
from .base import BaseConsensusFixture
4039

41-
_SPEC = LstarSpec()
42-
"""Active fork spec — stateless, safe to share across all fixture invocations."""
43-
4440

4541
class ForkChoiceTest(BaseConsensusFixture):
4642
"""
@@ -190,6 +186,8 @@ def make_fixture(self) -> Self:
190186
assert self.anchor_block is not None, "anchor block must be set before making fixture"
191187
assert self.max_slot is not None, "max slot must be set before making fixture"
192188

189+
spec = LstarSpec()
190+
193191
# Expected anchor-init failure path.
194192
#
195193
# When anchor_valid is False, the test asserts that Store.from_anchor
@@ -202,7 +200,7 @@ def make_fixture(self) -> Self:
202200
"Store.from_anchor is expected to fail before any step can run"
203201
)
204202
try:
205-
Store.from_anchor(
203+
spec.create_store(
206204
self.anchor_state,
207205
self.anchor_block,
208206
validator_id=ValidatorIndex(0),
@@ -257,7 +255,7 @@ def make_fixture(self) -> Self:
257255
#
258256
# The Store is the node's local view of the chain.
259257
# It starts from a trusted anchor (usually genesis).
260-
store = Store.from_anchor(
258+
store = spec.create_store(
261259
self.anchor_state,
262260
self.anchor_block,
263261
validator_id=ValidatorIndex(0),
@@ -293,7 +291,7 @@ def make_fixture(self) -> Self:
293291
target_interval = Interval.from_unix_time(
294292
Uint64(step.time), store.config.genesis_time
295293
)
296-
store, _ = _SPEC.on_tick(
294+
store, _ = spec.on_tick(
297295
store,
298296
target_interval,
299297
has_proposal=step.has_proposal,
@@ -326,13 +324,13 @@ def make_fixture(self) -> Self:
326324
# This tick includes a block (has proposal).
327325
# Always act as aggregator to ensure gossip signatures are aggregated
328326
target_interval = Interval.from_slot(block.slot)
329-
store, _ = _SPEC.on_tick(
327+
store, _ = spec.on_tick(
330328
store, target_interval, has_proposal=True, is_aggregator=True
331329
)
332330

333331
# Process the block through Store.
334332
# This validates, applies state transition, and updates the store's head.
335-
store = _SPEC.on_block(
333+
store = spec.on_block(
336334
store,
337335
signed_block,
338336
scheme=LEAN_ENV_TO_SCHEMES[self.lean_env],
@@ -350,7 +348,7 @@ def make_fixture(self) -> Self:
350348
step.valid,
351349
)
352350
step._filled_attestation = signed_attestation
353-
store = _SPEC.on_gossip_attestation(
351+
store = spec.on_gossip_attestation(
354352
store,
355353
signed_attestation,
356354
scheme=LEAN_ENV_TO_SCHEMES[self.lean_env],
@@ -364,7 +362,7 @@ def make_fixture(self) -> Self:
364362
key_manager,
365363
)
366364
step._filled_attestation = signed_aggregated
367-
store = _SPEC.on_gossip_aggregated_attestation(store, signed_aggregated)
365+
store = spec.on_gossip_aggregated_attestation(store, signed_aggregated)
368366

369367
case _:
370368
raise ValueError(f"Step {i}: unknown step type {type(step).__name__}")

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@
1717
from ..test_types import AggregatedAttestationSpec, BlockSpec, StateExpectation
1818
from .base import BaseConsensusFixture
1919

20-
_SPEC = LstarSpec()
21-
"""Active fork spec — stateless, safe to share across all fixture invocations."""
22-
2320

2421
class StateTransitionTest(BaseConsensusFixture):
2522
"""
@@ -113,6 +110,7 @@ def make_fixture(self) -> "StateTransitionTest":
113110
"""
114111
actual_post_state: State | None = None
115112
exception_raised: Exception | None = None
113+
spec = LstarSpec()
116114

117115
# Initialize filled_blocks list that will be populated as we process blocks
118116
filled_blocks: list[Block] = []
@@ -140,9 +138,9 @@ def make_fixture(self) -> "StateTransitionTest":
140138
if cached_state is not None:
141139
state = cached_state
142140
elif getattr(block_spec, "skip_slot_processing", False):
143-
state = _SPEC.process_block(state, block)
141+
state = spec.process_block(state, block)
144142
else:
145-
state = _SPEC.state_transition(
143+
state = spec.state_transition(
146144
state,
147145
block=block,
148146
valid_signatures=True,
@@ -217,7 +215,7 @@ def _build_block_from_spec(
217215
# Advance slots unless the spec intentionally skips slot processing.
218216
slot_advanced_state: State | None = None
219217
if not spec.skip_slot_processing:
220-
slot_advanced_state = _SPEC.process_slots(state, spec.slot)
218+
slot_advanced_state = LstarSpec().process_slots(state, spec.slot)
221219

222220
# Resolve the parent root.
223221
# Default: latest block header from the slot-advanced state.
@@ -260,7 +258,7 @@ def _build_block_from_spec(
260258

261259
known_block_roots = frozenset(hash_tree_root(b) for b in block_registry.values())
262260

263-
block, post_state, _, _ = _SPEC.build_block(
261+
block, post_state, _, _ = LstarSpec().build_block(
264262
state,
265263
slot=spec.slot,
266264
proposer_index=proposer_index,
@@ -295,8 +293,8 @@ def _build_block_from_spec(
295293
# The body changed, so re-run the transition to get the correct
296294
# post-state and state root.
297295
if post_state is not None:
298-
post_state = _SPEC.process_slots(state, spec.slot)
299-
post_state = _SPEC.process_block(post_state, block)
296+
post_state = LstarSpec().process_slots(state, spec.slot)
297+
post_state = LstarSpec().process_block(post_state, block)
300298
block = block.model_copy(update={"state_root": hash_tree_root(post_state)})
301299

302300
return block, post_state

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222
from ..test_types import BlockSpec
2323
from .base import BaseConsensusFixture
2424

25-
_SPEC = LstarSpec()
26-
"""Active fork spec — stateless, safe to share across all fixture invocations."""
27-
2825

2926
class VerifySignaturesTest(BaseConsensusFixture):
3027
"""
@@ -115,7 +112,7 @@ def make_fixture(self) -> VerifySignaturesTest:
115112

116113
# Verify signatures
117114
try:
118-
_SPEC.verify_signatures(signed_block, self.anchor_state.validators)
115+
LstarSpec().verify_signatures(signed_block, self.anchor_state.validators)
119116
except AssertionError as e:
120117
exception_raised = e
121118
# If we expect an exception, this is fine

packages/testing/src/consensus_testing/test_types/block_spec.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@
3232
from ..keys import LEAN_ENV_TO_SCHEMES, XmssKeyManager, create_dummy_signature
3333
from .aggregated_attestation_spec import AggregatedAttestationSpec
3434

35-
_SPEC = LstarSpec()
36-
"""Active fork spec — stateless, safe to share across all spec invocations."""
37-
3835

3936
class BlockSpec(CamelModel):
4037
"""
@@ -294,6 +291,7 @@ def build_signed_block(
294291
Returns:
295292
Complete signed block with all attestation and proposer signatures.
296293
"""
294+
spec = LstarSpec()
297295
proposer_index = self.resolve_proposer_index(len(state.validators))
298296

299297
# Build a genesis block registry so attestation specs can resolve labels.
@@ -308,7 +306,7 @@ def build_signed_block(
308306

309307
# Resolve the parent root.
310308
# The default is the latest block header from the slot-advanced state.
311-
parent_state = _SPEC.process_slots(state, self.slot)
309+
parent_state = spec.process_slots(state, self.slot)
312310
parent_root = self.resolve_parent_root(
313311
block_registry,
314312
default_root=hash_tree_root(parent_state.latest_block_header),
@@ -364,7 +362,7 @@ def build_signed_block(
364362
for agg_att, proof in zip(aggregated_attestations, attestation_sigs.data, strict=True)
365363
}
366364

367-
final_block, _, _, aggregated_signatures = _SPEC.build_block(
365+
final_block, _, _, aggregated_signatures = spec.build_block(
368366
state,
369367
slot=self.slot,
370368
proposer_index=proposer_index,
@@ -405,6 +403,7 @@ def build_signed_block_with_store(
405403
Returns:
406404
Complete signed block ready for Store processing.
407405
"""
406+
spec = LstarSpec()
408407
proposer_index = self.resolve_proposer_index(len(store.states[store.head].validators))
409408

410409
# Resolve parent block.
@@ -429,7 +428,7 @@ def build_signed_block_with_store(
429428
# check rejects votes whose slot has not yet started locally.
430429
block_slot_interval = Interval.from_slot(self.slot)
431430
if store.time < block_slot_interval:
432-
store, _ = _SPEC.on_tick(
431+
store, _ = spec.on_tick(
433432
store, block_slot_interval, has_proposal=True, is_aggregator=True
434433
)
435434

@@ -442,7 +441,7 @@ def build_signed_block_with_store(
442441
or (signature := sigs_for_data.get(attestation.validator_id)) is None
443442
):
444443
continue
445-
store = _SPEC.on_gossip_attestation(
444+
store = spec.on_gossip_attestation(
446445
store,
447446
SignedAttestation(
448447
validator_id=attestation.validator_id,
@@ -454,11 +453,11 @@ def build_signed_block_with_store(
454453
)
455454

456455
# Trigger Store aggregation to merge gossip signatures into known payloads.
457-
aggregation_store, _ = store.aggregate()
458-
merged_store = aggregation_store.accept_new_attestations()
456+
aggregation_store, _ = spec.aggregate(store)
457+
merged_store = spec.accept_new_attestations(aggregation_store)
459458

460459
# Build the block through the spec's State.build_block().
461-
final_block, _, _, block_proofs = _SPEC.build_block(
460+
final_block, _, _, block_proofs = spec.build_block(
462461
parent_state,
463462
slot=self.slot,
464463
proposer_index=proposer_index,
@@ -470,9 +469,9 @@ def build_signed_block_with_store(
470469
# Append forced attestations that bypass the builder's MAX cap.
471470
# Each entry is signed and aggregated so the block carries valid proofs.
472471
if self.forced_attestations:
473-
for spec in self.forced_attestations:
474-
att_data = spec.build_attestation_data(block_registry, parent_state)
475-
proof = key_manager.sign_and_aggregate(spec.validator_ids, att_data)
472+
for att_spec in self.forced_attestations:
473+
att_data = att_spec.build_attestation_data(block_registry, parent_state)
474+
proof = key_manager.sign_and_aggregate(att_spec.validator_ids, att_data)
476475
block_proofs.append(proof)
477476
final_block = final_block.model_copy(
478477
update={
@@ -483,7 +482,7 @@ def build_signed_block_with_store(
483482
*final_block.body.attestations.data,
484483
AggregatedAttestation(
485484
aggregation_bits=ValidatorIndices(
486-
data=spec.validator_ids,
485+
data=att_spec.validator_ids,
487486
).to_aggregation_bits(),
488487
data=att_data,
489488
),
@@ -495,8 +494,8 @@ def build_signed_block_with_store(
495494
)
496495

497496
# Recompute state root with the modified body.
498-
post_state = _SPEC.process_slots(parent_state, self.slot)
499-
post_state = _SPEC.process_block(post_state, final_block)
497+
post_state = spec.process_slots(parent_state, self.slot)
498+
post_state = spec.process_block(post_state, final_block)
500499
final_block = final_block.model_copy(update={"state_root": hash_tree_root(post_state)})
501500

502501
return self._sign_block(final_block, block_proofs, proposer_index, key_manager)

packages/testing/src/consensus_testing/test_types/gossip_attestation_spec.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
from ..keys import XmssKeyManager, create_dummy_signature
1313
from .utils import resolve_checkpoint
1414

15-
_SPEC = LstarSpec()
16-
"""Active fork spec — stateless, safe to share across all spec invocations."""
17-
1815

1916
class GossipAttestationSpec(CamelModel):
2017
"""
@@ -204,7 +201,7 @@ def build_signed(
204201
attestation_data = self.build_attestation_data(block_registry, anchor_block)
205202
else:
206203
# Honest path: use the Store's own attestation data production.
207-
attestation_data = _SPEC.produce_attestation_data(store, self.slot)
204+
attestation_data = LstarSpec().produce_attestation_data(store, self.slot)
208205

209206
signature = (
210207
key_manager.sign_attestation_data(self.validator_id, attestation_data)

0 commit comments

Comments
 (0)