nimbus-eth1/nimbus/sync/protocol_eth65.nim
Jamie Lokier b28396d10d
Sync: Add packet tracing to eth/65 in a consistent format
The format is reasonably useful and not too large, when looking at the
behaviour of sync processes.  It doesn't try to show all the details of
packets, just something at a useful level of detail to see what's going on.
The consistent presentation has proven helpful too, e.g. when grepping.

Signed-off-by: Jamie Lokier <jamie@shareable.org>
2021-07-27 14:12:56 +01:00

235 lines
8.3 KiB
Nim

# Nimbus - Ethereum Wire Protocol, version eth/65
#
# Copyright (c) 2018-2021 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 65, `eth/65`.
## Specification:
## https://github.com/ethereum/devp2p/blob/master/caps/eth.md
import
chronos, stint, chronicles, stew/byteutils, macros,
eth/[common/eth_types, rlp, p2p],
eth/p2p/[rlpx, private/p2p_types, blockchain_utils],
../p2p/chain
type
NewBlockHashesAnnounce* = object
hash: KeccakHash
number: uint64 # Note: Was `uint`, wrong on 32-bit targets.
NewBlockAnnounce* = EthBlock
ForkId* = object
forkHash: array[4, byte] # The RLP encoding must be exactly 4 bytes.
forkNext: BlockNumber # The RLP encoding must be variable-length
PeerState = ref object
initialized*: bool
bestBlockHash*: KeccakHash
bestDifficulty*: DifficultyInt
const
tracePackets* = true # Set `true` or `false` to control packet traces.
const
maxStateFetch* = 384
maxBodiesFetch* = 128
maxReceiptsFetch* = 256
maxHeadersFetch* = 192
ethVersion = 65
func toHex*(x: KeccakHash): string = x.data.toHex
macro tracePacket*(msg: static[string], args: varargs[untyped]) =
quote do:
if tracePackets:
trace `msg`, `args`
p2pProtocol eth(version = ethVersion,
peerState = PeerState,
useRequestIds = false):
onPeerConnected do (peer: Peer):
let
network = peer.network
chain = network.chain
bestBlock = chain.getBestBlockHeader
chainForkId = chain.getForkId(bestBlock.blockNumber)
forkId = ForkId(
forkHash: chainForkId.crc.toBytesBe,
forkNext: chainForkId.nextFork.u256,
)
tracePacket ">> Sending eth.Status (0x00) [eth/" & $ethVersion & "]",
peer, td=bestBlock.difficulty,
bestHash=bestBlock.blockHash.toHex,
networkId=network.networkId,
genesis=chain.genesisHash.toHex,
forkHash=forkId.forkHash.toHex, forkNext=forkId.forkNext
let m = await peer.status(ethVersion,
network.networkId,
bestBlock.difficulty,
bestBlock.blockHash,
chain.genesisHash,
forkId,
timeout = chronos.seconds(10))
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 != chain.genesisHash:
trace "Peer for a different network (genesisHash)", peer,
expectGenesis=chain.genesisHash.toHex, gotGenesis=m.genesisHash.toHex
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: KeccakHash,
genesisHash: KeccakHash,
forkId: ForkId) =
tracePacket "<< Received eth.Status (0x00) [eth/" & $ethVersion & "]",
peer, td=totalDifficulty,
bestHash=bestHash.toHex,
networkId,
genesis=genesisHash.toHex,
forkHash=forkId.forkHash.toHex, forkNext=forkId.forkNext
# User message 0x01: NewBlockHashes.
proc newBlockHashes(peer: Peer, hashes: openArray[NewBlockHashesAnnounce]) =
tracePacket "<< Discarding eth.NewBlockHashes (0x01)",
peer, count=hashes.len
discard
# User message 0x02: Transactions.
proc transactions(peer: Peer, transactions: openArray[Transaction]) =
tracePacket "<< Discarding eth.Transactions (0x02)",
peer, count=transactions.len
discard
requestResponse:
# User message 0x03: GetBlockHeaders.
proc getBlockHeaders(peer: Peer, request: BlocksRequest) =
tracePacket "<< Received eth.GetBlockHeaders (0x03)",
peer, count=request.maxResults
if request.maxResults > uint64(maxHeadersFetch):
debug "eth.GetBlockHeaders (0x03) requested too many headers",
peer, requested=request.maxResults, max=maxHeadersFetch
await peer.disconnect(BreachOfProtocol)
return
let headers = peer.network.chain.getBlockHeaders(request)
if headers.len > 0:
tracePacket ">> Replying with eth.BlockHeaders (0x04)",
peer, count=headers.len
else:
tracePacket ">> Replying EMPTY eth.BlockHeaders (0x04)",
peer, count=0
await response.send(headers)
# User message 0x04: BlockHeaders.
proc blockHeaders(p: Peer, headers: openArray[BlockHeader])
requestResponse:
# User message 0x05: GetBlockBodies.
proc getBlockBodies(peer: Peer, hashes: openArray[KeccakHash]) =
tracePacket "<< Received eth.GetBlockBodies (0x05)",
peer, count=hashes.len
if hashes.len > maxBodiesFetch:
debug "eth.GetBlockBodies (0x05) requested too many bodies",
peer, requested=hashes.len, max=maxBodiesFetch
await peer.disconnect(BreachOfProtocol)
return
let bodies = peer.network.chain.getBlockBodies(hashes)
if bodies.len > 0:
tracePacket ">> Replying with eth.BlockBodies (0x06)",
peer, count=bodies.len
else:
tracePacket ">> Replying EMPTY eth.BlockBodies (0x06)",
peer, count=0
await response.send(bodies)
# User message 0x06: BlockBodies.
proc blockBodies(peer: Peer, blocks: openArray[BlockBody])
# User message 0x07: NewBlock.
proc newBlock(peer: Peer, bh: EthBlock, totalDifficulty: DifficultyInt) =
# (Note, needs to use `EthBlock` instead of its alias `NewBlockAnnounce`
# because either `p2pProtocol` or RLPx doesn't work with an alias.)
tracePacket "<< Discarding eth.NewBlock (0x07)",
peer, totalDifficulty,
blockNumber=bh.header.blockNumber, blockDifficulty=bh.header.difficulty
discard
# User message 0x08: NewPooledTransactionHashes.
proc newPooledTransactionHashes(peer: Peer, hashes: openArray[KeccakHash]) =
tracePacket "<< Discarding eth.NewPooledTransactionHashes (0x08)",
peer, count=hashes.len
discard
requestResponse:
# User message 0x09: GetPooledTransactions.
proc getPooledTransactions(peer: Peer, hashes: openArray[KeccakHash]) =
tracePacket "<< Received eth.GetPooledTransactions (0x09)",
peer, count=hashes.len
tracePacket ">> Replying EMPTY eth.PooledTransactions (0x10)",
peer, count=0
await response.send([])
# User message 0x0a: PooledTransactions.
proc pooledTransactions(peer: Peer, transactions: openArray[Transaction])
nextId 0x0d
requestResponse:
# User message 0x0d: GetNodeData.
proc getNodeData(peer: Peer, hashes: openArray[KeccakHash]) =
tracePacket "<< Received eth.GetNodeData (0x0d)",
peer, count=hashes.len
let blobs = peer.network.chain.getStorageNodes(hashes)
if blobs.len > 0:
tracePacket ">> Replying with eth.NodeData (0x0e)",
peer, count=blobs.len
else:
tracePacket ">> Replying EMPTY eth.NodeData (0x0e)",
peer, count=0
await response.send(blobs)
# User message 0x0e: NodeData.
proc nodeData(peer: Peer, data: openArray[Blob])
requestResponse:
# User message 0x0f: GetReceipts.
proc getReceipts(peer: Peer, hashes: openArray[KeccakHash]) =
tracePacket "<< Received eth.GetReceipts (0x0f)",
peer, count=hashes.len
tracePacket ">> Replying EMPTY eth.Receipts (0x10)",
peer, count=0
await response.send([])
# TODO: implement `getReceipts` and reactivate this code
# await response.send(peer.network.chain.getReceipts(hashes))
# User message 0x10: Receipts.
proc receipts(peer: Peer, receipts: openArray[Receipt])