mirror of
https://github.com/status-im/nim-eth-p2p.git
synced 2025-01-18 02:41:09 +00:00
157 lines
4.4 KiB
Nim
157 lines
4.4 KiB
Nim
|
#
|
||
|
# Ethereum P2P
|
||
|
# (c) Copyright 2018
|
||
|
# Status Research & Development GmbH
|
||
|
#
|
||
|
# Licensed under either of
|
||
|
# Apache License, version 2.0, (LICENSE-APACHEv2)
|
||
|
# MIT license (LICENSE-MIT)
|
||
|
#
|
||
|
|
||
|
## This module implements the Ethereum Wire Protocol:
|
||
|
## https://github.com/ethereum/wiki/wiki/Ethereum-Wire-Protocol
|
||
|
|
||
|
import
|
||
|
random, algorithm, hashes,
|
||
|
asyncdispatch2, rlp, stint, eth_common, chronicles,
|
||
|
../../eth_p2p
|
||
|
|
||
|
type
|
||
|
NewBlockHashesAnnounce* = object
|
||
|
hash: KeccakHash
|
||
|
number: uint
|
||
|
|
||
|
NewBlockAnnounce* = object
|
||
|
header: BlockHeader
|
||
|
body {.rlpInline.}: BlockBody
|
||
|
|
||
|
NetworkState = object
|
||
|
syncing: bool
|
||
|
|
||
|
PeerState = object
|
||
|
initialized: bool
|
||
|
bestBlockHash: KeccakHash
|
||
|
bestDifficulty: DifficultyInt
|
||
|
|
||
|
const
|
||
|
maxStateFetch = 384
|
||
|
maxBodiesFetch = 128
|
||
|
maxReceiptsFetch = 256
|
||
|
maxHeadersFetch = 192
|
||
|
protocolVersion = 63
|
||
|
minPeersToStartSync = 2 # Wait for consensus of at least this number of peers before syncing
|
||
|
|
||
|
rlpxProtocol eth, protocolVersion:
|
||
|
useRequestIds = false
|
||
|
|
||
|
type State = PeerState
|
||
|
|
||
|
onPeerConnected do (peer: Peer):
|
||
|
let
|
||
|
network = peer.network
|
||
|
chain = network.chain
|
||
|
bestBlock = chain.getBestBlockHeader
|
||
|
|
||
|
await peer.status(protocolVersion,
|
||
|
network.networkId,
|
||
|
bestBlock.difficulty,
|
||
|
bestBlock.blockHash,
|
||
|
chain.genesisHash)
|
||
|
|
||
|
let m = await peer.waitSingleMsg(eth.status)
|
||
|
if m.networkId == network.networkId and m.genesisHash == chain.genesisHash:
|
||
|
debug "Suitable peer", peer
|
||
|
else:
|
||
|
raise newException(UselessPeerError, "Eth handshake params mismatch")
|
||
|
peer.state.initialized = true
|
||
|
peer.state.bestDifficulty = m.totalDifficulty
|
||
|
peer.state.bestBlockHash = m.bestHash
|
||
|
|
||
|
proc status(peer: Peer,
|
||
|
protocolVersion: uint,
|
||
|
networkId: uint,
|
||
|
totalDifficulty: DifficultyInt,
|
||
|
bestHash: KeccakHash,
|
||
|
genesisHash: KeccakHash) =
|
||
|
# verify that the peer is on the same chain:
|
||
|
if peer.network.networkId != networkId or
|
||
|
peer.network.chain.genesisHash != genesisHash:
|
||
|
# TODO: Is there a more specific reason here?
|
||
|
await peer.disconnect(SubprotocolReason)
|
||
|
return
|
||
|
|
||
|
peer.state.bestBlockHash = bestHash
|
||
|
peer.state.bestDifficulty = totalDifficulty
|
||
|
|
||
|
proc newBlockHashes(peer: Peer, hashes: openarray[NewBlockHashesAnnounce]) =
|
||
|
discard
|
||
|
|
||
|
proc transactions(peer: Peer, transactions: openarray[Transaction]) =
|
||
|
discard
|
||
|
|
||
|
requestResponse:
|
||
|
proc getBlockHeaders(peer: Peer, request: BlocksRequest) =
|
||
|
if request.maxResults > uint64(maxHeadersFetch):
|
||
|
await peer.disconnect(BreachOfProtocol)
|
||
|
return
|
||
|
|
||
|
var headers = newSeqOfCap[BlockHeader](request.maxResults)
|
||
|
let chain = peer.network.chain
|
||
|
var foundBlock: BlockHeader
|
||
|
|
||
|
if chain.getBlockHeader(request.startBlock, foundBlock):
|
||
|
headers.add foundBlock
|
||
|
|
||
|
while uint64(headers.len) < request.maxResults:
|
||
|
if not chain.getSuccessorHeader(foundBlock, foundBlock):
|
||
|
break
|
||
|
headers.add foundBlock
|
||
|
|
||
|
await peer.blockHeaders(headers)
|
||
|
|
||
|
proc blockHeaders(p: Peer, headers: openarray[BlockHeader])
|
||
|
|
||
|
requestResponse:
|
||
|
proc getBlockBodies(peer: Peer, hashes: openarray[KeccakHash]) =
|
||
|
if hashes.len > maxBodiesFetch:
|
||
|
await peer.disconnect(BreachOfProtocol)
|
||
|
return
|
||
|
|
||
|
var chain = peer.network.chain
|
||
|
|
||
|
var blockBodies = newSeqOfCap[BlockBody](hashes.len)
|
||
|
for hash in hashes:
|
||
|
let blockBody = chain.getBlockBody(hash)
|
||
|
if not blockBody.isNil:
|
||
|
# TODO: should there be an else clause here.
|
||
|
# Is the peer responsible of figuring out that
|
||
|
# some blocks were not found?
|
||
|
blockBodies.add deref(blockBody)
|
||
|
|
||
|
await peer.blockBodies(blockBodies)
|
||
|
|
||
|
proc blockBodies(peer: Peer, blocks: openarray[BlockBody])
|
||
|
|
||
|
proc newBlock(peer: Peer, bh: NewBlockAnnounce, totalDifficulty: DifficultyInt) =
|
||
|
discard
|
||
|
|
||
|
nextID 13
|
||
|
|
||
|
requestResponse:
|
||
|
proc getNodeData(peer: Peer, hashes: openarray[KeccakHash]) =
|
||
|
await peer.nodeData([])
|
||
|
|
||
|
proc nodeData(peer: Peer, data: openarray[Blob]) =
|
||
|
discard
|
||
|
|
||
|
requestResponse:
|
||
|
proc getReceipts(peer: Peer, hashes: openarray[KeccakHash]) =
|
||
|
await peer.receipts([])
|
||
|
|
||
|
proc receipts(peer: Peer, receipts: openarray[Receipt]) =
|
||
|
discard
|
||
|
|
||
|
proc hash*(p: Peer): Hash {.inline.} = hash(cast[pointer](p))
|
||
|
|
||
|
|