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: 2 additions & 0 deletions packages/testing/src/consensus_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
VerifySignaturesTest,
)
from .test_types import (
AggregatedAttestationSpec,
AttestationCheck,
AttestationStep,
BaseForkChoiceStep,
Expand All @@ -29,6 +30,7 @@

__all__ = [
# Public API
"AggregatedAttestationSpec",
"BlockSpec",
"SignedAttestationSpec",
"forks",
Expand Down
93 changes: 42 additions & 51 deletions packages/testing/src/consensus_testing/test_fixtures/fork_choice.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from lean_spec.subspecs.containers.attestation import (
Attestation,
AttestationData,
SignedAttestation,
)
from lean_spec.subspecs.containers.block import (
Block,
Expand All @@ -36,11 +35,11 @@

from ..keys import LEAN_ENV_TO_SCHEMES, XmssKeyManager, get_shared_key_manager
from ..test_types import (
AggregatedAttestationSpec,
AttestationStep,
BlockSpec,
BlockStep,
ForkChoiceStep,
SignedAttestationSpec,
TickStep,
)
from .base import BaseConsensusFixture
Expand Down Expand Up @@ -405,52 +404,68 @@ def _build_attestations_from_spec(
parent_root: Bytes32,
key_manager: XmssKeyManager,
) -> tuple[list[Attestation], dict[SignatureKey, Signature]]:
"""Build attestations list from BlockSpec and their signatures."""
"""Build aggregated attestations from BlockSpec and their signatures."""
if spec.attestations is None:
return [], {}

parent_state = store.states[parent_root]
attestations = []
signature_lookup: dict[SignatureKey, Signature] = {}

for att_spec in spec.attestations:
if isinstance(att_spec, SignedAttestationSpec):
signed_att = self._build_signed_attestation_from_spec(
att_spec, block_registry, parent_state, key_manager
for aggregated_spec in spec.attestations:
# Build attestation data (shared across all validators in this aggregation spec)
attestation_data = self._build_attestation_data_from_spec(
aggregated_spec, block_registry, parent_state
)

# Create individual attestations and signatures for each validator
for validator_id in aggregated_spec.validator_ids:
attestation = Attestation(
validator_id=validator_id,
data=attestation_data,
)
else:
signed_att = att_spec
attestations.append(attestation)

# Sign the attestation
if aggregated_spec.valid_signature:
signature = key_manager.sign_attestation_data(
validator_id,
attestation_data,
)
else:
signature = Signature(
path=HashTreeOpening(siblings=HashDigestList(data=[])),
rho=Randomness(data=[Fp(0) for _ in range(Randomness.LENGTH)]),
hashes=HashDigestList(data=[]),
)

attestation = Attestation(validator_id=signed_att.validator_id, data=signed_att.message)
attestations.append(attestation)
sig_key = SignatureKey(attestation.validator_id, attestation.data.data_root_bytes())
signature_lookup[sig_key] = signed_att.signature
sig_key = SignatureKey(validator_id, attestation_data.data_root_bytes())
signature_lookup[sig_key] = signature

return attestations, signature_lookup

def _build_signed_attestation_from_spec(
def _build_attestation_data_from_spec(
self,
spec: SignedAttestationSpec,
spec: AggregatedAttestationSpec,
block_registry: dict[str, Block],
state: State,
key_manager: XmssKeyManager,
) -> SignedAttestation:
) -> AttestationData:
"""
Build a SignedAttestation from a SignedAttestationSpec.
Build AttestationData from an AggregatedAttestationSpec.

Parameters
----------
spec : SignedAttestationSpec
The attestation specification to resolve.
spec : AggregatedAttestationSpec
The aggregated attestation specification.
block_registry : dict[str, Block]
Registry of labeled blocks for resolving target_root_label.
state : State
The state to get latest_justified checkpoint from.

Returns:
-------
SignedAttestation
The resolved signed attestation.
AttestationData
The attestation data shared by all validators in this aggregation.
"""
# Resolve target checkpoint from label
if spec.target_root_label not in block_registry:
Expand All @@ -468,33 +483,9 @@ def _build_signed_attestation_from_spec(
# Derive source from state's latest justified checkpoint
source_checkpoint = state.latest_justified

# Create attestation
attestation = Attestation(
validator_id=spec.validator_id,
data=AttestationData(
slot=spec.slot,
head=head_checkpoint,
target=target_checkpoint,
source=source_checkpoint,
),
)

# Create signed attestation
if spec.signature is not None:
signature = spec.signature
elif spec.valid_signature:
signature = key_manager.sign_attestation_data(
attestation.validator_id, attestation.data
)
else:
signature = Signature(
path=HashTreeOpening(siblings=HashDigestList(data=[])),
rho=Randomness(data=[Fp(0) for _ in range(Randomness.LENGTH)]),
hashes=HashDigestList(data=[]),
)

return SignedAttestation(
validator_id=attestation.validator_id,
message=attestation.data,
signature=signature,
return AttestationData(
slot=spec.slot,
head=head_checkpoint,
target=target_checkpoint,
source=source_checkpoint,
)
Loading
Loading