diff --git a/nimbus/sync/handlers/eth.nim b/nimbus/sync/handlers/eth.nim index 2101c57eb..3df964eb4 100644 --- a/nimbus/sync/handlers/eth.nim +++ b/nimbus/sync/handlers/eth.nim @@ -11,7 +11,7 @@ {.push raises: [].} import - std/[tables, times, hashes, sets], + std/[tables, times, hashes, sets, sequtils], chronicles, chronos, stew/endians2, eth/p2p, @@ -21,12 +21,6 @@ import ../protocol/trace_config, # gossip noise control ../../core/[chain, tx_pool, tx_pool/tx_item] -# There is only one eth protocol version possible at compile time. This -# might change in future. -when ethVersion == 68: - import - std/sequtils - logScope: topics = "eth-wire" @@ -552,58 +546,21 @@ method handleAnnouncedTxs*(ctx: EthWireRef, except CatchableError as exc: return err(exc.msg) -when ethVersion == 68: - method handleAnnouncedTxsHashes*( - ctx: EthWireRef; - peer: Peer; - txTypes: Blob; - txSizes: openArray[int]; - txHashes: openArray[Hash256]; - ): Result[void, string] = - ## `Eth68` method - if ctx.enableTxPool != Enabled: - when trMissingOrDisabledGossipOk: - notEnabled("handleAnnouncedTxsHashes") - return ok() - - notImplemented "handleAnnouncedTxsHashes()/eth68" - -else: - method handleAnnouncedTxsHashes*(ctx: EthWireRef, - peer: Peer, - txHashes: openArray[Hash256]): - Result[void, string] = - ## Pre-eth68 method - if ctx.enableTxPool != Enabled: - when trMissingOrDisabledGossipOk: - notEnabled("handleAnnouncedTxsHashes") - return ok() - - if txHashes.len == 0: - return ok() - - if ctx.lastCleanup - getTime() > POOLED_STORAGE_TIME_LIMIT: - ctx.cleanupKnownByPeer() - - ctx.addToKnownByPeer(txHashes, peer) - var reqHashes = newSeqOfCap[Hash256](txHashes.len) - for txHash in txHashes: - if txHash in ctx.pending or ctx.inPool(txHash): - continue - reqHashes.add txHash - - if reqHashes.len == 0: - return ok() - - debug "handleAnnouncedTxsHashes: received new tx hashes", - number = reqHashes.len - - for txHash in reqHashes: - ctx.pending.incl txHash - - asyncSpawn ctx.fetchTransactions(reqHashes, peer) +method handleAnnouncedTxsHashes*( + ctx: EthWireRef; + peer: Peer; + txTypes: Blob; + txSizes: openArray[int]; + txHashes: openArray[Hash256]; + ): Result[void, string] = + ## `Eth68` method + if ctx.enableTxPool != Enabled: + when trMissingOrDisabledGossipOk: + notEnabled("handleAnnouncedTxsHashes") return ok() + notImplemented "handleAnnouncedTxsHashes()/eth68" + method handleNewBlock*(ctx: EthWireRef, peer: Peer, blk: EthBlock, @@ -657,25 +614,6 @@ method handleNewBlockHashes*(ctx: EthWireRef, except CatchableError as exc: return err(exc.msg) -# There is only one eth protocol version possible at compile time. This -# might change in future. -when ethVersion == 66: - method getStorageNodes*(ctx: EthWireRef, - hashes: openArray[Hash256]): - Result[seq[Blob], string] {.gcsafe.} = - let db = ctx.db.kvt - var list: seq[Blob] - for hash in hashes: - list.add db.get(hash.data) - ok(list) - - method handleNodeData*(ctx: EthWireRef, - peer: Peer, - data: openArray[Blob]): - Result[void, string] {.gcsafe.} = - notImplemented("handleNodeData") - ok() - # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------ diff --git a/nimbus/sync/protocol.nim b/nimbus/sync/protocol.nim index 1b3124409..bfc4df520 100644 --- a/nimbus/sync/protocol.nim +++ b/nimbus/sync/protocol.nim @@ -21,27 +21,10 @@ ## ..aliases.. -- type names, syntactic sugar (see below) ## -include - ./protocol/eth/eth_versions +import + ./protocol/eth68 as proto_eth -assert 0 < ethVersions.len - -# multi protocol not functional, yet -when 1 < buildEthVersions.len: - {.warning: "No multi eth-protocol yet (using latest version only)".} - -when 68 in ethVersions: - {.warning: "Protocol eth68 is not fully functional yet".} - import ./protocol/eth68 as proto_eth - type eth* = eth68 - -elif 67 in ethVersions: - import ./protocol/eth67 as proto_eth - type eth* = eth67 - -elif 66 in ethVersions: - import ./protocol/eth66 as proto_eth - type eth* = eth66 +type eth* = eth68 # --------------- diff --git a/nimbus/sync/protocol/eth/eth-variables.mk b/nimbus/sync/protocol/eth/eth-variables.mk index 60b6c5a60..8dfe724f8 100644 --- a/nimbus/sync/protocol/eth/eth-variables.mk +++ b/nimbus/sync/protocol/eth/eth-variables.mk @@ -9,36 +9,6 @@ # # When running make, the instructions here will define the variable # "NIM_ETH_PARAMS" which should be appended to the nim compiler options. -# -# This makefile snippet supports multi-protocol arguments as in -# "ENABLE_ETH_VERSION=66:67:999" where available protocol versions are -# extracted and processed. In this case, protocol version 999 will be -# silently ignored. - -# default wire protocol, triggered by empty/unset variable or zero value -ifeq ($(if $(ENABLE_ETH_VERSION),$(ENABLE_ETH_VERSION),0),0) -NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth67_enabled -endif - -# parse list for supported items -ifneq ($(findstring :66:,:$(ENABLE_ETH_VERSION):),) -NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth66_enabled -endif - -ifneq ($(findstring :67:,:$(ENABLE_ETH_VERSION):),) -NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth67_enabled -endif - -ifneq ($(findstring :68:,:$(ENABLE_ETH_VERSION):),) -NIM_ETH_PARAMS := $(NIM_ETH_PARAMS) -d:eth68_enabled -endif - -# There must be at least one protocol version. -ifeq ($(NIM_ETH_PARAMS),) -$(error Unacceptable protocol versions in "ENABLE_ETH_VERSION=$(ENABLE_ETH_VERSION)") -endif - -# ------------ # chunked messages enabled by default, use ENABLE_CHUNKED_RLPX=0 to disable ifneq ($(if $(ENABLE_CHUNKED_RLPX),$(ENABLE_CHUNKED_RLPX),1),0) diff --git a/nimbus/sync/protocol/eth/eth_types.nim b/nimbus/sync/protocol/eth/eth_types.nim index ef62c2b4a..d0b8a5ea1 100644 --- a/nimbus/sync/protocol/eth/eth_types.nim +++ b/nimbus/sync/protocol/eth/eth_types.nim @@ -16,9 +16,6 @@ import eth/[common, p2p, p2p/private/p2p_types], ../../types -include - ./eth_versions # early compile time list of proto versions - logScope: topics = "eth-wire" @@ -96,24 +93,15 @@ method handleAnnouncedTxs*(ctx: EthWireBase, {.base, gcsafe.} = notImplemented("handleAnnouncedTxs") -# Most recent setting, only the latest version is active -when 68 in ethVersions: - method handleAnnouncedTxsHashes*( - ctx: EthWireBase; - peer: Peer; - txTypes: Blob; - txSizes: openArray[int]; - txHashes: openArray[Hash256]; - ): Result[void, string] - {.base, gcsafe.} = - notImplemented("handleAnnouncedTxsHashes/eth68") -else: - method handleAnnouncedTxsHashes*(ctx: EthWireBase, - peer: Peer, - txHashes: openArray[Hash256]): - Result[void, string] - {.base, gcsafe.} = - notImplemented("handleAnnouncedTxsHashes") +method handleAnnouncedTxsHashes*( + ctx: EthWireBase; + peer: Peer; + txTypes: Blob; + txSizes: openArray[int]; + txHashes: openArray[Hash256]; + ): Result[void, string] + {.base, gcsafe.} = + notImplemented("handleAnnouncedTxsHashes/eth68") method handleNewBlockHashes*(ctx: EthWireBase, peer: Peer, @@ -122,17 +110,3 @@ method handleNewBlockHashes*(ctx: EthWireBase, {.base, gcsafe.} = notImplemented("handleNewBlockHashes") -# Legacy setting, currently the latest version is active only -when 66 in ethVersions and ethVersions.len == 1: - method getStorageNodes*(ctx: EthWireBase, - hashes: openArray[Hash256]): - Result[seq[Blob], string] - {.base, gcsafe.} = - notImplemented("getStorageNodes") - - method handleNodeData*(ctx: EthWireBase, - peer: Peer, - data: openArray[Blob]): - Result[void, string] - {.base, gcsafe.} = - notImplemented("handleNodeData") diff --git a/nimbus/sync/protocol/eth/eth_versions.nim b/nimbus/sync/protocol/eth/eth_versions.nim deleted file mode 100644 index 3b984eb39..000000000 --- a/nimbus/sync/protocol/eth/eth_versions.nim +++ /dev/null @@ -1,37 +0,0 @@ -# Nimbus -# Copyright (c) 2024 Status Research & Development GmbH -# Licensed and distributed under either of -# * MIT license (license terms in the root directory or at -# https://opensource.org/licenses/MIT). -# * Apache v2 license (license terms in the root directory or at -# https://www.apache.org/licenses/LICENSE-2.0). -# at your option. This file may not be copied, modified, or distributed -# except according to those terms. - -## Provision of `eth` versions list at compile time -## -## Note: This file allows read the `ethVersions` list at each level of the -## source code import hierarchy. It needs to be *included* though if -## `ethVersions` needs to be available at compile time (as of -## NIM 1.6.18). This allows to construct directives like: -## `when 66 in ethVersions: ..` -## - -const - buildEthVersions = block: - var rc: seq[int] - when defined(eth66_enabled): rc.add 66 - when defined(eth67_enabled): rc.add 67 - when defined(eth68_enabled): rc.add 68 - rc - -# Default protocol only -when buildEthVersions.len == 0: - const ethVersions* = @[67] - ## Compile time list of available/supported eth versions -else: - # One or more protocols - const ethVersions* = buildEthVersions - ## Compile time list of available/supported eth versions - -# End diff --git a/nimbus/sync/protocol/eth66.nim b/nimbus/sync/protocol/eth66.nim deleted file mode 100644 index 015f4c227..000000000 --- a/nimbus/sync/protocol/eth66.nim +++ /dev/null @@ -1,315 +0,0 @@ -# Nimbus - Ethereum Wire Protocol -# -# Copyright (c) 2018-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed -# except according to those terms. - -## This module implements Ethereum Wire Protocol version 66, `eth/66`. -## Specification: -## `eth/66 `_ - -import - stint, - chronicles, - chronos, - eth/[common, p2p, p2p/private/p2p_types], - stew/byteutils, - ./trace_config, - ./eth/eth_types, - ../types, - ../../utils/utils - -export - eth_types - -logScope: - topics = "eth66" - -static: - const stopCompilerGossip {.used.} = Hash256().toHex - -const - ethVersion* = 66 - prettyEthProtoName* = "[eth/" & $ethVersion & "]" - - # Pickeled tracer texts - trEthRecvReceived* = - "<< " & prettyEthProtoName & " Received " - trEthRecvReceivedBlockHeaders* = - trEthRecvReceived & "BlockHeaders (0x04)" - trEthRecvReceivedBlockBodies* = - trEthRecvReceived & "BlockBodies (0x06)" - - trEthRecvProtocolViolation* = - "<< " & prettyEthProtoName & " Protocol violation, " - trEthRecvError* = - "<< " & prettyEthProtoName & " Error " - trEthRecvTimeoutWaiting* = - "<< " & prettyEthProtoName & " Timeout waiting " - trEthRecvDiscarding* = - "<< " & prettyEthProtoName & " Discarding " - - trEthSendSending* = - ">> " & prettyEthProtoName & " Sending " - trEthSendSendingGetBlockHeaders* = - trEthSendSending & "GetBlockHeaders (0x03)" - trEthSendSendingGetBlockBodies* = - trEthSendSending & "GetBlockBodies (0x05)" - - trEthSendReplying* = - ">> " & prettyEthProtoName & " Replying " - - trEthSendDelaying* = - ">> " & prettyEthProtoName & " Delaying " - - trEthRecvNewBlock* = - "<< " & prettyEthProtoName & " Received NewBlock" - trEthRecvNewBlockHashes* = - "<< " & prettyEthProtoName & " Received NewBlockHashes" - trEthSendNewBlock* = - ">> " & prettyEthProtoName & " Sending NewBlock" - trEthSendNewBlockHashes* = - ">> " & prettyEthProtoName & " Sending NewBlockHashes" - -template handleHandlerError(x: untyped) = - if x.isErr: - raise newException(EthP2PError, x.error) - -p2pProtocol eth66(version = ethVersion, - rlpxName = "eth", - peerState = EthPeerState, - networkState = EthWireBase, - useRequestIds = true): - - onPeerConnected do (peer: Peer): - let - network = peer.network - ctx = peer.networkState - statusRes = ctx.getStatus() - handleHandlerError(statusRes) - let status = statusRes.get - - trace trEthSendSending & "Status (0x00)", peer, - td = status.totalDifficulty, - bestHash = short(status.bestBlockHash), - networkId = network.networkId, - genesis = short(status.genesisHash), - forkHash = status.forkId.forkHash.toHex, - forkNext = status.forkId.forkNext - - let m = await peer.status(ethVersion, - network.networkId, - status.totalDifficulty, - status.bestBlockHash, - status.genesisHash, - status.forkId, - timeout = chronos.seconds(10)) - - when trEthTraceHandshakesOk: - trace "Handshake: Local and remote networkId", - local=network.networkId, remote=m.networkId - trace "Handshake: Local and remote genesisHash", - local=status.genesisHash, remote=m.genesisHash - trace "Handshake: Local and remote forkId", - local=(status.forkId.forkHash.toHex & "/" & $status.forkId.forkNext), - remote=(m.forkId.forkHash.toHex & "/" & $m.forkId.forkNext) - - if m.networkId != network.networkId: - trace "Peer for a different network (networkId)", peer, - expectNetworkId=network.networkId, gotNetworkId=m.networkId - raise newException( - UselessPeerError, "Eth handshake for different network") - - if m.genesisHash != status.genesisHash: - trace "Peer for a different network (genesisHash)", peer, - expectGenesis=short(status.genesisHash), gotGenesis=short(m.genesisHash) - raise newException( - UselessPeerError, "Eth handshake for different network") - - trace "Peer matches our network", peer - peer.state.initialized = true - peer.state.bestDifficulty = m.totalDifficulty - peer.state.bestBlockHash = m.bestHash - - handshake: - # User message 0x00: Status. - proc status(peer: Peer, - ethVersionArg: uint, - networkId: NetworkId, - totalDifficulty: DifficultyInt, - bestHash: Hash256, - genesisHash: Hash256, - forkId: ChainForkId) = - trace trEthRecvReceived & "Status (0x00)", peer, - networkId, totalDifficulty, bestHash=short(bestHash), genesisHash=short(genesisHash), - forkHash=forkId.forkHash.toHex, forkNext=forkId.forkNext - - # User message 0x01: NewBlockHashes. - proc newBlockHashes(peer: Peer, hashes: openArray[NewBlockHashesAnnounce]) = - when trEthTraceGossipOk: - trace trEthRecvReceived & "NewBlockHashes (0x01)", peer, - hashes=hashes.len - - let ctx = peer.networkState() - let res = ctx.handleNewBlockHashes(peer, hashes) - handleHandlerError(res) - - # User message 0x02: Transactions. - proc transactions(peer: Peer, transactions: openArray[Transaction]) = - when trEthTraceGossipOk: - trace trEthRecvReceived & "Transactions (0x02)", peer, - transactions=transactions.len - - let ctx = peer.networkState() - let res = ctx.handleAnnouncedTxs(peer, transactions) - handleHandlerError(res) - - requestResponse: - # User message 0x03: GetBlockHeaders. - proc getBlockHeaders(peer: Peer, request: BlocksRequest) = - when trEthTracePacketsOk: - trace trEthRecvReceived & "GetBlockHeaders (0x03)", peer, - count=request.maxResults - - if request.maxResults > uint64(maxHeadersFetch): - debug "GetBlockHeaders (0x03) requested too many headers", - peer, requested=request.maxResults, max=maxHeadersFetch - await peer.disconnect(BreachOfProtocol) - return - - let ctx = peer.networkState() - let headers = ctx.getBlockHeaders(request) - handleHandlerError(headers) - if headers.get.len > 0: - trace trEthSendReplying & "with BlockHeaders (0x04)", peer, - sent=headers.get.len, requested=request.maxResults - else: - trace trEthSendReplying & "EMPTY BlockHeaders (0x04)", peer, - sent=0, requested=request.maxResults - - await response.send(headers.get) - - # User message 0x04: BlockHeaders. - proc blockHeaders(p: Peer, headers: openArray[BlockHeader]) - - requestResponse: - # User message 0x05: GetBlockBodies. - proc getBlockBodies(peer: Peer, hashes: openArray[Hash256]) = - trace trEthRecvReceived & "GetBlockBodies (0x05)", peer, - hashes=hashes.len - if hashes.len > maxBodiesFetch: - debug "GetBlockBodies (0x05) requested too many bodies", - peer, requested=hashes.len, max=maxBodiesFetch - await peer.disconnect(BreachOfProtocol) - return - - let ctx = peer.networkState() - let bodies = ctx.getBlockBodies(hashes) - handleHandlerError(bodies) - if bodies.get.len > 0: - trace trEthSendReplying & "with BlockBodies (0x06)", peer, - sent=bodies.get.len, requested=hashes.len - else: - trace trEthSendReplying & "EMPTY BlockBodies (0x06)", peer, - sent=0, requested=hashes.len - - await response.send(bodies.get) - - # User message 0x06: BlockBodies. - proc blockBodies(peer: Peer, blocks: openArray[BlockBody]) - - # User message 0x07: NewBlock. - proc newBlock(peer: Peer, blk: EthBlock, totalDifficulty: DifficultyInt) = - # (Note, needs to use `EthBlock` instead of its alias `NewBlockAnnounce` - # because either `p2pProtocol` or RLPx doesn't work with an alias.) - when trEthTraceGossipOk: - trace trEthRecvReceived & "NewBlock (0x07)", peer, - totalDifficulty, - blockNumber = blk.header.blockNumber, - blockDifficulty = blk.header.difficulty - - let ctx = peer.networkState() - let res = ctx.handleNewBlock(peer, blk, totalDifficulty) - handleHandlerError(res) - - # User message 0x08: NewPooledTransactionHashes. - proc newPooledTransactionHashes(peer: Peer, txHashes: openArray[Hash256]) = - when trEthTraceGossipOk: - trace trEthRecvReceived & "NewPooledTransactionHashes (0x08)", peer, - hashes=txHashes.len - - let ctx = peer.networkState() - let res = ctx.handleAnnouncedTxsHashes(peer, txHashes) - handleHandlerError(res) - - requestResponse: - # User message 0x09: GetPooledTransactions. - proc getPooledTransactions(peer: Peer, txHashes: openArray[Hash256]) = - trace trEthRecvReceived & "GetPooledTransactions (0x09)", peer, - hashes=txHashes.len - - let ctx = peer.networkState() - let txs = ctx.getPooledTxs(txHashes) - handleHandlerError(txs) - if txs.get.len > 0: - trace trEthSendReplying & "with PooledTransactions (0x0a)", peer, - sent=txs.get.len, requested=txHashes.len - else: - trace trEthSendReplying & "EMPTY PooledTransactions (0x0a)", peer, - sent=0, requested=txHashes.len - - await response.send(txs.get) - - # User message 0x0a: PooledTransactions. - proc pooledTransactions( - peer: Peer, transactions: openArray[PooledTransaction]) - - nextId 0x0d - - # User message 0x0d: GetNodeData. - proc getNodeData(peer: Peer, nodeHashes: openArray[Hash256]) = - trace trEthRecvReceived & "GetNodeData (0x0d)", peer, - hashes=nodeHashes.len - - let ctx = peer.networkState() - let data = ctx.getStorageNodes(nodeHashes) - handleHandlerError(data) - - trace trEthSendReplying & "NodeData (0x0e)", peer, - sent=data.get.len, requested=nodeHashes.len - - await peer.nodeData(data.get) - - # User message 0x0e: NodeData. - proc nodeData(peer: Peer, data: openArray[Blob]) = - trace trEthRecvReceived & "NodeData (0x0e)", peer, - bytes=data.len - let ctx = peer.networkState() - let res = ctx.handleNodeData(peer, data) - handleHandlerError(res) - - requestResponse: - # User message 0x0f: GetReceipts. - proc getReceipts(peer: Peer, hashes: openArray[Hash256]) = - trace trEthRecvReceived & "GetReceipts (0x0f)", peer, - hashes=hashes.len - - let ctx = peer.networkState() - let rec = ctx.getReceipts(hashes) - handleHandlerError(rec) - if rec.get.len > 0: - trace trEthSendReplying & "with Receipts (0x10)", peer, - sent=rec.get.len, requested=hashes.len - else: - trace trEthSendReplying & "EMPTY Receipts (0x10)", peer, - sent=0, requested=hashes.len - - await response.send(rec.get) - - # User message 0x10: Receipts. - proc receipts(peer: Peer, receipts: openArray[seq[Receipt]]) diff --git a/nimbus/sync/protocol/eth67.nim b/nimbus/sync/protocol/eth67.nim deleted file mode 100644 index c3b610ab1..000000000 --- a/nimbus/sync/protocol/eth67.nim +++ /dev/null @@ -1,297 +0,0 @@ -# Nimbus - Ethereum Wire Protocol -# -# Copyright (c) 2018-2024 Status Research & Development GmbH -# Licensed under either of -# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or -# http://www.apache.org/licenses/LICENSE-2.0) -# * MIT license ([LICENSE-MIT](LICENSE-MIT) or -# http://opensource.org/licenses/MIT) -# at your option. This file may not be copied, modified, or distributed -# except according to those terms. - -## This module implements Ethereum Wire Protocol version 67, `eth/67`. -## Specification: -## `eth/67 `_ - -import - stint, - chronicles, - chronos, - eth/[common, p2p, p2p/private/p2p_types], - stew/byteutils, - ./trace_config, - ./eth/eth_types, - ../types, - ../../utils/utils - -export - eth_types - -logScope: - topics = "eth67" - -static: - const stopCompilerGossip {.used.} = Hash256().toHex - -const - ethVersion* = 67 - prettyEthProtoName* = "[eth/" & $ethVersion & "]" - - # Pickeled tracer texts - trEthRecvReceived* = - "<< " & prettyEthProtoName & " Received " - trEthRecvReceivedBlockHeaders* = - trEthRecvReceived & "BlockHeaders (0x04)" - trEthRecvReceivedBlockBodies* = - trEthRecvReceived & "BlockBodies (0x06)" - - trEthRecvProtocolViolation* = - "<< " & prettyEthProtoName & " Protocol violation, " - trEthRecvError* = - "<< " & prettyEthProtoName & " Error " - trEthRecvTimeoutWaiting* = - "<< " & prettyEthProtoName & " Timeout waiting " - trEthRecvDiscarding* = - "<< " & prettyEthProtoName & " Discarding " - - trEthSendSending* = - ">> " & prettyEthProtoName & " Sending " - trEthSendSendingGetBlockHeaders* = - trEthSendSending & "GetBlockHeaders (0x03)" - trEthSendSendingGetBlockBodies* = - trEthSendSending & "GetBlockBodies (0x05)" - - trEthSendReplying* = - ">> " & prettyEthProtoName & " Replying " - - trEthSendDelaying* = - ">> " & prettyEthProtoName & " Delaying " - - trEthRecvNewBlock* = - "<< " & prettyEthProtoName & " Received NewBlock" - trEthRecvNewBlockHashes* = - "<< " & prettyEthProtoName & " Received NewBlockHashes" - trEthSendNewBlock* = - ">> " & prettyEthProtoName & " Sending NewBlock" - trEthSendNewBlockHashes* = - ">> " & prettyEthProtoName & " Sending NewBlockHashes" - -template handleHandlerError(x: untyped) = - if x.isErr: - raise newException(EthP2PError, x.error) - -p2pProtocol eth67(version = ethVersion, - rlpxName = "eth", - peerState = EthPeerState, - networkState = EthWireBase, - useRequestIds = true): - - onPeerConnected do (peer: Peer): - let - network = peer.network - ctx = peer.networkState - statusRes = ctx.getStatus() - - handleHandlerError(statusRes) - let status = statusRes.get - - trace trEthSendSending & "Status (0x00)", peer, - td = status.totalDifficulty, - bestHash = short(status.bestBlockHash), - networkId = network.networkId, - genesis = short(status.genesisHash), - forkHash = status.forkId.forkHash.toHex, - forkNext = status.forkId.forkNext - - let m = await peer.status(ethVersion, - network.networkId, - status.totalDifficulty, - status.bestBlockHash, - status.genesisHash, - status.forkId, - timeout = chronos.seconds(10)) - - when trEthTraceHandshakesOk: - trace "Handshake: Local and remote networkId", - local=network.networkId, remote=m.networkId - trace "Handshake: Local and remote genesisHash", - local=short(status.genesisHash), remote=short(m.genesisHash) - trace "Handshake: Local and remote forkId", - local=(status.forkId.forkHash.toHex & "/" & $status.forkId.forkNext), - remote=(m.forkId.forkHash.toHex & "/" & $m.forkId.forkNext) - - if m.networkId != network.networkId: - trace "Peer for a different network (networkId)", peer, - expectNetworkId=network.networkId, gotNetworkId=m.networkId - raise newException( - UselessPeerError, "Eth handshake for different network") - - if m.genesisHash != status.genesisHash: - trace "Peer for a different network (genesisHash)", peer, - expectGenesis=short(status.genesisHash), gotGenesis=short(m.genesisHash) - raise newException( - UselessPeerError, "Eth handshake for different network") - - trace "Peer matches our network", peer - peer.state.initialized = true - peer.state.bestDifficulty = m.totalDifficulty - peer.state.bestBlockHash = m.bestHash - - handshake: - # User message 0x00: Status. - proc status(peer: Peer, - ethVersionArg: uint, - networkId: NetworkId, - totalDifficulty: DifficultyInt, - bestHash: Hash256, - genesisHash: Hash256, - forkId: ChainForkId) = - trace trEthRecvReceived & "Status (0x00)", peer, - networkId, totalDifficulty, bestHash=short(bestHash), genesisHash=short(genesisHash), - forkHash=forkId.forkHash.toHex, forkNext=forkId.forkNext - - # User message 0x01: NewBlockHashes. - proc newBlockHashes(peer: Peer, hashes: openArray[NewBlockHashesAnnounce]) = - when trEthTraceGossipOk: - trace trEthRecvReceived & "NewBlockHashes (0x01)", peer, - hashes=hashes.len - - let ctx = peer.networkState() - let res = ctx.handleNewBlockHashes(peer, hashes) - handleHandlerError(res) - - # User message 0x02: Transactions. - proc transactions(peer: Peer, transactions: openArray[Transaction]) = - when trEthTraceGossipOk: - trace trEthRecvReceived & "Transactions (0x02)", peer, - transactions=transactions.len - - let ctx = peer.networkState() - let res = ctx.handleAnnouncedTxs(peer, transactions) - handleHandlerError(res) - - requestResponse: - # User message 0x03: GetBlockHeaders. - proc getBlockHeaders(peer: Peer, request: BlocksRequest) = - when trEthTracePacketsOk: - trace trEthRecvReceived & "GetBlockHeaders (0x03)", peer, - count=request.maxResults - - if request.maxResults > uint64(maxHeadersFetch): - debug "GetBlockHeaders (0x03) requested too many headers", - peer, requested=request.maxResults, max=maxHeadersFetch - await peer.disconnect(BreachOfProtocol) - return - - let ctx = peer.networkState() - let headers = ctx.getBlockHeaders(request) - handleHandlerError(headers) - if headers.get.len > 0: - trace trEthSendReplying & "with BlockHeaders (0x04)", peer, - sent=headers.get.len, requested=request.maxResults - else: - trace trEthSendReplying & "EMPTY BlockHeaders (0x04)", peer, - sent=0, requested=request.maxResults - - await response.send(headers.get) - - # User message 0x04: BlockHeaders. - proc blockHeaders(p: Peer, headers: openArray[BlockHeader]) - - requestResponse: - # User message 0x05: GetBlockBodies. - proc getBlockBodies(peer: Peer, hashes: openArray[Hash256]) = - trace trEthRecvReceived & "GetBlockBodies (0x05)", peer, - hashes=hashes.len - if hashes.len > maxBodiesFetch: - debug "GetBlockBodies (0x05) requested too many bodies", - peer, requested=hashes.len, max=maxBodiesFetch - await peer.disconnect(BreachOfProtocol) - return - - let ctx = peer.networkState() - let bodies = ctx.getBlockBodies(hashes) - handleHandlerError(bodies) - if bodies.get.len > 0: - trace trEthSendReplying & "with BlockBodies (0x06)", peer, - sent=bodies.get.len, requested=hashes.len - else: - trace trEthSendReplying & "EMPTY BlockBodies (0x06)", peer, - sent=0, requested=hashes.len - - await response.send(bodies.get) - - # User message 0x06: BlockBodies. - proc blockBodies(peer: Peer, blocks: openArray[BlockBody]) - - # User message 0x07: NewBlock. - proc newBlock(peer: Peer, blk: EthBlock, totalDifficulty: DifficultyInt) = - # (Note, needs to use `EthBlock` instead of its alias `NewBlockAnnounce` - # because either `p2pProtocol` or RLPx doesn't work with an alias.) - when trEthTraceGossipOk: - trace trEthRecvReceived & "NewBlock (0x07)", peer, - totalDifficulty, - blockNumber = blk.header.blockNumber, - blockDifficulty = blk.header.difficulty - - let ctx = peer.networkState() - let res = ctx.handleNewBlock(peer, blk, totalDifficulty) - handleHandlerError(res) - - # User message 0x08: NewPooledTransactionHashes. - proc newPooledTransactionHashes(peer: Peer, txHashes: openArray[Hash256]) = - when trEthTraceGossipOk: - trace trEthRecvReceived & "NewPooledTransactionHashes (0x08)", peer, - hashes=txHashes.len - - let ctx = peer.networkState() - let res = ctx.handleAnnouncedTxsHashes(peer, txHashes) - handleHandlerError(res) - - requestResponse: - # User message 0x09: GetPooledTransactions. - proc getPooledTransactions(peer: Peer, txHashes: openArray[Hash256]) = - trace trEthRecvReceived & "GetPooledTransactions (0x09)", peer, - hashes=txHashes.len - - let ctx = peer.networkState() - let txs = ctx.getPooledTxs(txHashes) - handleHandlerError(txs) - if txs.get.len > 0: - trace trEthSendReplying & "with PooledTransactions (0x0a)", peer, - sent=txs.get.len, requested=txHashes.len - else: - trace trEthSendReplying & "EMPTY PooledTransactions (0x0a)", peer, - sent=0, requested=txHashes.len - - await response.send(txs.get) - - # User message 0x0a: PooledTransactions. - proc pooledTransactions( - peer: Peer, transactions: openArray[PooledTransaction]) - - # User message 0x0d: GetNodeData -- removed, was so 66ish - # User message 0x0e: NodeData -- removed, was so 66ish - - nextId 0x0f - - requestResponse: - # User message 0x0f: GetReceipts. - proc getReceipts(peer: Peer, hashes: openArray[Hash256]) = - trace trEthRecvReceived & "GetReceipts (0x0f)", peer, - hashes=hashes.len - - let ctx = peer.networkState() - let rec = ctx.getReceipts(hashes) - handleHandlerError(rec) - if rec.get.len > 0: - trace trEthSendReplying & "with Receipts (0x10)", peer, - sent=rec.get.len, requested=hashes.len - else: - trace trEthSendReplying & "EMPTY Receipts (0x10)", peer, - sent=0, requested=hashes.len - - await response.send(rec.get) - - # User message 0x10: Receipts. - proc receipts(peer: Peer, receipts: openArray[seq[Receipt]]) diff --git a/nimbus/sync/protocol/wire_protocol.md b/nimbus/sync/protocol/wire_protocol.md deleted file mode 100644 index 29503961d..000000000 --- a/nimbus/sync/protocol/wire_protocol.md +++ /dev/null @@ -1,550 +0,0 @@ -# Ethereum Wire Protocol (ETH) - -'eth' is a protocol on the [RLPx] transport that facilitates exchange of Ethereum -blockchain information between peers. The current protocol version is **eth/67**. See end -of document for a list of changes in past protocol versions. - -### Basic Operation - -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, three high-level tasks can be performed: chain synchronization, block -propagation 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. - -### Chain Synchronization - -Nodes participating in the eth protocol are expected to have knowledge of the complete -chain of all blocks from the genesis block to current, latest block. The chain is obtained -by downloading it from other peers. - -Upon connection, both peers send their [Status] message, which includes the Total -Difficulty (TD) and hash of their 'best' known block. - -The client with the worst TD then proceeds to download block headers using the -[GetBlockHeaders] message. It verifies proof-of-work values in received headers and -fetches block bodies using the [GetBlockBodies] message. Received blocks are executed -using the Ethereum Virtual Machine, recreating the state tree and receipts. - -Note that header downloads, block body downloads and block execution may happen -concurrently. - -### State Synchronization (a.k.a. "fast sync") - -Protocol versions eth/63 through eth/66 also allowed synchronizing the state tree. Since -protocol version eth/67, the Ethereum state tree can no longer be retrieved using the eth -protocol, and state downloads are provided by the auxiliary [snap protocol] instead. - -State synchronization typically proceeds by downloading the chain of block headers, -verifying their validity. Block bodies are requested as in the Chain Synchronization -section but transactions aren't executed, only their 'data validity' is verified. The -client picks a block near the head of the chain (the 'pivot block') and downloads the -state of that block. - -### Block Propagation - -Newly-mined blocks must be relayed to all nodes. This happens through block propagation, -which is a two step process. When a [NewBlock] announcement message is received from a -peer, the client first verifies the basic header validity of the block, checking whether -the proof-of-work value is valid. It then sends the block to a small fraction of connected -peers (usually the square root of the total number of peers) using the [NewBlock] message. - -After the header validity check, the client imports the block into its local chain by -executing all transactions contained in the block, computing the block's 'post state'. The -block's `state-root` hash must match the computed post state root. Once the block is fully -processed, and considered valid, the client sends a [NewBlockHashes] message about the -block to all peers which it didn't notify earlier. Those peers may request the full block -later if they fail to receive it via [NewBlock] from anyone else. - -A node should never send a block announcement back to a peer which previously announced -the same block. This is usually achieved by remembering a large set of block hashes -recently relayed to or from each peer. - -The reception of a block announcement may also trigger chain synchronization if the block -is not the immediate successor of the client's current latest block. - -### Transaction Exchange - -All nodes must exchange pending transactions in order to relay them to miners, which will -pick them for inclusion into the blockchain. Client implementations keep track of the set -of pending transactions in the 'transaction pool'. The pool is subject to client-specific -limits and can contain many (i.e. thousands of) transactions. - -When a new peer connection is established, the transaction pools on both sides need to be -synchronized. Initially, both ends should send [NewPooledTransactionHashes] messages -containing all transaction hashes in the local pool to start the exchange. - -On receipt of a NewPooledTransactionHashes announcement, the client filters the received -set, collecting transaction hashes which it doesn't yet have in its own local pool. It can -then request the transactions using the [GetPooledTransactions] message. - -When new transactions appear in the client's pool, it should propagate them to the network -using the [Transactions] and [NewPooledTransactionHashes] messages. The Transactions -message relays complete transaction objects and is typically sent to a small, random -fraction of connected peers. All other peers receive a notification of the transaction -hash and can request the complete transaction object if it is unknown to them. The -dissemination of complete transactions to a fraction of peers usually ensures that all -nodes receive the transaction and won't need to request it. - -A node should never send a transaction back to a peer that it can determine already knows -of it (either because it was previously sent or because it was informed from this peer -originally). This is usually achieved by remembering a set of transaction hashes recently -relayed by the peer. - -### Transaction Encoding and Validity - -Transaction objects exchanged by peers have one of two encodings. In definitions across -this specification, we refer to transactions of either encoding using the identifier `txₙ`. - - tx = {legacy-tx, typed-tx} - -Untyped, legacy transactions are given as an RLP list. - - legacy-tx = [ - nonce: P, - gas-price: P, - gas-limit: P, - recipient: {B_0, B_20}, - value: P, - data: B, - V: P, - R: P, - S: P, - ] - -[EIP-2718] typed transactions are encoded as RLP byte arrays where the first byte is the -transaction type (`tx-type`) and the remaining bytes are opaque type-specific data. - - typed-tx = tx-type || tx-data - -Transactions must be validated when they are received. Validity depends on the Ethereum -chain state. The specific kind of validity this specification is concerned with is not -whether the transaction can be executed successfully by the EVM, but only whether it is -acceptable for temporary storage in the local pool and for exchange with other peers. - -Transactions are validated according to the rules below. While the encoding of typed -transactions is opaque, it is assumed that their `tx-data` provides values for `nonce`, -`gas-price`, `gas-limit`, and that the sender account of the transaction can be determined -from their signature. - -- If the transaction is typed, the `tx-type` must be known to the implementation. Defined - transaction types may be considered valid even before they become acceptable for - inclusion in a block. Implementations should disconnect peers sending transactions of - unknown type. -- The signature must be valid according to the signature schemes supported by the chain. - For typed transactions, signature handling is defined by the EIP introducing the type. - For legacy transactions, the two schemes in active use are the basic 'Homestead' scheme - and the [EIP-155] scheme. -- The `gas-limit` must cover the 'intrinsic gas' of the transaction. -- The sender account of the transaction, which is derived from the signature, must have - sufficient ether balance to cover the cost (`gas-limit * gas-price + value`) of the - transaction. -- The `nonce` of the transaction must be equal or greater than the current nonce of the - sender account. -- When considering the transaction for inclusion in the local pool, it is up to - implementations to determine how many 'future' transactions with nonce greater than the - current account nonce are valid, and to which degree 'nonce gaps' are acceptable. - -Implementations may enforce other validation rules for transactions. For example, it is -common practice to reject encoded transactions larger than 128 kB. - -Unless noted otherwise, implementations must not disconnect peers for sending invalid -transactions, and should simply discard them instead. This is because the peer might be -operating under slightly different validation rules. - -### Block Encoding and Validity - -Ethereum blocks are encoded as follows: - - block = [header, transactions, ommers] - transactions = [tx₁, tx₂, ...] - ommers = [header₁, header₂, ...] - header = [ - parent-hash: B_32, - ommers-hash: B_32, - coinbase: B_20, - state-root: B_32, - txs-root: B_32, - receipts-root: B_32, - bloom: B_256, - difficulty: P, - number: P, - gas-limit: P, - gas-used: P, - time: P, - extradata: B, - mix-digest: B_32, - block-nonce: B_8, - basefee-per-gas: P, - ] - -In certain protocol messages, the transaction and ommer lists are relayed together as a -single item called the 'block body'. - - block-body = [transactions, ommers] - -The validity of block headers depends on the context in which they are used. For a single -block header, only the validity of the proof-of-work seal (`mix-digest`, `block-nonce`) -can be verified. When a header is used to extend the client's local chain, or multiple -headers are processed in sequence during chain synchronization, the following rules apply: - -- Headers must form a chain where block numbers are consecutive and the `parent-hash` of - each header matches the hash of the preceding header. -- When extending the locally-stored chain, implementations must also verify that the - values of `difficulty`, `gas-limit` and `time` are within the bounds of protocol rules - given in the [Yellow Paper]. -- The `gas-used` header field must be less than or equal to the `gas-limit`. -- The `basefee-per-gas` header field must be present for blocks after the [London hard - fork]. Note that `basefee-per-gas` must be absent for earlier blocks. This rule was - added implicity by [EIP-1559], which added the field into the definition of the Ethereum - block hash. - -For complete blocks, we distinguish between the validity of the block's EVM state -transition, and the (weaker) 'data validity' of the block. The definition of state -transition rules is not dealt with in this specification. We require data validity of the -block for the purposes of immediate [block propagation] and during [state synchronization]. - -To determine the data validity of a block, use the rules below. Implementations should -disconnect peers sending invalid blocks. - -- The block `header` must be valid. -- The `transactions` contained in the block must be valid for inclusion into the chain at - the block's number. This means that, in addition to the transaction validation rules - given earlier, validating whether the `tx-type` is permitted at the block number is - required, and validation of transaction gas must take the block number into account. -- The sum of the `gas-limit`s of all transactions must not exceed the `gas-limit` of the - block. -- The `transactions` of the block must be verified against the `txs-root` by computing and - comparing the merkle trie hash of the transactions list. -- The `ommers` list may contain at most two headers. -- `keccak256(ommers)` must match the `ommers-hash` of the block header. -- The headers contained in the `ommers` list must be valid headers. Their block number - must not be greater than that of the block they are included in. The parent hash of an - ommer header must refer to an ancestor of depth 7 or less of its including block, and it - must not have been included in any earlier block contained in this ancestor set. - -### Receipt Encoding and Validity - -Receipts are the output of the EVM state transition of a block. Like transactions, -receipts have two distinct encodings and we will refer to either encoding using the -identifier `receiptₙ`. - - receipt = {legacy-receipt, typed-receipt} - -Untyped, legacy receipts are encoded as follows: - - legacy-receipt = [ - post-state-or-status: {B_32, {0, 1}}, - cumulative-gas: P, - bloom: B_256, - logs: [log₁, log₂, ...] - ] - log = [ - contract-address: B_20, - topics: [topic₁: B, topic₂: B, ...], - data: B - ] - -[EIP-2718] typed receipts are encoded as RLP byte arrays where the first byte gives the -receipt type (matching `tx-type`) and the remaining bytes are opaque data specific to the -type. - - typed-receipt = tx-type || receipt-data - -In the Ethereum Wire Protocol, receipts are always transferred as the complete list of all -receipts contained in a block. It is also assumed that the block containing the receipts -is valid and known. When a list of block receipts is received by a peer, it must be -verified by computing and comparing the merkle trie hash of the list against the -`receipts-root` of the block. Since the valid list of receipts is determined by the EVM -state transition, it is not necessary to define any further validity rules for receipts in -this specification. - -## Protocol Messages - -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 (0x00) - -`[version: P, networkid: P, td: P, blockhash: B_32, genesis: B_32, forkid]` - -Inform a peer of its current state. This message should be sent just after the connection -is established and prior to any other eth protocol messages. - -- `version`: the current protocol version -- `networkid`: integer identifying the blockchain, see table below -- `td`: total difficulty of the best chain. Integer, as found in block header. -- `blockhash`: the hash of the best (i.e. highest TD) known block -- `genesis`: the hash of the genesis block -- `forkid`: An [EIP-2124] fork identifier, encoded as `[fork-hash, fork-next]`. - -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. - -| ID | chain | -|----|-------------------------------| -| 0 | Olympic (disused) | -| 1 | Frontier (now mainnet) | -| 2 | Morden (disused) | -| 3 | Ropsten (current PoW testnet) | -| 4 | [Rinkeby] | - -For a community curated list of chain IDs, see . - -### NewBlockHashes (0x01) - -`[[blockhash₁: B_32, number₁: P], [blockhash₂: B_32, number₂: P], ...]` - -Specify one or more new blocks which have appeared on the network. To be maximally -helpful, nodes should inform peers of all blocks that they may not be aware of. Including -hashes that the sending peer could reasonably be considered to know (due to the fact they -were previously informed of because that node has itself advertised knowledge of the -hashes through NewBlockHashes) is considered bad form, and may reduce the reputation of -the sending node. Including hashes that the sending node later refuses to honour with a -proceeding [GetBlockHeaders] message is considered bad form, and may reduce the reputation -of the sending node. - -### Transactions (0x02) - -`[tx₁, tx₂, ...]` - -Specify transactions that the peer should make sure is included on its transaction queue. -The items in the list are transactions in the format described in the main Ethereum -specification. Transactions messages must contain at least one (new) transaction, empty -Transactions messages are discouraged and may lead to disconnection. - -Nodes must not resend the same transaction to a peer in the same session and must not -relay transactions to a peer they received that transaction from. In practice this is -often implemented by keeping a per-peer bloom filter or set of transaction hashes which -have already been sent or received. - -### GetBlockHeaders (0x03) - -`[request-id: P, [startblock: {P, B_32}, limit: P, skip: P, reverse: {0, 1}]]` - -Require peer to return a BlockHeaders message. The response must contain a number of block -headers, of rising number when `reverse` is `0`, falling when `1`, `skip` blocks apart, -beginning at block `startblock` (denoted by either number or hash) in the canonical chain, -and with at most `limit` items. - -### BlockHeaders (0x04) - -`[request-id: P, [header₁, header₂, ...]]` - -This is the response to GetBlockHeaders, containing the requested headers. The header list -may be empty 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. - -The recommended soft limit for BlockHeaders responses is 2 MiB. - -### GetBlockBodies (0x05) - -`[request-id: P, [blockhash₁: B_32, blockhash₂: B_32, ...]]` - -This message requests block body data by hash. The number of blocks that can be requested -in a single message may be subject to implementation-defined limits. - -### BlockBodies (0x06) - -`[request-id: P, [block-body₁, block-body₂, ...]]` - -This is the response to GetBlockBodies. The items in the list contain the body data of the -requested blocks. The list may be empty if none of the requested blocks were available. - -The recommended soft limit for BlockBodies responses is 2 MiB. - -### NewBlock (0x07) - -`[block, td: P]` - -Specify a single complete block that the peer should know about. `td` is the total -difficulty of the block, i.e. the sum of all block difficulties up to and including this -block. - -### NewPooledTransactionHashes (0x08) - -`[txhash₁: B_32, txhash₂: B_32, ...]` - -This message announces one or more transactions that have appeared in the network and -which have not yet been included in a block. To be maximally helpful, nodes should inform -peers of all transactions that they may not be aware of. - -The recommended soft limit for this message is 4096 hashes (128 KiB). - -Nodes should only announce hashes of transactions that the remote peer could reasonably be -considered not to know, but it is better to return more transactions than to have a nonce -gap in the pool. - -### GetPooledTransactions (0x09) - -`[request-id: P, [txhash₁: B_32, txhash₂: B_32, ...]]` - -This message requests transactions from the recipient's transaction pool by hash. - -The recommended soft limit for GetPooledTransactions requests is 256 hashes (8 KiB). The -recipient may enforce an arbitrary limit on the response (size or serving time), which -must not be considered a protocol violation. - -### PooledTransactions (0x0a) - -`[request-id: P, [tx₁, tx₂...]]` - -This is the response to GetPooledTransactions, returning the requested transactions from -the local pool. The items in the list are transactions in the format described in the main -Ethereum specification. - -The transactions must be in same order as in the request, but it is OK to skip -transactions which are not available. This way, if the response size limit is reached, -requesters will know which hashes to request again (everything starting from the last -returned transaction) and which to assume unavailable (all gaps before the last returned -transaction). - -It is permissible to first announce a transaction via NewPooledTransactionHashes, but then -to refuse serving it via PooledTransactions. This situation can arise when the transaction -is included in a block (and removed from the pool) in between the announcement and the -request. - -A peer may respond with an empty list iff none of the hashes match transactions in its -pool. - -### GetReceipts (0x0f) - -`[request-id: P, [blockhash₁: B_32, blockhash₂: B_32, ...]]` - -Require peer to return a Receipts message containing the receipts of the given block -hashes. The number of receipts that can be requested in a single message may be subject to -implementation-defined limits. - -### Receipts (0x10) - -`[request-id: P, [[receipt₁, receipt₂], ...]]` - -This is the response to GetReceipts, providing the requested block receipts. Each element -in the response list corresponds to a block hash of the GetReceipts request, and must -contain the complete list of receipts of the block. - -The recommended soft limit for Receipts responses is 2 MiB. - -## Change Log - -### eth/67 ([EIP-4938], March 2022) - -Version 67 removed the GetNodeData and NodeData messages. - -- GetNodeData (0x0d) - `[request_id: P, [hash_0: B_32, hash_1: B_32, ...]]` -- NodeData (0x0e) - `[request_id: P, [value_0: B, value_1: B, ...]]` - -### eth/66 ([EIP-2481], April 2021) - -Version 66 added the `request-id` element in messages [GetBlockHeaders], [BlockHeaders], -[GetBlockBodies], [BlockBodies], [GetPooledTransactions], [PooledTransactions], -GetNodeData, NodeData, [GetReceipts], [Receipts]. - -### eth/65 with typed transactions ([EIP-2976], April 2021) - -When typed transactions were introduced by [EIP-2718], client implementers decided to -accept the new transaction and receipt formats in the wire protocol without increasing the -protocol version. This specification update also added definitions for the encoding of all -consensus objects instead of referring to the Yellow Paper. - -### eth/65 ([EIP-2464], January 2020) - -Version 65 improved transaction exchange, introducing three additional messages: -[NewPooledTransactionHashes], [GetPooledTransactions], and [PooledTransactions]. - -Prior to version 65, peers always exchanged complete transaction objects. As activity and -transaction sizes increased on the Ethereum mainnet, the network bandwidth used for -transaction exchange became a significant burden on node operators. The update reduced the -required bandwidth by adopting a two-tier transaction broadcast system similar to block -propagation. - -### eth/64 ([EIP-2364], November 2019) - -Version 64 changed the [Status] message to include the [EIP-2124] ForkID. This allows -peers to determine mutual compatibility of chain execution rules without synchronizing the -blockchain. - -### eth/63 (2016) - -Version 63 added the GetNodeData, NodeData, [GetReceipts] and [Receipts] messages -which allow synchronizing transaction execution results. - -### eth/62 (2015) - -In version 62, the [NewBlockHashes] message was extended to include block numbers -alongside the announced hashes. The block number in [Status] was removed. Messages -GetBlockHashes (0x03), BlockHashes (0x04), GetBlocks (0x05) and Blocks (0x06) were -replaced by messages that fetch block headers and bodies. The BlockHashesFromNumber (0x08) -message was removed. - -Previous encodings of the reassigned/removed message codes were: - -- GetBlockHashes (0x03): `[hash: B_32, max-blocks: P]` -- BlockHashes (0x04): `[hash₁: B_32, hash₂: B_32, ...]` -- GetBlocks (0x05): `[hash₁: B_32, hash₂: B_32, ...]` -- Blocks (0x06): `[[header, transactions, ommers], ...]` -- BlockHashesFromNumber (0x08): `[number: P, max-blocks: P]` - -### eth/61 (2015) - -Version 61 added the BlockHashesFromNumber (0x08) message which could be used to request -blocks in ascending order. It also added the latest block number to the [Status] message. - -### eth/60 and below - -Version numbers below 60 were used during the Ethereum PoC development phase. - -- `0x00` for PoC-1 -- `0x01` for PoC-2 -- `0x07` for PoC-3 -- `0x09` for PoC-4 -- `0x17` for PoC-5 -- `0x1c` for PoC-6 - -[block propagation]: #block-propagation -[state synchronization]: #state-synchronization-aka-fast-sync -[snap protocol]: ./snap.md -[Status]: #status-0x00 -[NewBlockHashes]: #newblockhashes-0x01 -[Transactions]: #transactions-0x02 -[GetBlockHeaders]: #getblockheaders-0x03 -[BlockHeaders]: #blockheaders-0x04 -[GetBlockBodies]: #getblockbodies-0x05 -[BlockBodies]: #blockbodies-0x06 -[NewBlock]: #newblock-0x07 -[NewPooledTransactionHashes]: #newpooledtransactionhashes-0x08 -[GetPooledTransactions]: #getpooledtransactions-0x09 -[PooledTransactions]: #pooledtransactions-0x0a -[GetReceipts]: #getreceipts-0x0f -[Receipts]: #receipts-0x10 -[RLPx]: ../rlpx.md -[Rinkeby]: https://rinkeby.io -[EIP-155]: https://eips.ethereum.org/EIPS/eip-155 -[EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 -[EIP-2124]: https://eips.ethereum.org/EIPS/eip-2124 -[EIP-2364]: https://eips.ethereum.org/EIPS/eip-2364 -[EIP-2464]: https://eips.ethereum.org/EIPS/eip-2464 -[EIP-2481]: https://eips.ethereum.org/EIPS/eip-2481 -[EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718 -[EIP-2976]: https://eips.ethereum.org/EIPS/eip-2976 -[EIP-4938]: https://eips.ethereum.org/EIPS/eip-4938 -[London hard fork]: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/london.md -[Yellow Paper]: https://ethereum.github.io/yellowpaper/paper.pdf