EIP-2481 eth/66 request identifier
Final · Networking & nodes · EIP status Final · created 2020-01-17
A request number (request_id) is attached to every request and response message in the eth protocol, which Ethereum nodes use to exchange data with each other. This change bumps the protocol version from eth/65 to eth/66.
Why it is needed
Ethereum nodes operate by requesting data such as block headers, block bodies, and receipts from other nodes (peers) and receiving responses. However, the existing eth protocol had no number pairing requests with responses. If a node sent several requests to the same peer at once, there was no guarantee responses would come back in the order sent, so the node had to guess which request a given response belonged to by examining its contents against every pending request one by one. Responses that cannot be distinguished by content alone, such as empty responses, were especially hard to match. This approach forced unnecessarily complex code on every Ethereum client and also hurt communication efficiency. This EIP attaches a number when sending a request and has the response echo back the same number, allowing responses to be matched directly to requests.
What changes
Today
- Which request a response answers has to be guessed from its contents
- Code is needed to iterate over every pending request and cross-check it against the response
- Ambiguous responses, such as empty ones, are hard to match
After EIP-2481
- The number attached to the request comes back unchanged in the response, matching them immediately
- Response-handling code becomes simpler and communication efficiency improves
- This aligns the approach with the les protocol, which already used request identifiers
Who is affected
- Client teams: eth/66 must be implemented, but the complex code for cross-checking responses against requests can be removed.
- Node operators: Node-to-node sync communication works more efficiently. No separate action is required.
- Ordinary users: Since consensus rules are unchanged, there is no perceptible change, and node software simply becomes internally simpler.
In depth
How the message structure changes
The existing message is wrapped in two layers. First, a list is created with request_id as its first element. Second, the list that previously defined the entire command is placed as the second element. For example, GetBlockHeaders(0x03) changes from eth/65's [block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}] to eth/66's [request_id: P, [block: {P, B_32}, maxHeaders: P, skip: P, reverse: P in {0, 1}]]. The response, BlockHeaders(0x04), is changed the same way to [request_id: P, [blockHeader_0, blockHeader_1, ...]].
The ten messages that change
All five request/response pairs change: GetBlockHeaders(0x03) and BlockHeaders(0x04), GetBlockBodies(0x05) and BlockBodies(0x06), GetPooledTransactions(0x09) and PooledTransactions(0x0a), GetNodeData(0x0d) and NodeData(0x0e), and GetReceipts(0x0f) and Receipts(0x10). All are unified into the structure where request_id is the first element and the existing command list is the second.
Three properties of request_id
request_id is a 64-bit integer, need not be sequential (it may be random), and duplicates are allowed. Sequentiality is not enforced because a client that reconnects after a disconnect and starts counting from 0 again could collide with numbers used in a previous session, causing confusion. Duplicates are allowed for implementation flexibility and simplicity. Since different request types are naturally distinguishable, the same number can be shared across multiple request types, and an implementation that does not rely on request_id at all and uses the same constant for every request is also possible. 64 bits was chosen for compatibility with the les protocol.
Relationship to the les protocol
The les protocol, used for light clients, already defines a request identifier for every request/response pair. This EIP has the effect of making the eth protocol more similar to les. To the question of why not simply use les instead, the original document answers that in practice it is much harder to find peers serving les on the real network, citing client default settings, immature implementations, and lack of incentive as reasons. To the question of whether adding one more integer is unnecessary overhead, it answers that adding one integer per message is justified because it reduces code complexity and improves networking efficiency.
Backward compatibility: deployed without a hard fork
Because this change extends the eth protocol in a backward-incompatible way, a new version, eth/66, must be deployed. However, since devp2p can run multiple versions of the same wire protocol side by side, clients that have not updated can simply keep using eth/65. It can therefore be deployed without coordinating timing across clients. Since the consensus engine is untouched, no hard fork is needed either. This EIP presupposes EIP-2464, which defines eth/65.
Test cases and RLP encoding
The original document provides RLP encoding test cases for every redefined message type. Each test pairs a message defined in the data section with the result of RLP-encoding it. For example, the GetBlockHeadersPacket66 test shows that a request containing fields such as RequestId 1111, Amount 5, Skip 5, and Reverse false encodes to 0xe8820457e4a0...0580. The same kind of verification data exists for all ten messages, including BlockHeaders, BlockBodies, NodeData, Receipts, and PooledTransactions, allowing client implementations to be checked against it. This EIP's status is Final, meaning the specification is finalized.