Merge branch 'dev' into peer-das
This commit is contained in:
commit
696d443f0a
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)',
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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`
|
||||
|
|
|
@ -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`
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
```
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
```
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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`.
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.4.0-beta.5
|
||||
1.4.0-beta.6
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in New Issue