Skip to content

Commit

Permalink
feat: EIP-7549 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
madlabman committed Oct 31, 2024
1 parent 8cdfaee commit 0fc7d9a
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 11 deletions.
43 changes: 34 additions & 9 deletions src/modules/csm/checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from src import variables
from src.constants import SLOTS_PER_HISTORICAL_ROOT
from src.metrics.prometheus.csm import CSM_UNPROCESSED_EPOCHS_COUNT, CSM_MIN_UNPROCESSED_EPOCH
from src.metrics.prometheus.csm import CSM_MIN_UNPROCESSED_EPOCH, CSM_UNPROCESSED_EPOCHS_COUNT
from src.modules.csm.state import State
from src.providers.consensus.client import ConsensusClient
from src.providers.consensus.types import BlockAttestation
Expand All @@ -20,8 +20,7 @@
lock = Lock()


class MinStepIsNotReached(Exception):
...
class MinStepIsNotReached(Exception): ...


@dataclass
Expand Down Expand Up @@ -103,7 +102,9 @@ def _is_min_step_reached(self):
return False


type Committees = dict[tuple[str, str], list[ValidatorDuty]]
type Slot = str
type CommitteeIndex = str
type Committees = dict[tuple[Slot, CommitteeIndex], list[ValidatorDuty]]


class FrameCheckpointProcessor:
Expand Down Expand Up @@ -193,7 +194,10 @@ def _check_duty(
committees = self._prepare_committees(EpochNumber(duty_epoch))
for root in block_roots:
attestations = self.cc.get_block_attestations(BlockRoot(root))
process_attestations(attestations, committees)
if attestations and is_pectra_attestation(attestations[0]):
process_attestations_electra(attestations, committees)
else:
process_attestations(attestations, committees)

with lock:
for committee in committees.values():
Expand Down Expand Up @@ -227,11 +231,24 @@ def _prepare_committees(self, epoch: EpochNumber) -> Committees:
return committees


def process_attestations_electra(attestations: Iterable[BlockAttestation], committees: Committees) -> None:
for attestation in attestations:
assert is_pectra_attestation(attestation)
committee_offset = 0
for committee_idx in get_committee_indices(attestation):
committee = committees.get((attestation.data.slot, committee_idx), [])
att_bits = hex_bitvector_to_list(attestation.aggregation_bits)[committee_offset:][: len(committee)]
for index_in_committee, validator_duty in enumerate(committee):
validator_duty.included = validator_duty.included or _is_attested(att_bits, index_in_committee)
committee_offset += len(committee)


def process_attestations(attestations: Iterable[BlockAttestation], committees: Committees) -> None:
for attestation in attestations:
assert not is_pectra_attestation(attestation)
committee_id = (attestation.data.slot, attestation.data.index)
committee = committees.get(committee_id, [])
att_bits = _to_bits(attestation.aggregation_bits)
att_bits = hex_bitvector_to_list(attestation.aggregation_bits)
for index_in_committee, validator_duty in enumerate(committee):
validator_duty.included = validator_duty.included or _is_attested(att_bits, index_in_committee)

Expand All @@ -240,7 +257,15 @@ def _is_attested(bits: Sequence[bool], index: int) -> bool:
return bits[index]


def _to_bits(aggregation_bits: str) -> Sequence[bool]:
def is_pectra_attestation(attestation: BlockAttestation) -> bool:
return attestation.committee_bits != "" and attestation.data.index == "0"


def get_committee_indices(attestation: BlockAttestation) -> list[CommitteeIndex]:
return [str(idx) for (idx, bit) in enumerate(hex_bitvector_to_list(attestation.committee_bits)) if bit]


def hex_bitvector_to_list(bitvector: str) -> list[bool]:
# copied from https://github.com/ethereum/py-ssz/blob/main/ssz/sedes/bitvector.py#L66
att_bytes = bytes.fromhex(aggregation_bits[2:])
return [bool((att_bytes[bit_index // 8] >> bit_index % 8) % 2) for bit_index in range(len(att_bytes) * 8)]
bytes_ = bytes.fromhex(bitvector[2:]) if bitvector.startswith("0x") else bytes.fromhex(bitvector)
return [bool((bytes_[bit_index // 8] >> bit_index % 8) % 2) for bit_index in range(len(bytes_) * 8)]
2 changes: 1 addition & 1 deletion src/providers/consensus/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def get_attestation_committees(
self,
blockstamp: BlockStamp,
epoch: EpochNumber | None = None,
index: int | None = None,
index: int | None = None, # committee index
slot: SlotNumber | None = None
) -> list[SlotAttestationCommittee]:
"""Spec: https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochCommittees"""
Expand Down
3 changes: 2 additions & 1 deletion src/providers/consensus/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class Checkpoint:
@dataclass
class AttestationData(Nested, FromResponse):
slot: str
index: str
index: str # CommitteeIndex before Electra or 0 afterward.
beacon_block_root: BlockRoot
source: Checkpoint
target: Checkpoint
Expand All @@ -85,6 +85,7 @@ class AttestationData(Nested, FromResponse):
class BlockAttestation(Nested, FromResponse):
aggregation_bits: str
data: AttestationData
committee_bits: str = "" # [New in Electra:EIP7549]


@dataclass
Expand Down

0 comments on commit 0fc7d9a

Please sign in to comment.