diff --git a/specs/electra/beacon-chain.md b/specs/electra/beacon-chain.md index 62b50985af..32a42f0c08 100644 --- a/specs/electra/beacon-chain.md +++ b/specs/electra/beacon-chain.md @@ -31,6 +31,7 @@ - [`WithdrawalRequest`](#withdrawalrequest) - [`ConsolidationRequest`](#consolidationrequest) - [`SingleAttestation`](#singleattestation) + - [`CommitteeAttestation`](#committeeattestation) - [`ExecutionRequests`](#executionrequests) - [Modified Containers](#modified-containers) - [`AttesterSlashing`](#attesterslashing) @@ -281,6 +282,16 @@ class SingleAttestation(Container): signature: BLSSignature ``` +#### `CommitteeAttestation` + +```python +class CommitteeAttestation(Container): + aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] + data: AttestationData + committee_index: CommitteeIndex + signature: BLSSignature +``` + #### `ExecutionRequests` *Note*: This container holds requests from the execution layer that are received in [ diff --git a/specs/electra/p2p-interface.md b/specs/electra/p2p-interface.md index 0ea33df9f7..805ca2eece 100644 --- a/specs/electra/p2p-interface.md +++ b/specs/electra/p2p-interface.md @@ -46,10 +46,9 @@ The derivation of the `message-id` remains stable. ##### `beacon_aggregate_and_proof` The following convenience variables are re-defined -- `index = get_committee_indices(aggregate.committee_bits)[0]` +- `index = aggregate.committee_index` The following validations are added: -* [REJECT] `len(committee_indices) == 1`, where `committee_indices = get_committee_indices(aggregate)`. * [REJECT] `aggregate.data.index == 0` #### Attestation subnets diff --git a/specs/electra/validator.md b/specs/electra/validator.md index 0e26dedf94..d87760d0cb 100644 --- a/specs/electra/validator.md +++ b/specs/electra/validator.md @@ -28,6 +28,7 @@ - [Construct attestation](#construct-attestation) - [Attestation aggregation](#attestation-aggregation) - [Construct aggregate](#construct-aggregate) + - [Broadcast aggregate](#broadcast-aggregate) @@ -66,7 +67,7 @@ class GetPayloadResponse(object): ```python class AggregateAndProof(Container): aggregator_index: ValidatorIndex - aggregate: Attestation # [Modified in Electra:EIP7549] + aggregate: CommitteeAttestation # [Modified in Electra:EIP7549] selection_proof: BLSSignature ``` @@ -109,14 +110,14 @@ Changed the max attester slashings size to `MAX_ATTESTER_SLASHINGS_ELECTRA`. Changed the max attestations size to `MAX_ATTESTATIONS_ELECTRA`. The network attestation aggregates contain only the assigned committee attestations. -Attestation aggregates received by the block proposer from the committee aggregators with disjoint `committee_bits` sets and equal `AttestationData` SHOULD be consolidated into a single `Attestation` object. -The proposer should run the following function to construct an on chain final aggregate form a list of network aggregates with equal `AttestationData`: +Committee attestations received by the block proposer from the committee aggregators with different `committee_index` sets and equal `AttestationData` SHOULD be consolidated into a single `Attestation` object. +The proposer should run the following function to construct an on chain final aggregate form a list of committee attestations with equal `AttestationData`: ```python -def compute_on_chain_aggregate(network_aggregates: Sequence[Attestation]) -> Attestation: - aggregates = sorted(network_aggregates, key=lambda a: get_committee_indices(a.committee_bits)[0]) +def compute_on_chain_aggregate(committee_attestations: Sequence[CommitteeAttestation]) -> Attestation: + attestations = sorted(committee_attestations, key=lambda a: a.committee_index) - data = aggregates[0].data + data = attestations[0].data aggregation_bits = Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]() for a in aggregates: for b in a.aggregation_bits: @@ -124,7 +125,7 @@ def compute_on_chain_aggregate(network_aggregates: Sequence[Attestation]) -> Att signature = bls.Aggregate([a.signature for a in aggregates]) - committee_indices = [get_committee_indices(a.committee_bits)[0] for a in aggregates] + committee_indices = [a.committee_index for a in aggregates] committee_flags = [(index in committee_indices) for index in range(0, MAX_COMMITTEES_PER_SLOT)] committee_bits = Bitvector[MAX_COMMITTEES_PER_SLOT](committee_flags) @@ -220,4 +221,30 @@ with updated field assignments: - Set `attestation_data.index = 0`. - Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. -- Set `attestation.committee_bits = committee_bits`, where `committee_bits` has the bit set corresponding to `committee_index` in each individual attestation. +- Set `attestation.committee_index = committee_index`, where `committee_index` is the `committee_index` in each individual attestation. + +#### Aggregate signature + +Set `aggregate_attestation.signature = aggregate_signature` where `aggregate_signature` is obtained from: + +```python +def get_aggregate_signature(attestations: Sequence[CommitteeAttestation]) -> BLSSignature: + signatures = [attestation.signature for attestation in attestations] + return bls.Aggregate(signatures) +``` + +### Broadcast aggregate + +`get_aggregate_and_proof` is modified to accept `CommitteeAttestation` for `aggregate`. + +```python +def get_aggregate_and_proof(state: BeaconState, + aggregator_index: ValidatorIndex, + committee_attestation: CommitteeAttestation, + privkey: int) -> AggregateAndProof: + return AggregateAndProof( + aggregator_index=aggregator_index, + aggregate=committee_attestation, + selection_proof=get_slot_signature(state, aggregate.data.slot, privkey), + ) +``` \ No newline at end of file