From e4a1ef16e6a42424d7f617d342183c2d29ba9b56 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Tue, 12 Mar 2019 13:46:58 -0700 Subject: [PATCH 01/11] Add networking specs --- specs/networking/messaging.md | 41 ++++ specs/networking/node-identification.md | 32 +++ specs/networking/rpc-interface.md | 246 ++++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 specs/networking/messaging.md create mode 100644 specs/networking/node-identification.md create mode 100644 specs/networking/rpc-interface.md diff --git a/specs/networking/messaging.md b/specs/networking/messaging.md new file mode 100644 index 0000000000..e88116f46d --- /dev/null +++ b/specs/networking/messaging.md @@ -0,0 +1,41 @@ +ETH 2.0 Networking Spec - Messaging +=== + +# Abstract + +This specification describes how individual Ethereum 2.0 messages are represented on the wire. + +The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL”, NOT", “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. + +# Motivation + +This specification seeks to define a messaging protocol that is flexible enough to be changed easily as the ETH 2.0 specification evolves. + +# Specification + +## Message Structure + +An ETH 2.0 message consists of a single byte representing the message version followed by the encoded, potentially compressed body. We separate the message's version from the version included in the `libp2p` protocol path in order to allow encoding and compression schemes to be updated independently of the `libp2p` protocols themselves. + +It is unlikely that more than 255 message versions will need to be supported, so a single byte should suffice. + +Visually, a message looks like this: + +``` ++--------------------------+ +| version byte | ++--------------------------+ +| | +| body | +| | ++--------------------------+ +``` + +Clients MUST ignore messages with mal-formed bodies. The `version` byte MUST be one of the below values: + +## Version Byte Values + +### `0x01` + +- **Encoding Scheme:** SSZ +- **Compression Scheme:** Snappy diff --git a/specs/networking/node-identification.md b/specs/networking/node-identification.md new file mode 100644 index 0000000000..27c1ebf9d8 --- /dev/null +++ b/specs/networking/node-identification.md @@ -0,0 +1,32 @@ +ETH 2.0 Networking Spec - Node Identification +=== + +# Abstract + +This specification describes how Ethereum 2.0 nodes identify and address each other on the network. + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL", NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. + +# Specification + +Clients use Ethereum Node Records (as described in [EIP-778](http://eips.ethereum.org/EIPS/eip-778)) to discover one another. Each ENR includes, among other things, the following keys: + +- The node's IP. +- The node's TCP port. +- The node's public key. + +For clients to be addressable, their ENR responses MUST contain all of the above keys. Client MUST verify the signature of any received ENRs, and disconnect from peers whose ENR signatures are invalid. Each node's public key MUST be unique. + +The keys above are enough to construct a [multiaddr](https://github.com/multiformats/multiaddr) for use with the rest of the `libp2p` stack. + +It is RECOMMENDED that clients set their TCP port to the default of `9000`. + +## Peer ID Generation + +The `libp2p` networking stack identifies peers via a "peer ID." Simply put, a node's Peer ID is the SHA2-256 `multihash` of the node's public key. `go-libp2p-crypto` contains the canonical implementation of how to hash `secp256k1` keys for use as a peer ID. + +# See Also + +- [multiaddr](https://github.com/multiformats/multiaddr) +- [multihash](https://multiformats.io/multihash/) +- [go-libp2p-crypto](https://github.com/libp2p/go-libp2p-crypto) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md new file mode 100644 index 0000000000..fdc9a11b37 --- /dev/null +++ b/specs/networking/rpc-interface.md @@ -0,0 +1,246 @@ +ETH 2.0 Networking Spec - RPC Interface +=== + +# Abstract + +The Ethereum 2.0 networking stack uses two modes of communication: a broadcast protocol that gossips information to interested parties via GossipSub, and an RPC protocol that retrieves information from specific clients. This specification defines the RPC protocol. + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL", NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. + +# Dependencies + +This specification assumes familiarity with the [Messaging](./messaging.md), [Node Identification](./node-identification), and [Beacon Chain](../core/0_beacon-chain.md) specifications. + +# Specification + +## Message Schemas + +Message body schemas are notated like this: + +``` +( + field_name_1: type + field_name_2: type +) +``` + +SSZ serialization is field-order dependent. Therefore, fields MUST be encoded and decoded according to the order described in this document. The encoded values of each field are concatenated to form the final encoded message body. Embedded structs are serialized as Containers unless otherwise noted. + +All referenced data structures can be found in the [0-beacon-chain](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#data-structures) specification. + +## `libp2p` Protocol Names + +A "Protocol Name" in `libp2p` parlance refers to a human-readable identifier `libp2p` uses in order to identify sub-protocols and stream messages of different types over the same connection. A client's supported protocol paths are negotiated by the `libp2p` stack at connection time; as such they are not part of individual message bodies. + +## RPC-Over-`libp2p` + +To facilitate RPC-over-`libp2p`, a single protocol path is used: `/eth/serenity/rpc/1.0.0`. Remote method calls are wrapped in a "request" structure: + +``` +( + id: uint64 + method_id: uint16 + body: Request +) +``` + +and their corresponding responses are wrapped in a "response" structure: + +``` +( + id: uint64 + result: Response +) +``` + +If an error occurs, a variant of the response structure is returned: + +``` +( + id: uint64 + error: ( + code: uint16 + data: bytes + ) +) +``` + +The details of the RPC-Over-`libp2p` protocol are similar to [JSON-RPC 2.0](https://www.jsonrpc.org/specification). Specifically: + +1. The `id` member is REQUIRED. +2. The `id` member in the response MUST be the same as the value of the `id` in the request. +3. The `method_id` member is REQUIRED. +4. The `result` member is required on success, and MUST NOT exist if there was an error. +5. The `error` member is REQUIRED on errors, and MUST NOT exist if there wasn't an error. + +Structuring RPC requests in this manner allows multiple calls and responses to be multiplexed over the same stream without switching. + +The "method ID" fields in the below messages refer to the `method` field in the request structure above. + +The first 1,000 values in `error.code` are reserved for system use. The following error codes are predefined: + +1. `0`: Parse error. +2. `10`: Invalid request. +3. `20`: Method not found. +4. `30`: Server error. + +## Messages + +### Hello + +**Method ID:** `0` + +**Body**: + +``` +( + network_id: uint8 + latest_finalized_root: bytes32 + latest_finalized_epoch: uint64 + best_root: bytes32 + best_slot: uint64 +) +``` + +Clients exchange `hello` messages upon connection, forming a two-phase handshake. The first message the initiating client sends MUST be the `hello` message. In response, the receiving client MUST respond with its own `hello` message. + +Clients SHOULD immediately disconnect from one another following the handshake above under the following conditions: + +1. If `network_id` belongs to a different chain, since the client definitionally cannot sync with this client. +2. If the `latest_finalized_root` shared by the peer is not in the client's chain at the expected epoch. For example, if Peer 1 in the diagram below has `(root, epoch)` of `(A, 5)` and Peer 2 has `(B, 3)`, Peer 1 would disconnect because it knows that `B` is not the root in their chain at epoch 3: + +``` + Root A + + +---+ + |xxx| +----+ Epoch 5 + +-+-+ + ^ + | + +-+-+ + | | +----+ Epoch 4 + +-+-+ +Root B ^ + | ++---+ +-+-+ +|xxx+<---+--->+ | +----+ Epoch 3 ++---+ | +---+ + | + +-+-+ + | | +-----------+ Epoch 2 + +-+-+ + ^ + | + +-+-+ + | | +-----------+ Epoch 1 + +---+ +``` + +Once the handshake completes, the client with the higher `latest_finalized_epoch` or `best_slot` (if the clients have equal `latest_finalized_epoch`s) SHOULD send beacon block roots to its counterparty via `beacon_block_roots` (i.e., RPC method `10`). + +### Goodbye + +**Method ID:** `1` + +**Body:** + +``` +( + reason: uint64 +) +``` + +Client MAY send `goodbye` messages upon disconnection. The reason field MUST be one of the following values: + +- `1`: Client shut down. +- `2`: Irrelevant network. +- `3`: Irrelevant shard. + +### Provide Beacon Block Roots + +**Method ID:** `10` + +**Body:** + +``` +# BlockRootSlot +( + block_root: HashTreeRoot + slot: uint64 +) + +( + roots: []BlockRootSlot +) +``` + +Send a list of block roots and slots to the peer. + +### Beacon Block Headers + +**Method ID:** `11` + +**Request Body** + +``` +( + start_root: HashTreeRoot + start_slot: uint64 + max_headers: uint64 + skip_slots: uint64 +) +``` + +**Response Body:** + +``` +( + headers: []BlockHeader +) +``` + +Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `2` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is undefined for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were undefined in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further undefined, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. + +The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Client could, for instance, request every 10th block from a set of peers where each per has a different starting block in order to populate block data. + +### Beacon Block Bodies + +**Method ID:** `12` + +**Request Body:** + +``` +( + block_roots: []HashTreeRoot +) +``` + +**Response Body:** + +``` +( + block_bodies: []BeaconBlockBody +) +``` + +Requests the `block_bodies` associated with the provided `block_roots` from the peer. Responses MUST return `block_roots` in the order provided in the request. If the receiver does not have a particular `block_root`, it must return a zero-value `block_body` (i.e., a `block_body` container with all zero fields). + +### Beacon Chain State + +**Note:** This section is preliminary, pending the definition of the data structures to be transferred over the wire during fast sync operations. + +**Method ID:** `13` + +**Request Body:** + +``` +( + hashes: []HashTreeRoot +) +``` + +**Response Body:** TBD + +Requests contain the hashes of Merkle tree nodes that when merkelized yield the block's `state_root`. + +The response will contain the values that, when hashed, yield the hashes inside the request body. From 29caafc7567096325c14e7961550c4ba6f7c046b Mon Sep 17 00:00:00 2001 From: jannikluhn Date: Wed, 13 Mar 2019 21:52:25 -0700 Subject: [PATCH 02/11] Update specs/networking/rpc-interface.md Co-Authored-By: mslipper --- specs/networking/rpc-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index fdc9a11b37..e59f6a6b1a 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -199,7 +199,7 @@ Send a list of block roots and slots to the peer. ) ``` -Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `2` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is undefined for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were undefined in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further undefined, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. +Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `2` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is empty for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were empty in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further empty, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Client could, for instance, request every 10th block from a set of peers where each per has a different starting block in order to populate block data. From f3bddee7a5dcc8df1dfe0deeea9c875df0911415 Mon Sep 17 00:00:00 2001 From: jannikluhn Date: Wed, 13 Mar 2019 21:55:48 -0700 Subject: [PATCH 03/11] Update specs/networking/rpc-interface.md Co-Authored-By: mslipper --- specs/networking/rpc-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index e59f6a6b1a..e087abe96a 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -165,7 +165,7 @@ Client MAY send `goodbye` messages upon disconnection. The reason field MUST be ``` # BlockRootSlot ( - block_root: HashTreeRoot + block_root: bytes32 slot: uint64 ) From 5a9ef0fd982f7c23c55afcfd43e07a022a2878b9 Mon Sep 17 00:00:00 2001 From: jannikluhn Date: Wed, 13 Mar 2019 21:55:59 -0700 Subject: [PATCH 04/11] Update specs/networking/rpc-interface.md Co-Authored-By: mslipper --- specs/networking/rpc-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index e087abe96a..e69f608015 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -201,7 +201,7 @@ Send a list of block roots and slots to the peer. Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `2` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is empty for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were empty in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further empty, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. -The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Client could, for instance, request every 10th block from a set of peers where each per has a different starting block in order to populate block data. +The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Clients could, for instance, request every 10th block from a set of peers where each per has a different starting block in order to populate block data. ### Beacon Block Bodies From 22e6212e6f08581aeca48dd6efee5e3c81c78f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Wed, 13 Mar 2019 21:56:47 -0700 Subject: [PATCH 05/11] Update specs/networking/node-identification.md Co-Authored-By: mslipper --- specs/networking/node-identification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/node-identification.md b/specs/networking/node-identification.md index 27c1ebf9d8..0f1f9832b7 100644 --- a/specs/networking/node-identification.md +++ b/specs/networking/node-identification.md @@ -23,7 +23,7 @@ It is RECOMMENDED that clients set their TCP port to the default of `9000`. ## Peer ID Generation -The `libp2p` networking stack identifies peers via a "peer ID." Simply put, a node's Peer ID is the SHA2-256 `multihash` of the node's public key. `go-libp2p-crypto` contains the canonical implementation of how to hash `secp256k1` keys for use as a peer ID. +The `libp2p` networking stack identifies peers via a "peer ID." Simply put, a node's Peer ID is the SHA2-256 `multihash` of the node's public key struct (serialized in protobuf, refer to the [Peer ID spec](https://github.com/libp2p/specs/pull/100)). `go-libp2p-crypto` contains the canonical implementation of how to hash `secp256k1` keys for use as a peer ID. # See Also From 863f85c45ab2e3327c8c2e5f620af040b239fb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Kripalani?= Date: Wed, 13 Mar 2019 21:57:29 -0700 Subject: [PATCH 06/11] Update specs/networking/rpc-interface.md Co-Authored-By: mslipper --- specs/networking/rpc-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index e69f608015..d07e728c92 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -30,7 +30,7 @@ All referenced data structures can be found in the [0-beacon-chain](https://gith ## `libp2p` Protocol Names -A "Protocol Name" in `libp2p` parlance refers to a human-readable identifier `libp2p` uses in order to identify sub-protocols and stream messages of different types over the same connection. A client's supported protocol paths are negotiated by the `libp2p` stack at connection time; as such they are not part of individual message bodies. +A "Protocol ID" in `libp2p` parlance refers to a human-readable identifier `libp2p` uses in order to identify sub-protocols and stream messages of different types over the same connection. Peers exchange supported protocol IDs via the `Identify` protocol upon connection. When opening a new stream, peers pin a particular protocol ID to it, and the stream remains contextualised thereafter. Since messages are sent inside a stream, they do not need to bear the protocol ID. ## RPC-Over-`libp2p` From fba333c79185f8eaa84cad816f82dc124c581988 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Sun, 17 Mar 2019 21:19:12 -0700 Subject: [PATCH 07/11] Updates from review --- specs/networking/rpc-interface.md | 35 ++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index d07e728c92..f505a4663e 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -24,7 +24,7 @@ Message body schemas are notated like this: ) ``` -SSZ serialization is field-order dependent. Therefore, fields MUST be encoded and decoded according to the order described in this document. The encoded values of each field are concatenated to form the final encoded message body. Embedded structs are serialized as Containers unless otherwise noted. +Embedded types are serialized as SSZ Containers unless otherwise noted. All referenced data structures can be found in the [0-beacon-chain](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#data-structures) specification. @@ -34,7 +34,7 @@ A "Protocol ID" in `libp2p` parlance refers to a human-readable identifier `libp ## RPC-Over-`libp2p` -To facilitate RPC-over-`libp2p`, a single protocol path is used: `/eth/serenity/rpc/1.0.0`. Remote method calls are wrapped in a "request" structure: +To facilitate RPC-over-`libp2p`, a single protocol path is used: `/eth/serenity/beacon/rpc/1.0.0`. Remote method calls are wrapped in a "request" structure: ``` ( @@ -49,6 +49,7 @@ and their corresponding responses are wrapped in a "response" structure: ``` ( id: uint64 + is_error: boolean result: Response ) ``` @@ -58,7 +59,8 @@ If an error occurs, a variant of the response structure is returned: ``` ( id: uint64 - error: ( + is_error: boolean + result: ( code: uint16 data: bytes ) @@ -69,11 +71,13 @@ The details of the RPC-Over-`libp2p` protocol are similar to [JSON-RPC 2.0](http 1. The `id` member is REQUIRED. 2. The `id` member in the response MUST be the same as the value of the `id` in the request. -3. The `method_id` member is REQUIRED. -4. The `result` member is required on success, and MUST NOT exist if there was an error. -5. The `error` member is REQUIRED on errors, and MUST NOT exist if there wasn't an error. +3. The `id` member MUST be unique within the context of a single connection. Monotonically increasing `id`s are RECOMMENDED. +4. The `method_id` member is REQUIRED. +5. The `result` member is required on success, and MUST NOT exist if there was an error. +6. The `error` member is REQUIRED on errors, and MUST NOT exist if there wasn't an error. +7. `is_error` MUST be `true` on errors, or `false` otherwise. -Structuring RPC requests in this manner allows multiple calls and responses to be multiplexed over the same stream without switching. +Structuring RPC requests in this manner allows multiple calls and responses to be multiplexed over the same stream without switching. Note that this implies that responses MAY arrive in a different order than requests. The "method ID" fields in the below messages refer to the `method` field in the request structure above. @@ -136,7 +140,7 @@ Root B ^ +---+ ``` -Once the handshake completes, the client with the higher `latest_finalized_epoch` or `best_slot` (if the clients have equal `latest_finalized_epoch`s) SHOULD send beacon block roots to its counterparty via `beacon_block_roots` (i.e., RPC method `10`). +Once the handshake completes, the client with the higher `latest_finalized_epoch` or `best_slot` (if the clients have equal `latest_finalized_epoch`s) SHOULD request beacon block roots from its counterparty via `beacon_block_roots` (i.e., RPC method `10`). ### Goodbye @@ -154,13 +158,20 @@ Client MAY send `goodbye` messages upon disconnection. The reason field MUST be - `1`: Client shut down. - `2`: Irrelevant network. -- `3`: Irrelevant shard. +- `3`: Too many peers. +- `4`: Fault/error. -### Provide Beacon Block Roots +### Request Beacon Block Roots **Method ID:** `10` -**Body:** +**Request Body** + +``` +() +``` + +**Response Body:** ``` # BlockRootSlot @@ -174,7 +185,7 @@ Client MAY send `goodbye` messages upon disconnection. The reason field MUST be ) ``` -Send a list of block roots and slots to the peer. +Send a list of block roots and slots to the requesting peer. ### Beacon Block Headers From 2dce326310cc99adccf083c4a06b7cc09b68d244 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Mon, 18 Mar 2019 16:02:31 -0700 Subject: [PATCH 08/11] Bring back envelope --- specs/networking/messaging.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/specs/networking/messaging.md b/specs/networking/messaging.md index e88116f46d..de92fe6d4c 100644 --- a/specs/networking/messaging.md +++ b/specs/networking/messaging.md @@ -15,15 +15,17 @@ This specification seeks to define a messaging protocol that is flexible enough ## Message Structure -An ETH 2.0 message consists of a single byte representing the message version followed by the encoded, potentially compressed body. We separate the message's version from the version included in the `libp2p` protocol path in order to allow encoding and compression schemes to be updated independently of the `libp2p` protocols themselves. - -It is unlikely that more than 255 message versions will need to be supported, so a single byte should suffice. +An ETH 2.0 message consists of an envelope that defines the message's compression, encoding, and length followed by the body itself. Visually, a message looks like this: ``` +--------------------------+ -| version byte | +| compression nibble | ++--------------------------+ +| encoding nibble | ++--------------------------+ +| body length (uint64) | +--------------------------+ | | | body | @@ -31,11 +33,12 @@ Visually, a message looks like this: +--------------------------+ ``` -Clients MUST ignore messages with mal-formed bodies. The `version` byte MUST be one of the below values: +Clients MUST ignore messages with mal-formed bodies. The compression/encoding nibbles MUST be one of the following values: + +## Compression Nibble Values -## Version Byte Values +- `0x0`: no compression -### `0x01` +## Encoding Nibble Values -- **Encoding Scheme:** SSZ -- **Compression Scheme:** Snappy +- `0x1`: SSZ From 472d9c5c20a93c0b1608013c03f5ca92a0a9a1d8 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Tue, 19 Mar 2019 15:32:38 -0700 Subject: [PATCH 09/11] Updates from review --- specs/networking/messaging.md | 2 ++ specs/networking/rpc-interface.md | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/specs/networking/messaging.md b/specs/networking/messaging.md index de92fe6d4c..b64e1d5d83 100644 --- a/specs/networking/messaging.md +++ b/specs/networking/messaging.md @@ -11,6 +11,8 @@ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL This specification seeks to define a messaging protocol that is flexible enough to be changed easily as the ETH 2.0 specification evolves. +Note that while `libp2p` is the chosen networking stack for Ethereum 2.0, as of this writing some clients do not have workable `libp2p` implementations. To allow those clients to communicate, we define a message envelope that includes the body's compression, encoding, and body length. Once `libp2p` is available across all implementations, this message envelope will be removed because `libp2p` will negotiate the values defined in the envelope upfront. + # Specification ## Message Structure diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index f505a4663e..ef85f32d5f 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -34,7 +34,9 @@ A "Protocol ID" in `libp2p` parlance refers to a human-readable identifier `libp ## RPC-Over-`libp2p` -To facilitate RPC-over-`libp2p`, a single protocol path is used: `/eth/serenity/beacon/rpc/1.0.0`. Remote method calls are wrapped in a "request" structure: +To facilitate RPC-over-`libp2p`, a single protocol name is used: `/eth/serenity/beacon/rpc/1`. The version number in the protocol name is neither backwards or forwards compatible, and will be incremented whenever changes to the below structures are required. + +Remote method calls are wrapped in a "request" structure: ``` ( @@ -88,6 +90,10 @@ The first 1,000 values in `error.code` are reserved for system use. The followin 3. `20`: Method not found. 4. `30`: Server error. +### Alternative for Non-`libp2p` Clients + +Since some clients are waiting for `libp2p` implementations in their respective languages. As such, they MAY listen for raw TCP messages on port `9000`. To distinguish RPC messages from other messages on that port, a byte prefix of `ETH` (`0x455448`) MUST be prepended to all messages. This option will be removed once `libp2p` is ready in all supported languages. + ## Messages ### Hello @@ -154,12 +160,13 @@ Once the handshake completes, the client with the higher `latest_finalized_epoch ) ``` -Client MAY send `goodbye` messages upon disconnection. The reason field MUST be one of the following values: +Client MAY send `goodbye` messages upon disconnection. The reason field MAY be one of the following values: - `1`: Client shut down. - `2`: Irrelevant network. -- `3`: Too many peers. -- `4`: Fault/error. +- `3`: Fault/error. + +Clients MAY define custom goodbye reasons as long as the value is larger than `1000`. ### Request Beacon Block Roots @@ -168,7 +175,10 @@ Client MAY send `goodbye` messages upon disconnection. The reason field MUST be **Request Body** ``` -() +( + start_slot: uint64 + count: uint64 +) ``` **Response Body:** @@ -185,7 +195,7 @@ Client MAY send `goodbye` messages upon disconnection. The reason field MUST be ) ``` -Send a list of block roots and slots to the requesting peer. +Requests a list of block roots and slots from the peer. The `count` parameter MUST be less than or equal to `32768`. ### Beacon Block Headers @@ -210,7 +220,7 @@ Send a list of block roots and slots to the requesting peer. ) ``` -Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `2` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is empty for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were empty in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further empty, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. +Requests beacon block headers from the peer starting from `(start_root, start_slot)`. The response MUST contain no more than `max_headers` headers. `skip_slots` defines the maximum number of slots to skip between blocks. For example, requesting blocks starting at slots `2` a `skip_slots` value of `1` would return the blocks at `[2, 4, 6, 8, 10]`. In cases where a slot is empty for a given slot number, the closest previous block MUST be returned. For example, if slot `4` were empty in the previous example, the returned array would contain `[2, 3, 6, 8, 10]`. If slot three were further empty, the array would contain `[2, 6, 8, 10]` - i.e., duplicate blocks MUST be collapsed. A `skip_slots` value of `0` returns all blocks. The function of the `skip_slots` parameter helps facilitate light client sync - for example, in [#459](https://github.com/ethereum/eth2.0-specs/issues/459) - and allows clients to balance the peers from whom they request headers. Clients could, for instance, request every 10th block from a set of peers where each per has a different starting block in order to populate block data. From 8794d03517ea2b6160f032d6619fe01594f2a645 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Wed, 20 Mar 2019 19:04:04 -0700 Subject: [PATCH 10/11] Updates with Whiteblock --- specs/networking/rpc-interface.md | 59 ++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index ef85f32d5f..51dc3a9001 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -51,8 +51,8 @@ and their corresponding responses are wrapped in a "response" structure: ``` ( id: uint64 - is_error: boolean - result: Response + response_code: uint16 + result: bytes ) ``` @@ -61,11 +61,8 @@ If an error occurs, a variant of the response structure is returned: ``` ( id: uint64 - is_error: boolean - result: ( - code: uint16 - data: bytes - ) + response_code: uint16 + result: bytes ) ``` @@ -75,20 +72,21 @@ The details of the RPC-Over-`libp2p` protocol are similar to [JSON-RPC 2.0](http 2. The `id` member in the response MUST be the same as the value of the `id` in the request. 3. The `id` member MUST be unique within the context of a single connection. Monotonically increasing `id`s are RECOMMENDED. 4. The `method_id` member is REQUIRED. -5. The `result` member is required on success, and MUST NOT exist if there was an error. -6. The `error` member is REQUIRED on errors, and MUST NOT exist if there wasn't an error. -7. `is_error` MUST be `true` on errors, or `false` otherwise. +5. The `result` member is REQUIRED on success. +6. The `result` member is OPTIONAL on errors, and MAY contain additional information about the error. +7. `response_code` MUST be `0` on success. Structuring RPC requests in this manner allows multiple calls and responses to be multiplexed over the same stream without switching. Note that this implies that responses MAY arrive in a different order than requests. The "method ID" fields in the below messages refer to the `method` field in the request structure above. -The first 1,000 values in `error.code` are reserved for system use. The following error codes are predefined: +The first 1,000 values in `response_code` are reserved for system use. The following response codes are predefined: -1. `0`: Parse error. -2. `10`: Invalid request. -3. `20`: Method not found. -4. `30`: Server error. +1. `0`: No error. +2. `10`: Parse error. +2. `20`: Invalid request. +3. `30`: Method not found. +4. `40`: Server error. ### Alternative for Non-`libp2p` Clients @@ -105,6 +103,7 @@ Since some clients are waiting for `libp2p` implementations in their respective ``` ( network_id: uint8 + chain_id: uint8 latest_finalized_root: bytes32 latest_finalized_epoch: uint64 best_root: bytes32 @@ -168,6 +167,32 @@ Client MAY send `goodbye` messages upon disconnection. The reason field MAY be o Clients MAY define custom goodbye reasons as long as the value is larger than `1000`. +### Get Status + +**Method ID:** `2` + +**Request Body:** + +``` +( + sha: bytes32 + user_agent: bytes + timestamp: uint64 +) +``` + +**Response Body:** + +``` +( + sha: bytes32 + user_agent: bytes + timestamp: uint64 +) +``` + +Returns metadata about the remote node. + ### Request Beacon Block Roots **Method ID:** `10` @@ -195,7 +220,7 @@ Clients MAY define custom goodbye reasons as long as the value is larger than `1 ) ``` -Requests a list of block roots and slots from the peer. The `count` parameter MUST be less than or equal to `32768`. +Requests a list of block roots and slots from the peer. The `count` parameter MUST be less than or equal to `32768`. The slots MUST be returned in ascending slot order. ### Beacon Block Headers @@ -216,7 +241,7 @@ Requests a list of block roots and slots from the peer. The `count` parameter MU ``` ( - headers: []BlockHeader + headers: []BeaconBlockHeader ) ``` From 6cc82278b4a1208bc2da94a37f398eb12c96e4e1 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Mon, 25 Mar 2019 13:27:18 -0700 Subject: [PATCH 11/11] Update rpc-interface.md --- specs/networking/rpc-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index 51dc3a9001..fa49bcd75a 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -103,7 +103,7 @@ Since some clients are waiting for `libp2p` implementations in their respective ``` ( network_id: uint8 - chain_id: uint8 + chain_id: uint64 latest_finalized_root: bytes32 latest_finalized_epoch: uint64 best_root: bytes32