Skip to content

Commit

Permalink
EIP 2481: Adding request IDs to ETH protocol request and response obj…
Browse files Browse the repository at this point in the history
…ects (#2481)

* Add EIP arguing for request ids

* Assign EIP number 2481

* Add link to draft implementation in Trinity

* Fix spelling issues

* Change file name

* Remove question mark from security considerations
  • Loading branch information
cburgdorf authored May 15, 2020
1 parent 8ee164d commit 9ba544f
Showing 1 changed file with 166 additions and 0 deletions.
166 changes: 166 additions & 0 deletions EIPS/eip-2481.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
---
eip: 2481
title: "eth/66: request identifier"
author: Christoph Burgdorf <christoph@ethereum.org>
discussions-to: https://github.com/ethereum/EIPs/issues/2482
status: Draft
type: Standards Track
category: Networking
created: 2020-01-17
requires: 2464
---

<!--You can leave these HTML comments in your merged EIP and delete the visible duplicate text guides, they will not appear and may be helpful to refer to if you edit it again. This is the suggested template for new EIPs. Note that an EIP number will be assigned by an editor. When opening a pull request to submit your EIP, please use an abbreviated title in the filename, `eip-draft_title_abbrev.md`. The title should be 44 characters or less.-->

## Simple Summary
<!--"If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the EIP.-->
This document proposes a way to increase the efficiency of the `eth` networking protocol while at the same time reducing complexity in Ethereum node implementations. It does so by introducing a request id to all requests which their corresponding responses must include.

## Abstract
<!--A short (~200 word) description of the technical issue being addressed.-->
The `eth` protocol defines various request and response commands that are used to exchange data between Ethereum nodes. For example, to ask a peer node for a specific set of headers, a node sends it the [`GetBlockHeaders`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getblockheaders-0x03) command.

*Citing from the [`GetBlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#getblockheaders-0x03):*

>`[block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]`
>Require peer to return a `BlockHeaders` message. Reply must contain a number of block
headers, of rising number when `reverse` is `0`, falling when `1`, `skip` blocks apart,
beginning at block `block` (denoted by either number or hash) in the canonical chain, and
with at most `maxHeaders` items.

The node that receives the `GetBlockHeaders` command should answer it with the [`BlockHeaders`](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#blockheaders-0x04) response command accordingly.

*Citing from the [`BlockHeaders` spec definition](https://github.com/ethereum/devp2p/blob/master/caps/eth.md#blockheaders-0x04):*

>`[blockHeader_0, blockHeader_1, ...]`
>Reply to `GetBlockHeaders`. The items in the list (following the message ID) are block
headers in the format described in the main Ethereum specification, previously asked for
in a GetBlockHeaders message. This may validly contain no block headers if none of the
requested block headers were found. The number of headers that can be requested in a
single message may be subject to implementation-defined limits.

Let's consider a client making many simultaneous requests for `GetBlockHeaders` to one of its peers. By nature it can not be guaranteed that the expected responses arrive in the same order as they were sent. For the client to associate the incoming responses to the correct requests it has to loop through all pending requests trying to match it with the incoming response based on its contents.

This can be particular tricky for responses that are ambiguous such as empty responses.

This EIP proposes to change the `GetBlockHeaders` and the `BlockHeaders` command to include a `request_id` as shown below.

* `GetBlockHeaders (0x03)`
* **Current (eth/65):** `[block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]`
* **Then (eth/66)**: `[request_id: P, [block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]]`
* `BlockHeaders (0x04)`
* **Current (eth/65):** `[blockHeader_0, blockHeader_1, ...]`
* **Then (eth/66)**: `[request_id: P, [blockHeader_0, blockHeader_1, ...]]`

The `request_id` is a 64-bit integer set by the client when it makes the request. On the responding side, the exact same `request_id` from the incoming request is put back into the response object.

This change allows the requesting client to match incoming responses **directly** back to their pending requests without going through all of the pending requests to check if they might match based on the response data.

The selected request/response pair serves as an example for many similar request/response pairs in the `eth` networking protocol.

## Motivation
<!--The motivation is critical for EIPs that want to change the Ethereum protocol. It should clearly explain why the existing protocol specification is inadequate to address the problem that the EIP solves. EIP submissions without sufficient motivation may be rejected outright.-->
The lack of request identifiers in the request / response paris of the `eth` protocol puts unnecessary burden of code complexity into every Ethereum client. It also makes the communication slightly less efficient. Another argument can be made that the addition of request identifiers makes the protocol more aligned with the `les` protocol which **does** already defines request identifiers for each request / response pair.

## Specification
<!--The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow competing, interoperable implementations for any of the current Ethereum platforms (go-ethereum, parity, cpp-ethereum, ethereumj, ethereumjs, and [others](https://github.com/ethereum/wiki/wiki/Clients)).-->

Change the following message types in the `eth` protocol:

* `GetBlockHeaders (0x03)`
* **Current (eth/65):** `[block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]`
* **Then (eth/66)**: `[request_id: P, [block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]]`
* `BlockHeaders (0x04)`
* **Current (eth/65):** `[blockHeader_0, blockHeader_1, ...]`
* **Then (eth/66)**: `[request_id: P, [blockHeader_0, blockHeader_1, ...]]`
* `GetBlockBodies (0x05)`
* **Current (eth/65):** `[hash_0: B_32, hash_1: B_32, ...]`
* **Then (eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]`
* `GetPooledTransactions (0x09)`:
* **Current (eth/65)**`[hash_0: B_32, hash_1: B_32, ...]`
* **Then (eth/66)**`[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]`
* `PooledTransactions (0x0a)`:
* **Current (eth/65)**`[[nonce: P, receivingAddress: B_20, value: P, ...], ...]`
* **Then (eth/66)**`[request_id: P, [[nonce: P, receivingAddress: B_20, value: P, ...], ...]]`
* `BlockBodies (0x06)`
* **Current (eth/65):** `[hash_0: B_32, hash_1: B_32, ...]`
* **Then (eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]`
* `GetNodeData (0x0d)`
* **Current (eth/65):** `[hash_0: B_32, hash_1: B_32, ...]`
* **Then (eth/66)**: `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]`
* `NodeData (0x0e)`
* **Current (eth/65):** `[value_0: B, value_1: B, ...]`
* **Then (eth/66)**: `[request_id: P, [value_0: B, value_1: B, ...]]`
* `GetReceipts (0x0f)`
* **Current (eth/65):** `[blockHash_0: B_32, blockHash_1: B_32, ...]`
* **Then (eth/66)**: `[request_id: P, [blockHash_0: B_32, blockHash_1: B_32, ...]]`
* `Receipts (0x10)`
* **Current (eth/65):** `[[receipt_0, receipt_1], ...]`
* **Then (eth/66)**: `[request_id: P, [[receipt_0, receipt_1], ...]]`


To elaborate, each command is altered in the following way:

1. Create a list with the `request_id` being the first element.
2. Make the second element the list that defines the whole command in the current scheme.

This is consistent with the request / response pairs in the `les` protocol.

The ``request_id`` has the following characteristics:

* 64 bit integer
* Doesn't need to be sequential (can be random)
* Does allow duplicates

## Rationale
<!--The rationale fleshes out the specification by describing what motivated the design and why particular design decisions were made. It should describe alternate designs that were considered and related work, e.g. how the feature is supported in other languages. The rationale may also provide evidence of consensus within the community, and should discuss important objections or concerns raised during discussion.-->

**Q: The efficiency gains might encourage clients to flood their peers with too many simultaneous requests**

Peers can always throttle or disconnect if they don't feel treated well. This is the same as today.

**Q: If `les` already defines the commands like this, why not just use the `les` protocol?**

In practice, peers that serve the `les` protocol are much harder to find in the network. The reasons for this are varied but might boil down to client defaults, immature implementations or missing incentives.

**Q: Networking works today, isn't this just adding bloat?**

This is adding a single integer per command while at the same time reducing code complexity and improving networking efficiency. The addition seems justified.

**Q: Why not demand request ids to be sequential?**

Assuming request ids start always to count from `0` upon connection, things will become messy when
connections are lost and clients reconnect and start over with the same request ids that they had used
in the previous session.

**Q: Why allow duplicate request ids?**

The main benefit is flexibility and simplicity on the implementation side. Clients could decide to share
the same ids across multiple different request types since they are naturally separated anyway. Clients
could even decide to not rely on request ids at all, therefore using the same constant request id across
all requests.

**Q: Why choose a 64-bit integer for the request ids**

64-bit integer were chosen to keep compatibility with the `les` protocol.

## Backwards Compatibility
<!--All EIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The EIP must explain how the author proposes to deal with these incompatibilities. EIP submissions without a sufficient backwards compatibility treatise may be rejected outright.-->
This EIP extends the `eth` protocol in a backwards incompatible way and requires rolling out a new version, `eth/66`. However, `devp2p` supports running multiple versions of the same wire protocol side-by-side, so rolling out `eth/66` does not require client coordination, since non-updated clients can keep using `eth/65`.

This EIP does not change the consensus engine, thus does *not* require a hard fork.

## Implementation
<!--The implementations must be completed before any EIP is given status "Final", but it need not be completed before the EIP is accepted. While there is merit to the approach of reaching consensus on the specification and rationale before writing code, the principle of "rough consensus and running code" is still useful when it comes to resolving many discussions of API details.-->

Trinity has a [draft PR](https://github.com/ethereum/trinity/pull/1672) with an implementation.

## Security Considerations
<!--All EIPs must contain a section that discusses the security implications/considerations relevant to the proposed change. Include information that might be important for security discussions, surfaces risks and can be used throughout the life cycle of the proposal. E.g. include security-relevant design decisions, concerns, important discussions, implementation-specific guidance and pitfalls, an outline of threats and risks and how they are being addressed. EIP submissions missing the "Security Considerations" section will be rejected. An EIP cannot proceed to status "Final" without a Security Considerations discussion deemed sufficient by the reviewers.-->

None

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).

0 comments on commit 9ba544f

Please sign in to comment.