Skip to content

Commit

Permalink
Merge pull request #1614 from ethereum/eth2-enr
Browse files Browse the repository at this point in the history
add eth2 key/value ENR to phase 0 p2p
  • Loading branch information
djrtwo authored Mar 10, 2020
2 parents 1707d2b + 1818f34 commit bd5231a
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 6 deletions.
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def get_spec(file_name: str) -> SpecObject:
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.utils.ssz.ssz_typing import (
View, boolean, Container, List, Vector, uint64,
Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
)
from eth2spec.utils import bls
Expand All @@ -117,7 +117,7 @@ def get_spec(file_name: str) -> SpecObject:
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.utils.ssz.ssz_typing import (
View, boolean, Container, List, Vector, uint64, uint8, bit,
ByteList, Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
ByteList, Bytes1, Bytes4, Bytes32, Bytes48, Bytes96, Bitlist, Bitvector,
)
from eth2spec.utils import bls
Expand Down
15 changes: 11 additions & 4 deletions specs/phase0/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ We define the following Python custom types for type hinting and readability:
| `Root` | `Bytes32` | a Merkle root |
| `Version` | `Bytes4` | a fork version number |
| `DomainType` | `Bytes4` | a domain type |
| `Domain` | `Bytes8` | a signature domain |
| `ForkDigest` | `Bytes4` | a digest of the current fork data |
| `Domain` | `Bytes32` | a signature domain |
| `BLSPubkey` | `Bytes48` | a BLS12-381 public key |
| `BLSSignature` | `Bytes96` | a BLS12-381 signature |

Expand Down Expand Up @@ -473,6 +474,7 @@ class BeaconBlock(Container):
class BeaconState(Container):
# Versioning
genesis_time: uint64
genesis_validators_root: Root
slot: Slot
fork: Fork
# History
Expand Down Expand Up @@ -795,13 +797,15 @@ def compute_activation_exit_epoch(epoch: Epoch) -> Epoch:
#### `compute_domain`

```python
def compute_domain(domain_type: DomainType, fork_version: Optional[Version]=None) -> Domain:
def compute_domain(domain_type: DomainType, fork_version: Optional[Version]=None, genesis_root: Root=None) -> Domain:
"""
Return the domain for the ``domain_type`` and ``fork_version``.
"""
if fork_version is None:
fork_version = GENESIS_FORK_VERSION
return Domain(domain_type + fork_version)
if genesis_root is None:
genesis_root = Root() # all bytes zero by default
return Domain(domain_type + fork_version + genesis_root[:24])
```

#### `compute_signing_root`
Expand Down Expand Up @@ -977,7 +981,7 @@ def get_domain(state: BeaconState, domain_type: DomainType, epoch: Epoch=None) -
"""
epoch = get_current_epoch(state) if epoch is None else epoch
fork_version = state.fork.previous_version if epoch < state.fork.epoch else state.fork.current_version
return compute_domain(domain_type, fork_version)
return compute_domain(domain_type, fork_version, state.genesis_validators_root)
```

#### `get_indexed_attestation`
Expand Down Expand Up @@ -1122,6 +1126,9 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Bytes32,
validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH

# Set genesis validators root for domain separation and chain versioning
state.genesis_validators_root = hash_tree_root(state.validators)

return state
```

Expand Down
55 changes: 55 additions & 0 deletions specs/phase0/p2p-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ It consists of four main sections:
- [Attestation subnet bitfield](#attestation-subnet-bitfield)
- [Interop](#interop-5)
- [Mainnet](#mainnet-5)
- [`eth2` field](#eth2-field)
- [General capabilities](#general-capabilities)
- [Topic advertisement](#topic-advertisement)
- [Mainnet](#mainnet-6)
- [Design decision rationale](#design-decision-rationale)
Expand Down Expand Up @@ -88,6 +90,7 @@ It consists of four main sections:
- [Why are we sending entire objects in the pubsub and not just hashes?](#why-are-we-sending-entire-objects-in-the-pubsub-and-not-just-hashes)
- [Should clients gossip blocks if they *cannot* validate the proposer signature due to not yet being synced, not knowing the head block, etc?](#should-clients-gossip-blocks-if-they-cannot-validate-the-proposer-signature-due-to-not-yet-being-synced-not-knowing-the-head-block-etc)
- [How are we going to discover peers in a gossipsub topic?](#how-are-we-going-to-discover-peers-in-a-gossipsub-topic)
- [How should fork version be used in practice?](#how-should-fork-version-be-used-in-practice)
- [Req/Resp](#reqresp)
- [Why segregate requests into dedicated protocol IDs?](#why-segregate-requests-into-dedicated-protocol-ids)
- [Why are messages length-prefixed with a protobuf varint in the SSZ-encoding?](#why-are-messages-length-prefixed-with-a-protobuf-varint-in-the-ssz-encoding)
Expand Down Expand Up @@ -632,6 +635,50 @@ Nonetheless, ENRs MUST carry a generic `eth2` key with nil value, denoting that

#### Mainnet

##### `eth2` field

ENRs MUST carry a generic `eth2` key with an 16-byte value of the node's current fork version, next fork version, and next fork epoch to ensure connections are made with peers on the intended eth2 network.

| Key | Value |
|:-------------|:--------------------|
| `eth2` | SSZ `ENRForkID` |

First we define `current_fork_data` as the following SSZ encoded object

```
(
current_fork_version: Version
genesis_validators_root: 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`

Specifically, the value of the `eth2` key MUST be the following SSZ encoded object (`ENRForkID`)

```
(
current_fork_digest: ForkDigest
next_fork_version: Version
next_fork_epoch: Epoch
)
```

where the fields of `ENRForkID` are defined as

* `current_fork_digest` is `ForkDigest(hash_tree_root(current_fork_data)[:4])`
* `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_epoch` is the epoch at which the next fork is planned and the `current_fork_version` will be updated. If no future fork is planned, set `next_fork_epoch = FAR_FUTURE_EPOCH` to signal this fact

Clients SHOULD connect to peers with `current_fork_digest`, `next_fork_version`, and `next_fork_epoch` that match local values.

Clients MAY connect to peers with the same `current_fork_version` 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`.

##### General capabilities

On mainnet, ENRs MUST include a structure enumerating the capabilities offered by the peer in an efficient manner. The concrete solution is currently undefined. Proposals include using namespaced bloom filters mapping capabilities to specific protocol IDs supported under that capability.

### Topic advertisement
Expand Down Expand Up @@ -851,6 +898,14 @@ In Phase 0, peers for attestation subnets will be found using the `attnets` entr

Although this method will be sufficient for early phases of Eth2, we aim to use the more appropriate discv5 topics for this and other similar tasks in the future. ENRs should ultimately not be used for this purpose. They are best suited to store identity, location, and capability information, rather than more volatile advertisements.

### How should fork version be used in practice?

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).

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.

## Req/Resp

### Why segregate requests into dedicated protocol IDs?
Expand Down
1 change: 1 addition & 0 deletions specs/phase1/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ Note that aside from the new additions, `Validator` and `PendingAttestation` hav
class BeaconState(Container):
# Versioning
genesis_time: uint64
genesis_validators_root: Root
slot: Slot
fork: Fork
# History
Expand Down
3 changes: 3 additions & 0 deletions tests/core/pyspec/eth2spec/test/helpers/genesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
validator.activation_eligibility_epoch = spec.GENESIS_EPOCH
validator.activation_epoch = spec.GENESIS_EPOCH

# Set genesis validators root for domain separation and chain versioning
state.genesis_validators_root = spec.hash_tree_root(state.validators)

return state

0 comments on commit bd5231a

Please sign in to comment.