Merge branch 'dev' into peer-das

This commit is contained in:
Hsiao-Wei Wang 2024-01-19 17:56:10 +08:00
commit 696d443f0a
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
29 changed files with 16839 additions and 16659 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,11 @@ def collect_prev_forks(fork: str) -> list[str]:
forks.append(fork)
def is_byte_vector(value: str) -> bool:
return value.startswith(('ByteVector'))
def requires_mypy_type_ignore(value: str) -> bool:
return (
value.startswith(('ByteVector'))
or (value.startswith(('Vector')) and 'floorlog2' in value)
)
def make_function_abstract(protocol_def: ProtocolDefinition, key: str):
@ -41,7 +44,8 @@ def objects_to_spec(preset_name: str,
new_type_definitions = (
'\n\n'.join(
[
f"class {key}({value}):\n pass\n" if not is_byte_vector(value) else f"class {key}({value}): # type: ignore\n pass\n"
f"class {key}({value}):\n pass\n" if not requires_mypy_type_ignore(value)
else f"class {key}({value}): # type: ignore\n pass\n"
for key, value in spec_object.custom_types.items()
]
)
@ -108,7 +112,7 @@ def objects_to_spec(preset_name: str,
if vardef.comment is not None:
out += f' # {vardef.comment}'
return out
# Merge all constant objects
hardcoded_ssz_dep_constants = reduce(lambda obj, builder: {**obj, **builder.hardcoded_ssz_dep_constants()}, builders, {})
hardcoded_custom_type_dep_constants = reduce(lambda obj, builder: {**obj, **builder.hardcoded_custom_type_dep_constants(spec_object)}, builders, {})
@ -131,12 +135,13 @@ def objects_to_spec(preset_name: str,
imports,
preparations,
f"fork = \'{fork}\'\n",
# The helper functions that some SSZ containers require. Need to be defined before `custom_type_dep_constants`
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS,
# The constants that some SSZ containers require. Need to be defined before `new_type_definitions`
custom_type_dep_constants,
new_type_definitions,
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS,
# The constants that some SSZ containers require. Need to be defined before `constants_spec`
ssz_dep_constants,
new_type_definitions,
constant_vars_spec,
preset_vars_spec,
config_spec,

View File

@ -42,9 +42,9 @@ def compute_merkle_proof(object: SSZObject,
@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
return {
'FINALIZED_ROOT_INDEX': 'GeneralizedIndex(105)',
'CURRENT_SYNC_COMMITTEE_INDEX': 'GeneralizedIndex(54)',
'NEXT_SYNC_COMMITTEE_INDEX': 'GeneralizedIndex(55)',
'FINALIZED_ROOT_GINDEX': 'GeneralizedIndex(105)',
'CURRENT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(54)',
'NEXT_SYNC_COMMITTEE_GINDEX': 'GeneralizedIndex(55)',
}
@classmethod

View File

@ -16,5 +16,5 @@ from eth2spec.bellatrix import {preset_name} as bellatrix
@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
return {
'EXECUTION_PAYLOAD_INDEX': 'GeneralizedIndex(25)',
'EXECUTION_PAYLOAD_GINDEX': 'GeneralizedIndex(25)',
}

View File

@ -27,6 +27,6 @@ import json
@classmethod
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
constants = {
'EXECUTION_PAYLOAD_INDEX': 'GeneralizedIndex(41)',
'EXECUTION_PAYLOAD_GINDEX': 'GeneralizedIndex(41)',
}
return {**super().hardcoded_ssz_dep_constants(), **constants}

View File

@ -93,6 +93,8 @@ def _get_class_info_from_source(source: str) -> Tuple[str, Optional[str]]:
base = class_def.bases[0]
if isinstance(base, ast.Name):
parent_class = base.id
elif isinstance(base, ast.Subscript):
parent_class = base.value.id
else:
# NOTE: SSZ definition derives from earlier phase...
# e.g. `phase0.SignedBeaconBlock`

View File

@ -75,7 +75,8 @@ def create_light_client_bootstrap(state: BeaconState,
return LightClientBootstrap(
header=block_to_light_client_header(block),
current_sync_committee=state.current_sync_committee,
current_sync_committee_branch=compute_merkle_proof(state, CURRENT_SYNC_COMMITTEE_INDEX),
current_sync_committee_branch=CurrentSyncCommitteeBranch(
compute_merkle_proof(state, CURRENT_SYNC_COMMITTEE_GINDEX)),
)
```
@ -122,7 +123,8 @@ def create_light_client_update(state: BeaconState,
# `next_sync_committee` is only useful if the message is signed by the current sync committee
if update_attested_period == update_signature_period:
update.next_sync_committee = attested_state.next_sync_committee
update.next_sync_committee_branch = compute_merkle_proof(attested_state, NEXT_SYNC_COMMITTEE_INDEX)
update.next_sync_committee_branch = NextSyncCommitteeBranch(
compute_merkle_proof(attested_state, NEXT_SYNC_COMMITTEE_GINDEX))
# Indicate finality whenever possible
if finalized_block is not None:
@ -131,7 +133,8 @@ def create_light_client_update(state: BeaconState,
assert hash_tree_root(update.finalized_header.beacon) == attested_state.finalized_checkpoint.root
else:
assert attested_state.finalized_checkpoint.root == Bytes32()
update.finality_branch = compute_merkle_proof(attested_state, FINALIZED_ROOT_INDEX)
update.finality_branch = FinalityBranch(
compute_merkle_proof(attested_state, FINALIZED_ROOT_GINDEX))
update.sync_aggregate = block.message.body.sync_aggregate
update.signature_slot = block.message.slot
@ -158,7 +161,7 @@ def create_light_client_finality_update(update: LightClientUpdate) -> LightClien
)
```
Full nodes SHOULD provide the `LightClientFinalityUpdate` with the highest `attested_header.beacon.slot` (if multiple, highest `signature_slot`) as selected by fork choice, and SHOULD support a push mechanism to deliver new `LightClientFinalityUpdate` whenever `finalized_header` changes.
Full nodes SHOULD provide the `LightClientFinalityUpdate` with the highest `attested_header.beacon.slot` (if multiple, highest `signature_slot`) as selected by fork choice, and SHOULD support a push mechanism to deliver new `LightClientFinalityUpdate` whenever `finalized_header` changes. If that `LightClientFinalityUpdate` does not have supermajority (> 2/3) sync committee participation, a second `LightClientFinalityUpdate` SHOULD be delivered for the same `finalized_header` once supermajority participation is obtained.
### `create_light_client_optimistic_update`

View File

@ -59,7 +59,7 @@ New global topics are added to provide light clients with the latest updates.
This topic is used to propagate the latest `LightClientFinalityUpdate` to light clients, allowing them to keep track of the latest `finalized_header`.
The following validations MUST pass before forwarding the `finality_update` on the network.
- _[IGNORE]_ The `finalized_header.beacon.slot` is greater than that of all previously forwarded `finality_update`s
- _[IGNORE]_ The `finalized_header.beacon.slot` is greater than that of all previously forwarded `finality_update`s, or it matches the highest previously forwarded slot and also has a `sync_aggregate` indicating supermajority (> 2/3) sync committee participation while the previously forwarded `finality_update` for that slot did not indicate supermajority
- _[IGNORE]_ The `finality_update` is received after the block at `signature_slot` was given enough time to propagate through the network -- i.e. validate that one-third of `finality_update.signature_slot` has transpired (`SECONDS_PER_SLOT / INTERVALS_PER_SLOT` seconds after the start of the slot, with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance)
For full nodes, the following validations MUST additionally pass before forwarding the `finality_update` on the network.

View File

@ -9,6 +9,7 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Preset](#preset)
- [Misc](#misc)
@ -56,13 +57,21 @@ Additional documents describe how the light client sync protocol can be used:
- [Light client](./light-client.md)
- [Networking](./p2p-interface.md)
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
| `FinalityBranch` | `Vector[Bytes32, floorlog2(FINALIZED_ROOT_GINDEX)]` | Merkle branch of `finalized_checkpoint.root` within `BeaconState` |
| `CurrentSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX)]` | Merkle branch of `current_sync_committee` within `BeaconState` |
| `NextSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_GINDEX)]` | Merkle branch of `next_sync_committee` within `BeaconState` |
## Constants
| Name | Value |
| - | - |
| `FINALIZED_ROOT_INDEX` | `get_generalized_index(BeaconState, 'finalized_checkpoint', 'root')` (= 105) |
| `CURRENT_SYNC_COMMITTEE_INDEX` | `get_generalized_index(BeaconState, 'current_sync_committee')` (= 54) |
| `NEXT_SYNC_COMMITTEE_INDEX` | `get_generalized_index(BeaconState, 'next_sync_committee')` (= 55) |
| `FINALIZED_ROOT_GINDEX` | `get_generalized_index(BeaconState, 'finalized_checkpoint', 'root')` (= 105) |
| `CURRENT_SYNC_COMMITTEE_GINDEX` | `get_generalized_index(BeaconState, 'current_sync_committee')` (= 54) |
| `NEXT_SYNC_COMMITTEE_GINDEX` | `get_generalized_index(BeaconState, 'next_sync_committee')` (= 55) |
## Preset
@ -93,7 +102,7 @@ class LightClientBootstrap(Container):
header: LightClientHeader
# Current sync committee corresponding to `header.beacon.state_root`
current_sync_committee: SyncCommittee
current_sync_committee_branch: Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_INDEX)]
current_sync_committee_branch: CurrentSyncCommitteeBranch
```
### `LightClientUpdate`
@ -104,10 +113,10 @@ class LightClientUpdate(Container):
attested_header: LightClientHeader
# Next sync committee corresponding to `attested_header.beacon.state_root`
next_sync_committee: SyncCommittee
next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)]
next_sync_committee_branch: NextSyncCommitteeBranch
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header: LightClientHeader
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
finality_branch: FinalityBranch
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
@ -122,7 +131,7 @@ class LightClientFinalityUpdate(Container):
attested_header: LightClientHeader
# Finalized header corresponding to `attested_header.beacon.state_root`
finalized_header: LightClientHeader
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)]
finality_branch: FinalityBranch
# Sync committee aggregate signature
sync_aggregate: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
@ -174,14 +183,14 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
```python
def is_sync_committee_update(update: LightClientUpdate) -> bool:
return update.next_sync_committee_branch != [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
return update.next_sync_committee_branch != NextSyncCommitteeBranch()
```
### `is_finality_update`
```python
def is_finality_update(update: LightClientUpdate) -> bool:
return update.finality_branch != [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
return update.finality_branch != FinalityBranch()
```
### `is_better_update`
@ -286,8 +295,8 @@ def initialize_light_client_store(trusted_block_root: Root,
assert is_valid_merkle_branch(
leaf=hash_tree_root(bootstrap.current_sync_committee),
branch=bootstrap.current_sync_committee_branch,
depth=floorlog2(CURRENT_SYNC_COMMITTEE_INDEX),
index=get_subtree_index(CURRENT_SYNC_COMMITTEE_INDEX),
depth=floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX),
index=get_subtree_index(CURRENT_SYNC_COMMITTEE_GINDEX),
root=bootstrap.header.beacon.state_root,
)
@ -358,8 +367,8 @@ def validate_light_client_update(store: LightClientStore,
assert is_valid_merkle_branch(
leaf=finalized_root,
branch=update.finality_branch,
depth=floorlog2(FINALIZED_ROOT_INDEX),
index=get_subtree_index(FINALIZED_ROOT_INDEX),
depth=floorlog2(FINALIZED_ROOT_GINDEX),
index=get_subtree_index(FINALIZED_ROOT_GINDEX),
root=update.attested_header.beacon.state_root,
)
@ -373,8 +382,8 @@ def validate_light_client_update(store: LightClientStore,
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.next_sync_committee),
branch=update.next_sync_committee_branch,
depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX),
index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX),
depth=floorlog2(NEXT_SYNC_COMMITTEE_GINDEX),
index=get_subtree_index(NEXT_SYNC_COMMITTEE_GINDEX),
root=update.attested_header.beacon.state_root,
)
@ -493,7 +502,7 @@ def process_light_client_finality_update(store: LightClientStore,
update = LightClientUpdate(
attested_header=finality_update.attested_header,
next_sync_committee=SyncCommittee(),
next_sync_committee_branch=[Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))],
next_sync_committee_branch=NextSyncCommitteeBranch(),
finalized_header=finality_update.finalized_header,
finality_branch=finality_update.finality_branch,
sync_aggregate=finality_update.sync_aggregate,
@ -512,9 +521,9 @@ def process_light_client_optimistic_update(store: LightClientStore,
update = LightClientUpdate(
attested_header=optimistic_update.attested_header,
next_sync_committee=SyncCommittee(),
next_sync_committee_branch=[Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))],
next_sync_committee_branch=NextSyncCommitteeBranch(),
finalized_header=LightClientHeader(),
finality_branch=[Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))],
finality_branch=FinalityBranch(),
sync_aggregate=optimistic_update.sync_aggregate,
signature_slot=optimistic_update.signature_slot,
)

View File

@ -294,7 +294,7 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) ->
### Epoch processing
*Note*: The function `process_historical_summaries_update` replaces `process_historical_roots_update` in Bellatrix.
*Note*: The function `process_historical_summaries_update` replaces `process_historical_roots_update` in Capella.
```python
def process_epoch(state: BeaconState) -> None:

View File

@ -46,14 +46,15 @@ def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
transactions_root=hash_tree_root(payload.transactions),
withdrawals_root=hash_tree_root(payload.withdrawals),
)
execution_branch = compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_INDEX)
execution_branch = ExecutionBranch(
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
else:
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
# it was not included in the corresponding light client data. To ensure compatibility
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
execution_header = ExecutionPayloadHeader()
execution_branch = [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
execution_branch = ExecutionBranch()
return LightClientHeader(
beacon=BeaconBlockHeader(

View File

@ -9,6 +9,7 @@
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Introduction](#introduction)
- [Custom types](#custom-types)
- [Constants](#constants)
- [Containers](#containers)
- [Modified `LightClientHeader`](#modified-lightclientheader)
@ -27,11 +28,17 @@ Additional documents describes the impact of the upgrade on certain roles:
- [Full node](./full-node.md)
- [Networking](./p2p-interface.md)
## Custom types
| Name | SSZ equivalent | Description |
| - | - | - |
| `ExecutionBranch` | `Vector[Bytes32, floorlog2(EXECUTION_PAYLOAD_GINDEX)]` | Merkle branch of `execution_payload` within `BeaconBlockBody` |
## Constants
| Name | Value |
| - | - |
| `EXECUTION_PAYLOAD_INDEX` | `get_generalized_index(BeaconBlockBody, 'execution_payload')` (= 25) |
| `EXECUTION_PAYLOAD_GINDEX` | `get_generalized_index(BeaconBlockBody, 'execution_payload')` (= 25) |
## Containers
@ -43,7 +50,7 @@ class LightClientHeader(Container):
beacon: BeaconBlockHeader
# Execution payload header corresponding to `beacon.body_root` (from Capella onward)
execution: ExecutionPayloadHeader
execution_branch: Vector[Bytes32, floorlog2(EXECUTION_PAYLOAD_INDEX)]
execution_branch: ExecutionBranch
```
## Helper functions
@ -69,14 +76,14 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
if epoch < CAPELLA_FORK_EPOCH:
return (
header.execution == ExecutionPayloadHeader()
and header.execution_branch == [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
and header.execution_branch == ExecutionBranch()
)
return is_valid_merkle_branch(
leaf=get_lc_execution_root(header),
branch=header.execution_branch,
depth=floorlog2(EXECUTION_PAYLOAD_INDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_INDEX),
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
root=header.beacon.body_root,
)
```

View File

@ -52,14 +52,15 @@ def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
execution_header.blob_gas_used = payload.blob_gas_used
execution_header.excess_blob_gas = payload.excess_blob_gas
execution_branch = compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_INDEX)
execution_branch = ExecutionBranch(
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
else:
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
# it was not included in the corresponding light client data. To ensure compatibility
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
execution_header = ExecutionPayloadHeader()
execution_branch = [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
execution_branch = ExecutionBranch()
return LightClientHeader(
beacon=BeaconBlockHeader(

View File

@ -74,14 +74,14 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
if epoch < CAPELLA_FORK_EPOCH:
return (
header.execution == ExecutionPayloadHeader()
and header.execution_branch == [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
and header.execution_branch == ExecutionBranch()
)
return is_valid_merkle_branch(
leaf=get_lc_execution_root(header),
branch=header.execution_branch,
depth=floorlog2(EXECUTION_PAYLOAD_INDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_INDEX),
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
root=header.beacon.body_root,
)
```

View File

@ -175,7 +175,7 @@ The following validations MUST pass before forwarding the `blob_sidecar` on the
- _[REJECT]_ The sidecar's index is consistent with `MAX_BLOBS_PER_BLOCK` -- i.e. `blob_sidecar.index < MAX_BLOBS_PER_BLOCK`.
- _[REJECT]_ The sidecar is for the correct subnet -- i.e. `compute_subnet_for_blob_sidecar(blob_sidecar.index) == subnet_id`.
- _[IGNORE]_ The sidecar is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `block_header.slot <= current_slot` (a client MAY queue future sidecars for processing at the appropriate slot).
- _[IGNORE]_ The sidecar is from a slot greater than the latest finalized slot -- i.e. validate that `block_header.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)`
- _[IGNORE]_ The sidecar is from a slot greater than the latest finalized slot -- i.e. validate that `block_header.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
- _[REJECT]_ The proposer signature of `blob_sidecar.signed_block_header`, is valid with respect to the `block_header.proposer_index` pubkey.
- _[IGNORE]_ The sidecar's block's parent (defined by `block_header.parent_root`) has been seen (via both gossip and non-gossip sources) (a client MAY queue sidecars for processing once the parent block is retrieved).
- _[REJECT]_ The sidecar's block's parent (defined by `block_header.parent_root`) passes validation.

View File

@ -16,10 +16,12 @@
- [`get_forkchoice_store`](#get_forkchoice_store)
- [`get_slots_since_genesis`](#get_slots_since_genesis)
- [`get_current_slot`](#get_current_slot)
- [`get_current_store_epoch`](#get_current_store_epoch)
- [`compute_slots_since_epoch_start`](#compute_slots_since_epoch_start)
- [`get_ancestor`](#get_ancestor)
- [`calculate_committee_fraction`](#calculate_committee_fraction)
- [`get_checkpoint_block`](#get_checkpoint_block)
- [`get_proposer_score`](#get_proposer_score)
- [`get_weight`](#get_weight)
- [`get_voting_source`](#get_voting_source)
- [`filter_block_tree`](#filter_block_tree)
@ -140,8 +142,7 @@ class Store(object):
```python
def is_previous_epoch_justified(store: Store) -> bool:
current_slot = get_current_slot(store)
current_epoch = compute_epoch_at_slot(current_slot)
current_epoch = get_current_store_epoch(store)
return store.justified_checkpoint.epoch + 1 == current_epoch
```
@ -190,6 +191,13 @@ def get_current_slot(store: Store) -> Slot:
return Slot(GENESIS_SLOT + get_slots_since_genesis(store))
```
#### `get_current_store_epoch`
```python
def get_current_store_epoch(store: Store) -> Epoch:
return compute_epoch_at_slot(get_current_slot(store))
```
#### `compute_slots_since_epoch_start`
```python
@ -226,6 +234,15 @@ def get_checkpoint_block(store: Store, root: Root, epoch: Epoch) -> Root:
return get_ancestor(store, root, epoch_first_slot)
```
#### `get_proposer_score`
```python
def get_proposer_score(store: Store) -> Gwei:
justified_checkpoint_state = store.checkpoint_states[store.justified_checkpoint]
committee_weight = get_total_active_balance(justified_checkpoint_state) // SLOTS_PER_EPOCH
return (committee_weight * PROPOSER_SCORE_BOOST) // 100
```
#### `get_weight`
```python
@ -249,7 +266,7 @@ def get_weight(store: Store, root: Root) -> Gwei:
proposer_score = Gwei(0)
# Boost is applied if ``root`` is an ancestor of ``proposer_boost_root``
if get_ancestor(store, store.proposer_boost_root, store.blocks[root].slot) == root:
proposer_score = calculate_committee_fraction(state, PROPOSER_SCORE_BOOST)
proposer_score = get_proposer_score(store)
return attestation_score + proposer_score
```
@ -261,7 +278,7 @@ def get_voting_source(store: Store, block_root: Root) -> Checkpoint:
Compute the voting source checkpoint in event that block with root ``block_root`` is the head block
"""
block = store.blocks[block_root]
current_epoch = compute_epoch_at_slot(get_current_slot(store))
current_epoch = get_current_store_epoch(store)
block_epoch = compute_epoch_at_slot(block.slot)
if current_epoch > block_epoch:
# The block is from a prior epoch, the voting source will be pulled-up
@ -293,23 +310,17 @@ def filter_block_tree(store: Store, block_root: Root, blocks: Dict[Root, BeaconB
return True
return False
current_epoch = compute_epoch_at_slot(get_current_slot(store))
current_epoch = get_current_store_epoch(store)
voting_source = get_voting_source(store, block_root)
# The voting source should be at the same height as the store's justified checkpoint
# The voting source should be either at the same height as the store's justified checkpoint or
# not more than two epochs ago
correct_justified = (
store.justified_checkpoint.epoch == GENESIS_EPOCH
or voting_source.epoch == store.justified_checkpoint.epoch
or voting_source.epoch + 2 >= current_epoch
)
# If the previous epoch is justified, the block should be pulled-up. In this case, check that unrealized
# justification is higher than the store and that the voting source is not more than two epochs ago
if not correct_justified and is_previous_epoch_justified(store):
correct_justified = (
store.unrealized_justifications[block_root].epoch >= store.justified_checkpoint.epoch and
voting_source.epoch + 2 >= current_epoch
)
finalized_checkpoint_block = get_checkpoint_block(
store,
block_root,
@ -519,7 +530,7 @@ def compute_pulled_up_tip(store: Store, block_root: Root) -> None:
# If the block is from a prior epoch, apply the realized values
block_epoch = compute_epoch_at_slot(store.blocks[block_root].slot)
current_epoch = compute_epoch_at_slot(get_current_slot(store))
current_epoch = get_current_store_epoch(store)
if block_epoch < current_epoch:
update_checkpoints(store, state.current_justified_checkpoint, state.finalized_checkpoint)
```
@ -556,7 +567,7 @@ def validate_target_epoch_against_current_time(store: Store, attestation: Attest
target = attestation.data.target
# Attestations must be from the current or previous epoch
current_epoch = compute_epoch_at_slot(get_current_slot(store))
current_epoch = get_current_store_epoch(store)
# Use GENESIS_EPOCH for previous when genesis to avoid underflow
previous_epoch = current_epoch - 1 if current_epoch > GENESIS_EPOCH else GENESIS_EPOCH
# If attestation target is from a future epoch, delay consideration until the epoch arrives
@ -653,7 +664,7 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
block.parent_root,
store.finalized_checkpoint.epoch,
)
assert store.finalized_checkpoint.root == finalized_checkpoint_block
assert store.finalized_checkpoint.root == finalized_checkpoint_block
# Check the block is valid and compute the post-state
state = pre_state.copy()

View File

@ -330,7 +330,7 @@ The following validations MUST pass before forwarding the `signed_beacon_block`
i.e. validate that `signed_beacon_block.message.slot <= current_slot`
(a client MAY queue future blocks for processing at the appropriate slot).
- _[IGNORE]_ The block is from a slot greater than the latest finalized slot --
i.e. validate that `signed_beacon_block.message.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)`
i.e. validate that `signed_beacon_block.message.slot > compute_start_slot_at_epoch(store.finalized_checkpoint.epoch)`
(a client MAY choose to validate and store such blocks for additional purposes -- e.g. slashing detection, archive nodes, etc).
- _[IGNORE]_ The block is the first block with valid signature received for the proposer for the slot, `signed_beacon_block.message.slot`.
- _[REJECT]_ The proposer signature, `signed_beacon_block.signature`, is valid with respect to the `proposer_index` pubkey.
@ -355,17 +355,20 @@ to subscribing nodes (typically validators) to be included in future blocks.
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` and `aggregate = aggregate_and_proof.aggregate`)
- _[REJECT]_ The committee index is within the expected range -- i.e. `aggregate.data.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`
(a client MAY queue future aggregates for processing at the appropriate slot).
- _[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))`.
- _[REJECT]_ The aggregate attestation has participants --
that is, `len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 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
with index `aggregate_and_proof.aggregator_index` for the epoch `aggregate.data.target.epoch`.
- _[REJECT]_ The attestation has participants --
that is, `len(get_attesting_indices(state, aggregate.data, aggregate.aggregation_bits)) >= 1`.
- _[REJECT]_ `aggregate_and_proof.selection_proof` selects the validator as an aggregator for the slot --
i.e. `is_aggregator(state, aggregate.data.slot, aggregate.data.index, aggregate_and_proof.selection_proof)` returns `True`.
- _[REJECT]_ The aggregator's validator index is within the committee --
@ -378,6 +381,8 @@ The following validations MUST pass before forwarding the `signed_aggregate_and_
(via both gossip and non-gossip sources)
(a client MAY queue aggregates for processing once block is retrieved).
- _[REJECT]_ The block being voted for (`aggregate.data.beacon_block_root`) passes validation.
- _[REJECT]_ The aggregate attestation's target block is an ancestor of the block named in the LMD vote -- i.e.
`get_checkpoint_block(store, aggregate.data.beacon_block_root, aggregate.data.target.epoch) == aggregate.data.target.root`
- _[IGNORE]_ The current `finalized_checkpoint` is an ancestor of the `block` defined by `aggregate.data.beacon_block_root` -- i.e.
`get_checkpoint_block(store, aggregate.data.beacon_block_root, finalized_checkpoint.epoch)
== store.finalized_checkpoint.root`
@ -425,7 +430,7 @@ The `beacon_attestation_{subnet_id}` topics are used to propagate unaggregated a
to the subnet `subnet_id` (typically beacon and persistent committees) to be aggregated before being gossiped to `beacon_aggregate_and_proof`.
The following validations MUST pass before forwarding the `attestation` on the subnet.
- _[REJECT]_ The committee index is within the expected range -- i.e. `data.index < get_committee_count_per_slot(state, data.target.epoch)`.
- _[REJECT]_ The committee index is within the expected range -- i.e. `attestation.data.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, attestation.data.index) == subnet_id`,
where `committees_per_slot = get_committee_count_per_slot(state, attestation.data.target.epoch)`,
@ -439,7 +444,7 @@ The following validations MUST pass before forwarding the `attestation` on the s
- _[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).
- _[REJECT]_ The number of aggregation bits matches the committee size -- i.e.
`len(attestation.aggregation_bits) == len(get_beacon_committee(state, data.slot, data.index))`.
`len(attestation.aggregation_bits) == len(get_beacon_committee(state, attestation.data.slot, attestation.data.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.
@ -691,9 +696,9 @@ The fields are, as seen by the client at the time of sending the message:
- `current_fork_version` is the fork version at the node's current epoch defined by the wall-clock time
(not necessarily the epoch to which the node is sync)
- `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root`
- `finalized_root`: `state.finalized_checkpoint.root` for the state corresponding to the head block
- `finalized_root`: `store.finalized_checkpoint.root` according to [fork choice](./fork-choice.md).
(Note this defaults to `Root(b'\x00' * 32)` for the genesis finalized checkpoint).
- `finalized_epoch`: `state.finalized_checkpoint.epoch` for the state corresponding to the head block.
- `finalized_epoch`: `store.finalized_checkpoint.epoch` according to [fork choice](./fork-choice.md).
- `head_root`: The `hash_tree_root` root of the current head block (`BeaconBlock`).
- `head_slot`: The slot of the block corresponding to the `head_root`.

View File

@ -1 +1 @@
1.4.0-beta.5
1.4.0-beta.6

View File

@ -10,17 +10,17 @@ from eth2spec.test.context import (
@spec_state_test
def test_current_sync_committee_merkle_proof(spec, state):
yield "object", state
current_sync_committee_branch = spec.compute_merkle_proof(state, spec.CURRENT_SYNC_COMMITTEE_INDEX)
current_sync_committee_branch = spec.compute_merkle_proof(state, spec.CURRENT_SYNC_COMMITTEE_GINDEX)
yield "proof", {
"leaf": "0x" + state.current_sync_committee.hash_tree_root().hex(),
"leaf_index": spec.CURRENT_SYNC_COMMITTEE_INDEX,
"leaf_index": spec.CURRENT_SYNC_COMMITTEE_GINDEX,
"branch": ['0x' + root.hex() for root in current_sync_committee_branch]
}
assert spec.is_valid_merkle_branch(
leaf=state.current_sync_committee.hash_tree_root(),
branch=current_sync_committee_branch,
depth=spec.floorlog2(spec.CURRENT_SYNC_COMMITTEE_INDEX),
index=spec.get_subtree_index(spec.CURRENT_SYNC_COMMITTEE_INDEX),
depth=spec.floorlog2(spec.CURRENT_SYNC_COMMITTEE_GINDEX),
index=spec.get_subtree_index(spec.CURRENT_SYNC_COMMITTEE_GINDEX),
root=state.hash_tree_root(),
)
@ -30,17 +30,17 @@ def test_current_sync_committee_merkle_proof(spec, state):
@spec_state_test
def test_next_sync_committee_merkle_proof(spec, state):
yield "object", state
next_sync_committee_branch = spec.compute_merkle_proof(state, spec.NEXT_SYNC_COMMITTEE_INDEX)
next_sync_committee_branch = spec.compute_merkle_proof(state, spec.NEXT_SYNC_COMMITTEE_GINDEX)
yield "proof", {
"leaf": "0x" + state.next_sync_committee.hash_tree_root().hex(),
"leaf_index": spec.NEXT_SYNC_COMMITTEE_INDEX,
"leaf_index": spec.NEXT_SYNC_COMMITTEE_GINDEX,
"branch": ['0x' + root.hex() for root in next_sync_committee_branch]
}
assert spec.is_valid_merkle_branch(
leaf=state.next_sync_committee.hash_tree_root(),
branch=next_sync_committee_branch,
depth=spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX),
index=spec.get_subtree_index(spec.NEXT_SYNC_COMMITTEE_INDEX),
depth=spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_GINDEX),
index=spec.get_subtree_index(spec.NEXT_SYNC_COMMITTEE_GINDEX),
root=state.hash_tree_root(),
)
@ -50,17 +50,17 @@ def test_next_sync_committee_merkle_proof(spec, state):
@spec_state_test
def test_finality_root_merkle_proof(spec, state):
yield "object", state
finality_branch = spec.compute_merkle_proof(state, spec.FINALIZED_ROOT_INDEX)
finality_branch = spec.compute_merkle_proof(state, spec.FINALIZED_ROOT_GINDEX)
yield "proof", {
"leaf": "0x" + state.finalized_checkpoint.root.hex(),
"leaf_index": spec.FINALIZED_ROOT_INDEX,
"leaf_index": spec.FINALIZED_ROOT_GINDEX,
"branch": ['0x' + root.hex() for root in finality_branch]
}
assert spec.is_valid_merkle_branch(
leaf=state.finalized_checkpoint.root,
branch=finality_branch,
depth=spec.floorlog2(spec.FINALIZED_ROOT_INDEX),
index=spec.get_subtree_index(spec.FINALIZED_ROOT_INDEX),
depth=spec.floorlog2(spec.FINALIZED_ROOT_GINDEX),
index=spec.get_subtree_index(spec.FINALIZED_ROOT_GINDEX),
root=state.hash_tree_root(),
)

View File

@ -16,16 +16,15 @@ from eth2spec.test.helpers.attestations import (
state_transition_with_full_block,
)
from eth2spec.test.helpers.constants import (
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
ALTAIR, BELLATRIX, CAPELLA, DENEB,
MINIMAL,
ALL_PHASES,
)
from eth2spec.test.helpers.fork_transition import (
do_fork,
)
from eth2spec.test.helpers.forks import (
get_spec_for_fork_version,
is_post_capella, is_post_deneb,
is_post_fork,
)
from eth2spec.test.helpers.light_client import (
compute_start_slot_at_next_sync_committee_period,
@ -40,19 +39,6 @@ from eth2spec.test.helpers.state import (
)
def get_spec_for_fork_version(spec, fork_version, phases):
if phases is None:
return spec
for fork in [fork for fork in ALL_PHASES if is_post_fork(spec.fork, fork)]:
if fork == PHASE0:
fork_version_field = 'GENESIS_FORK_VERSION'
else:
fork_version_field = fork.upper() + '_FORK_VERSION'
if fork_version == getattr(spec.config, fork_version_field):
return phases[fork]
raise ValueError("Unknown fork version %s" % fork_version)
class LightClientSyncTest(object):
steps: List[Dict[str, Any]]
genesis_validators_root: Any
@ -164,7 +150,7 @@ def emit_update(test, spec, state, block, attested_state, attested_block, finali
if not with_next:
data.next_sync_committee = spec.SyncCommittee()
data.next_sync_committee_branch = \
[spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))]
[spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_GINDEX))]
current_slot = state.slot
upgraded = upgrade_lc_update_to_new_spec(d_spec, test.s_spec, data)
@ -535,7 +521,7 @@ def run_test_single_fork(spec, phases, state, fork):
finalized_state = state.copy()
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
assert test.store.finalized_header.beacon.slot == finalized_state.slot
@ -548,7 +534,7 @@ def run_test_single_fork(spec, phases, state, fork):
transition_to(spec, state, spec.compute_start_slot_at_epoch(fork_epoch) - 4)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
update = yield from emit_update(
test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
@ -564,7 +550,7 @@ def run_test_single_fork(spec, phases, state, fork):
# Final slot before fork, check that importing the pre-fork format still works
attested_block = block.copy()
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
assert test.store.finalized_header.beacon.slot == finalized_state.slot
@ -575,7 +561,7 @@ def run_test_single_fork(spec, phases, state, fork):
# Upgrade to post-fork spec, attested block is still before the fork
attested_block = block.copy()
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(phases[fork], state)
sync_aggregate, _ = get_sync_aggregate(phases[fork], state, phases=phases)
state, block = do_fork(state, spec, phases[fork], fork_epoch, sync_aggregate=sync_aggregate)
spec = phases[fork]
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
@ -587,7 +573,7 @@ def run_test_single_fork(spec, phases, state, fork):
# Another block after the fork, this time attested block is after the fork
attested_block = block.copy()
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
assert test.store.finalized_header.beacon.slot == finalized_state.slot
@ -599,7 +585,7 @@ def run_test_single_fork(spec, phases, state, fork):
transition_to(spec, state, spec.compute_start_slot_at_epoch(fork_epoch + 1) - 2)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
assert test.store.finalized_header.beacon.slot == finalized_state.slot
@ -613,7 +599,7 @@ def run_test_single_fork(spec, phases, state, fork):
_, _, state = next_slots_with_attestations(spec, state, 2 * spec.SLOTS_PER_EPOCH - 1, True, True)
attested_block = state_transition_with_full_block(spec, state, True, True)
attested_state = state.copy()
sync_aggregate, _ = get_sync_aggregate(spec, state)
sync_aggregate, _ = get_sync_aggregate(spec, state, phases=phases)
block = state_transition_with_full_block(spec, state, True, True, sync_aggregate=sync_aggregate)
yield from emit_update(test, spec, state, block, attested_state, attested_block, finalized_block, phases=phases)
assert test.store.finalized_header.beacon.slot == finalized_state.slot

View File

@ -418,3 +418,37 @@ def test_transition_with_no_attestations_until_after_fork(state, fork_epoch, spe
yield "blocks", blocks
yield "post", state
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2) for pre, post in ALL_PRE_POST_FORKS])
def test_non_empty_historical_roots(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
"""
Test with non-empty pre-state `state.historical_roots`.
Since Capella froze `historical_roots`, Capella spec doesn't invoke `process_historical_roots_update` anymore.
Therefore, we need to fill in `historical_roots` with non-empty value.
"""
# fill in historical_roots with non-empty values
pre_historical_roots = [b'\x56' * 32]
state.historical_roots = pre_historical_roots
transition_until_fork(spec, state, fork_epoch)
# check pre state
assert spec.get_current_epoch(state) < fork_epoch
assert len(state.historical_roots) > 0
yield "pre", state
# irregular state transition to handle fork:
blocks = []
state, block = do_fork(state, spec, post_spec, fork_epoch)
blocks.append(post_tag(block))
# continue regular state transition with new spec into next epoch
transition_to_next_epoch_and_append_blocks(post_spec, state, post_tag, blocks, only_last_block=True)
yield "blocks", blocks
yield "post", state
assert len(state.historical_roots) > 0
assert state.historical_roots == pre_historical_roots

View File

@ -15,16 +15,16 @@ def test_execution_merkle_proof(spec, state):
block = state_transition_with_full_block(spec, state, True, False)
yield "object", block.message.body
execution_branch = spec.compute_merkle_proof(block.message.body, spec.EXECUTION_PAYLOAD_INDEX)
execution_branch = spec.compute_merkle_proof(block.message.body, spec.EXECUTION_PAYLOAD_GINDEX)
yield "proof", {
"leaf": "0x" + block.message.body.execution_payload.hash_tree_root().hex(),
"leaf_index": spec.EXECUTION_PAYLOAD_INDEX,
"leaf_index": spec.EXECUTION_PAYLOAD_GINDEX,
"branch": ['0x' + root.hex() for root in execution_branch]
}
assert spec.is_valid_merkle_branch(
leaf=block.message.body.execution_payload.hash_tree_root(),
branch=execution_branch,
depth=spec.floorlog2(spec.EXECUTION_PAYLOAD_INDEX),
index=spec.get_subtree_index(spec.EXECUTION_PAYLOAD_INDEX),
depth=spec.floorlog2(spec.EXECUTION_PAYLOAD_GINDEX),
index=spec.get_subtree_index(spec.EXECUTION_PAYLOAD_GINDEX),
root=block.message.body.hash_tree_root(),
)

View File

@ -280,7 +280,7 @@ def state_transition_with_full_block(spec,
)
for attestation in attestations:
block.body.attestations.append(attestation)
if fill_prev_epoch:
if fill_prev_epoch and state.slot >= spec.SLOTS_PER_EPOCH:
slot_to_attest = state.slot - spec.SLOTS_PER_EPOCH + 1
attestations = get_valid_attestation_at_slot(
state,

View File

@ -3,7 +3,10 @@ from enum import Enum, auto
from eth2spec.test.helpers.attester_slashings import (
get_valid_attester_slashing_by_indices,
)
from eth2spec.test.helpers.attestations import next_slots_with_attestations
from eth2spec.test.helpers.attestations import (
next_slots_with_attestations,
state_transition_with_full_block,
)
from eth2spec.test.helpers.block import (
build_empty_block_for_next_slot,
build_empty_block,
@ -21,6 +24,9 @@ from eth2spec.test.helpers.deposits import (
from eth2spec.test.helpers.proposer_slashings import (
get_valid_proposer_slashing,
)
from eth2spec.test.helpers.forks import (
get_next_fork_transition,
)
from eth2spec.test.helpers.state import (
next_slot,
state_transition_and_sign_block,
@ -196,6 +202,34 @@ def _transition_until_fork_minus_one(spec, state, fork_epoch):
transition_to(spec, state, to_slot)
def transition_across_forks(spec, state, to_slot, phases=None, with_block=False, sync_aggregate=None):
assert to_slot > state.slot
state = state.copy()
block = None
to_epoch = spec.compute_epoch_at_slot(to_slot)
while state.slot < to_slot:
assert block is None
epoch = spec.compute_epoch_at_slot(state.slot)
post_spec, fork_epoch = get_next_fork_transition(spec, epoch, phases)
if fork_epoch is None or to_epoch < fork_epoch:
if with_block and (to_slot == state.slot + 1):
transition_to(spec, state, to_slot - 1)
block = state_transition_with_full_block(
spec, state, True, True,
sync_aggregate=sync_aggregate)
else:
transition_to(spec, state, to_slot)
else:
transition_until_fork(spec, state, fork_epoch)
state, block = do_fork(
state, spec, post_spec, fork_epoch,
with_block=with_block and (to_slot == state.slot + 1),
sync_aggregate=sync_aggregate,
)
spec = post_spec
return spec, state, block
def transition_to_next_epoch_and_append_blocks(spec,
state,
post_tag,

View File

@ -1,5 +1,5 @@
from .constants import (
ALTAIR, BELLATRIX, CAPELLA, DENEB,
PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB,
EIP6110, EIP7002, WHISK,
PREVIOUS_FORK_OF,
)
@ -47,3 +47,27 @@ def is_post_eip7002(spec):
def is_post_whisk(spec):
return is_post_fork(spec.fork, WHISK)
def get_spec_for_fork_version(spec, fork_version, phases):
if phases is None:
return spec
for fork in [fork for fork in phases if is_post_fork(spec.fork, fork)]:
if fork == PHASE0:
fork_version_field = 'GENESIS_FORK_VERSION'
else:
fork_version_field = fork.upper() + '_FORK_VERSION'
if fork_version == getattr(spec.config, fork_version_field):
return phases[fork]
raise ValueError("Unknown fork version %s" % fork_version)
def get_next_fork_transition(spec, epoch, phases):
if phases is None:
return None, None
for fork in [fork for fork in phases if PREVIOUS_FORK_OF[fork] == spec.fork]:
assert fork != PHASE0 # PHASE0 does not have previous fork
fork_epoch = getattr(phases[fork].config, fork.upper() + '_FORK_EPOCH')
assert fork_epoch > epoch # Forks through given epoch already applied
return phases[fork], fork_epoch
return None, None # Already at latest fork

View File

@ -1,5 +1,5 @@
from eth2spec.test.helpers.state import (
transition_to,
from eth2spec.test.helpers.fork_transition import (
transition_across_forks,
)
from eth2spec.test.helpers.forks import (
is_post_capella, is_post_deneb,
@ -20,14 +20,14 @@ def compute_start_slot_at_next_sync_committee_period(spec, state):
return compute_start_slot_at_sync_committee_period(spec, sync_committee_period + 1)
def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None):
def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None, phases=None):
# By default, the sync committee signs the previous slot
if signature_slot is None:
signature_slot = state.slot + 1
assert signature_slot > state.slot
# Ensure correct sync committee and fork version are selected
signature_state = state.copy()
transition_to(spec, signature_state, signature_slot)
signature_spec, signature_state, _ = transition_across_forks(spec, state, signature_slot, phases)
# Fetch sync committee
committee_indices = compute_committee_indices(signature_state)
@ -41,12 +41,12 @@ def get_sync_aggregate(spec, state, num_participants=None, signature_slot=None):
# Compute sync aggregate
sync_committee_bits = [True] * num_participants + [False] * (committee_size - num_participants)
sync_committee_signature = compute_aggregate_sync_committee_signature(
spec,
signature_spec,
signature_state,
max(signature_slot, 1) - 1,
committee_indices[:num_participants],
)
sync_aggregate = spec.SyncAggregate(
sync_aggregate = signature_spec.SyncAggregate(
sync_committee_bits=sync_committee_bits,
sync_committee_signature=sync_committee_signature,
)
@ -68,11 +68,11 @@ def create_update(spec,
if with_next:
update.next_sync_committee = attested_state.next_sync_committee
update.next_sync_committee_branch = spec.compute_merkle_proof(attested_state, spec.NEXT_SYNC_COMMITTEE_INDEX)
update.next_sync_committee_branch = spec.compute_merkle_proof(attested_state, spec.NEXT_SYNC_COMMITTEE_GINDEX)
if with_finality:
update.finalized_header = spec.block_to_light_client_header(finalized_block)
update.finality_branch = spec.compute_merkle_proof(attested_state, spec.FINALIZED_ROOT_INDEX)
update.finality_branch = spec.compute_merkle_proof(attested_state, spec.FINALIZED_ROOT_GINDEX)
update.sync_aggregate, update.signature_slot = get_sync_aggregate(
spec, attested_state, num_participants)

View File

@ -286,34 +286,43 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif
for _ in range(2):
state, store, _ = yield from apply_next_epoch_with_attestations(
spec, state, store, True, True, test_steps=test_steps)
if is_justifying_previous_epoch:
# build chain with head in epoch 3 and justified checkpoint in epoch 2
block_a = build_empty_block_for_next_slot(spec, state)
signed_block_a = state_transition_and_sign_block(spec, state, block_a)
yield from tick_and_add_block(spec, store, signed_block_a, test_steps)
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 3
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 2
else:
# fill one more epoch
# build chain with head in epoch 4 and justified checkpoint in epoch 3
state, store, _ = yield from apply_next_epoch_with_attestations(
spec, state, store, True, True, test_steps=test_steps)
signed_block_a = state_transition_with_full_block(spec, state, True, True)
yield from tick_and_add_block(spec, store, signed_block_a, test_steps)
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 4
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3
spec.get_head(store) == signed_block_a.message.hash_tree_root()
state = store.block_states[spec.get_head(store)].copy()
if is_justifying_previous_epoch:
assert state.current_justified_checkpoint.epoch == 2
else:
assert state.current_justified_checkpoint.epoch == 3
state_a = state.copy()
if is_justifying_previous_epoch:
# try to find the block that can justify epoch 3
_, justifying_slot = find_next_justifying_slot(spec, state, False, True)
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 3
assert spec.compute_epoch_at_slot(state.slot) == 3
assert state.current_justified_checkpoint.epoch == 2
else:
# try to find the block that can justify epoch 4
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 4
assert spec.compute_epoch_at_slot(state.slot) == 4
assert state.current_justified_checkpoint.epoch == 3
if is_justifying_previous_epoch:
# try to find the block that can justify epoch 3 by including only previous epoch attesations
_, justifying_slot = find_next_justifying_slot(spec, state, False, True)
assert spec.compute_epoch_at_slot(justifying_slot) == 4
else:
# try to find the block that can justify epoch 4 by including current epoch attestations
_, justifying_slot = find_next_justifying_slot(spec, state, True, True)
assert spec.compute_epoch_at_slot(justifying_slot) == 4
last_slot_of_z = justifying_slot if enough_ffg else justifying_slot - 1
last_slot_of_y = justifying_slot if is_justifying_previous_epoch else last_slot_of_z - 1
@ -324,15 +333,14 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif
# build an empty chain to the slot prior epoch boundary
signed_blocks_of_empty_chain = []
states_of_empty_chain = []
for slot in range(state.slot + 1, last_slot_of_y + 1):
block = build_empty_block(spec, state, slot=slot)
signed_block = state_transition_and_sign_block(spec, state, block)
signed_blocks_of_empty_chain.append(signed_block)
states_of_empty_chain.append(state.copy())
signed_blocks_of_y.append(signed_block)
signed_block_y = signed_blocks_of_empty_chain[-1]
assert spec.compute_epoch_at_slot(signed_block_y.message.slot) == 4
# create 2/3 votes for the empty chain
attestations_for_y = []
@ -345,7 +353,6 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif
state = state_a.copy()
signed_block_z = None
for slot in range(state_a.slot + 1, last_slot_of_z + 1):
# apply chain y, the empty chain
if slot <= last_slot_of_y and len(signed_blocks_of_y) > 0:
@ -368,12 +375,21 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif
if is_ready_to_justify(spec, state):
break
assert spec.get_head(store) == signed_block_y.message.hash_tree_root()
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 4
assert spec.compute_epoch_at_slot(signed_block_y.message.slot) == 4
assert spec.compute_epoch_at_slot(signed_block_z.message.slot) == 4
# y is not filtered out & wins the LMD competition, so y should be the head
y_voting_source_epoch = spec.get_voting_source(store, signed_block_y.message.hash_tree_root()).epoch
if is_justifying_previous_epoch:
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 2
assert y_voting_source_epoch == 2
assert y_voting_source_epoch == store.justified_checkpoint.epoch
else:
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3
assert y_voting_source_epoch == 3
assert y_voting_source_epoch == store.justified_checkpoint.epoch
assert spec.get_head(store) == signed_block_y.message.hash_tree_root()
if enough_ffg:
assert is_ready_to_justify(spec, state)
@ -386,17 +402,57 @@ def _run_include_votes_of_another_empty_chain(spec, state, enough_ffg, is_justif
on_tick_and_append_step(spec, store, current_time, test_steps)
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 5
if enough_ffg:
# reorg
assert spec.get_head(store) == signed_block_z.message.hash_tree_root()
if is_justifying_previous_epoch:
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3
else:
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 4
else:
# no reorg
assert spec.get_head(store) == signed_block_y.message.hash_tree_root()
y_voting_source_epoch = spec.get_voting_source(store, signed_block_y.message.hash_tree_root()).epoch
if is_justifying_previous_epoch:
# y is filtered out & so z should be the head
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3
assert y_voting_source_epoch == 2
assert y_voting_source_epoch != store.justified_checkpoint.epoch
assert not (y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store)))
assert spec.get_head(store) == signed_block_z.message.hash_tree_root()
else:
if enough_ffg:
# y is not filtered out & wins the LMD competition, so y should be the head
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 4
assert y_voting_source_epoch == 3
assert y_voting_source_epoch != store.justified_checkpoint.epoch
assert y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store))
assert spec.get_head(store) == signed_block_y.message.hash_tree_root()
else:
# y is not filtered out & wins the LMD competition, so y should be the head
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3
assert y_voting_source_epoch == 3
assert y_voting_source_epoch == store.justified_checkpoint.epoch
assert spec.get_head(store) == signed_block_y.message.hash_tree_root()
# to next epoch
next_epoch(spec, state)
current_time = state.slot * spec.config.SECONDS_PER_SLOT + store.genesis_time
on_tick_and_append_step(spec, store, current_time, test_steps)
assert spec.compute_epoch_at_slot(spec.get_current_slot(store)) == 6
y_voting_source_epoch = spec.get_voting_source(store, signed_block_y.message.hash_tree_root()).epoch
if is_justifying_previous_epoch:
# y is filtered out & so z should be the head
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3
assert y_voting_source_epoch == 2
assert y_voting_source_epoch != store.justified_checkpoint.epoch
assert not (y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store)))
assert spec.get_head(store) == signed_block_z.message.hash_tree_root()
else:
if enough_ffg:
# y is filtered out & so z should be the head
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 4
assert y_voting_source_epoch == 3
assert y_voting_source_epoch != store.justified_checkpoint.epoch
assert not (y_voting_source_epoch + 2 >= spec.compute_epoch_at_slot(spec.get_current_slot(store)))
assert spec.get_head(store) == signed_block_z.message.hash_tree_root()
else:
# y is not filtered out & wins the LMD competition, so y should be the head
assert state.current_justified_checkpoint.epoch == store.justified_checkpoint.epoch == 3
assert y_voting_source_epoch == 3
assert y_voting_source_epoch == store.justified_checkpoint.epoch
assert spec.get_head(store) == signed_block_y.message.hash_tree_root()
yield 'steps', test_steps

View File

@ -9,6 +9,8 @@ Clients should assume forks happen sequentially in the following manner:
0. `phase0`
1. `altair`
2. `bellatrix`
3. `capella`
4. `deneb`
For example, if a test case has `post_fork` of `altair`, the test consumer should assume the test begins in `phase0` and use that specification to process the initial state and any blocks up until the fork epoch. After the fork happens, the test consumer should use the specification according to the `altair` fork to process the remaining data.