-
Notifications
You must be signed in to change notification settings - Fork 998
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Phase 0 Networking Specifications #763
Changes from 8 commits
e4a1ef1
29caafc
f3bddee
5a9ef0f
22e6212
863f85c
fba333c
2dce326
472d9c5
8794d03
6cc8227
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
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 an envelope that defines the message's compression, encoding, and length followed by the body itself. | ||
|
||
Visually, a message looks like this: | ||
|
||
``` | ||
+--------------------------+ | ||
| compression nibble | | ||
+--------------------------+ | ||
| encoding nibble | | ||
+--------------------------+ | ||
| body length (uint64) | | ||
+--------------------------+ | ||
| | | ||
| body | | ||
| | | ||
+--------------------------+ | ||
``` | ||
|
||
Clients MUST ignore messages with mal-formed bodies. The compression/encoding nibbles MUST be one of the following values: | ||
|
||
## Compression Nibble Values | ||
|
||
- `0x0`: no compression | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Permission to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See above - I don't want to commit teams to an implementation without getting consensus. |
||
|
||
## Encoding Nibble Values | ||
|
||
- `0x1`: SSZ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Permission to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe it would be best if we could agree on a single encoding format to maximize compatibility and minimize implementation overhead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would prefer not, for the same reasons articulated on our call - we should agree together on a single encoding scheme. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
ETH 2.0 Networking Spec - Node Identification | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like we don't need to specify anything here as everything's already either part of the referenced EIP or multiaddr. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, will remove. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be appropriate to file an EIP to allocate a key for multiaddrs in the pre-defined key/value table in the ENR standard? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc: @fjl |
||
=== | ||
|
||
# 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One other consideration maybe: ENR (and Discovery v5) is being designed to support multiple types of identity. It is not going to be a hard requirement that secp256k1 EC pubkeys will identify the node. ENRs will describe the identity type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. libp2p peer IDs are derived from the public key protobuf, which is just key type + bytes. Here's the spec: libp2p/specs#100. Both SECIO and TLS 1.3 validate peer IDs against the pubkey, so following the spec is important or connections will fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I mention in https://github.com/libp2p/specs/pull/100/files#r266291995 - protobuf is not deterministic, and thus not great for feeding into a hashing function or using to determine an ID, unless you used a modified protobuf version that's locked down. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't this be handled at the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it would, but libp2p itself looks broken in this case - we need to keep an eye on that upstream issue so that we don't spread the breakage further. Does using ENR require decoding RLP in this context? |
||
|
||
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 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. | ||
mslipper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# See Also | ||
|
||
- [multiaddr](https://github.com/multiformats/multiaddr) | ||
- [multihash](https://multiformats.io/multihash/) | ||
- [go-libp2p-crypto](https://github.com/libp2p/go-libp2p-crypto) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
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 | ||
) | ||
``` | ||
|
||
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. | ||
|
||
## `libp2p` Protocol Names | ||
|
||
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` | ||
|
||
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: | ||
mslipper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
``` | ||
( | ||
id: uint64 | ||
method_id: uint16 | ||
body: Request | ||
) | ||
``` | ||
|
||
and their corresponding responses are wrapped in a "response" structure: | ||
mslipper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
``` | ||
( | ||
id: uint64 | ||
is_error: boolean | ||
result: Response | ||
) | ||
``` | ||
|
||
If an error occurs, a variant of the response structure is returned: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At least with SSZ it's not easily possible to distinguish between normal and error responses, as one needs to know the schema before being able to decode the message. What one could do is have a general response format and then an embedded result/error blob that can be decoded in a second step. E.g.:
Not really elegant, but I don't really see a better solution (for SSZ that is). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, this is a good point. SSZ doesn't support There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the alternative would be to use a list - empty if there's no error, and one item if there is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just to be clear - when encoding or decoding ssz, there generally exists no provision for skipping fields - even if That said, this issue stems from using SSZ in a wire protocol setting for which it is not.. great. |
||
|
||
``` | ||
( | ||
id: uint64 | ||
is_error: boolean | ||
result: ( | ||
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. | ||
mslipper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
2. The `id` member in the response MUST be the same as the value of the `id` in the request. | ||
mslipper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the error codes, this seems very useful (e.g. for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sort of like port numbers :) |
||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Taking into account the backwards sync suggested elsewhere, and that we can use attestations as a (strong) heuristic that a block is valid and useful, it seems prudent to include (some) attestations here - instead of simply supplying some data like best_root that cannot be trusted anyway, a recent attestation would help the connecting client both with head / fork selection and to know with a higher degree of certainty that the root sent "makes sense" and should be downloaded. The details of this are TBD - but probably we're looking at something like |
||
best_slot: uint64 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. slots are based on wall time - what's the best_slot field for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pretty sure this is supposed to refer to the slot of the head block. Maybe rename There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
) | ||
``` | ||
|
||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should consider spelling out network ID and chain ID as separate fields. Chain ID should be set to a fixed number "1" for ETH, and if others want to run their own chain they can change that ID. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NetworkId vs ChainId +1. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still not convinced that we actually need a network id at all and not only a chain id. Especially for RPC as arguably this isn't even a network, just a set of bidirectional connections (as opposed to the gossip layer where we actually relay data). |
||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe clarify that this is (because it can only be) checked by the peer with the higher latest finalized epoch. I tried to come up with a one sentence fix, but it's probably better, to rewrite the whole paragraph from the point of view of one node shaking hands with another node (right now it's talking about both at the same time). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool got it, will do. |
||
|
||
``` | ||
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 request beacon block roots from its counterparty via `beacon_block_roots` (i.e., RPC method `10`). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How would this be handled if the clients both have equal There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. then you discard the client because you know it's either at genesis or providing invalid data.. finalization happens several epochs behind There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @arnetheduck ah no, I was referring to if both clients have the same There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah. right. I find that the situation is somewhat analogous to receiving a block whose parent is unknown to it - you have to make a very similar decision there - the information in this hello, just like in the parentless block, is essentially useless, from a trust perspective, and you need to turn to other sources. @djrtwo suggested that attestations might be a good heurestic as signing one carries risk for the validator that does so. The information here can, from what I can see, be used to quickly disconnect from a client, if they're saying the network is different. The rest is advisory, and you're hoping for the best. |
||
|
||
### Goodbye | ||
|
||
**Method ID:** `1` | ||
|
||
**Body:** | ||
|
||
``` | ||
( | ||
reason: uint64 | ||
) | ||
``` | ||
|
||
Client MAY send `goodbye` messages upon disconnection. The reason field MUST be one of the following values: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some more from the top of my head that might be helpful:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't we still be connected after There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. generally, the standard way to sync in these kinds of "live" protocols is to start listening to broadcasts, then initiate sync.. else you'll miss packets during sync and will have to recover again. |
||
|
||
- `1`: Client shut down. | ||
- `2`: Irrelevant network. | ||
- `3`: Too many peers. | ||
- `4`: Fault/error. | ||
|
||
### Request Beacon Block Roots | ||
|
||
**Method ID:** `10` | ||
|
||
**Request Body** | ||
|
||
``` | ||
() | ||
``` | ||
|
||
**Response Body:** | ||
|
||
``` | ||
# BlockRootSlot | ||
( | ||
block_root: bytes32 | ||
slot: uint64 | ||
) | ||
|
||
( | ||
roots: []BlockRootSlot | ||
mslipper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
``` | ||
|
||
Send a list of block roots and slots to the requesting peer. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. which beacon block roots? known heads? all? |
||
|
||
### Beacon Block Headers | ||
|
||
**Method ID:** `11` | ||
|
||
**Request Body** | ||
|
||
``` | ||
( | ||
start_root: HashTreeRoot | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the root? It seems redundant to me, except for the case of chain reorgs which shouldn't happen frequently at sync (and even then, it's probably better to get blocks from the current chain that we'll be able to use later, instead of getting outdated ones). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ẁe need a mechanism for recovering blocks, in case something is lost or the client goes offline for a short bit and loses a few (computer went to sleep / ISP went down for 10 minutes). I argue in the original issue (#692 (comment)) that it's often natural to request blocks backwards for this reason: the data structure we're syncing is a singly linked list pointing backwards in time and we receive attestations and blocks that let us discover heads "naturally" by listening to the broadcasts. With a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting! To summarize my understanding of your comment: Syncing forwards is safer as we can verify each block immediately when we receive it, but syncing backwards is more efficient/doesn't require additional database indexing (and I guess syncing forwards may require a negotiating phase to discover the best shared block). You're proposing to interpret the fact that I see lots of attestations on top of my sync peer's head flying around the network as evidence that their head is valid? And therefore, I'd be pretty safe syncing backwards? That sounds reasonable. My original concern was that this requires me to know (at least some good fraction of) the validator set as otherwise my sync peer could create lots of fraudulent attestations for free that I have no chance of verifying. But I would notice this if I have at least one single honest peer (if I try to sync from them or compare the attestations coming from them). Do you think having only a backwards sync is fine or do we need both (e.g. for highly adversarial environments, or resource constrained devices that don't participate in gossiping?). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In terms of network / bandwidth, I'd say it's about the same but there are some nuances:
In terms of client implementations, I think of backward sync as biased to make it cheaper for the server: the server already has the data necessary - also because the head is kept hot - while the client has to keep a chain of "unknown" blocks around / can't validate eagerly. An additional rule that the response must be forward-ordered could help the client apply / validate the blocks eagerly. The backwards sync can be seen as more passive/reactive/lazy while forward sync is more active..
right. the assumption rests on several premises (thanks @djrtwo!):
I'm not sure :) I'm curious to hear feedback on this point, but here are some thoughts:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It seems reasonable to sync backwards from the latest received gossiped block (at least as an initial implementation)
Do we really need start_slot? if we give clients the option to request a block by either start_slot or start_root then that forces us to maintain a lookup or search mechanism for both. if we are saying that both fields (start_slot and start_root) required to sync, then I would disagree. we should be able to simply perform a lookup by block_root and walk the chain backwards until we reach max_headers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
or even better, latest gossiped attestation
I would say that if we go with backwards sync, we should not implement forwards sync here or elsewhere unless there's a strong case for that direction. Having to implement both directions negates some of the benefits of backward sync and adds implementation surface. It is quite possible to add forward sync in a later version of the protocol as well should it prove necessary. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I can dig that @arnetheduck or anyone else. Why do we need start_slot |
||
start_slot: uint64 | ||
max_headers: uint64 | ||
skip_slots: uint64 | ||
) | ||
``` | ||
|
||
**Response Body:** | ||
|
||
``` | ||
( | ||
headers: []BlockHeader | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I note BlockHeader is not defined in the beacon chain spec. I opened a PR to define it as a struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's not needed specifically for the spec, we could also just define it here. |
||
) | ||
``` | ||
|
||
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. 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 | ||
|
||
**Method ID:** `12` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just like in LES, I would use different method ids for requests and responses. So it's possible for me to send you proactively blocks and headers using RPC, and you don't need to know about it in advance. |
||
|
||
**Request Body:** | ||
|
||
``` | ||
( | ||
block_roots: []HashTreeRoot | ||
) | ||
``` | ||
|
||
**Response Body:** | ||
|
||
``` | ||
( | ||
block_bodies: []BeaconBlockBody | ||
mslipper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
``` | ||
|
||
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). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems to me that when everything is going smoothly, block bodies consist of very few attestations (they should be combined by then), and a few minor items like the transfers etc. has anything looked at the numbers to see how much value there is in having separate requests for headers and bodies? Requesting headers then bodies creates additional round-trips which are a cost on its own. |
||
|
||
### 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If other comments are accepted, this enveloping can go away.