Skip to main content
netspecsEthereum networking

3.3.1.eth session

Stage
Current
Validity
In force since Fusaka.
Pulled
devp2peth.md
Related
Chain data exchangeTransaction exchangeSnappy wire compressionExecution discovery profileRLPx sessiondevp2p stack

eth is the protocol execution clients use to exchange blocks, receipts and transactions. It runs as a capability on top of an RLPx connection. RLPx has already authenticated the peer and agreed that both sides speak eth, but not whether they follow the same chain. The eth session starts by settling that:

  1. RLPx connects, and the Hello exchange picks the highest eth version both sides support.
  2. Each side sends Status, stating its network, genesis block, fork state and the block range it can serve.
  3. Each side checks the other’s Status. If anything is incompatible, it disconnects.
  4. Otherwise the session is open. Either side may send any eth message, and sends BlockRangeUpdate from time to time as its range moves.

This page covers those steps and the rules that apply to every message in the session. The messages themselves are covered on Chain data and Transactions.

Session activation and limits

No other eth message may come before Status. Once both sides have sent theirs, the session carries two independent kinds of traffic at once: chain synchronization (headers, bodies, receipts) and transaction exchange. Neither should starve the other. That is why each response has a recommended soft size limit, well below the hard limit at which a peer is dropped.

Once a connection is established, a Status message must be sent. Following the reception of the peer’s Status message, the Ethereum session is active and any other message may be sent.

Within a session, two high-level tasks can be performed: chain synchronization and transaction exchange. These tasks use disjoint sets of protocol messages and clients typically perform them as concurrent activities on all peer connections.

Client implementations should enforce limits on protocol message sizes. The underlying RLPx transport limits the size of a single message to 16.7 MiB. The practical limits for the eth protocol are lower, typically 10 MiB. If a received message is larger than the limit, the peer should be disconnected.

In addition to the hard limit on received messages, clients should also impose ‘soft’ limits on the requests and responses which they send. The recommended soft limit varies per message type. Limiting requests and responses ensures that concurrent activity, e.g. block synchronization and transaction exchange work smoothly over the same peer connection.

Request identifiers

Requests and responses are matched by a request-id. The requester picks it and the responder copies it, so several requests of the same type can be in flight on one connection and their answers can come back in any order.

In most messages, the first element of the message data list is the request-id. For requests, this is a 64-bit integer value chosen by the requesting peer. The responding peer must mirror the value in the request-id element of the response message.

Status handshake

Status tells the other side which chain the node is on and how much of it the node can serve. The receiver checks it field by field:

FieldReceiver’s check
vsnEquals the version RLPx negotiated
networkidEquals ours; mainnet is 1
genesisEquals our genesis block hash
forkidCompatible with ours, per the fork ID rules below
earliest, latest, latestHashTell us which blocks we can ask this peer for; they are not a reason to disconnect

Network ID and genesis hash only rule out peers on an entirely different network. The fork ID (EIP-2124) also catches peers on the same network that disagree about upgrades. It has two parts:

  • fork-hash: a CRC32 over the genesis hash and every fork block number or timestamp already passed.
  • fork-next: the next fork this node expects, or 0 if none is scheduled.

The two fork IDs are not simply compared for equality. A peer that is still syncing, and so is behind on forks, is accepted as long as its past forks are a prefix of ours and its fork-next is the fork we passed next. A peer that has passed a fork we don’t know about, or that expects a fork at a point we have already passed without it, is rejected. The full rules and test vectors are on Execution discovery. Discovery runs the same check on the eth entry of a node’s record, so most incompatible peers are filtered out before any connection is made.

[vsn: P, networkid: P, genesis: B_32, forkid, earliest: P, latest: P, latestHash: B_32]

This is the initial message, informing the peer about the local node state and configuration. This message should be sent just after the connection is established and prior to any other eth protocol messages.

  • vsn: the current protocol version
  • networkid: integer identifying the blockchain, see table below
  • genesis: the hash of the genesis block
  • forkid: An EIP-2124 fork identifier, encoded as [fork-hash, fork-next].

The Status message also announces the available block range. See BlockRangeUpdate for more information.

  • earliest: number of the earliest available full block
  • latest: number of the latest available full block number
  • latestHash: hash of the latest available full block

This table lists common Network IDs and their corresponding networks. Other IDs exist which aren’t listed, i.e. clients should not require that any particular network ID is used. Note that the Network ID may or may not correspond with the EIP-155 Chain ID used for transaction replay prevention.

IDchain
0Olympic (disused)
1Frontier (now mainnet)
2Morden testnet (disused)
3Ropsten testnet (disused)
4Rinkeby testnet (disused)
5Goerli testnet

For a community curated list of chain IDs, see https://chainid.network.

History range updates

Since eth/69, nodes no longer have to keep the whole chain. A node that has pruned old history keeps headers from genesis, but only serves bodies and receipts from earliest up to latest. It announces that range in Status and updates it with BlockRangeUpdate, about every two minutes.

A peer uses the range to route requests: don’t ask for bodies or receipts outside a peer’s range. It is also a health signal: if every peer’s latest lags far behind our head, we are surrounded by syncing nodes. That still isn’t a reason to drop them all, because a syncing node is a normal peer.

A block b is available when earliest <= b <= latest. The upstream text below writes the inequality the other way round. The next rule makes the intended reading clear: a peer that sends earliest > latest is dropped.

[earliest: P, latest: P, latestHash: B_32]

This is a notification about a change in the available block range on a peer.

With this message, the peer announces that all blocks b with earliest >= b >= latest are available via GetBlockBodies, and also that receipts for these blocks are available via GetReceipts. Headers are always assumed to be available for the full range of blocks from genesis.

The notification doesn’t need to be sent for every update to the node’s head block. It is recommended to send an update about once every two minutes. Clients should validate received updates.

  • If earliest > latest, the peer should be disconnected.
  • Updates can be used to detect the condition where a node is surrounded by syncing peers. At the same time, client implementations must take care to not disconnect all syncing peers purely on the basis of their BlockRangeUpdate.

Versions

The current version is eth/72. Each version since eth/69 changed what a session can carry:

VersionChange
eth/69Status carries the block range; BlockRangeUpdate added; bloom filters dropped from receipts
eth/70GetReceipts can ask for part of a block’s receipts, so a block’s receipts can be larger than one message
eth/71Block access lists for Amsterdam: GetBlockAccessLists and BlockAccessLists
eth/72Blob transaction announcements carry cell custody; GetCells and Cells added