Remove eth/66 and eth/67 wire protocol (#2238)
This commit is contained in:
parent
5ca2bf80b0
commit
abf1e58ed4
|
@ -11,7 +11,7 @@
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[tables, times, hashes, sets],
|
std/[tables, times, hashes, sets, sequtils],
|
||||||
chronicles, chronos,
|
chronicles, chronos,
|
||||||
stew/endians2,
|
stew/endians2,
|
||||||
eth/p2p,
|
eth/p2p,
|
||||||
|
@ -21,12 +21,6 @@ import
|
||||||
../protocol/trace_config, # gossip noise control
|
../protocol/trace_config, # gossip noise control
|
||||||
../../core/[chain, tx_pool, tx_pool/tx_item]
|
../../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:
|
logScope:
|
||||||
topics = "eth-wire"
|
topics = "eth-wire"
|
||||||
|
|
||||||
|
@ -552,7 +546,6 @@ method handleAnnouncedTxs*(ctx: EthWireRef,
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
return err(exc.msg)
|
return err(exc.msg)
|
||||||
|
|
||||||
when ethVersion == 68:
|
|
||||||
method handleAnnouncedTxsHashes*(
|
method handleAnnouncedTxsHashes*(
|
||||||
ctx: EthWireRef;
|
ctx: EthWireRef;
|
||||||
peer: Peer;
|
peer: Peer;
|
||||||
|
@ -568,42 +561,6 @@ when ethVersion == 68:
|
||||||
|
|
||||||
notImplemented "handleAnnouncedTxsHashes()/eth68"
|
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)
|
|
||||||
return ok()
|
|
||||||
|
|
||||||
method handleNewBlock*(ctx: EthWireRef,
|
method handleNewBlock*(ctx: EthWireRef,
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
blk: EthBlock,
|
blk: EthBlock,
|
||||||
|
@ -657,25 +614,6 @@ method handleNewBlockHashes*(ctx: EthWireRef,
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
return err(exc.msg)
|
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
|
# End
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -21,28 +21,11 @@
|
||||||
## ..aliases.. -- type names, syntactic sugar (see below)
|
## ..aliases.. -- type names, syntactic sugar (see below)
|
||||||
##
|
##
|
||||||
|
|
||||||
include
|
import
|
||||||
./protocol/eth/eth_versions
|
./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
|
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
|
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|
||||||
import
|
import
|
||||||
|
|
|
@ -9,36 +9,6 @@
|
||||||
#
|
#
|
||||||
# When running make, the instructions here will define the variable
|
# When running make, the instructions here will define the variable
|
||||||
# "NIM_ETH_PARAMS" which should be appended to the nim compiler options.
|
# "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
|
# chunked messages enabled by default, use ENABLE_CHUNKED_RLPX=0 to disable
|
||||||
ifneq ($(if $(ENABLE_CHUNKED_RLPX),$(ENABLE_CHUNKED_RLPX),1),0)
|
ifneq ($(if $(ENABLE_CHUNKED_RLPX),$(ENABLE_CHUNKED_RLPX),1),0)
|
||||||
|
|
|
@ -16,9 +16,6 @@ import
|
||||||
eth/[common, p2p, p2p/private/p2p_types],
|
eth/[common, p2p, p2p/private/p2p_types],
|
||||||
../../types
|
../../types
|
||||||
|
|
||||||
include
|
|
||||||
./eth_versions # early compile time list of proto versions
|
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "eth-wire"
|
topics = "eth-wire"
|
||||||
|
|
||||||
|
@ -96,8 +93,6 @@ method handleAnnouncedTxs*(ctx: EthWireBase,
|
||||||
{.base, gcsafe.} =
|
{.base, gcsafe.} =
|
||||||
notImplemented("handleAnnouncedTxs")
|
notImplemented("handleAnnouncedTxs")
|
||||||
|
|
||||||
# Most recent setting, only the latest version is active
|
|
||||||
when 68 in ethVersions:
|
|
||||||
method handleAnnouncedTxsHashes*(
|
method handleAnnouncedTxsHashes*(
|
||||||
ctx: EthWireBase;
|
ctx: EthWireBase;
|
||||||
peer: Peer;
|
peer: Peer;
|
||||||
|
@ -107,13 +102,6 @@ when 68 in ethVersions:
|
||||||
): Result[void, string]
|
): Result[void, string]
|
||||||
{.base, gcsafe.} =
|
{.base, gcsafe.} =
|
||||||
notImplemented("handleAnnouncedTxsHashes/eth68")
|
notImplemented("handleAnnouncedTxsHashes/eth68")
|
||||||
else:
|
|
||||||
method handleAnnouncedTxsHashes*(ctx: EthWireBase,
|
|
||||||
peer: Peer,
|
|
||||||
txHashes: openArray[Hash256]):
|
|
||||||
Result[void, string]
|
|
||||||
{.base, gcsafe.} =
|
|
||||||
notImplemented("handleAnnouncedTxsHashes")
|
|
||||||
|
|
||||||
method handleNewBlockHashes*(ctx: EthWireBase,
|
method handleNewBlockHashes*(ctx: EthWireBase,
|
||||||
peer: Peer,
|
peer: Peer,
|
||||||
|
@ -122,17 +110,3 @@ method handleNewBlockHashes*(ctx: EthWireBase,
|
||||||
{.base, gcsafe.} =
|
{.base, gcsafe.} =
|
||||||
notImplemented("handleNewBlockHashes")
|
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")
|
|
||||||
|
|
|
@ -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
|
|
|
@ -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 <https://github.com/ethereum/devp2p/blob/master/caps/eth.md>`_
|
|
||||||
|
|
||||||
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]])
|
|
|
@ -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 <https://github.com/ethereum/devp2p/blob/master/caps/eth.md>`_
|
|
||||||
|
|
||||||
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]])
|
|
|
@ -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 <https://chainid.network>.
|
|
||||||
|
|
||||||
### 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
|
|
Loading…
Reference in New Issue