cleanup gossip topic fork digest based on PR feedback

This commit is contained in:
Danny Ryan 2020-03-11 11:51:31 -06:00
parent baee673124
commit 0881e21dc5
No known key found for this signature in database
GPG Key ID: 2765A792E42CE07A
3 changed files with 39 additions and 23 deletions

View File

@ -106,7 +106,7 @@ SSZObject = TypeVar('SSZObject', bound=View)
PHASE1_IMPORTS = '''from eth2spec.phase0 import spec as phase0 PHASE1_IMPORTS = '''from eth2spec.phase0 import spec as phase0
from eth2spec.config.config_util import apply_constants_config from eth2spec.config.config_util import apply_constants_config
from typing import ( from typing import (
Any, Dict, Set, Sequence, NewType, Tuple, TypeVar, Callable, Optional Any, Dict, Set, Sequence, NewType, Tuple, TypeVar, Callable
) )
from dataclasses import ( from dataclasses import (

View File

@ -77,6 +77,7 @@
- [`compute_start_slot_at_epoch`](#compute_start_slot_at_epoch) - [`compute_start_slot_at_epoch`](#compute_start_slot_at_epoch)
- [`compute_activation_exit_epoch`](#compute_activation_exit_epoch) - [`compute_activation_exit_epoch`](#compute_activation_exit_epoch)
- [`compute_fork_data_root`](#compute_fork_data_root) - [`compute_fork_data_root`](#compute_fork_data_root)
- [`compute_fork_digest`](#compute_fork_digest)
- [`compute_domain`](#compute_domain) - [`compute_domain`](#compute_domain)
- [`compute_signing_root`](#compute_signing_root) - [`compute_signing_root`](#compute_signing_root)
- [Beacon state accessors](#beacon-state-accessors) - [Beacon state accessors](#beacon-state-accessors)
@ -293,7 +294,7 @@ class Fork(Container):
```python ```python
class ForkData(Container): class ForkData(Container):
current_version: Version current_version: Version
genesis_root: Root genesis_validators_root: Root
``` ```
#### `Checkpoint` #### `Checkpoint`
@ -809,7 +810,8 @@ def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
```python ```python
def compute_fork_data_root(current_version: Version, genesis_validators_root: Root) -> Root: def compute_fork_data_root(current_version: Version, genesis_validators_root: Root) -> Root:
""" """
Return the fork digest for the ``current_fork_version`` and ``genesis_validators_root`` Return the 32-byte fork data root for the ``current_version`` and ``genesis_validators_root``.
This is used primarily in signature domains to avoid collisions across forks/chains.
""" """
return hash_tree_root(ForkData( return hash_tree_root(ForkData(
current_version=current_version, current_version=current_version,
@ -817,18 +819,30 @@ def compute_fork_data_root(current_version: Version, genesis_validators_root: Ro
)) ))
``` ```
#### `compute_fork_digest`
```python
def compute_fork_digest(current_version: Version, genesis_validators_root: Root) -> Root:
"""
Return the 4-byte fork digest for the ``current_version`` and ``genesis_validators_root``.
This is a digest primarily used for domain separation on the p2p layer.
4-bytes suffices for practical separation of forks/chains.
"""
return ForkDigest(compute_fork_data_root(current_version, genesis_validators_root)[:4])
```
#### `compute_domain` #### `compute_domain`
```python ```python
def compute_domain(domain_type: DomainType, fork_version: Optional[Version]=None, genesis_root: Root=None) -> Domain: def compute_domain(domain_type: DomainType, fork_version: Version=None, genesis_validators_root: Root=None) -> Domain:
""" """
Return the domain for the ``domain_type`` and ``fork_version``. Return the domain for the ``domain_type`` and ``fork_version``.
""" """
if fork_version is None: if fork_version is None:
fork_version = GENESIS_FORK_VERSION fork_version = GENESIS_FORK_VERSION
if genesis_root is None: if genesis_validators_root is None:
genesis_root = Root() # all bytes zero by default genesis_validators_root = Root() # all bytes zero by default
fork_data_root = compute_fork_data_root(fork_version, genesis_root) fork_data_root = compute_fork_data_root(fork_version, genesis_validators_root)
return Domain(domain_type + fork_data_root[:28]) return Domain(domain_type + fork_data_root[:28])
``` ```

View File

@ -219,9 +219,9 @@ The following gossipsub [parameters](https://github.com/libp2p/specs/tree/master
### Topics and messages ### Topics and messages
Topics are plain UTF-8 strings and are encoded on the wire as determined by protobuf (gossipsub messages are enveloped in protobuf messages). Topic strings have form: `/eth2/ForkDigest/Name/Encoding`. This defines both the type of data being sent on the topic and how the data field of the message is encoded. Topics are plain UTF-8 strings and are encoded on the wire as determined by protobuf (gossipsub messages are enveloped in protobuf messages). Topic strings have form: `/eth2/ForkDigestValue/Name/Encoding`. This defines both the type of data being sent on the topic and how the data field of the message is encoded.
- `ForkDigest` - the lowercase hex-encoded (no "0x" prefix) bytes of `ForkDigest(compute_fork_data_root(current_fork_version, genesis_validators_root)[:4])` where - `ForkDigestValue` - the lowercase hex-encoded (no "0x" prefix) bytes of `compute_fork_digest(current_fork_version, genesis_validators_root)` where
- `current_fork_version` is the fork version of the epoch of the message to be sent on the topic - `current_fork_version` is the fork version of the epoch of the message to be sent on the topic
- `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root` - `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root`
- `Name` - see table below - `Name` - see table below
@ -423,7 +423,7 @@ Here, `result` represents the 1-byte response code.
The token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction. Two values are possible at this time: The token of the negotiated protocol ID specifies the type of encoding to be used for the req/resp interaction. Two values are possible at this time:
- `ssz`: the contents are [SSZ-encoded](../../ssz/simple-serialize.md). This encoding type MUST be supported by all clients. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Bytes32`'s. - `ssz`: the contents are [SSZ-encoded](../../ssz/simple-serialize.md). This encoding type MUST be supported by all clients. For objects containing a single field, only the field is SSZ-encoded not a container with a single field. For example, the `BeaconBlocksByRoot` request is an SSZ-encoded list of `Root`'s.
- `ssz_snappy`: The contents are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy). MAY be supported in the interoperability testnet; MUST be supported in mainnet. - `ssz_snappy`: The contents are SSZ-encoded and then compressed with [Snappy](https://github.com/google/snappy). MAY be supported in the interoperability testnet; MUST be supported in mainnet.
#### SSZ-encoding strategy (with or without Snappy) #### SSZ-encoding strategy (with or without Snappy)
@ -475,16 +475,18 @@ constituents individually as `response_chunk`s. For example, the
Request, Response Content: Request, Response Content:
``` ```
( (
head_fork_version: Bytes4 fork_digest: ForkDigest
finalized_root: Bytes32 finalized_root: Root
finalized_epoch: uint64 finalized_epoch: Epoch
head_root: Bytes32 head_root: Root
head_slot: uint64 head_slot: Slot
) )
``` ```
The fields are, as seen by the client at the time of sending the message: The fields are, as seen by the client at the time of sending the message:
- `head_fork_version`: The beacon_state `Fork` version. - `fork_digest`: The node's `ForkDigest` (`compute_fork_digest(current_fork_version, genesis_validators_root)`) where
- `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`: `state.finalized_checkpoint.root` for the state corresponding to the head block.
- `finalized_epoch`: `state.finalized_checkpoint.epoch` for the state corresponding to the head block. - `finalized_epoch`: `state.finalized_checkpoint.epoch` for the state corresponding to the head block.
- `head_root`: The hash_tree_root root of the current head block. - `head_root`: The hash_tree_root root of the current head block.
@ -498,7 +500,7 @@ The response MUST consist of a single `response_chunk`.
Clients SHOULD immediately disconnect from one another following the handshake above under the following conditions: Clients SHOULD immediately disconnect from one another following the handshake above under the following conditions:
1. If `head_fork_version` does not match the expected fork version at the epoch of the `head_slot`, since the clients chain is on another fork. `head_fork_version` can also be used to segregate testnets. 1. If `fork_digest` does not match the node's local `fork_digest`, since the clients chain is on another fork.
2. If the (`finalized_root`, `finalized_epoch`) shared by the peer is not in the client's chain at the expected epoch. For example, if Peer 1 sends (root, epoch) of (A, 5) and Peer 2 sends (B, 3) but Peer 1 has root C at epoch 3, then Peer 1 would disconnect because it knows that their chains are irreparably disjoint. 2. If the (`finalized_root`, `finalized_epoch`) shared by the peer is not in the client's chain at the expected epoch. For example, if Peer 1 sends (root, epoch) of (A, 5) and Peer 2 sends (B, 3) but Peer 1 has root C at epoch 3, then Peer 1 would disconnect because it knows that their chains are irreparably disjoint.
Once the handshake completes, the client with the lower `finalized_epoch` or `head_slot` (if the clients have equal `finalized_epoch`s) SHOULD request beacon blocks from its counterparty via the `BeaconBlocksByRange` request. Once the handshake completes, the client with the lower `finalized_epoch` or `head_slot` (if the clients have equal `finalized_epoch`s) SHOULD request beacon blocks from its counterparty via the `BeaconBlocksByRange` request.
@ -536,7 +538,7 @@ The response MUST consist of a single `response_chunk`.
Request Content: Request Content:
``` ```
( (
start_slot: uint64 start_slot: Slot
count: uint64 count: uint64
step: uint64 step: uint64
) )
@ -575,7 +577,7 @@ Request Content:
``` ```
( (
[]Bytes32 []Root
) )
``` ```
@ -662,7 +664,7 @@ Specifically, the value of the `eth2` key MUST be the following SSZ encoded obje
where the fields of `ENRForkID` are defined as where the fields of `ENRForkID` are defined as
* `fork_digest` is `ForkDigest(compute_fork_data_root(current_fork_version, genesis_validators_root)[:4])` where * `fork_digest` is `compute_fork_digest(current_fork_version, genesis_validators_root)` where
* `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) * `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` * `genesis_validators_root` is the static `Root` found in `state.genesis_validators_root`
* `next_fork_version` is the fork version corresponding to the next planned hard fork at a future epoch. If no future fork is planned, set `next_fork_version = current_fork_version` to signal this fact * `next_fork_version` is the fork version corresponding to the next planned hard fork at a future epoch. If no future fork is planned, set `next_fork_version = current_fork_version` to signal this fact
@ -670,7 +672,7 @@ where the fields of `ENRForkID` are defined as
Clients SHOULD connect to peers with `fork_digest`, `next_fork_version`, and `next_fork_epoch` that match local values. Clients SHOULD connect to peers with `fork_digest`, `next_fork_version`, and `next_fork_epoch` that match local values.
Clients MAY connect to peers with the same `fork_digest` but a different `next_fork_version`/`next_fork_epoch`. Unless `ENRForkID` is manually updated to matching prior to the earlier `next_fork_epoch` of the two clients, these type of connecting clients will be unable to successfully interact starting at the earlier `next_fork_epoch`. Clients MAY connect to peers with the same `fork_digest` but a different `next_fork_version`/`next_fork_epoch`. Unless `ENRForkID` is manually updated to matching prior to the earlier `next_fork_epoch` of the two clients, these connecting clients will be unable to successfully interact starting at the earlier `next_fork_epoch`.
##### General capabilities ##### General capabilities
@ -897,9 +899,9 @@ Although this method will be sufficient for early phases of Eth2, we aim to use
Fork versions are to be manually updated (likely via incrementing) at each hard fork. This is to provide native domain separation for signatures as well as to aid in usefulness for identitying peers (via ENRs) and versioning network protocols (e.g. using fork version to naturally version gossipsub topics). Fork versions are to be manually updated (likely via incrementing) at each hard fork. This is to provide native domain separation for signatures as well as to aid in usefulness for identitying peers (via ENRs) and versioning network protocols (e.g. using fork version to naturally version gossipsub topics).
`BeaconState.genesis_validators_root` is mixed into signature and ENR fork domains to aid in the ease of domain separation between chains. This allows fork versions to safely be reused across chains except for the case of contentious forks using the same genesis. In these cases, extra care should be taken to isolate fork versions (e.g. flip a high order bit in all future versions of one of the chains). `BeaconState.genesis_validators_root` is mixed into signature and ENR fork domains (`ForkDigest`) to aid in the ease of domain separation between chains. This allows fork versions to safely be reused across chains except for the case of contentious forks using the same genesis. In these cases, extra care should be taken to isolate fork versions (e.g. flip a high order bit in all future versions of one of the chains).
A node locally stores all previous and future planned fork versions along with the each fork epoch. This allows for handling sync starting from past forks/epochs and for connections to safely be made with peers syncing from past forks/epochs. A node locally stores all previous and future planned fork versions along with the each fork epoch. This allows for handling sync and processing messages starting from past forks/epochs.
## Req/Resp ## Req/Resp