Merge pull request #2362 from ethereum/sync-cleanup

Minor altair cleanups
This commit is contained in:
Danny Ryan 2021-04-28 08:42:23 -06:00 committed by GitHub
commit c3dfc2e085
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 48 additions and 38 deletions

View File

@ -30,6 +30,7 @@ The current features are:
* [Altair fork](specs/altair/fork.md) * [Altair fork](specs/altair/fork.md)
* [Light client sync protocol](specs/altair/sync-protocol.md) * [Light client sync protocol](specs/altair/sync-protocol.md)
* [Honest Validator guide changes](specs/altair/validator.md) * [Honest Validator guide changes](specs/altair/validator.md)
* [P2P Networking](specs/altair/p2p-interface.md)
### Merge ### Merge

View File

@ -118,7 +118,7 @@ def get_sync_subcommittee_pubkeys(state: BeaconState, subcommittee_index: uint64
- _[REJECT]_ `contribution_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_sync_committee_aggregator(contribution_and_proof.selection_proof)` returns `True`. - _[REJECT]_ `contribution_and_proof.selection_proof` selects the validator as an aggregator for the slot -- i.e. `is_sync_committee_aggregator(contribution_and_proof.selection_proof)` returns `True`.
- _[REJECT]_ The aggregator's validator index is in the declared subcommittee of the current sync committee -- - _[REJECT]_ The aggregator's validator index is in the declared subcommittee of the current sync committee --
i.e. `state.validators[contribution_and_proof.aggregator_index].pubkey in get_sync_subcommittee_pubkeys(state, contribution.subcommittee_index)`. i.e. `state.validators[contribution_and_proof.aggregator_index].pubkey in get_sync_subcommittee_pubkeys(state, contribution.subcommittee_index)`.
- _[REJECT]_ The `contribution_and_proof.selection_proof` is a valid signature of the `SyncCommitteeSigningData` derived from the `contribution` by the validator with index `contribution_and_proof.aggregator_index`. - _[REJECT]_ The `contribution_and_proof.selection_proof` is a valid signature of the `SyncAggregatorSelectionData` derived from the `contribution` by the validator with index `contribution_and_proof.aggregator_index`.
- _[REJECT]_ The aggregator signature, `signed_contribution_and_proof.signature`, is valid. - _[REJECT]_ The aggregator signature, `signed_contribution_and_proof.signature`, is valid.
- _[REJECT]_ The aggregate signature is valid for the message `beacon_block_root` and aggregate pubkey derived from the participation info in `aggregation_bits` for the subcommittee specified by the `contribution.subcommittee_index`. - _[REJECT]_ The aggregate signature is valid for the message `beacon_block_root` and aggregate pubkey derived from the participation info in `aggregation_bits` for the subcommittee specified by the `contribution.subcommittee_index`.

View File

@ -18,7 +18,7 @@ This is an accompanying document to [Ethereum 2.0 Altair -- The Beacon Chain](./
- [`SyncCommitteeContribution`](#synccommitteecontribution) - [`SyncCommitteeContribution`](#synccommitteecontribution)
- [`ContributionAndProof`](#contributionandproof) - [`ContributionAndProof`](#contributionandproof)
- [`SignedContributionAndProof`](#signedcontributionandproof) - [`SignedContributionAndProof`](#signedcontributionandproof)
- [`SyncCommitteeSigningData`](#synccommitteesigningdata) - [`SyncAggregatorSelectionData`](#syncaggregatorselectiondata)
- [Validator assignments](#validator-assignments) - [Validator assignments](#validator-assignments)
- [Sync Committee](#sync-committee) - [Sync Committee](#sync-committee)
- [Lookahead](#lookahead) - [Lookahead](#lookahead)
@ -49,7 +49,7 @@ This is an accompanying document to [Ethereum 2.0 Altair -- The Beacon Chain](./
## Introduction ## Introduction
This document represents the expected behavior of an "honest validator" with respect to Altair of the Ethereum 2.0 protocol. This document represents the expected behavior of an "honest validator" with respect to the Altair upgrade of the Ethereum 2.0 protocol.
It builds on the [previous document for the behavior of an "honest validator" from Phase 0](../phase0/validator.md) of the Ethereum 2.0 protocol. It builds on the [previous document for the behavior of an "honest validator" from Phase 0](../phase0/validator.md) of the Ethereum 2.0 protocol.
This previous document is referred to below as the "Phase 0 document". This previous document is referred to below as the "Phase 0 document".
@ -126,10 +126,10 @@ class SignedContributionAndProof(Container):
signature: BLSSignature signature: BLSSignature
``` ```
### `SyncCommitteeSigningData` ### `SyncAggregatorSelectionData`
```python ```python
class SyncCommitteeSigningData(Container): class SyncAggregatorSelectionData(Container):
slot: Slot slot: Slot
subcommittee_index: uint64 subcommittee_index: uint64
``` ```
@ -178,7 +178,7 @@ For this reason, *always* get committee assignments via the fields of the `Beaco
A validator should plan for future sync committee assignments by noting which sync committee periods they are selected for participation. A validator should plan for future sync committee assignments by noting which sync committee periods they are selected for participation.
Specifically, a validator should: Specifically, a validator should:
* Upon (re)syncing the chain and upon sync committee period boundaries, check for assignments in the current and next sync committee periods. * Upon (re)syncing the chain and upon sync committee period boundaries, check for assignments in the current and next sync committee periods.
* If the validator is in the current sync committee period, they can perform the responsibilities below for sync committee rewards. * If the validator is in the current sync committee period, then they perform the responsibilities below for sync committee rewards.
* If the validator is in the next sync committee period, they should wait until the next `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` boundary and then perform the responsibilities throughout that period. * If the validator is in the next sync committee period, they should wait until the next `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` boundary and then perform the responsibilities throughout that period.
## Beacon chain responsibilities ## Beacon chain responsibilities
@ -187,7 +187,7 @@ A validator maintains the responsibilities given in the Phase 0 document.
Block proposals are modified to incorporate the sync committee signatures as detailed below. Block proposals are modified to incorporate the sync committee signatures as detailed below.
When assigned to a sync committee, validators have a new responsibility to sign beacon block roots during each slot of the sync committee period. When assigned to a sync committee, validators have a new responsibility to sign and broadcast beacon block roots during each slot of the sync committee period.
These signatures are aggregated and routed to the proposer over gossip for inclusion into a beacon block. These signatures are aggregated and routed to the proposer over gossip for inclusion into a beacon block.
Assignments to a particular sync committee are infrequent at normal validator counts; however, an action every slot is required when in the current active sync committee. Assignments to a particular sync committee are infrequent at normal validator counts; however, an action every slot is required when in the current active sync committee.
@ -209,8 +209,8 @@ After constructing the `BeaconBlockBody` as per that section, the proposer has a
The proposer receives a number of `SyncCommitteeContribution`s (wrapped in `SignedContributionAndProof`s on the wire) from validators in the sync committee who are selected to partially aggregate signatures from independent subcommittees formed by breaking the full sync committee into `SYNC_COMMITTEE_SUBNET_COUNT` pieces (see below for details). The proposer receives a number of `SyncCommitteeContribution`s (wrapped in `SignedContributionAndProof`s on the wire) from validators in the sync committee who are selected to partially aggregate signatures from independent subcommittees formed by breaking the full sync committee into `SYNC_COMMITTEE_SUBNET_COUNT` pieces (see below for details).
The proposer collects these contributions for further aggregation when preparing a block. The proposer collects the contributions that match their local view of the chain (i.e. `contribution.beacon_block_root == block.parent_root`) for further aggregation when preparing a block.
Proposers should select the best contribution seen across all aggregators for each subnet/subcommittee when preparing a block. Of these contributions, proposers should select the best contribution seen across all aggregators for each subnet/subcommittee.
A contribution with more valid signatures is better than a contribution with fewer signatures. A contribution with more valid signatures is better than a contribution with fewer signatures.
Recall `block.body.sync_aggregate.sync_committee_bits` is a `Bitvector` where the `i`th bit is `True` if the corresponding validator in the sync committee has produced a valid signature, Recall `block.body.sync_aggregate.sync_committee_bits` is a `Bitvector` where the `i`th bit is `True` if the corresponding validator in the sync committee has produced a valid signature,
@ -229,7 +229,8 @@ def process_sync_committee_contributions(block: BeaconBlock,
subcommittee_index = contribution.subcommittee_index subcommittee_index = contribution.subcommittee_index
for index, participated in enumerate(contribution.aggregation_bits): for index, participated in enumerate(contribution.aggregation_bits):
if participated: if participated:
participant_index = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT * subcommittee_index + index sync_subcommittee_size = SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT
participant_index = sync_subcommittee_size * subcommittee_index + index
sync_aggregate.sync_committee_bits[participant_index] = True sync_aggregate.sync_committee_bits[participant_index] = True
signatures.append(contribution.signature) signatures.append(contribution.signature)
@ -260,12 +261,12 @@ This process occurs each slot.
##### Prepare sync committee signature ##### Prepare sync committee signature
If a validator is in the current sync committee (i.e. `is_assigned_to_sync_committee` above returns `True`), then for every slot in the current sync committee period the validator should prepare a `SyncCommitteeSignature` according to the logic in `get_sync_committee_signature` as soon as they have determined the head block of the current slot. If a validator is in the current sync committee (i.e. `is_assigned_to_sync_committee()` above returns `True`), then for every slot in the current sync committee period, the validator should prepare a `SyncCommitteeSignature` according to the logic in `get_sync_committee_signature` as soon as they have determined the head block of the current slot.
This logic is triggered upon the same conditions as when producing an attestation. This logic is triggered upon the same conditions as when producing an attestation.
Meaning, a sync committee member should produce and broadcast a `SyncCommitteeSignature` either when (a) the validator has received a valid block from the expected block proposer for the current `slot` or (b) one-third of the slot has transpired (`SECONDS_PER_SLOT / 3` seconds after the start of the slot) -- whichever comes first. Meaning, a sync committee member should produce and broadcast a `SyncCommitteeSignature` either when (a) the validator has received a valid block from the expected block proposer for the current `slot` or (b) one-third of the slot has transpired (`SECONDS_PER_SLOT / 3` seconds after the start of the slot) -- whichever comes first.
`get_sync_committee_signature` assumes `state` is the head state corresponding to processing the block at the current slot as determined by the fork choice (including any empty slots processed with `process_slots`), `block_root` is the root of the head block whose processing results in `state`, `validator_index` is the index of the validator in the registry `state.validators` controlled by `privkey`, and `privkey` is the BLS private key for the validator. `get_sync_committee_signature()` assumes `state` is the head state corresponding to processing the block up to the current slot as determined by the fork choice (including any empty slots up to the current slot processed with `process_slots` on top of the latest block), `block_root` is the root of the head block, `validator_index` is the index of the validator in the registry `state.validators` controlled by `privkey`, and `privkey` is the BLS private key for the validator.
```python ```python
def get_sync_committee_signature(state: BeaconState, def get_sync_committee_signature(state: BeaconState,
@ -285,14 +286,17 @@ def get_sync_committee_signature(state: BeaconState,
The validator broadcasts the assembled signature to the assigned subnet, the `sync_committee_{subnet_id}` pubsub topic. The validator broadcasts the assembled signature to the assigned subnet, the `sync_committee_{subnet_id}` pubsub topic.
The `subnet_id` is derived from the position in the sync committee such that the sync committee is divided into "subcommittees". The `subnet_id` is derived from the position in the sync committee such that the sync committee is divided into "subcommittees".
It can be computed via `compute_subnets_for_sync_committee` where `state` is a `BeaconState` during the matching sync committee period. `subnet_id` can be computed via `compute_subnets_for_sync_committee()` where `state` is a `BeaconState` during the matching sync committee period.
This function returns multiple subnets if a given validator index is included multiple times in a given sync committee across multiple subcommittees.
*Note*: This function returns multiple subnets if a given validator index is included multiple times in a given sync committee across multiple subcommittees.
```python ```python
def compute_subnets_for_sync_committee(state: BeaconState, validator_index: ValidatorIndex) -> Sequence[uint64]: def compute_subnets_for_sync_committee(state: BeaconState, validator_index: ValidatorIndex) -> Sequence[uint64]:
target_pubkey = state.validators[validator_index].pubkey target_pubkey = state.validators[validator_index].pubkey
sync_committee_indices = [index for index, pubkey in enumerate(state.current_sync_committee.pubkeys) sync_committee_indices = [
if pubkey == target_pubkey] index for index, pubkey in enumerate(state.current_sync_committee.pubkeys)
if pubkey == target_pubkey
]
return [ return [
uint64(index // (SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT)) uint64(index // (SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT))
for index in sync_committee_indices for index in sync_committee_indices
@ -305,18 +309,20 @@ def compute_subnets_for_sync_committee(state: BeaconState, validator_index: Vali
#### Sync committee contributions #### Sync committee contributions
Each slot some sync committee members in each subcommittee are selected to aggregate the `SyncCommitteeSignature`s into a `SyncCommitteeContribution` which is broadcast on a global channel for inclusion into the next block. Each slot, some sync committee members in each subcommittee are selected to aggregate the `SyncCommitteeSignature`s into a `SyncCommitteeContribution` which is broadcast on a global channel for inclusion into the next block.
##### Aggregation selection ##### Aggregation selection
A validator is selected to aggregate based on the computation in `is_sync_committee_aggregator` where `signature` is the BLS signature returned by `get_sync_committee_selection_proof`. A validator is selected to aggregate based on the value returned by `is_sync_committee_aggregator()` where `signature` is the BLS signature returned by `get_sync_committee_selection_proof()`.
The signature function takes a `BeaconState` with the relevant sync committees for the queried `slot` (i.e. `state.slot` is within the span covered by the current or next sync committee period), the `subcommittee_index` equal to the `subnet_id`, and the `privkey` is the BLS private key associated with the validator. The signature function takes a `BeaconState` with the relevant sync committees for the queried `slot` (i.e. `state.slot` is within the span covered by the current or next sync committee period), the `subcommittee_index` equal to the `subnet_id`, and the `privkey` is the BLS private key associated with the validator.
```python ```python
def get_sync_committee_selection_proof(state: BeaconState, slot: Slot, def get_sync_committee_selection_proof(state: BeaconState,
subcommittee_index: uint64, privkey: int) -> BLSSignature: slot: Slot,
subcommittee_index: uint64,
privkey: int) -> BLSSignature:
domain = get_domain(state, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, compute_epoch_at_slot(slot)) domain = get_domain(state, DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF, compute_epoch_at_slot(slot))
signing_data = SyncCommitteeSigningData( signing_data = SyncAggregatorSelectionData(
slot=slot, slot=slot,
subcommittee_index=subcommittee_index, subcommittee_index=subcommittee_index,
) )
@ -330,7 +336,7 @@ def is_sync_committee_aggregator(signature: BLSSignature) -> bool:
return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0 return bytes_to_uint64(hash(signature)[0:8]) % modulo == 0
``` ```
*NOTE*: the set of aggregators generally changes every slot; however, the assignments can be computed ahead of time as soon as the committee is known *NOTE*: The set of aggregators generally changes every slot; however, the assignments can be computed ahead of time as soon as the committee is known.
##### Construct sync committee contribution ##### Construct sync committee contribution
@ -353,18 +359,21 @@ Set `contribution.subcommittee_index` to the index for the subcommittee index co
###### Aggregation bits ###### Aggregation bits
Let `contribution.aggregation_bits` be a `Bitvector[SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT]`, where the `index`th bit is set in the `Bitvector` for each corresponding validator included in this aggregate from the corresponding subcommittee. Let `contribution.aggregation_bits` be a `Bitvector[SYNC_COMMITTEE_SIZE // SYNC_COMMITTEE_SUBNET_COUNT]`, where the `index`th bit is set in the `Bitvector` for each corresponding validator included in this aggregate from the corresponding subcommittee.
An aggregator needs to find the index in the sync committee (as returned by `get_sync_committee_indices`) for a given validator referenced by `sync_committee_signature.validator_index` and map the sync committee index to an index in the subcommittee (along with the prior `subcommittee_index`). This index within the subcommittee is the one set in the `Bitvector`. An aggregator finds the index in the sync committee (as returned by `get_sync_committee_indices()`) for a given validator referenced by `sync_committee_signature.validator_index` and maps the sync committee index to an index in the subcommittee (along with the prior `subcommittee_index`). This index within the subcommittee is set in `contribution.aggegration_bits`.
For example, if a validator with index `2044` is pseudo-randomly sampled to sync committee index `135`. This sync committee index maps to `subcommittee_index` `1` with position `7` in the `Bitvector` for the contribution. For example, if a validator with index `2044` is pseudo-randomly sampled to sync committee index `135`. This sync committee index maps to `subcommittee_index` `1` with position `7` in the `Bitvector` for the contribution.
Also note that a validator **could be included multiple times** in a given subcommittee such that multiple bits are set for a single `SyncCommitteeSignature`.
*Note*: A validator **could be included multiple times** in a given subcommittee such that multiple bits are set for a single `SyncCommitteeSignature`.
###### Signature ###### Signature
Set `contribution.signature = aggregate_signature` where `aggregate_signature` is obtained by assembling the appropriate collection of `BLSSignature`s from the set of `sync_committee_signatures` and using the `bls.Aggregate` function to produce an aggregate `BLSSignature`. Set `contribution.signature = aggregate_signature` where `aggregate_signature` is obtained by assembling the appropriate collection of `BLSSignature`s from the set of `sync_committee_signatures` and using the `bls.Aggregate()` function to produce an aggregate `BLSSignature`.
The collection of input signatures should include one signature per validator who had a bit set in the `aggregation_bits` bitfield, with repeated signatures if one validator maps to multiple indices within the subcommittee. The collection of input signatures should include one signature per validator who had a bit set in the `aggregation_bits` bitfield, with repeated signatures if one validator maps to multiple indices within the subcommittee.
##### Broadcast sync committee contribution ##### Broadcast sync committee contribution
If the validator is selected to aggregate (`is_sync_committee_aggregator`), then they broadcast their best aggregate as a `SignedContributionAndProof` to the global aggregate channel (`sync_committee_contribution_and_proof` topic) two-thirds of the way through the `slot`-that is, `SECONDS_PER_SLOT * 2 / 3` seconds after the start of `slot`. If the validator is selected to aggregate (`is_sync_committee_aggregator()`), then they broadcast their best aggregate as a `SignedContributionAndProof` to the global aggregate channel (`sync_committee_contribution_and_proof` topic) two-thirds of the way through the `slot`-that is, `SECONDS_PER_SLOT * 2 / 3` seconds after the start of `slot`.
Selection proofs are provided in `ContributionAndProof` to prove to the gossip channel that the validator has been selected as an aggregator. Selection proofs are provided in `ContributionAndProof` to prove to the gossip channel that the validator has been selected as an aggregator.
@ -410,9 +419,9 @@ To provide this stability, a validator must do the following:
* Maintain advertisement of the subnet the validator in the sync committee is assigned to in their node's ENR as soon as they have joined the subnet. * Maintain advertisement of the subnet the validator in the sync committee is assigned to in their node's ENR as soon as they have joined the subnet.
Subnet assignments are known `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` epochs in advance and can be computed with `compute_subnets_for_sync_committee` defined above. Subnet assignments are known `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` epochs in advance and can be computed with `compute_subnets_for_sync_committee` defined above.
ENR advertisement is indicated by setting the appropriate bit(s) of the bitfield found under the `syncnets` key in the ENR corresponding to the derived `subnet_id`(s). ENR advertisement is indicated by setting the appropriate bit(s) of the bitfield found under the `syncnets` key in the ENR corresponding to the derived `subnet_id`(s).
Any bits modified for the sync committee responsibilities are unset in the ENR after any validators have left the sync committee. Any bits modified for the sync committee responsibilities are unset in the ENR once the node no longer has any validators in the subcommittee.
*Note*: The first sync committee from phase 0 to the Altair fork will not be known until the fork happens which implies subnet assignments are not known until then. *Note*: The first sync committee from phase 0 to the Altair fork will not be known until the fork happens, which implies subnet assignments are not known until then.
Early sync committee members should listen for topic subscriptions from peers and employ discovery via the ENR advertisements near the fork boundary to form initial subnets. Early sync committee members should listen for topic subscriptions from peers and employ discovery via the ENR advertisements near the fork boundary to form initial subnets.
Some early sync committee rewards may be missed while the initial subnets form. Some early sync committee rewards may be missed while the initial subnets form.