nim-eth-p2p/eth_p2p/rlpx_protocols/eth.nim

159 lines
4.7 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)
#
import
asyncdispatch2, rlp, stint, eth_common,
../../eth_p2p
type
NewBlockHashesAnnounce* = object
hash: KeccakHash
number: uint
NewBlockAnnounce* = object
header: BlockHeader
body {.rlpInline.}: BlockBody
NetworkState = object
syncing: bool
PeerState = object
reportedTotalDifficulty: DifficultyInt
latestBlockHash: KeccakHash
const
maxStateFetch = 384
maxBodiesFetch = 128
maxReceiptsFetch = 256
maxHeadersFetch = 192
rlpxProtocol eth, 63:
useRequestIds = false
type State = PeerState
proc status(peer: Peer,
protocolVersion, networkId: uint,
totalDifficulty: DifficultyInt,
bestHash, 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.reportedTotalDifficulty = 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 chain = peer.network.chain
var foundBlock = chain.getBlockHeader(request.startBlock)
if not foundBlock.isNil:
var headers = newSeqOfCap[BlockHeader](request.maxResults)
while uint64(headers.len) < request.maxResults:
headers.add deref(foundBlock)
foundBlock = chain.getSuccessorHeader deref(foundBlock)
if foundBlock.isNil: break
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]) =
discard
proc nodeData(peer: Peer, data: openarray[Blob]) =
discard
requestResponse:
proc getReceipts(peer: Peer, hashes: openarray[KeccakHash]) =
discard
proc receipts(peer: Peer, receipts: openarray[Receipt]) =
discard
proc fastBlockchainSync*(node: EthereumNode) {.async.} =
# 1. obtain last N block headers from all peers
var latestBlocksRequest: BlocksRequest
var requests = newSeqOfCap[Future[Option[eth.blockHeaders]]](32)
for peer in node.peers:
if peer.supports(eth):
requests.add peer.getBlockHeaders(latestBlocksRequest)
discard await all(requests)
# 2. find out what is the block with best total difficulty
var bestBlockDifficulty: DifficultyInt = 0.stuint(256)
for req in requests:
if req.read.isNone: continue
for header in req.read.get.headers:
if header.difficulty > bestBlockDifficulty:
discard
# 3. establish the highest valid block for each peer
# keep in mind that some of the peers may report an alternative history, so
# we must find the last block where each peer agreed with the best peer
# 4. Start making requests in parallel for the block headers that we are
# missing (by requesting blocks from peers while honoring maxHeadersFetch).
# Make sure the blocks hashes add up. Don't count on everyone replying, ask
# a different peer in case of time-out. Handle invalid or incomplete replies
# properly. The peer may respond with fewer headers than requested (or with
# different ones if the peer is not behaving properly).
# 5. Store the obtained headers in the blockchain DB
# 6. Once the sync is complete, repeat from 1. until to further progress is
# possible
# 7. Start downloading the blockchain state in parallel
# (maybe this could start earlier).