Merge branch 'dev' into vbuterin-patch-12

This commit is contained in:
Danny Ryan
2021-05-10 07:38:32 -06:00
34 changed files with 382 additions and 180 deletions

View File

@@ -12,12 +12,10 @@ PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: 2
# Sync committee
# ---------------------------------------------------------------
# 2**10 (= 1,024)
SYNC_COMMITTEE_SIZE: 1024
# 2**6 (= 64)
SYNC_PUBKEYS_PER_AGGREGATE: 64
# 2**8 (= 256)
EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 256
# 2**9 (= 512)
SYNC_COMMITTEE_SIZE: 512
# 2**9 (= 512)
EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 512
# Misc
@@ -45,10 +43,6 @@ ALTAIR_FORK_EPOCH: 18446744073709551615
# ---------------------------------------------------------------
# 1
MIN_SYNC_COMMITTEE_PARTICIPANTS: 1
# 2**13
MAX_VALID_LIGHT_CLIENT_UPDATES: 8192
# 2**13 (=8192)
LIGHT_CLIENT_UPDATE_TIMEOUT: 8192
# Validator

View File

@@ -15,8 +15,6 @@ PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: 2
# [customized]
SYNC_COMMITTEE_SIZE: 32
# [customized]
SYNC_PUBKEYS_PER_AGGREGATE: 16
# [customized]
EPOCHS_PER_SYNC_COMMITTEE_PERIOD: 8
@@ -45,10 +43,7 @@ ALTAIR_FORK_EPOCH: 18446744073709551615
# ---------------------------------------------------------------
# 1
MIN_SYNC_COMMITTEE_PARTICIPANTS: 1
# [customized]
MAX_VALID_LIGHT_CLIENT_UPDATES: 32
# [customized]
LIGHT_CLIENT_UPDATE_TIMEOUT: 32
# Validator

View File

@@ -167,7 +167,7 @@ def get_spec(file_name: str) -> SpecObject:
comment = _get_eth2_spec_comment(child)
if comment == "skip":
should_skip = True
return SpecObject(
functions=functions,
custom_types=custom_types,
@@ -441,7 +441,7 @@ ExecutionState = Any
def get_pow_block(hash: Bytes32) -> PowBlock:
pass
return PowBlock(block_hash=hash, is_valid=True, is_processed=True, total_difficulty=TRANSITION_TOTAL_DIFFICULTY)
def get_execution_state(execution_state_root: Bytes32) -> ExecutionState:
@@ -548,7 +548,7 @@ ignored_dependencies = [
'Bytes1', 'Bytes4', 'Bytes20', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector',
'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
'bytes', 'byte', 'ByteList', 'ByteVector',
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2',
'Dict', 'dict', 'field', 'ceillog2', 'floorlog2', 'Set',
]

View File

@@ -104,7 +104,7 @@ Altair is the first beacon chain hard fork. Its main features are:
### Updated penalty values
This patch updates a few configuration values to move penalty parameters toward their final, maximum security values.
This patch updates a few configuration values to move penalty parameters closer to their final, maximum security values.
*Note*: The spec does *not* override previous configuration values but instead creates new values and replaces usage throughout.
@@ -118,9 +118,8 @@ This patch updates a few configuration values to move penalty parameters toward
| Name | Value | Unit | Duration |
| - | - | - | - |
| `SYNC_COMMITTEE_SIZE` | `uint64(2**10)` (= 1,024) | | |
| `SYNC_PUBKEYS_PER_AGGREGATE` | `uint64(2**6)` (= 64) | | |
| `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` | `uint64(2**8)` (= 256) | epochs | ~27 hours |
| `SYNC_COMMITTEE_SIZE` | `uint64(2**9)` (= 512) | Validators | |
| `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` | `uint64(2**9)` (= 512) | epochs | ~54 hours |
### Misc
@@ -212,7 +211,7 @@ class SyncAggregate(Container):
```python
class SyncCommittee(Container):
pubkeys: Vector[BLSPubkey, SYNC_COMMITTEE_SIZE]
pubkey_aggregates: Vector[BLSPubkey, SYNC_COMMITTEE_SIZE // SYNC_PUBKEYS_PER_AGGREGATE]
aggregate_pubkey: BLSPubkey
```
## Helper functions
@@ -278,6 +277,9 @@ def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[Val
"""
Return the sequence of sync committee indices (which may include duplicate indices)
for a given ``state`` and ``epoch``.
Note: This function is not stable during a sync committee period as
a validator's effective balance may change enough to affect the sampling.
"""
MAX_RANDOM_BYTE = 2**8 - 1
base_epoch = Epoch((max(epoch // EPOCHS_PER_SYNC_COMMITTEE_PERIOD, 1) - 1) * EPOCHS_PER_SYNC_COMMITTEE_PERIOD)
@@ -303,12 +305,22 @@ def get_sync_committee_indices(state: BeaconState, epoch: Epoch) -> Sequence[Val
def get_sync_committee(state: BeaconState, epoch: Epoch) -> SyncCommittee:
"""
Return the sync committee for a given ``state`` and ``epoch``.
``SyncCommittee`` contains an aggregate pubkey that enables
resource-constrained clients to save some computation when verifying
the sync committee's signature.
``SyncCommittee`` can also contain duplicate pubkeys, when ``get_sync_committee_indices``
returns duplicate indices. Implementations must take care when handling
optimizations relating to aggregation and verification in the presence of duplicates.
Note: This function should only be called at sync committee period boundaries, as
``get_sync_committee_indices`` is not stable within a given period.
"""
indices = get_sync_committee_indices(state, epoch)
pubkeys = [state.validators[index].pubkey for index in indices]
partition = [pubkeys[i:i + SYNC_PUBKEYS_PER_AGGREGATE] for i in range(0, len(pubkeys), SYNC_PUBKEYS_PER_AGGREGATE)]
pubkey_aggregates = [bls.AggregatePKs(preaggregate) for preaggregate in partition]
return SyncCommittee(pubkeys=pubkeys, pubkey_aggregates=pubkey_aggregates)
aggregate_pubkey = bls.AggregatePKs(pubkeys)
return SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=aggregate_pubkey)
```
#### `get_base_reward_per_increment`
@@ -409,7 +421,7 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S
#### Modified `slash_validator`
*Note*: The function `slash_validator` is modified to use `MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR`
*Note*: The function `slash_validator` is modified to use `MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR`
and use `PROPOSER_WEIGHT` when calculating the proposer reward.
```python
@@ -565,7 +577,8 @@ def process_sync_committee(state: BeaconState, aggregate: SyncAggregate) -> None
proposer_reward = Gwei(participant_reward * PROPOSER_WEIGHT // (WEIGHT_DENOMINATOR - PROPOSER_WEIGHT))
# Apply participant and proposer rewards
committee_indices = get_sync_committee_indices(state, get_current_epoch(state))
all_pubkeys = [v.pubkey for v in state.validators]
committee_indices = [ValidatorIndex(all_pubkeys.index(pubkey)) for pubkey in state.current_sync_committee.pubkeys]
participant_indices = [index for index, bit in zip(committee_indices, aggregate.sync_committee_bits) if bit]
for participant_index in participant_indices:
increase_balance(state, participant_index, participant_reward)

View File

@@ -40,8 +40,8 @@ Altair adds new messages, topics and data to the Req-Resp, Gossip and Discovery
## Warning
This document is currently illustrative for early Altair testnets and some parts are subject to change.
Refer to the note in the [validator guide](./validator.md) for further details.
This document is currently illustrative for early Altair testnets and some parts are subject to change.
Refer to the note in the [validator guide](./validator.md) for further details.
# Modifications in Altair
@@ -64,12 +64,12 @@ Where
## The gossip domain: gossipsub
Gossip meshes are added in Altair to support the consensus activities of the sync committees.
Gossip meshes are added in Altair to support the consensus activities of the sync committees.
Validators use an aggregation scheme to balance the processing and networking load across all of the relevant actors.
### Topics and messages
Topics follow the same specification as in the Phase 0 document.
Topics follow the same specification as in the Phase 0 document.
New topics are added in Altair to support the sync committees and the beacon block topic is updated with the modified type.
The specification around the creation, validation, and dissemination of messages has not changed from the Phase 0 document.
@@ -93,13 +93,13 @@ Altair changes the type of the global beacon block topic and adds one global top
##### `beacon_block`
The existing specification for this topic does not change from the Phase 0 document,
but the type of the payload does change to the (modified) `SignedBeaconBlock`.
but the type of the payload does change to the (modified) `SignedBeaconBlock`.
This type changes due to the inclusion of the inner `BeaconBlockBody` that is modified in Altair.
See the [state transition document](./beacon-chain.md#beaconblockbody) for Altair for further details.
##### `sync_committee_contribution_and_proof`
This topic is used to propagate partially aggregated sync committee signatures to be included in future blocks.
The following validations MUST pass before forwarding the `signed_contribution_and_proof` on the network; define `contribution_and_proof = signed_contribution_and_proof.message`, `contribution = contribution_and_proof.contribution`, and the following function `get_sync_subcommittee_pubkeys` for convenience:
@@ -135,16 +135,16 @@ The following validations MUST pass before forwarding the `sync_committee_signat
- _[IGNORE]_ The signature's slot is for the current slot, i.e. `sync_committee_signature.slot == current_slot`.
- _[IGNORE]_ The block being signed over (`sync_committee_signature.beacon_block_root`) has been seen (via both gossip and non-gossip sources).
- _[IGNORE]_ There has been no other valid sync committee signature for the declared `slot` for the validator referenced by `sync_committee_signature.validator_index`.
- _[REJECT]_ The `subnet_id` is valid for the given validator, i.e. `subnet_id in compute_subnets_for_sync_committee(state, sync_committee_signature.validator_index)`.
- _[REJECT]_ The `subnet_id` is valid for the given validator, i.e. `subnet_id in compute_subnets_for_sync_committee(state, sync_committee_signature.validator_index)`.
Note this validation implies the validator is part of the broader current sync committee along with the correct subcommittee.
- _[REJECT]_ The `signature` is valid for the message `beacon_block_root` for the validator referenced by `validator_index`.
#### Sync committees and aggregation
The aggregation scheme closely follows the design of the attestation aggregation scheme.
Sync committee signatures are broadcast into "subnets" defined by a topic.
The number of subnets is defined by `SYNC_COMMITTEE_SUBNET_COUNT` in the [Altair validator guide](./validator.md#constants).
Sync committee members are divided into "subcommittees" which are then assigned to a subnet for the duration of tenure in the sync committee.
The aggregation scheme closely follows the design of the attestation aggregation scheme.
Sync committee signatures are broadcast into "subnets" defined by a topic.
The number of subnets is defined by `SYNC_COMMITTEE_SUBNET_COUNT` in the [Altair validator guide](./validator.md#constants).
Sync committee members are divided into "subcommittees" which are then assigned to a subnet for the duration of tenure in the sync committee.
Individual validators can be duplicated in the broader sync committee such that they are included multiple times in a given subcommittee or across multiple subcommittees.
Unaggregated signatures (along with metadata) are sent as `SyncCommitteeSignature`s on the `sync_committee_{subnet_id}` topics.
@@ -244,7 +244,7 @@ Response Content:
)
```
Requests the MetaData of a peer, using the new `MetaData` definition given above
Requests the MetaData of a peer, using the new `MetaData` definition given above
that is extended from phase 0 in Altair. Other conditions for the `GetMetaData`
protocol are unchanged from the phase 0 p2p networking document.
@@ -253,7 +253,7 @@ protocol are unchanged from the phase 0 p2p networking document.
In advance of the fork, implementations can opt in to both run the v1 and v2 for a smooth transition.
This is non-breaking, and is recommended as soon as the fork specification is stable.
The v1 variants will be deprecated, and implementations should use v2 when available
The v1 variants will be deprecated, and implementations should use v2 when available
(as negotiated with peers via LibP2P multistream-select).
The v1 method MAY be unregistered at the fork boundary.
@@ -265,7 +265,7 @@ the responder MUST return the **InvalidRequest** response code.
The `attnets` key of the ENR is used as defined in the Phase 0 document.
An additional bitfield is added to the ENR under the key `syncnets` to facilitate sync committee subnet discovery.
The length of this bitfield is `SYNC_COMMITTEE_SUBNET_COUNT` where each bit corresponds to a distinct `subnet_id` for a specific sync committee subnet.
The length of this bitfield is `SYNC_COMMITTEE_SUBNET_COUNT` where each bit corresponds to a distinct `subnet_id` for a specific sync committee subnet.
The `i`th bit is set in this bitfield if the validator is currently subscribed to the `sync_committee_{i}` topic.
See the [validator document](./validator.md#sync-committee-subnet-stability) for further details on how the new bits are used.

View File

@@ -12,7 +12,6 @@
- [Constants](#constants)
- [Configuration](#configuration)
- [Misc](#misc)
- [Time parameters](#time-parameters)
- [Containers](#containers)
- [`LightClientSnapshot`](#lightclientsnapshot)
- [`LightClientUpdate`](#lightclientupdate)
@@ -51,13 +50,6 @@ uses sync committees introduced in [this beacon chain extension](./beacon-chain.
| Name | Value |
| - | - |
| `MIN_SYNC_COMMITTEE_PARTICIPANTS` | `1` |
| `MAX_VALID_LIGHT_CLIENT_UPDATES` | `uint64(2**64 - 1)` |
### Time parameters
| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
| `LIGHT_CLIENT_UPDATE_TIMEOUT` | `Slot(2**13)` | slots | ~27 hours |
## Containers
@@ -94,9 +86,10 @@ class LightClientUpdate(Container):
### `LightClientStore`
```python
class LightClientStore(Container):
@dataclass
class LightClientStore(object):
snapshot: LightClientSnapshot
valid_updates: List[LightClientUpdate, MAX_VALID_LIGHT_CLIENT_UPDATES]
valid_updates: Set[LightClientUpdate]
```
## Helper functions
@@ -182,20 +175,21 @@ def apply_light_client_update(snapshot: LightClientSnapshot, update: LightClient
def process_light_client_update(store: LightClientStore, update: LightClientUpdate, current_slot: Slot,
genesis_validators_root: Root) -> None:
validate_light_client_update(store.snapshot, update, genesis_validators_root)
store.valid_updates.append(update)
store.valid_updates.add(update)
update_timeout = SLOTS_PER_EPOCH * EPOCHS_PER_SYNC_COMMITTEE_PERIOD
if (
sum(update.sync_committee_bits) * 3 > len(update.sync_committee_bits) * 2
sum(update.sync_committee_bits) * 3 >= len(update.sync_committee_bits) * 2
and update.finality_header != BeaconBlockHeader()
):
# Apply update if (1) 2/3 quorum is reached and (2) we have a finality proof.
# Note that (2) means that the current light client design needs finality.
# It may be changed to re-organizable light client design. See the on-going issue eth2.0-specs#2182.
apply_light_client_update(store.snapshot, update)
store.valid_updates = []
elif current_slot > store.snapshot.header.slot + LIGHT_CLIENT_UPDATE_TIMEOUT:
store.valid_updates = set()
elif current_slot > store.snapshot.header.slot + update_timeout:
# Forced best update when the update timeout has elapsed
apply_light_client_update(store.snapshot,
max(store.valid_updates, key=lambda update: sum(update.sync_committee_bits)))
store.valid_updates = []
store.valid_updates = set()
```

View File

@@ -49,18 +49,18 @@ This is an accompanying document to [Ethereum 2.0 Altair -- The Beacon Chain](./
## Introduction
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.
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.
This previous document is referred to below as the "Phase 0 document".
Altair introduces a new type of committee: the sync committee. Sync committees are responsible for signing each block of the canonical chain and there exists an efficient algorithm for light clients to sync the chain using the output of the sync committees.
See the [sync protocol](./sync-protocol.md) for further details on the light client sync.
Under this network upgrade, validators track their participation in this new committee type and produce the relevant signatures as required.
Altair introduces a new type of committee: the sync committee. Sync committees are responsible for signing each block of the canonical chain and there exists an efficient algorithm for light clients to sync the chain using the output of the sync committees.
See the [sync protocol](./sync-protocol.md) for further details on the light client sync.
Under this network upgrade, validators track their participation in this new committee type and produce the relevant signatures as required.
Block proposers incorporate the (aggregated) sync committee signatures into each block they produce.
## Prerequisites
All terminology, constants, functions, and protocol mechanics defined in the [Altair -- The Beacon Chain](./beacon-chain.md) doc are requisite for this document and used throughout.
All terminology, constants, functions, and protocol mechanics defined in the [Altair -- The Beacon Chain](./beacon-chain.md) doc are requisite for this document and used throughout.
Please see this document before continuing and use as a reference throughout.
## Warning
@@ -74,7 +74,7 @@ This document is currently illustrative for early Altair testnets and some parts
| Name | Value | Unit |
| - | - | :-: |
| `TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE` | `2**2` (= 4) | validators |
| `SYNC_COMMITTEE_SUBNET_COUNT` | `8` | The number of sync committee subnets used in the gossipsub aggregation protocol. |
| `SYNC_COMMITTEE_SUBNET_COUNT` | `4` | The number of sync committee subnets used in the gossipsub aggregation protocol. |
## Containers
@@ -168,11 +168,11 @@ def is_assigned_to_sync_committee(state: BeaconState,
### Lookahead
The sync committee shufflings give validators 1 sync committee period of lookahead which amounts to `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` epochs.
At any given `epoch`, the `BeaconState` contains the current `SyncCommittee` and the next `SyncCommittee`.
At any given `epoch`, the `BeaconState` contains the current `SyncCommittee` and the next `SyncCommittee`.
Once every `EPOCHS_PER_SYNC_COMMITTEE_PERIOD` epochs, the next `SyncCommittee` becomes the current `SyncCommittee` and the next committee is computed and stored.
*Note*: The data required to compute a given committee is not cached in the `BeaconState` after committees are calculated at the period boundaries.
This means that calling `get_sync_commitee()` in a given `epoch` can return a different result than what was computed during the relevant epoch transition.
*Note*: The data required to compute a given committee is not cached in the `BeaconState` after committees are calculated at the period boundaries.
This means that calling `get_sync_commitee()` in a given `epoch` can return a different result than what was computed during the relevant epoch transition.
For this reason, *always* get committee assignments via the fields of the `BeaconState` (`current_sync_committee` and `next_sync_committee`) or use the above reference code.
A validator should plan for future sync committee assignments by noting which sync committee periods they are selected for participation.
@@ -188,7 +188,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.
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.
### Block proposal
@@ -202,25 +202,25 @@ No change to [Preparing for a `BeaconBlock`](../phase0/validator.md#preparing-fo
#### Constructing the `BeaconBlockBody`
Each section of [Constructing the `BeaconBlockBody`](../phase0/validator.md#constructing-the-beaconblockbody) should be followed.
Each section of [Constructing the `BeaconBlockBody`](../phase0/validator.md#constructing-the-beaconblockbody) should be followed.
After constructing the `BeaconBlockBody` as per that section, the proposer has an additional task to include the sync committee signatures:
##### Sync committee
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 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.
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.
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.
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,
and that `block.body.sync_aggregate.sync_committee_signature` is the aggregate BLS signature combining all of the valid signatures.
Given a collection of the best seen `contributions` (with no repeating `subcommittee_index` values) and the `BeaconBlock` under construction,
Given a collection of the best seen `contributions` (with no repeating `subcommittee_index` values) and the `BeaconBlock` under construction,
the proposer processes them as follows:
```python
def process_sync_committee_contributions(block: BeaconBlock,
def process_sync_committee_contributions(block: BeaconBlock,
contributions: Set[SyncCommitteeContribution]) -> None:
sync_aggregate = SyncAggregate()
signatures = []
@@ -248,7 +248,7 @@ No change to [Packaging into a `SignedBeaconBlock`](../phase0/validator.md#packa
### Attesting and attestation aggregation
Refer to the phase 0 document for the [attesting](../phase0/validator.md#attesting) and [attestation aggregation](../phase0/validator.md#attestation-aggregation) responsibilities.
Refer to the phase 0 document for the [attesting](../phase0/validator.md#attesting) and [attestation aggregation](../phase0/validator.md#attestation-aggregation) responsibilities.
There is no change compared to the phase 0 document.
### Sync committees
@@ -263,15 +263,15 @@ This process occurs each 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.
`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
def get_sync_committee_signature(state: BeaconState,
def get_sync_committee_signature(state: BeaconState,
block_root: Root,
validator_index: ValidatorIndex,
validator_index: ValidatorIndex,
privkey: int) -> SyncCommitteeSignature:
epoch = get_current_epoch(state)
domain = get_domain(state, DOMAIN_SYNC_COMMITTEE, epoch)
@@ -286,7 +286,7 @@ 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 `subnet_id` is derived from the position in the sync committee such that the sync committee is divided into "subcommittees".
`subnet_id` 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.
*Note*: This function returns multiple subnets if a given validator index is included multiple times in a given sync committee across multiple subcommittees.
@@ -340,7 +340,7 @@ def is_sync_committee_aggregator(signature: BLSSignature) -> bool:
##### Construct sync committee contribution
If a validator is selected to aggregate the `SyncCommitteeSignature`s produced on a subnet during a given `slot`, they construct an aggregated `SyncCommitteeContribution`.
If a validator is selected to aggregate the `SyncCommitteeSignature`s produced on a subnet during a given `slot`, they construct an aggregated `SyncCommitteeContribution`.
Given all of the (valid) collected `sync_committee_signatures: Set[SyncCommitteeSignature]` from the `sync_committee_{subnet_id}` gossip during the selected `slot` with an equivalent `beacon_block_root` to that of the aggregator, the aggregator creates a `contribution: SyncCommitteeContribution` with the following fields:
@@ -361,13 +361,13 @@ Set `contribution.subcommittee_index` to the index for the subcommittee index co
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 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.
*Note*: A validator **could be included multiple times** in a given subcommittee such that multiple bits are set for a single `SyncCommitteeSignature`.
###### 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.
@@ -402,8 +402,8 @@ def get_contribution_and_proof(state: BeaconState,
Then `signed_contribution_and_proof = SignedContributionAndProof(message=contribution_and_proof, signature=signature)` is constructed and broadcast. Where `signature` is obtained from:
```python
def get_contribution_and_proof_signature(state: BeaconState,
contribution_and_proof: ContributionAndProof,
def get_contribution_and_proof_signature(state: BeaconState,
contribution_and_proof: ContributionAndProof,
privkey: int) -> BLSSignature:
contribution = contribution_and_proof.contribution
domain = get_domain(state, DOMAIN_CONTRIBUTION_AND_PROOF, compute_epoch_at_slot(contribution.slot))

View File

@@ -13,8 +13,8 @@
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Transition](#transition)
- [Execution](#execution)
- [Configuration](#configuration)
- [Containers](#containers)
- [Extended containers](#extended-containers)
- [`BeaconBlockBody`](#beaconblockbody)
@@ -24,6 +24,7 @@
- [`ExecutionPayloadHeader`](#executionpayloadheader)
- [Helper functions](#helper-functions)
- [Misc](#misc)
- [`is_execution_enabled`](#is_execution_enabled)
- [`is_transition_completed`](#is_transition_completed)
- [`is_transition_block`](#is_transition_block)
- [`compute_time_at_slot`](#compute_time_at_slot)
@@ -50,12 +51,6 @@ We define the following Python custom types for type hinting and readability:
## Constants
### Transition
| Name | Value |
| - | - |
| `TRANSITION_TOTAL_DIFFICULTY` | **TBD** |
### Execution
| Name | Value |
@@ -64,6 +59,16 @@ We define the following Python custom types for type hinting and readability:
| `MAX_EXECUTION_TRANSACTIONS` | `uint64(2**14)` (= 16,384) |
| `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) |
## Configuration
Warning: this configuration is not definitive.
| Name | Value |
| - | - |
| `MERGE_FORK_VERSION` | `Version('0x02000000')` |
| `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
| `TRANSITION_TOTAL_DIFFICULTY` | **TBD** |
## Containers
### Extended containers
@@ -136,6 +141,13 @@ class ExecutionPayloadHeader(Container):
### Misc
#### `is_execution_enabled`
```python
def is_execution_enabled(state: BeaconState, block: BeaconBlock) -> bool:
return is_transition_completed(state) or is_transition_block(state, block)
```
#### `is_transition_completed`
```python
@@ -146,8 +158,8 @@ def is_transition_completed(state: BeaconState) -> bool:
#### `is_transition_block`
```python
def is_transition_block(state: BeaconState, block_body: BeaconBlockBody) -> bool:
return not is_transition_completed(state) and block_body.execution_payload != ExecutionPayload()
def is_transition_block(state: BeaconState, block: BeaconBlock) -> bool:
return not is_transition_completed(state) and block.body.execution_payload != ExecutionPayload()
```
#### `compute_time_at_slot`
@@ -168,7 +180,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body)
process_execution_payload(state, block.body) # [New in Merge]
# Pre-merge, skip execution payload processing
if is_execution_enabled(state, block):
process_execution_payload(state, block.body.execution_payload) # [New in Merge]
```
#### Execution payload processing
@@ -181,16 +195,10 @@ The body of the function is implementation dependent.
##### `process_execution_payload`
```python
def process_execution_payload(state: BeaconState, body: BeaconBlockBody) -> None:
def process_execution_payload(state: BeaconState, execution_payload: ExecutionPayload) -> None:
"""
Note: This function is designed to be able to be run in parallel with the other `process_block` sub-functions
"""
# Pre-merge, skip processing
if not is_transition_completed(state) and not is_transition_block(state, body):
return
execution_payload = body.execution_payload
if is_transition_completed(state):
assert execution_payload.parent_hash == state.latest_execution_payload_header.block_hash
assert execution_payload.number == state.latest_execution_payload_header.number + 1

View File

@@ -75,7 +75,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
# [New in Merge]
if is_transition_block(pre_state, block.body):
if is_transition_block(pre_state, block):
# Delay consideration of block until PoW block is processed by the PoW node
pow_block = get_pow_block(block.body.execution_payload.parent_hash)
assert pow_block.is_processed

View File

@@ -402,7 +402,7 @@ def get_shard_proposer_index(beacon_state: BeaconState, slot: Slot, shard: Shard
"""
epoch = compute_epoch_at_slot(slot)
committee = get_shard_committee(beacon_state, epoch, shard)
seed = hash(get_seed(beacon_state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(slot))
seed = hash(get_seed(beacon_state, epoch, DOMAIN_SHARD_PROPOSER) + uint_to_bytes(slot))
# Proposer must have sufficient balance to pay for worst case fee burn
EFFECTIVE_BALANCE_MAX_DOWNWARD_DEVIATION = (

View File

@@ -7,9 +7,9 @@ from eth2spec.test.helpers.block_processing import run_block_processing_to
from eth2spec.test.helpers.state import (
state_transition_and_sign_block,
transition_to,
next_epoch,
)
from eth2spec.test.helpers.constants import (
PHASE0,
MAINNET, MINIMAL,
)
from eth2spec.test.helpers.sync_committee import (
@@ -17,7 +17,7 @@ from eth2spec.test.helpers.sync_committee import (
)
from eth2spec.test.context import (
expect_assertion_error,
with_all_phases_except,
with_altair_and_later,
with_configs,
spec_state_test,
always_bls,
@@ -62,7 +62,7 @@ def get_committee_indices(spec, state, duplicates=False):
state.randao_mixes[randao_index] = hash(state.randao_mixes[randao_index])
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
def test_invalid_signature_missing_participant(spec, state):
@@ -84,7 +84,7 @@ def test_invalid_signature_missing_participant(spec, state):
yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
def test_invalid_signature_extra_participant(spec, state):
@@ -160,13 +160,13 @@ def validate_sync_committee_rewards(spec, pre_state, post_state, committee, comm
committee_bits,
)
if proposer_index == index:
reward += compute_sync_committee_proposer_reward(
spec,
pre_state,
committee,
committee_bits,
)
if proposer_index == index:
reward += compute_sync_committee_proposer_reward(
spec,
pre_state,
committee,
committee_bits,
)
assert post_state.balances[index] == pre_state.balances[index] + reward
@@ -197,7 +197,7 @@ def run_successful_sync_committee_test(spec, state, committee, committee_bits):
)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@with_configs([MINIMAL], reason="to create nonduplicate committee")
@spec_state_test
def test_sync_committee_rewards_nonduplicate_committee(spec, state):
@@ -213,7 +213,7 @@ def test_sync_committee_rewards_nonduplicate_committee(spec, state):
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@with_configs([MAINNET], reason="to create duplicate committee")
@spec_state_test
def test_sync_committee_rewards_duplicate_committee(spec, state):
@@ -229,7 +229,7 @@ def test_sync_committee_rewards_duplicate_committee(spec, state):
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
def test_sync_committee_rewards_not_full_participants(spec, state):
@@ -240,7 +240,7 @@ def test_sync_committee_rewards_not_full_participants(spec, state):
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
def test_sync_committee_rewards_empty_participants(spec, state):
@@ -250,7 +250,7 @@ def test_sync_committee_rewards_empty_participants(spec, state):
yield from run_successful_sync_committee_test(spec, state, committee, committee_bits)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
def test_invalid_signature_past_block(spec, state):
@@ -289,7 +289,7 @@ def test_invalid_signature_past_block(spec, state):
yield from run_sync_committee_processing(spec, state, invalid_block, expect_exception=True)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@with_configs([MINIMAL], reason="to produce different committee sets")
@spec_state_test
@always_bls
@@ -326,7 +326,7 @@ def test_invalid_signature_previous_committee(spec, state):
yield from run_sync_committee_processing(spec, state, block, expect_exception=True)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
@with_configs([MINIMAL], reason="too slow")
@@ -367,3 +367,43 @@ def test_valid_signature_future_committee(spec, state):
)
yield from run_sync_committee_processing(spec, state, block)
@with_altair_and_later
@spec_state_test
def test_sync_committee_is_only_computed_at_epoch_boundary(spec, state):
"""
Sync committees can only be computed at sync committee period boundaries.
Ensure a client respects the committee in the state (assumed to be derived
in the correct way).
"""
current_epoch = spec.get_current_epoch(state)
# use a "synthetic" committee to simulate the situation
# where ``spec.get_sync_committee`` at the sync committee
# period epoch boundary would have diverged some epochs into the
# period; ``aggregate_pubkey`` is not relevant to this test
pubkeys = []
committee_indices = []
i = 0
active_validator_count = len(spec.get_active_validator_indices(state, current_epoch))
while len(pubkeys) < spec.SYNC_COMMITTEE_SIZE:
v = state.validators[i % active_validator_count]
if spec.is_active_validator(v, current_epoch):
pubkeys.append(v.pubkey)
committee_indices.append(i)
i += 1
synthetic_committee = spec.SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=spec.BLSPubkey())
state.current_sync_committee = synthetic_committee
assert spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD > 3
for _ in range(3):
next_epoch(spec, state)
committee = get_committee_indices(spec, state)
assert committee != committee_indices
committee_size = len(committee_indices)
committee_bits = [True] * committee_size
yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits)

View File

@@ -2,16 +2,13 @@ from eth2spec.test.context import (
always_bls,
spec_state_test,
spec_test,
with_all_phases_except,
with_altair_and_later,
with_configs,
with_custom_state,
single_phase,
misc_balances,
)
from eth2spec.test.helpers.constants import (
PHASE0,
MINIMAL,
)
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.helpers.state import transition_to
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with,
@@ -49,7 +46,7 @@ def run_sync_committees_progress_test(spec, state):
assert state.next_sync_committee == third_sync_committee
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
@with_configs([MINIMAL], reason="too slow")
@@ -60,7 +57,7 @@ def test_sync_committees_progress_genesis(spec, state):
yield from run_sync_committees_progress_test(spec, state)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
@always_bls
@with_configs([MINIMAL], reason="too slow")
@@ -73,7 +70,7 @@ def test_sync_committees_progress_not_genesis(spec, state):
yield from run_sync_committees_progress_test(spec, state)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
@spec_test
@single_phase

View File

@@ -11,9 +11,8 @@ from eth2spec.test.helpers.block import (
from eth2spec.test.helpers.sync_committee import (
compute_aggregate_sync_committee_signature,
)
from eth2spec.test.helpers.constants import PHASE0
from eth2spec.test.context import (
with_all_phases_except,
with_altair_and_later,
spec_state_test,
)
@@ -40,46 +39,46 @@ def run_sync_committee_sanity_test(spec, state, fraction_full=1.0):
yield 'post', state
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
def test_full_sync_committee_committee(spec, state):
next_epoch(spec, state)
yield from run_sync_committee_sanity_test(spec, state, fraction_full=1.0)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
def test_half_sync_committee_committee(spec, state):
next_epoch(spec, state)
yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.5)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
def test_empty_sync_committee_committee(spec, state):
next_epoch(spec, state)
yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.0)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
def test_full_sync_committee_committee_genesis(spec, state):
yield from run_sync_committee_sanity_test(spec, state, fraction_full=1.0)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
def test_half_sync_committee_committee_genesis(spec, state):
yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.5)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
def test_empty_sync_committee_committee_genesis(spec, state):
yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.0)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@spec_state_test
def test_inactivity_scores(spec, state):
for _ in range(spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2):

View File

@@ -32,7 +32,7 @@ def test_process_light_client_update_not_updated(spec, state):
)
store = spec.LightClientStore(
snapshot=pre_snapshot,
valid_updates=[]
valid_updates=set(),
)
# Block at slot 1 doesn't increase sync committee period, so it won't update snapshot
@@ -76,7 +76,7 @@ def test_process_light_client_update_not_updated(spec, state):
spec.process_light_client_update(store, update, state.slot, state.genesis_validators_root)
assert len(store.valid_updates) == 1
assert store.valid_updates[0] == update
assert store.valid_updates.pop() == update
assert store.snapshot == pre_snapshot
@@ -91,7 +91,7 @@ def test_process_light_client_update_timeout(spec, state):
)
store = spec.LightClientStore(
snapshot=pre_snapshot,
valid_updates=[]
valid_updates=set(),
)
# Forward to next sync committee period
@@ -156,7 +156,7 @@ def test_process_light_client_update_finality_updated(spec, state):
)
store = spec.LightClientStore(
snapshot=pre_snapshot,
valid_updates=[]
valid_updates=set(),
)
# Change finality

View File

@@ -7,8 +7,7 @@ from eth2spec.test.helpers.state import transition_to
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
from eth2spec.test.context import (
PHASE0,
with_all_phases_except,
with_altair_and_later,
with_state,
)
@@ -25,7 +24,7 @@ def ensure_assignments_in_sync_committee(
assert spec.is_assigned_to_sync_committee(state, epoch, validator_index)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@with_state
def test_is_assigned_to_sync_committee(phases, spec, state):
epoch = spec.get_current_epoch(state)
@@ -91,7 +90,7 @@ def _get_sync_committee_signature(
@only_with_bls()
@with_all_phases_except([PHASE0])
@with_altair_and_later
@with_state
def test_process_sync_committee_contributions(phases, spec, state):
# skip over slots at genesis
@@ -144,7 +143,7 @@ def _subnet_for_sync_committee_index(spec, i):
return i // (spec.SYNC_COMMITTEE_SIZE // spec.SYNC_COMMITTEE_SUBNET_COUNT)
@with_all_phases_except([PHASE0])
@with_altair_and_later
@with_state
def test_compute_subnets_for_sync_committee(state, spec, phases):
some_sync_committee_members = list(

View File

@@ -7,8 +7,8 @@ from eth2spec.utils import bls
from .exceptions import SkippedTest
from .helpers.constants import (
PHASE0, ALTAIR,
ALL_PHASES, FORKS_BEFORE_ALTAIR,
PHASE0, ALTAIR, MERGE,
ALL_PHASES, FORKS_BEFORE_ALTAIR, FORKS_BEFORE_MERGE,
)
from .helpers.genesis import create_genesis_state
from .utils import vector_test, with_meta_tags
@@ -312,7 +312,7 @@ def with_phases(phases, other_phases=None):
return None
run_phases = [phase]
if PHASE0 not in run_phases and ALTAIR not in run_phases:
if PHASE0 not in run_phases and ALTAIR not in run_phases and MERGE not in run_phases:
dump_skipping_message("none of the recognized phases are executable, skipping test.")
return None
@@ -330,6 +330,8 @@ def with_phases(phases, other_phases=None):
phase_dir[PHASE0] = spec_phase0
if ALTAIR in available_phases:
phase_dir[ALTAIR] = spec_altair
if MERGE in available_phases:
phase_dir[MERGE] = spec_merge
# return is ignored whenever multiple phases are ran.
# This return is for test generators to emit python generators (yielding test vector outputs)
@@ -337,6 +339,8 @@ def with_phases(phases, other_phases=None):
ret = fn(spec=spec_phase0, phases=phase_dir, *args, **kw)
if ALTAIR in run_phases:
ret = fn(spec=spec_altair, phases=phase_dir, *args, **kw)
if MERGE in run_phases:
ret = fn(spec=spec_merge, phases=phase_dir, *args, **kw)
# TODO: merge, sharding, custody_game and das are not executable yet.
# Tests that specify these features will not run, and get ignored for these specific phases.
@@ -362,6 +366,20 @@ def with_configs(configs, reason=None):
def is_post_altair(spec):
if spec.fork == MERGE: # TODO: remove parallel Altair-Merge condition after rebase.
return False
if spec.fork in FORKS_BEFORE_ALTAIR:
return False
return True
def is_post_merge(spec):
if spec.fork == ALTAIR: # TODO: remove parallel Altair-Merge condition after rebase.
return False
if spec.fork in FORKS_BEFORE_MERGE:
return False
return True
with_altair_and_later = with_phases([ALTAIR]) # TODO: include Merge, but not until Merge work is rebased.
with_merge_and_later = with_phases([MERGE])

View File

@@ -1,4 +1,5 @@
from eth2spec.test.context import is_post_altair
from eth2spec.test.context import is_post_altair, is_post_merge
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
@@ -94,6 +95,9 @@ def build_empty_block(spec, state, slot=None):
if is_post_altair(spec):
empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY
if is_post_merge(spec):
empty_block.body.execution_payload = build_empty_execution_payload(spec, state)
apply_randao_reveal(spec, state, empty_block)
return empty_block

View File

@@ -7,21 +7,24 @@ from .typing import SpecForkName, ConfigName
# Some of the Spec module functionality is exposed here to deal with phase-specific changes.
PHASE0 = SpecForkName('phase0')
ALTAIR = SpecForkName('altair')
MERGE = SpecForkName('merge')
# Experimental phases (not included in default "ALL_PHASES"):
MERGE = SpecForkName('merge')
SHARDING = SpecForkName('sharding')
CUSTODY_GAME = SpecForkName('custody_game')
DAS = SpecForkName('das')
# The forks that pytest runs with.
ALL_PHASES = (PHASE0, ALTAIR)
ALL_PHASES = (PHASE0, ALTAIR, MERGE)
# The forks that output to the test vectors.
TESTGEN_FORKS = (PHASE0, ALTAIR)
TESTGEN_FORKS = (PHASE0, ALTAIR, MERGE)
# TODO: everything runs in parallel to Altair.
# After features are rebased on the Altair fork, this can be reduced to just PHASE0.
FORKS_BEFORE_ALTAIR = (PHASE0, MERGE, SHARDING, CUSTODY_GAME, DAS)
# TODO: when rebasing Merge onto Altair, add ALTAIR to this tuple.
FORKS_BEFORE_MERGE = (PHASE0,)
#
# Config
#

View File

@@ -0,0 +1,26 @@
def build_empty_execution_payload(spec, state):
"""
Assuming a pre-state of the same slot, build a valid ExecutionPayload without any transactions.
"""
latest = state.latest_execution_payload_header
timestamp = spec.compute_time_at_slot(state, state.slot)
empty_txs = spec.List[spec.OpaqueTransaction, spec.MAX_EXECUTION_TRANSACTIONS]()
payload = spec.ExecutionPayload(
block_hash=spec.Hash32(),
parent_hash=latest.block_hash,
coinbase=spec.Bytes20(),
state_root=latest.state_root, # no changes to the state
number=latest.number + 1,
gas_limit=latest.gas_limit, # retain same limit
gas_used=0, # empty block, 0 gas
timestamp=timestamp,
receipt_root=b"no receipts here" + b"\x00" * 16, # TODO: root of empty MPT may be better.
logs_bloom=spec.ByteVector[spec.BYTES_PER_LOGS_BLOOM](), # TODO: zeroed logs bloom for empty logs ok?
transactions=empty_txs,
)
# TODO: real RLP + block hash logic would be nice, requires RLP and keccak256 dependency however.
payload.block_hash = spec.Hash32(spec.hash(payload.hash_tree_root() + b"FAKE RLP HASH"))
return payload

View File

@@ -1,6 +1,7 @@
from eth2spec.test.helpers.constants import (
ALTAIR,
FORKS_BEFORE_ALTAIR,
MERGE,
)
from eth2spec.test.helpers.keys import pubkeys
@@ -28,6 +29,8 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
if spec.fork == ALTAIR:
current_version = spec.ALTAIR_FORK_VERSION
elif spec.fork == MERGE:
current_version = spec.MERGE_FORK_VERSION
state = spec.BeaconState(
genesis_time=0,

View File

@@ -0,0 +1,43 @@
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_merge_and_later
from eth2spec.test.helpers.state import next_slot
def run_execution_payload_processing(spec, state, execution_payload, valid=True, execution_valid=True):
"""
Run ``process_execution_payload``, yielding:
- pre-state ('pre')
- execution payload ('execution_payload')
- execution details, to mock EVM execution ('execution.yml', a dict with 'execution_valid' key and boolean value)
- post-state ('post').
If ``valid == False``, run expecting ``AssertionError``
"""
pre_exec_header = state.latest_execution_payload_header.copy()
yield 'pre', state
yield 'execution', {'execution_valid': execution_valid}
yield 'execution_payload', execution_payload
if not valid:
expect_assertion_error(lambda: spec.process_execution_payload(state, execution_payload))
yield 'post', None
return
spec.process_execution_payload(state, execution_payload)
yield 'post', state
assert pre_exec_header != state.latest_execution_payload_header
# TODO: any more assertions to make?
@with_merge_and_later
@spec_state_test
def test_success_first_payload(spec, state):
next_slot(spec, state)
assert not spec.is_transition_completed(state)
execution_payload = build_empty_execution_payload(spec, state)
yield from run_execution_payload_processing(spec, state, execution_payload)

View File

@@ -0,0 +1,25 @@
from eth2spec.test.helpers.state import (
state_transition_and_sign_block
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot
)
from eth2spec.test.context import (
with_merge_and_later, spec_state_test
)
@with_merge_and_later
@spec_state_test
def test_empty_block_transition(spec, state):
yield 'pre', state
block = build_empty_block_for_next_slot(spec, state)
assert len(block.body.execution_payload.transactions) == 0
signed_block = state_transition_and_sign_block(spec, state, block)
yield 'blocks', [signed_block]
yield 'post', state
# TODO: tests with EVM, mock or replacement?

View File

@@ -1,7 +1,7 @@
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.attestations import get_valid_attestation, sign_attestation
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
from eth2spec.test.helpers.state import transition_to, state_transition_and_sign_block, next_epoch, next_slot
from eth2spec.test.helpers.fork_choice import get_genesis_forkchoice_store
@@ -19,7 +19,7 @@ def run_on_attestation(spec, state, store, attestation, valid=True):
spec.on_attestation(store, attestation)
sample_index = indexed_attestation.attesting_indices[0]
if spec.fork in (PHASE0, ALTAIR):
if spec.fork in (PHASE0, ALTAIR, MERGE):
latest_message = spec.LatestMessage(
epoch=attestation.data.target.epoch,
root=attestation.data.beacon_block_root,

View File

@@ -33,17 +33,23 @@ This excludes the other parts of the block-transition.
Operations:
| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* |
|-------------------------|-----------------------|----------------------|-----------------------------------------------------------------|
| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` |
| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` |
| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` |
| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` |
| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` |
| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` |
| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_committee(state, sync_aggregate)` (new in Altair) |
| *`operation-name`* | *`operation-object`* | *`input name`* | *`processing call`* |
|-------------------------|-----------------------|----------------------|----------------------------------------------------------------------|
| `attestation` | `Attestation` | `attestation` | `process_attestation(state, attestation)` |
| `attester_slashing` | `AttesterSlashing` | `attester_slashing` | `process_attester_slashing(state, attester_slashing)` |
| `block_header` | `BeaconBlock` | **`block`** | `process_block_header(state, block)` |
| `deposit` | `Deposit` | `deposit` | `process_deposit(state, deposit)` |
| `proposer_slashing` | `ProposerSlashing` | `proposer_slashing` | `process_proposer_slashing(state, proposer_slashing)` |
| `voluntary_exit` | `SignedVoluntaryExit` | `voluntary_exit` | `process_voluntary_exit(state, voluntary_exit)` |
| `sync_aggregate` | `SyncAggregate` | `sync_aggregate` | `process_sync_committee(state, sync_aggregate)` (new in Altair) |
| `execution_payload` | `ExecutionPayload` | `execution_payload` | `process_execution_payload(state, execution_payload)` (new in Merge) |
Note that `block_header` is not strictly an operation (and is a full `Block`), but processed in the same manner, and hence included here.
The `execution_payload` processing normally requires a `verify_execution_state_transition(execution_payload)`,
a responsibility of an (external) execution engine.
During testing this execution is mocked, an `execution.yml` is provided instead:
a dict containing an `execution_valid` boolean field with the verification result.
The resulting state should match the expected `post` state, or if the `post` state is left blank,
the handler should reject the input operation as invalid.

View File

@@ -1,10 +1,11 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.altair import spec as spec_altair
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.merge import spec as spec_merge
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
specs = (spec_phase0, spec_altair)
specs = (spec_phase0, spec_altair, spec_merge)
if __name__ == "__main__":
@@ -27,6 +28,10 @@ if __name__ == "__main__":
**phase_0_mods,
} # also run the previous phase 0 tests
# No epoch-processing changes in Merge and previous testing repeats with new types, so no additional tests required.
# TODO: rebase onto Altair testing later.
merge_mods = phase_0_mods
# TODO Custody Game testgen is disabled for now
# custody_game_mods = {**{key: 'eth2spec.test.custody_game.epoch_processing.test_process_' + key for key in [
# 'reveal_deadlines',
@@ -37,6 +42,7 @@ if __name__ == "__main__":
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
}
run_state_test_generators(runner_name="epoch_processing", specs=specs, all_mods=all_mods)

View File

@@ -1,19 +1,22 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.altair import spec as spec_altair
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.merge import spec as spec_merge
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
specs = (spec_phase0, spec_altair)
specs = (spec_phase0, spec_altair, spec_merge)
if __name__ == "__main__":
phase_0_mods = {'finality': 'eth2spec.test.phase0.finality.test_finality'}
altair_mods = phase_0_mods # No additional altair specific finality tests
altair_mods = phase_0_mods # No additional Altair specific finality tests
merge_mods = phase_0_mods # No additional Merge specific finality tests
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: spec_merge,
}
run_state_test_generators(runner_name="finality", specs=specs, all_mods=all_mods)

View File

@@ -1,10 +1,11 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.altair import spec as spec_altair
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.merge import spec as spec_merge
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
specs = (spec_phase0, spec_altair)
specs = (spec_phase0, spec_altair, spec_merge)
if __name__ == "__main__":
@@ -13,10 +14,13 @@ if __name__ == "__main__":
]}
# No additional Altair specific finality tests, yet.
altair_mods = phase_0_mods
# No specific Merge tests yet. TODO: rebase onto Altair testing later.
merge_mods = phase_0_mods
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
}
run_state_test_generators(runner_name="fork_choice", specs=specs, all_mods=all_mods)

View File

@@ -1,10 +1,11 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.altair import spec as spec_altair
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.merge import spec as spec_merge
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
specs = (spec_phase0, spec_altair)
specs = (spec_phase0, spec_altair, spec_merge)
if __name__ == "__main__":
@@ -23,6 +24,13 @@ if __name__ == "__main__":
**phase_0_mods,
} # also run the previous phase 0 tests
merge_mods = {
**{key: 'eth2spec.test.merge.block_processing.test_process_' + key for key in [
'execution_payload',
]},
**phase_0_mods, # TODO: runs phase0 tests. Rebase to include `altair_mods` testing later.
}
# TODO Custody Game testgen is disabled for now
# custody_game_mods = {**{key: 'eth2spec.test.custody_game.block_processing.test_process_' + key for key in [
# 'attestation',
@@ -35,6 +43,7 @@ if __name__ == "__main__":
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
}
run_state_test_generators(runner_name="operations", specs=specs, all_mods=all_mods)

View File

@@ -1,10 +1,11 @@
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.altair import spec as spec_altair
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.merge import spec as spec_merge
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
specs = (spec_phase0, spec_altair)
specs = (spec_phase0, spec_altair, spec_merge)
if __name__ == "__main__":
@@ -16,9 +17,15 @@ if __name__ == "__main__":
# No additional altair specific rewards tests, yet.
altair_mods = phase_0_mods
# No additional merge specific rewards tests, yet.
# Note: Block rewards are non-epoch rewards and are tested as part of block processing tests.
# Transaction fees are part of the execution-layer.
merge_mods = phase_0_mods
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
}
run_state_test_generators(runner_name="rewards", specs=specs, all_mods=all_mods)

View File

@@ -1,11 +1,12 @@
from eth2spec.phase0 import spec as spec_phase0
from eth2spec.altair import spec as spec_altair
from eth2spec.test.helpers.constants import PHASE0, ALTAIR
from eth2spec.merge import spec as spec_merge
from eth2spec.test.helpers.constants import PHASE0, ALTAIR, MERGE
from eth2spec.gen_helpers.gen_from_tests.gen import run_state_test_generators
specs = (spec_phase0, spec_altair)
specs = (spec_phase0, spec_altair, spec_merge)
if __name__ == "__main__":
@@ -17,9 +18,15 @@ if __name__ == "__main__":
'blocks',
]}, **phase_0_mods} # also run the previous phase 0 tests
# Altair-specific test cases are ignored, but should be included after the Merge is rebased onto Altair work.
merge_mods = {**{key: 'eth2spec.test.merge.sanity.test_' + key for key in [
'blocks',
]}, **phase_0_mods} # TODO: Merge inherits phase0 tests for now.
all_mods = {
PHASE0: phase_0_mods,
ALTAIR: altair_mods,
MERGE: merge_mods,
}
run_state_test_generators(runner_name="sanity", specs=specs, all_mods=all_mods)

View File

@@ -93,8 +93,7 @@ if __name__ == "__main__":
seed += 1
settings.append((seed, MAINNET, random_value.RandomizationMode.mode_random, False, 5))
seed += 1
# TODO: enable testing for the whole merge spec.
for fork in TESTGEN_FORKS + (MERGE,):
for fork in TESTGEN_FORKS:
gen_runner.run_generator("ssz_static", [
create_provider(fork, config_name, seed, mode, chaos, cases_if_random)
for (seed, config_name, mode, chaos, cases_if_random) in settings