From 38f269ca12c9cc1321cb0b613daa0527ee73b507 Mon Sep 17 00:00:00 2001 From: dapplion <35266934+dapplion@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:46:45 +0800 Subject: [PATCH] Update spec with @mkalinin suggestions --- specs/_features/eip7549/beacon-chain.md | 128 +++++++++++++++++++---- specs/_features/eip7549/p2p-interface.md | 54 ++++++++++ specs/_features/eip7549/validator.md | 49 +++++++++ specs/deneb/beacon-chain.md | 5 +- specs/phase0/beacon-chain.md | 10 +- specs/phase0/p2p-interface.md | 20 ++-- 6 files changed, 225 insertions(+), 41 deletions(-) create mode 100644 specs/_features/eip7549/p2p-interface.md create mode 100644 specs/_features/eip7549/validator.md diff --git a/specs/_features/eip7549/beacon-chain.md b/specs/_features/eip7549/beacon-chain.md index a29164ecd..f2936ebb4 100644 --- a/specs/_features/eip7549/beacon-chain.md +++ b/specs/_features/eip7549/beacon-chain.md @@ -7,13 +7,19 @@ - [Introduction](#introduction) +- [Preset](#preset) - [Containers](#containers) - - [Extended containers](#extended-containers) - - [AttestationData](#attestationdata) + - [Modified containers](#modified-containers) - [Attestation](#attestation) + - [IndexedAttestation](#indexedattestation) - [Helper functions](#helper-functions) + - [Misc](#misc) + - [`get_committee_indices`](#get_committee_indices) - [Beacon state accessors](#beacon-state-accessors) - - [Modified `get_attestation_index`](#modified-get_attestation_index) + - [New `get_committee_attesters`](#new-get_committee_attesters) + - [Modified `get_attesting_indices`](#modified-get_attesting_indices) + - [Block processing](#block-processing) + - [Modified `process_attestation`](#modified-process_attestation) @@ -24,41 +30,117 @@ This is the beacon chain specification to move the attestation committee index o *Note:* This specification is built upon [Deneb](../../deneb/beacon_chain.md) and is under active development. +## Preset + +| Name | Value | Description | +| - | - | - | +| `MAX_ATTESTER_SLASHINGS` | `2**0` (= 1) | +| `MAX_ATTESTATIONS` | `2**3` (= 8) | + ## Containers -### Extended containers - -#### AttestationData - -```python -class AttestationData(Container): - slot: Slot - # index: CommitteeIndex # [Modified in EIP7549] - # LMD GHOST vote - beacon_block_root: Root - # FFG vote - source: Checkpoint - target: Checkpoint -``` +### Modified containers #### Attestation ```python class Attestation(Container): - aggregation_bits: Bitlist[MAX_VALIDATORS_PER_COMMITTEE] + aggregation_bits: List[Bitlist[MAX_VALIDATORS_PER_COMMITTEE], MAX_COMMITTEES_PER_SLOT] # [Modified in EIP7549] + data: AttestationData + committee_bits: Bitvector[MAX_COMMITTEES_PER_SLOT] # [New in EIP7549] + signature: BLSSignature +``` + +#### IndexedAttestation + +```python +class IndexedAttestation(Container): + attesting_indices: List[ValidatorIndex, MAX_VALIDATORS_PER_COMMITTEE * MAX_COMMITTEES_PER_SLOT] # [Modified in EIP7549] data: AttestationData - index: CommitteeIndex # [New in EIP7549] signature: BLSSignature ``` ## Helper functions -### Beacon state accessors +### Misc -#### Modified `get_attestation_index` +#### `get_committee_indices` ```python -def get_attestation_index(attestation: Attestation) -> CommitteeIndex: - return attestation.index +def get_committee_indices(commitee_bits: Bitvector) -> List[CommitteeIndex]: + return [CommitteeIndex(index) for bit, index in enumerate(commitee_bits) if bit] ``` +### Beacon state accessors + +#### New `get_committee_attesters` + +```python +def get_committee_attesters(state: BeaconState, attesting_bits: Bitlist, index: CommitteeIndex) -> Set[ValidatorIndex]: + committee = get_beacon_committee(state, data.slot, index) + return set(index for i, index in enumerate(committee) if attesting_bits[i]) +``` + +#### Modified `get_attesting_indices` + +```python +def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[ValidatorIndex]: + """ + Return the set of attesting indices corresponding to ``aggregation_bits`` and ``committee_bits``. + """ + + output = set() + committee_indices = get_committee_indices(attestation.committee_bits) + for index in committee_indices: + attesting_bits = attestation.attesting_bits[index] + committee_attesters = get_committee_attesters(state, attesting_bits, index) + output = output.union(committee_attesters) + + return output +``` + +### Block processing + +#### Modified `process_attestation` + +```python +def process_attestation(state: BeaconState, attestation: Attestation) -> None: + data = attestation.data + assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) + assert data.target.epoch == compute_epoch_at_slot(data.slot) + assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot + + # [Modified in EIP7549] + assert data.index == 0 + committee_indices = get_committee_indices(attestation.committee_bits) + assert len(committee_indices) > 0 + assert len(committee_indices) == len(attestation.aggregation_bits) + for index in committee_indices: + assert index < get_committee_count_per_slot(state, data.target.epoch) + committee = get_beacon_committee(state, data.slot, index) + assert len(attestation.aggregation_bits[index]) == len(committee) + + # Participation flag indices + participation_flag_indices = get_attestation_participation_flag_indices(state, data, state.slot - data.slot) + + # Verify signature + assert is_valid_indexed_attestation(state, get_indexed_attestation(state, attestation)) + + # Update epoch participation flags + if data.target.epoch == get_current_epoch(state): + epoch_participation = state.current_epoch_participation + else: + epoch_participation = state.previous_epoch_participation + + proposer_reward_numerator = 0 + for index in get_attesting_indices(state, attestation): + for flag_index, weight in enumerate(PARTICIPATION_FLAG_WEIGHTS): + if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index): + epoch_participation[index] = add_flag(epoch_participation[index], flag_index) + proposer_reward_numerator += get_base_reward(state, index) * weight + + # Reward proposer + proposer_reward_denominator = (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT) * WEIGHT_DENOMINATOR // PROPOSER_WEIGHT + proposer_reward = Gwei(proposer_reward_numerator // proposer_reward_denominator) + increase_balance(state, get_beacon_proposer_index(state), proposer_reward) +``` diff --git a/specs/_features/eip7549/p2p-interface.md b/specs/_features/eip7549/p2p-interface.md new file mode 100644 index 000000000..c7413ea4b --- /dev/null +++ b/specs/_features/eip7549/p2p-interface.md @@ -0,0 +1,54 @@ +# EIP-7549 -- Networking + +This document contains the consensus-layer networking specification for EIP-7549. + +The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite. + +## Table of contents + + + + + +- [Modifications in EIP-7549](#modifications-in-eip-7549) + - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) + - [Topics and messages](#topics-and-messages) + - [Global topics](#global-topics) + - [`beacon_aggregate_and_proof`](#beacon_aggregate_and_proof) + - [`beacon_attestation_{subnet_id}`](#beacon_attestation_subnet_id) + + + + +## Modifications in EIP-7549 + +### The gossip domain: gossipsub + +#### Topics and messages + +The `beacon_aggregate_and_proof` and `beacon_attestation_{subnet_id}` topics are modified to support the gossip of a new attestation type. + +##### Global topics + +###### `beacon_aggregate_and_proof` + +*[Modified in EIP7549]* + +The following convenience variables are re-defined +- `index = get_committee_indices(aggregate.committee_bits)[0]` +- `aggregation_bits = aggregate.aggregation_bits[0]` + +The following validations are added: +* [REJECT] `len(committee_indices) == len(aggregate.attestation_bits) == 1`, where `committee_indices = get_committee_indices(aggregate)`. +* [REJECT] `aggregate.data.index == 0` + +###### `beacon_attestation_{subnet_id}` + +The following convenience variables are re-defined +- `index = get_committee_indices(attestation.committee_bits)[0]` +- `aggregation_bits = attestation.aggregation_bits[0]` + +The following validations are added: +* [REJECT] `len(committee_indices) == len(attestation.attestation_bits) == 1`, where `committee_indices = get_committee_indices(attestation)`. +* [REJECT] `attestation.data.index == 0` + diff --git a/specs/_features/eip7549/validator.md b/specs/_features/eip7549/validator.md new file mode 100644 index 000000000..1efe3bfbc --- /dev/null +++ b/specs/_features/eip7549/validator.md @@ -0,0 +1,49 @@ +# Deneb -- Honest Validator + +## Table of contents + + + + + +- [Modifications in EIP-7549](#modifications-in-eip-7549) + - [Block proposal](#block-proposal) + - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) + - [Attestations](#attestations) + - [Attesting](#attesting) + - [Construct attestation](#construct-attestation) + - [Attestation aggregation](#attestation-aggregation) + - [Construct aggregate](#construct-aggregate) + + + + +## Modifications in EIP-7549 + +### Block proposal + +#### Constructing the `BeaconBlockBody` + +##### Attestations + +Attestations received from aggregators with disjoint `committee_bits` sets and equal `AttestationData` SHOULD be consolidated into a single `Attestation` object. + +### Attesting + +#### Construct attestation + +- Set `attestation_data.index = 0`. +- Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where the bit of the index of the validator in the `committee` is set to `0b1`. +- Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 + +*Note*: Calling `get_attesting_indices(state, attestation)` should return a list of length equal to 1, containing `validator_index`. + +### Attestation aggregation + +#### Construct aggregate + +- Set `attestation_data.index = 0`. +- Let `aggregation_bits` be a `Bitlist[MAX_VALIDATORS_PER_COMMITTEE]` of length `len(committee)`, where each bit set from each individual attestation is set to `0b1`. +- Set `attestation.aggregation_bits = [aggregation_bits]`, a list of length 1 + + diff --git a/specs/deneb/beacon-chain.md b/specs/deneb/beacon-chain.md index 01523baf5..57badd6b9 100644 --- a/specs/deneb/beacon-chain.md +++ b/specs/deneb/beacon-chain.md @@ -316,13 +316,12 @@ def verify_and_notify_new_payload(self: ExecutionEngine, ```python def process_attestation(state: BeaconState, attestation: Attestation) -> None: data = attestation.data - index = get_attestation_index(attestation) assert data.target.epoch in (get_previous_epoch(state), get_current_epoch(state)) assert data.target.epoch == compute_epoch_at_slot(data.slot) assert data.slot + MIN_ATTESTATION_INCLUSION_DELAY <= state.slot # [Modified in Deneb:EIP7045] - assert index < get_committee_count_per_slot(state, data.target.epoch) + assert data.index < get_committee_count_per_slot(state, data.target.epoch) - committee = get_beacon_committee(state, data.slot, index) + committee = get_beacon_committee(state, data.slot, data.index) assert len(attestation.aggregation_bits) == len(committee) # Participation flag indices diff --git a/specs/phase0/beacon-chain.md b/specs/phase0/beacon-chain.md index d4c5b7f6c..9d5f8446a 100644 --- a/specs/phase0/beacon-chain.md +++ b/specs/phase0/beacon-chain.md @@ -100,7 +100,6 @@ - [`get_domain`](#get_domain) - [`get_indexed_attestation`](#get_indexed_attestation) - [`get_attesting_indices`](#get_attesting_indices) - - [`get_attestation_index`](#get_attestation_index) - [Beacon state mutators](#beacon-state-mutators) - [`increase_balance`](#increase_balance) - [`decrease_balance`](#decrease_balance) @@ -1103,17 +1102,10 @@ def get_attesting_indices(state: BeaconState, attestation: Attestation) -> Set[V """ Return the set of attesting indices corresponding to ``data`` and ``bits``. """ - committee = get_beacon_committee(state, attestation.data.slot, get_attestation_index(attestation)) + committee = get_beacon_committee(state, attestation.data.slot, attestation.data.index) return set(index for i, index in enumerate(committee) if attestation.aggregation_bits[i]) ``` -#### `get_attestation_index` - -```python -def get_attestation_index(attestation: Attestation) -> CommitteeIndex: - return attestation.data.index -``` - ### Beacon state mutators #### `increase_balance` diff --git a/specs/phase0/p2p-interface.md b/specs/phase0/p2p-interface.md index 9a7efdaab..a34b34233 100644 --- a/specs/phase0/p2p-interface.md +++ b/specs/phase0/p2p-interface.md @@ -353,8 +353,13 @@ The following validations MUST pass before forwarding the `signed_beacon_block` The `beacon_aggregate_and_proof` topic is used to propagate aggregated attestations (as `SignedAggregateAndProof`s) to subscribing nodes (typically validators) to be included in future blocks. +We define the following variables for convenience: +- `aggregate_and_proof = signed_aggregate_and_proof.message` +- `aggregate = aggregate_and_proof.aggregate` +- `index = aggregate.index` +- `aggregation_bits = attestation.aggregation_bits` + The following validations MUST pass before forwarding the `signed_aggregate_and_proof` on the network. -(We define the following for convenience -- `aggregate_and_proof = signed_aggregate_and_proof.message`, `aggregate = aggregate_and_proof.aggregate` and `index = get_attestation_index(aggregate)`) - _[REJECT]_ The committee index is within the expected range -- i.e. `index < get_committee_count_per_slot(state, aggregate.data.target.epoch)`. - _[IGNORE]_ `aggregate.data.slot` is within the last `ATTESTATION_PROPAGATION_SLOT_RANGE` slots (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. `aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= aggregate.data.slot` @@ -362,9 +367,9 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_ - _[REJECT]_ The aggregate attestation's epoch matches its target -- i.e. `aggregate.data.target.epoch == compute_epoch_at_slot(aggregate.data.slot)` - _[REJECT]_ The number of aggregation bits matches the committee size -- i.e. - `len(aggregate.aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, aggregate.data.index))`. + `len(aggregation_bits) == len(get_beacon_committee(state, aggregate.data.slot, aggregate.data.index))`. - _[REJECT]_ The aggregate attestation has participants -- - that is, `len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 1`. + that is, `len(get_attesting_indices(state, aggregate)) >= 1`. - _[IGNORE]_ A valid aggregate attestation defined by `hash_tree_root(aggregate.data)` whose `aggregation_bits` is a non-strict superset has _not_ already been seen. (via aggregate gossip, within a verified block, or through the creation of an equivalent aggregate locally). - _[IGNORE]_ The `aggregate` is the first valid aggregate received for the aggregator @@ -430,8 +435,11 @@ Attestation subnets are used to propagate unaggregated attestations to subsectio The `beacon_attestation_{subnet_id}` topics are used to propagate unaggregated attestations to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`. +We define the following variables for convenience: +- `index = attestation.index` +- `aggregation_bits = attestation.aggregation_bits` + The following validations MUST pass before forwarding the `attestation` on the subnet. -(We define the following for convenience -- `index = get_attestation_index(attestation)`) - _[REJECT]_ The committee index is within the expected range -- i.e. `index < get_committee_count_per_slot(state, attestation.data.target.epoch)`. - _[REJECT]_ The attestation is for the correct subnet -- i.e. `compute_subnet_for_attestation(committees_per_slot, attestation.data.slot, index) == subnet_id`, @@ -444,9 +452,9 @@ The following validations MUST pass before forwarding the `attestation` on the s - _[REJECT]_ The attestation's epoch matches its target -- i.e. `attestation.data.target.epoch == compute_epoch_at_slot(attestation.data.slot)` - _[REJECT]_ The attestation is unaggregated -- - that is, it has exactly one participating validator (`len([bit for bit in attestation.aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set). + that is, it has exactly one participating validator (`len([bit for bit in aggregation_bits if bit]) == 1`, i.e. exactly 1 bit is set). - _[REJECT]_ The number of aggregation bits matches the committee size -- i.e. - `len(attestation.aggregation_bits) == len(get_beacon_committee(state, attestation.data.slot, index))`. + `len(aggregation_bits) == len(get_beacon_committee(state, attestation.data.slot, index))`. - _[IGNORE]_ There has been no other valid attestation seen on an attestation subnet that has an identical `attestation.data.target.epoch` and participating validator index. - _[REJECT]_ The signature of `attestation` is valid.