les: remove (#2211)
LES was [removed from geth](https://github.com/ethereum/go-ethereum/pull/28586) and is generally no longer supported on the network.
This commit is contained in:
parent
2c322054e0
commit
fe296213cf
|
@ -117,7 +117,6 @@ type
|
||||||
## Protocol flags
|
## Protocol flags
|
||||||
Eth ## enable eth subprotocol
|
Eth ## enable eth subprotocol
|
||||||
#Snap ## enable snap sub-protocol
|
#Snap ## enable snap sub-protocol
|
||||||
Les ## enable les subprotocol
|
|
||||||
|
|
||||||
RpcFlag* {.pure.} = enum
|
RpcFlag* {.pure.} = enum
|
||||||
## RPC flags
|
## RPC flags
|
||||||
|
@ -373,8 +372,8 @@ type
|
||||||
|
|
||||||
protocols {.
|
protocols {.
|
||||||
desc: "Enable specific set of server protocols (available: Eth, " &
|
desc: "Enable specific set of server protocols (available: Eth, " &
|
||||||
" Les, None.) This will not affect the sync mode"
|
" None.) This will not affect the sync mode"
|
||||||
# " Snap, Les, None.) This will not affect the sync mode"
|
# " Snap, None.) This will not affect the sync mode"
|
||||||
defaultValue: @[]
|
defaultValue: @[]
|
||||||
defaultValueDesc: $ProtocolFlag.Eth
|
defaultValueDesc: $ProtocolFlag.Eth
|
||||||
name: "protocols" .}: seq[string]
|
name: "protocols" .}: seq[string]
|
||||||
|
@ -635,7 +634,6 @@ proc getProtocolFlags*(conf: NimbusConf): set[ProtocolFlag] =
|
||||||
for item in repeatingList(conf.protocols):
|
for item in repeatingList(conf.protocols):
|
||||||
case item.toLowerAscii()
|
case item.toLowerAscii()
|
||||||
of "eth": result.incl ProtocolFlag.Eth
|
of "eth": result.incl ProtocolFlag.Eth
|
||||||
of "les": result.incl ProtocolFlag.Les
|
|
||||||
# of "snap": result.incl ProtocolFlag.Snap
|
# of "snap": result.incl ProtocolFlag.Snap
|
||||||
of "none": noneOk = true
|
of "none": noneOk = true
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -28,8 +28,7 @@ import
|
||||||
./core/clique/clique_desc,
|
./core/clique/clique_desc,
|
||||||
./core/clique/clique_sealer,
|
./core/clique/clique_sealer,
|
||||||
./sync/protocol,
|
./sync/protocol,
|
||||||
./sync/handlers,
|
./sync/handlers
|
||||||
./sync/protocol/les_protocol
|
|
||||||
|
|
||||||
when defined(evmc_enabled):
|
when defined(evmc_enabled):
|
||||||
import transaction/evmc_dynamic_loader
|
import transaction/evmc_dynamic_loader
|
||||||
|
@ -131,8 +130,6 @@ proc setupP2P(nimbus: NimbusNode, conf: NimbusConf,
|
||||||
nimbus.ethNode.peerPool,
|
nimbus.ethNode.peerPool,
|
||||||
nimbus.chainRef,
|
nimbus.chainRef,
|
||||||
nimbus.txPool)
|
nimbus.txPool)
|
||||||
of ProtocolFlag.Les:
|
|
||||||
nimbus.ethNode.addCapability les
|
|
||||||
#of ProtocolFlag.Snap:
|
#of ProtocolFlag.Snap:
|
||||||
# nimbus.ethNode.addSnapHandlerCapability(
|
# nimbus.ethNode.addSnapHandlerCapability(
|
||||||
# nimbus.ethNode.peerPool,
|
# nimbus.ethNode.peerPool,
|
||||||
|
|
|
@ -1,509 +0,0 @@
|
||||||
# Nimbus
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
import
|
|
||||||
std/[tables, sets],
|
|
||||||
chronicles, chronos,
|
|
||||||
eth/[rlp, common],
|
|
||||||
eth/p2p/[rlpx, private/p2p_types],
|
|
||||||
./private/les_types
|
|
||||||
|
|
||||||
const
|
|
||||||
maxSamples = 100000
|
|
||||||
rechargingScale = 1000000
|
|
||||||
|
|
||||||
lesStatsKey = "les.flow_control.stats"
|
|
||||||
lesStatsVer = 0
|
|
||||||
|
|
||||||
logScope:
|
|
||||||
topics = "les flow_control"
|
|
||||||
|
|
||||||
# TODO: move this somewhere
|
|
||||||
proc pop[A, B](t: var Table[A, B], key: A): B =
|
|
||||||
result = t[key]
|
|
||||||
t.del(key)
|
|
||||||
|
|
||||||
when LesTime is SomeInteger:
|
|
||||||
template `/`(lhs, rhs: LesTime): LesTime =
|
|
||||||
lhs div rhs
|
|
||||||
|
|
||||||
when defined(testing):
|
|
||||||
var lesTime* = LesTime(0)
|
|
||||||
template now(): LesTime = lesTime
|
|
||||||
template advanceTime(t) = lesTime += LesTime(t)
|
|
||||||
|
|
||||||
else:
|
|
||||||
import times
|
|
||||||
let startTime = epochTime()
|
|
||||||
|
|
||||||
proc now(): LesTime =
|
|
||||||
return LesTime((times.epochTime() - startTime) * 1000.0)
|
|
||||||
|
|
||||||
proc addSample(ra: var StatsRunningAverage; x, y: float64) =
|
|
||||||
if ra.count >= maxSamples:
|
|
||||||
let decay = float64(ra.count + 1 - maxSamples) / maxSamples
|
|
||||||
template applyDecay(x) = x -= x * decay
|
|
||||||
|
|
||||||
applyDecay ra.sumX
|
|
||||||
applyDecay ra.sumY
|
|
||||||
applyDecay ra.sumXX
|
|
||||||
applyDecay ra.sumXY
|
|
||||||
ra.count = maxSamples - 1
|
|
||||||
|
|
||||||
inc ra.count
|
|
||||||
ra.sumX += x
|
|
||||||
ra.sumY += y
|
|
||||||
ra.sumXX += x * x
|
|
||||||
ra.sumXY += x * y
|
|
||||||
|
|
||||||
proc calc(ra: StatsRunningAverage): tuple[m, b: float] =
|
|
||||||
if ra.count == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
let count = float64(ra.count)
|
|
||||||
let d = count * ra.sumXX - ra.sumX * ra.sumX
|
|
||||||
if d < 0.001:
|
|
||||||
return (m: ra.sumY / count, b: 0.0)
|
|
||||||
|
|
||||||
result.m = (count * ra.sumXY - ra.sumX * ra.sumY) / d
|
|
||||||
result.b = (ra.sumY / count) - (result.m * ra.sumX / count)
|
|
||||||
|
|
||||||
proc currentRequestsCosts*(network: LesNetwork,
|
|
||||||
les: ProtocolInfo): seq[ReqCostInfo] =
|
|
||||||
# Make sure the message costs are already initialized
|
|
||||||
doAssert network.messageStats.len > les.messages[^1].id,
|
|
||||||
"Have you called `initFlowControl`"
|
|
||||||
|
|
||||||
for msg in les.messages:
|
|
||||||
var (m, b) = network.messageStats[msg.id].calc()
|
|
||||||
if m < 0:
|
|
||||||
b += m
|
|
||||||
m = 0
|
|
||||||
|
|
||||||
if b < 0:
|
|
||||||
b = 0
|
|
||||||
|
|
||||||
result.add ReqCostInfo(msgId: msg.id,
|
|
||||||
baseCost: ReqCostInt(b * 2),
|
|
||||||
reqCost: ReqCostInt(m * 2))
|
|
||||||
|
|
||||||
proc persistMessageStats*(network: LesNetwork) =
|
|
||||||
# XXX: Because of the package_visible_types template magic, Nim complains
|
|
||||||
# when we pass the messageStats expression directly to `encodeList`
|
|
||||||
let stats = network.messageStats
|
|
||||||
network.setSetting(lesStatsKey, rlp.encodeList(lesStatsVer, stats))
|
|
||||||
|
|
||||||
proc loadMessageStats*(network: LesNetwork,
|
|
||||||
les: ProtocolInfo): bool =
|
|
||||||
block readFromDB:
|
|
||||||
var stats = network.getSetting(lesStatsKey)
|
|
||||||
if stats.len == 0:
|
|
||||||
notice "LES stats not present in the database"
|
|
||||||
break readFromDB
|
|
||||||
|
|
||||||
try:
|
|
||||||
var statsRlp = rlpFromBytes(stats)
|
|
||||||
if not statsRlp.enterList:
|
|
||||||
notice "Found a corrupted LES stats record"
|
|
||||||
break readFromDB
|
|
||||||
|
|
||||||
let version = statsRlp.read(int)
|
|
||||||
if version != lesStatsVer:
|
|
||||||
notice "Found an outdated LES stats record"
|
|
||||||
break readFromDB
|
|
||||||
|
|
||||||
statsRlp >> network.messageStats
|
|
||||||
if network.messageStats.len <= les.messages[^1].id:
|
|
||||||
notice "Found an incomplete LES stats record"
|
|
||||||
break readFromDB
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
except RlpError as e:
|
|
||||||
error "Error while loading LES message stats", err = e.msg
|
|
||||||
|
|
||||||
newSeq(network.messageStats, les.messages[^1].id + 1)
|
|
||||||
return false
|
|
||||||
|
|
||||||
proc update(s: var FlowControlState, t: LesTime) =
|
|
||||||
let dt = max(t - s.lastUpdate, LesTime(0))
|
|
||||||
|
|
||||||
s.bufValue = min(
|
|
||||||
s.bufValue + s.minRecharge * dt,
|
|
||||||
s.bufLimit)
|
|
||||||
|
|
||||||
s.lastUpdate = t
|
|
||||||
|
|
||||||
proc init(s: var FlowControlState,
|
|
||||||
bufLimit: BufValueInt, minRecharge: int, t: LesTime) =
|
|
||||||
s.bufValue = bufLimit
|
|
||||||
s.bufLimit = bufLimit
|
|
||||||
s.minRecharge = minRecharge
|
|
||||||
s.lastUpdate = t
|
|
||||||
|
|
||||||
#func canMakeRequest(s: FlowControlState,
|
|
||||||
# maxCost: ReqCostInt): (LesTime, float64) =
|
|
||||||
# ## Returns the required waiting time before sending a request and
|
|
||||||
# ## the estimated buffer level afterwards (as a fraction of the limit)
|
|
||||||
# const safetyMargin = 50
|
|
||||||
#
|
|
||||||
# var maxCost = min(
|
|
||||||
# maxCost + safetyMargin * s.minRecharge,
|
|
||||||
# s.bufLimit)
|
|
||||||
#
|
|
||||||
# if s.bufValue >= maxCost:
|
|
||||||
# result[1] = float64(s.bufValue - maxCost) / float64(s.bufLimit)
|
|
||||||
# else:
|
|
||||||
# result[0] = (maxCost - s.bufValue) / s.minRecharge
|
|
||||||
|
|
||||||
func canServeRequest(srv: LesNetwork): bool =
|
|
||||||
result = srv.reqCount < srv.maxReqCount and
|
|
||||||
srv.reqCostSum < srv.maxReqCostSum
|
|
||||||
|
|
||||||
proc rechargeReqCost(peer: LesPeer, t: LesTime) =
|
|
||||||
let dt = t - peer.lastRechargeTime
|
|
||||||
peer.reqCostVal += peer.reqCostGradient * dt / rechargingScale
|
|
||||||
peer.lastRechargeTime = t
|
|
||||||
if peer.isRecharging and t >= peer.rechargingEndsAt:
|
|
||||||
peer.isRecharging = false
|
|
||||||
peer.reqCostGradient = 0
|
|
||||||
peer.reqCostVal = 0
|
|
||||||
|
|
||||||
proc updateRechargingParams(peer: LesPeer, network: LesNetwork) =
|
|
||||||
peer.reqCostGradient = 0
|
|
||||||
if peer.reqCount > 0:
|
|
||||||
peer.reqCostGradient = rechargingScale / network.reqCount
|
|
||||||
|
|
||||||
if peer.isRecharging:
|
|
||||||
peer.reqCostGradient = (network.rechargingRate * (peer.rechargingPower /
|
|
||||||
network.totalRechargingPower).int64).int
|
|
||||||
peer.rechargingEndsAt = peer.lastRechargeTime +
|
|
||||||
LesTime(peer.reqCostVal * rechargingScale /
|
|
||||||
-peer.reqCostGradient )
|
|
||||||
|
|
||||||
proc trackRequests(network: LesNetwork, peer: LesPeer, reqCountChange: int) =
|
|
||||||
peer.reqCount += reqCountChange
|
|
||||||
network.reqCount += reqCountChange
|
|
||||||
|
|
||||||
doAssert peer.reqCount >= 0 and network.reqCount >= 0
|
|
||||||
|
|
||||||
if peer.reqCount == 0:
|
|
||||||
# All requests have been finished. Start recharging.
|
|
||||||
peer.isRecharging = true
|
|
||||||
network.totalRechargingPower += peer.rechargingPower
|
|
||||||
elif peer.reqCount == reqCountChange and peer.isRecharging:
|
|
||||||
# `peer.reqCount` must have been 0 for the condition above to hold.
|
|
||||||
# This is a transition from recharging to serving state.
|
|
||||||
peer.isRecharging = false
|
|
||||||
network.totalRechargingPower -= peer.rechargingPower
|
|
||||||
peer.startReqCostVal = peer.reqCostVal
|
|
||||||
|
|
||||||
updateRechargingParams peer, network
|
|
||||||
|
|
||||||
proc updateFlowControl(network: LesNetwork, t: LesTime) =
|
|
||||||
while true:
|
|
||||||
var firstTime = t
|
|
||||||
for peer in network.peers:
|
|
||||||
# TODO: perhaps use a bin heap here
|
|
||||||
if peer.isRecharging and peer.rechargingEndsAt < firstTime:
|
|
||||||
firstTime = peer.rechargingEndsAt
|
|
||||||
|
|
||||||
let rechargingEndedForSomePeer = firstTime < t
|
|
||||||
|
|
||||||
network.reqCostSum = 0
|
|
||||||
for peer in network.peers:
|
|
||||||
peer.rechargeReqCost firstTime
|
|
||||||
network.reqCostSum += peer.reqCostVal
|
|
||||||
|
|
||||||
if rechargingEndedForSomePeer:
|
|
||||||
for peer in network.peers:
|
|
||||||
if peer.isRecharging:
|
|
||||||
updateRechargingParams peer, network
|
|
||||||
else:
|
|
||||||
network.lastUpdate = t
|
|
||||||
return
|
|
||||||
|
|
||||||
proc endPendingRequest*(network: LesNetwork, peer: LesPeer, t: LesTime) =
|
|
||||||
if peer.reqCount > 0:
|
|
||||||
network.updateFlowControl t
|
|
||||||
network.trackRequests peer, -1
|
|
||||||
network.updateFlowControl t
|
|
||||||
|
|
||||||
proc enlistInFlowControl*(network: LesNetwork,
|
|
||||||
peer: LesPeer,
|
|
||||||
peerRechargingPower = 100) =
|
|
||||||
let t = now()
|
|
||||||
|
|
||||||
doAssert peer.isServer or peer.isClient
|
|
||||||
# Each Peer must be potential communication partner for us.
|
|
||||||
# There will be useless peers on the network, but the logic
|
|
||||||
# should make sure to disconnect them earlier in `onPeerConnected`.
|
|
||||||
|
|
||||||
if peer.isServer:
|
|
||||||
peer.localFlowState.init network.bufferLimit, network.minRechargingRate, t
|
|
||||||
peer.pendingReqs = initTable[int, ReqCostInt]()
|
|
||||||
|
|
||||||
if peer.isClient:
|
|
||||||
peer.remoteFlowState.init network.bufferLimit, network.minRechargingRate, t
|
|
||||||
peer.lastRechargeTime = t
|
|
||||||
peer.rechargingEndsAt = t
|
|
||||||
peer.rechargingPower = peerRechargingPower
|
|
||||||
|
|
||||||
network.updateFlowControl t
|
|
||||||
|
|
||||||
proc delistFromFlowControl*(network: LesNetwork, peer: LesPeer) =
|
|
||||||
let t = now()
|
|
||||||
|
|
||||||
# XXX: perhaps this is not safe with our reqCount logic.
|
|
||||||
# The original code may depend on the binarity of the `serving` flag.
|
|
||||||
network.endPendingRequest peer, t
|
|
||||||
network.updateFlowControl t
|
|
||||||
|
|
||||||
proc initFlowControl*(network: LesNetwork, les: ProtocolInfo,
|
|
||||||
maxReqCount, maxReqCostSum, reqCostTarget: int) =
|
|
||||||
network.rechargingRate = rechargingScale * (rechargingScale /
|
|
||||||
(100 * rechargingScale / reqCostTarget - rechargingScale))
|
|
||||||
network.maxReqCount = maxReqCount
|
|
||||||
network.maxReqCostSum = maxReqCostSum
|
|
||||||
|
|
||||||
if not network.loadMessageStats(les):
|
|
||||||
warn "Failed to load persisted LES message stats. " &
|
|
||||||
"Flow control will be re-initilized."
|
|
||||||
|
|
||||||
#proc canMakeRequest(peer: var LesPeer, maxCost: int): (LesTime, float64) =
|
|
||||||
# peer.localFlowState.update now()
|
|
||||||
# return peer.localFlowState.canMakeRequest(maxCost)
|
|
||||||
|
|
||||||
template getRequestCost(peer: LesPeer, localOrRemote: untyped,
|
|
||||||
msgId, costQuantity: int): ReqCostInt =
|
|
||||||
let
|
|
||||||
baseCost = peer.`localOrRemote ReqCosts`[msgId].baseCost
|
|
||||||
reqCost = peer.`localOrRemote ReqCosts`[msgId].reqCost
|
|
||||||
|
|
||||||
min(baseCost + reqCost * costQuantity,
|
|
||||||
peer.`localOrRemote FlowState`.bufLimit)
|
|
||||||
|
|
||||||
proc trackOutgoingRequest*(network: LesNetwork, peer: LesPeer,
|
|
||||||
msgId, reqId, costQuantity: int) =
|
|
||||||
let maxCost = peer.getRequestCost(local, msgId, costQuantity)
|
|
||||||
|
|
||||||
peer.localFlowState.bufValue -= maxCost
|
|
||||||
peer.pendingReqsCost += maxCost
|
|
||||||
peer.pendingReqs[reqId] = peer.pendingReqsCost
|
|
||||||
|
|
||||||
proc trackIncomingResponse*(peer: LesPeer, reqId: int, bv: BufValueInt) =
|
|
||||||
let bv = min(bv, peer.localFlowState.bufLimit)
|
|
||||||
if not peer.pendingReqs.hasKey(reqId):
|
|
||||||
return
|
|
||||||
|
|
||||||
let costsSumAtSending = peer.pendingReqs.pop(reqId)
|
|
||||||
let costsSumChange = peer.pendingReqsCost - costsSumAtSending
|
|
||||||
|
|
||||||
peer.localFlowState.bufValue = if bv > costsSumChange: bv - costsSumChange
|
|
||||||
else: 0
|
|
||||||
peer.localFlowState.lastUpdate = now()
|
|
||||||
|
|
||||||
proc acceptRequest*(network: LesNetwork, peer: LesPeer,
|
|
||||||
msgId, costQuantity: int): Future[bool] {.async.} =
|
|
||||||
let t = now()
|
|
||||||
let reqCost = peer.getRequestCost(remote, msgId, costQuantity)
|
|
||||||
|
|
||||||
peer.remoteFlowState.update t
|
|
||||||
network.updateFlowControl t
|
|
||||||
|
|
||||||
while not network.canServeRequest:
|
|
||||||
await sleepAsync(chronos.milliseconds(10))
|
|
||||||
|
|
||||||
if peer notin network.peers:
|
|
||||||
# The peer was disconnected or the network
|
|
||||||
# was shut down while we waited
|
|
||||||
return false
|
|
||||||
|
|
||||||
network.trackRequests peer, +1
|
|
||||||
network.updateFlowControl network.lastUpdate
|
|
||||||
|
|
||||||
if reqCost > peer.remoteFlowState.bufValue:
|
|
||||||
error "LES peer sent request too early",
|
|
||||||
recharge = (reqCost - peer.remoteFlowState.bufValue) * rechargingScale /
|
|
||||||
peer.remoteFlowState.minRecharge
|
|
||||||
return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
|
|
||||||
proc bufValueAfterRequest*(network: LesNetwork, peer: LesPeer,
|
|
||||||
msgId: int, quantity: int): BufValueInt =
|
|
||||||
let t = now()
|
|
||||||
let costs = peer.remoteReqCosts[msgId]
|
|
||||||
var reqCost = costs.baseCost + quantity * costs.reqCost
|
|
||||||
|
|
||||||
peer.remoteFlowState.update t
|
|
||||||
peer.remoteFlowState.bufValue -= reqCost
|
|
||||||
|
|
||||||
network.endPendingRequest peer, t
|
|
||||||
|
|
||||||
let curReqCost = peer.reqCostVal
|
|
||||||
if curReqCost < peer.remoteFlowState.bufLimit:
|
|
||||||
let bv = peer.remoteFlowState.bufLimit - curReqCost
|
|
||||||
if bv > peer.remoteFlowState.bufValue:
|
|
||||||
peer.remoteFlowState.bufValue = bv
|
|
||||||
|
|
||||||
network.messageStats[msgId].addSample(float64(quantity),
|
|
||||||
float64(curReqCost - peer.startReqCostVal))
|
|
||||||
|
|
||||||
return peer.remoteFlowState.bufValue
|
|
||||||
|
|
||||||
when defined(testing):
|
|
||||||
import unittest2, random, ../../rlpx
|
|
||||||
|
|
||||||
proc isMax(s: FlowControlState): bool =
|
|
||||||
s.bufValue == s.bufLimit
|
|
||||||
|
|
||||||
p2pProtocol dummyLes(version = 1, rlpxName = "abc"):
|
|
||||||
proc a(p: Peer)
|
|
||||||
proc b(p: Peer)
|
|
||||||
proc c(p: Peer)
|
|
||||||
proc d(p: Peer)
|
|
||||||
proc e(p: Peer)
|
|
||||||
|
|
||||||
template fequals(lhs, rhs: float64, epsilon = 0.0001): bool =
|
|
||||||
abs(lhs-rhs) < epsilon
|
|
||||||
|
|
||||||
proc tests* =
|
|
||||||
randomize(3913631)
|
|
||||||
|
|
||||||
suite "les flow control":
|
|
||||||
suite "running averages":
|
|
||||||
test "consistent costs":
|
|
||||||
var s: StatsRunningAverage
|
|
||||||
for i in 0..100:
|
|
||||||
s.addSample(5.0, 100.0)
|
|
||||||
|
|
||||||
let (cost, base) = s.calc
|
|
||||||
|
|
||||||
check:
|
|
||||||
fequals(cost, 100.0)
|
|
||||||
fequals(base, 0.0)
|
|
||||||
|
|
||||||
test "randomized averages":
|
|
||||||
proc performTest(qBase, qRandom: int, cBase, cRandom: float64) =
|
|
||||||
var
|
|
||||||
s: StatsRunningAverage
|
|
||||||
expectedFinalCost = cBase + cRandom / 2
|
|
||||||
error = expectedFinalCost
|
|
||||||
|
|
||||||
for samples in [100, 1000, 10000]:
|
|
||||||
for i in 0..samples:
|
|
||||||
let q = float64(qBase + rand(10))
|
|
||||||
s.addSample(q, q * (cBase + rand(cRandom)))
|
|
||||||
|
|
||||||
let (newCost, newBase) = s.calc
|
|
||||||
# With more samples, our error should decrease, getting
|
|
||||||
# closer and closer to the average (unless we are already close enough)
|
|
||||||
let newError = abs(newCost - expectedFinalCost)
|
|
||||||
# This check fails with Nim-1.6:
|
|
||||||
# check newError < error
|
|
||||||
error = newError
|
|
||||||
|
|
||||||
# After enough samples we should be very close the final result
|
|
||||||
check error < (expectedFinalCost * 0.02)
|
|
||||||
|
|
||||||
performTest(1, 10, 5.0, 100.0)
|
|
||||||
performTest(1, 4, 200.0, 1000.0)
|
|
||||||
|
|
||||||
suite "buffer value calculations":
|
|
||||||
type TestReq = object
|
|
||||||
peer: LesPeer
|
|
||||||
msgId, quantity: int
|
|
||||||
accepted: bool
|
|
||||||
|
|
||||||
setup:
|
|
||||||
var lesNetwork = new LesNetwork
|
|
||||||
lesNetwork.peers = initHashSet[LesPeer]()
|
|
||||||
lesNetwork.initFlowControl(dummyLes.protocolInfo,
|
|
||||||
reqCostTarget = 300,
|
|
||||||
maxReqCount = 5,
|
|
||||||
maxReqCostSum = 1000)
|
|
||||||
|
|
||||||
for i in 0 ..< lesNetwork.messageStats.len:
|
|
||||||
lesNetwork.messageStats[i].addSample(1.0, float(i) * 100.0)
|
|
||||||
|
|
||||||
var client = new LesPeer
|
|
||||||
client.isClient = true
|
|
||||||
|
|
||||||
var server = new LesPeer
|
|
||||||
server.isServer = true
|
|
||||||
|
|
||||||
var clientServer = new LesPeer
|
|
||||||
clientServer.isClient = true
|
|
||||||
clientServer.isServer = true
|
|
||||||
|
|
||||||
var client2 = new LesPeer
|
|
||||||
client2.isClient = true
|
|
||||||
|
|
||||||
var client3 = new LesPeer
|
|
||||||
client3.isClient = true
|
|
||||||
|
|
||||||
var bv: BufValueInt
|
|
||||||
|
|
||||||
template enlist(peer: LesPeer) {.dirty.} =
|
|
||||||
let reqCosts = currentRequestsCosts(lesNetwork, dummyLes.protocolInfo)
|
|
||||||
peer.remoteReqCosts = reqCosts
|
|
||||||
peer.localReqCosts = reqCosts
|
|
||||||
lesNetwork.peers.incl peer
|
|
||||||
lesNetwork.enlistInFlowControl peer
|
|
||||||
|
|
||||||
template startReq(p: LesPeer, msg, q: int): TestReq =
|
|
||||||
var req: TestReq
|
|
||||||
req.peer = p
|
|
||||||
req.msgId = msg
|
|
||||||
req.quantity = q
|
|
||||||
req.accepted = waitFor lesNetwork.acceptRequest(p, msg, q)
|
|
||||||
req
|
|
||||||
|
|
||||||
template endReq(req: TestReq): BufValueInt =
|
|
||||||
bufValueAfterRequest(lesNetwork, req.peer, req.msgId, req.quantity)
|
|
||||||
|
|
||||||
test "single peer recharging":
|
|
||||||
lesNetwork.bufferLimit = 1000
|
|
||||||
lesNetwork.minRechargingRate = 100
|
|
||||||
|
|
||||||
enlist client
|
|
||||||
|
|
||||||
check:
|
|
||||||
client.remoteFlowState.isMax
|
|
||||||
client.rechargingPower > 0
|
|
||||||
|
|
||||||
advanceTime 100
|
|
||||||
|
|
||||||
let r1 = client.startReq(0, 100)
|
|
||||||
check r1.accepted
|
|
||||||
check client.isRecharging == false
|
|
||||||
|
|
||||||
advanceTime 50
|
|
||||||
|
|
||||||
let r2 = client.startReq(1, 1)
|
|
||||||
check r2.accepted
|
|
||||||
check client.isRecharging == false
|
|
||||||
|
|
||||||
advanceTime 25
|
|
||||||
bv = endReq r2
|
|
||||||
check client.isRecharging == false
|
|
||||||
|
|
||||||
advanceTime 130
|
|
||||||
bv = endReq r1
|
|
||||||
check client.isRecharging == true
|
|
||||||
|
|
||||||
advanceTime 300
|
|
||||||
lesNetwork.updateFlowControl now()
|
|
||||||
|
|
||||||
check:
|
|
||||||
client.isRecharging == false
|
|
||||||
client.remoteFlowState.isMax
|
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
# Nimbus
|
|
||||||
# Copyright (c) 2018-2023 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.
|
|
||||||
|
|
||||||
import
|
|
||||||
std/[hashes, tables, sets],
|
|
||||||
eth/common
|
|
||||||
|
|
||||||
type
|
|
||||||
AnnounceType* = enum
|
|
||||||
None,
|
|
||||||
Simple,
|
|
||||||
Signed,
|
|
||||||
Unspecified
|
|
||||||
|
|
||||||
ReqCostInfo* = object
|
|
||||||
msgId*: int
|
|
||||||
baseCost*, reqCost*: ReqCostInt
|
|
||||||
|
|
||||||
FlowControlState* = object
|
|
||||||
bufValue*, bufLimit*: int
|
|
||||||
minRecharge*: int
|
|
||||||
lastUpdate*: LesTime
|
|
||||||
|
|
||||||
StatsRunningAverage* = object
|
|
||||||
sumX*, sumY*, sumXX*, sumXY*: float64
|
|
||||||
count*: int
|
|
||||||
|
|
||||||
LesPeer* = ref object of RootRef
|
|
||||||
isServer*: bool
|
|
||||||
isClient*: bool
|
|
||||||
announceType*: AnnounceType
|
|
||||||
|
|
||||||
bestDifficulty*: DifficultyInt
|
|
||||||
bestBlockHash*: KeccakHash
|
|
||||||
bestBlockNumber*: BlockNumber
|
|
||||||
|
|
||||||
hasChainSince*: HashOrNum
|
|
||||||
hasStateSince*: HashOrNum
|
|
||||||
relaysTransactions*: bool
|
|
||||||
|
|
||||||
# The variables below are used to implement the flow control
|
|
||||||
# mechanisms of LES from our point of view as a server.
|
|
||||||
# They describe how much load has been generated by this
|
|
||||||
# particular peer.
|
|
||||||
reqCount*: int # How many outstanding requests are there?
|
|
||||||
#
|
|
||||||
rechargingPower*: int # Do we give this peer any extra priority
|
|
||||||
# (implemented as a faster recharning rate)
|
|
||||||
# 100 is the default. You can go higher and lower.
|
|
||||||
#
|
|
||||||
isRecharging*: bool # This is true while the peer is not making
|
|
||||||
# any requests
|
|
||||||
#
|
|
||||||
reqCostGradient*: int # Measures the speed of recharging or accumulating
|
|
||||||
# "requests cost" at any given moment.
|
|
||||||
#
|
|
||||||
reqCostVal*: int # The accumulated "requests cost"
|
|
||||||
#
|
|
||||||
rechargingEndsAt*: int # When will recharging end?
|
|
||||||
# (the buffer of the Peer will be fully restored)
|
|
||||||
#
|
|
||||||
lastRechargeTime*: LesTime # When did we last update the recharging parameters
|
|
||||||
#
|
|
||||||
startReqCostVal*: int # TODO
|
|
||||||
|
|
||||||
remoteFlowState*: FlowControlState
|
|
||||||
remoteReqCosts*: seq[ReqCostInfo]
|
|
||||||
|
|
||||||
# The next variables are used to limit ourselves as a client in order to
|
|
||||||
# not violate the control-flow requirements of the remote LES server.
|
|
||||||
|
|
||||||
pendingReqs*: Table[int, ReqCostInt]
|
|
||||||
pendingReqsCost*: int
|
|
||||||
|
|
||||||
localFlowState*: FlowControlState
|
|
||||||
localReqCosts*: seq[ReqCostInfo]
|
|
||||||
|
|
||||||
LesNetwork* = ref object of RootRef
|
|
||||||
peers*: HashSet[LesPeer]
|
|
||||||
messageStats*: seq[StatsRunningAverage]
|
|
||||||
ourAnnounceType*: AnnounceType
|
|
||||||
|
|
||||||
# The fields below are relevant when serving data.
|
|
||||||
bufferLimit*: int
|
|
||||||
minRechargingRate*: int
|
|
||||||
|
|
||||||
reqCostSum*, maxReqCostSum*: ReqCostInt
|
|
||||||
reqCount*, maxReqCount*: int
|
|
||||||
sumWeigth*: int
|
|
||||||
|
|
||||||
rechargingRate*: int64
|
|
||||||
totalRechargedUnits*: int
|
|
||||||
totalRechargingPower*: int
|
|
||||||
|
|
||||||
lastUpdate*: LesTime
|
|
||||||
|
|
||||||
KeyValuePair* = object
|
|
||||||
key*: string
|
|
||||||
value*: Blob
|
|
||||||
|
|
||||||
HandshakeError* = object of CatchableError
|
|
||||||
|
|
||||||
LesTime* = int # this is in milliseconds
|
|
||||||
BufValueInt* = int
|
|
||||||
ReqCostInt* = int
|
|
||||||
|
|
||||||
template hash*(peer: LesPeer): Hash = hash(cast[pointer](peer))
|
|
||||||
|
|
||||||
template areWeServingData*(network: LesNetwork): bool =
|
|
||||||
network.maxReqCount != 0
|
|
||||||
|
|
||||||
template areWeRequestingData*(network: LesNetwork): bool =
|
|
||||||
network.ourAnnounceType != AnnounceType.Unspecified
|
|
||||||
|
|
||||||
proc setSetting*(ctx: LesNetwork, key: string, val: openArray[byte]) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getSetting*(ctx: LesNetwork, key: string): seq[byte] =
|
|
||||||
discard
|
|
||||||
|
|
|
@ -1,524 +0,0 @@
|
||||||
# Nimbus
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
import
|
|
||||||
std/[times, tables, options, sets, hashes, strutils],
|
|
||||||
stew/shims/macros, chronicles, chronos, nimcrypto/[keccak, hash],
|
|
||||||
eth/[rlp, keys, common],
|
|
||||||
eth/p2p/[rlpx, kademlia, private/p2p_types],
|
|
||||||
./les/private/les_types, ./les/flow_control,
|
|
||||||
../types
|
|
||||||
|
|
||||||
export
|
|
||||||
les_types
|
|
||||||
|
|
||||||
type
|
|
||||||
ProofRequest* = object
|
|
||||||
blockHash*: KeccakHash
|
|
||||||
accountKey*: Blob
|
|
||||||
key*: Blob
|
|
||||||
fromLevel*: uint
|
|
||||||
|
|
||||||
ContractCodeRequest* = object
|
|
||||||
blockHash*: KeccakHash
|
|
||||||
key*: EthAddress
|
|
||||||
|
|
||||||
HelperTrieProofRequest* = object
|
|
||||||
subType*: uint
|
|
||||||
sectionIdx*: uint
|
|
||||||
key*: Blob
|
|
||||||
fromLevel*: uint
|
|
||||||
auxReq*: uint
|
|
||||||
|
|
||||||
LesStatus = object
|
|
||||||
difficulty : DifficultyInt
|
|
||||||
blockHash : Hash256
|
|
||||||
blockNumber: BlockNumber
|
|
||||||
genesisHash: Hash256
|
|
||||||
|
|
||||||
const
|
|
||||||
lesVersion = 2
|
|
||||||
maxHeadersFetch = 192
|
|
||||||
maxBodiesFetch = 32
|
|
||||||
maxReceiptsFetch = 128
|
|
||||||
maxCodeFetch = 64
|
|
||||||
maxProofsFetch = 64
|
|
||||||
maxHeaderProofsFetch = 64
|
|
||||||
maxTransactionsFetch = 64
|
|
||||||
|
|
||||||
# Handshake properties:
|
|
||||||
# https://github.com/zsfelfoldi/go-ethereum/wiki/Light-Ethereum-Subprotocol-(LES)
|
|
||||||
keyProtocolVersion = "protocolVersion"
|
|
||||||
## P: is 1 for the LPV1 protocol version.
|
|
||||||
|
|
||||||
keyNetworkId = "networkId"
|
|
||||||
## P: should be 0 for testnet, 1 for mainnet.
|
|
||||||
|
|
||||||
keyHeadTotalDifficulty = "headTd"
|
|
||||||
## P: Total Difficulty of the best chain.
|
|
||||||
## Integer, as found in block header.
|
|
||||||
|
|
||||||
keyHeadHash = "headHash"
|
|
||||||
## B_32: the hash of the best (i.e. highest TD) known block.
|
|
||||||
|
|
||||||
keyHeadNumber = "headNum"
|
|
||||||
## P: the number of the best (i.e. highest TD) known block.
|
|
||||||
|
|
||||||
keyGenesisHash = "genesisHash"
|
|
||||||
## B_32: the hash of the Genesis block.
|
|
||||||
|
|
||||||
#keyServeHeaders = "serveHeaders"
|
|
||||||
# ## (optional, no value)
|
|
||||||
# ## present if the peer can serve header chain downloads.
|
|
||||||
|
|
||||||
keyServeChainSince = "serveChainSince"
|
|
||||||
## P (optional)
|
|
||||||
## present if the peer can serve Body/Receipts ODR requests
|
|
||||||
## starting from the given block number.
|
|
||||||
|
|
||||||
keyServeStateSince = "serveStateSince"
|
|
||||||
## P (optional):
|
|
||||||
## present if the peer can serve Proof/Code ODR requests
|
|
||||||
## starting from the given block number.
|
|
||||||
|
|
||||||
keyRelaysTransactions = "txRelay"
|
|
||||||
## (optional, no value)
|
|
||||||
## present if the peer can relay transactions to the ETH network.
|
|
||||||
|
|
||||||
keyFlowControlBL = "flowControl/BL"
|
|
||||||
keyFlowControlMRC = "flowControl/MRC"
|
|
||||||
keyFlowControlMRR = "flowControl/MRR"
|
|
||||||
## see Client Side Flow Control:
|
|
||||||
## https://github.com/zsfelfoldi/go-ethereum/wiki/Client-Side-Flow-Control-model-for-the-LES-protocol
|
|
||||||
|
|
||||||
keyAnnounceType = "announceType"
|
|
||||||
keyAnnounceSignature = "sign"
|
|
||||||
|
|
||||||
proc getStatus(ctx: LesNetwork): LesStatus =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getBlockBodies(ctx: LesNetwork, hashes: openArray[Hash256]): seq[BlockBody] =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getBlockHeaders(ctx: LesNetwork, req: BlocksRequest): seq[BlockHeader] =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getReceipts(ctx: LesNetwork, hashes: openArray[Hash256]): seq[Receipt] =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getProofs(ctx: LesNetwork, proofs: openArray[ProofRequest]): seq[Blob] =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getContractCodes(ctx: LesNetwork, reqs: openArray[ContractCodeRequest]): seq[Blob] =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getHeaderProofs(ctx: LesNetwork, reqs: openArray[ProofRequest]): seq[Blob] =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getHelperTrieProofs(ctx: LesNetwork,
|
|
||||||
reqs: openArray[HelperTrieProofRequest],
|
|
||||||
outNodes: var seq[Blob], outAuxData: var seq[Blob]) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc getTransactionStatus(ctx: LesNetwork, txHash: KeccakHash): TransactionStatusMsg =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc addTransactions(ctx: LesNetwork, transactions: openArray[Transaction]) =
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc initProtocolState(network: LesNetwork, node: EthereumNode) {.gcsafe.} =
|
|
||||||
network.peers = initHashSet[LesPeer]()
|
|
||||||
|
|
||||||
proc addPeer(network: LesNetwork, peer: LesPeer) =
|
|
||||||
network.enlistInFlowControl peer
|
|
||||||
network.peers.incl peer
|
|
||||||
|
|
||||||
proc removePeer(network: LesNetwork, peer: LesPeer) =
|
|
||||||
network.delistFromFlowControl peer
|
|
||||||
network.peers.excl peer
|
|
||||||
|
|
||||||
template costQuantity(quantityExpr, max: untyped) {.pragma.}
|
|
||||||
|
|
||||||
proc getCostQuantity(fn: NimNode): tuple[quantityExpr, maxQuantity: NimNode] =
|
|
||||||
# XXX: `getCustomPragmaVal` doesn't work yet on regular nnkProcDef nodes
|
|
||||||
# (TODO: file as an issue)
|
|
||||||
let costQuantity = fn.pragma.findPragma(bindSym"costQuantity")
|
|
||||||
doAssert costQuantity != nil
|
|
||||||
|
|
||||||
result.quantityExpr = costQuantity[1]
|
|
||||||
result.maxQuantity= costQuantity[2]
|
|
||||||
|
|
||||||
if result.maxQuantity.kind == nnkExprEqExpr:
|
|
||||||
result.maxQuantity = result.maxQuantity[1]
|
|
||||||
|
|
||||||
macro outgoingRequestDecorator(n: untyped): untyped =
|
|
||||||
result = n
|
|
||||||
#let (costQuantity, maxQuantity) = n.getCostQuantity
|
|
||||||
let (costQuantity, _) = n.getCostQuantity
|
|
||||||
|
|
||||||
result.body.add quote do:
|
|
||||||
trackOutgoingRequest(peer.networkState(les),
|
|
||||||
peer.state(les),
|
|
||||||
perProtocolMsgId, reqId, `costQuantity`)
|
|
||||||
#echo "outgoingRequestDecorator: ", result.repr
|
|
||||||
|
|
||||||
macro incomingResponseDecorator(n: untyped): untyped =
|
|
||||||
result = n
|
|
||||||
|
|
||||||
let trackingCall = quote do:
|
|
||||||
try:
|
|
||||||
trackIncomingResponse(peer.state(les), reqId, msg.bufValue)
|
|
||||||
except KeyError as exc:
|
|
||||||
raise newException(EthP2PError, exc.msg)
|
|
||||||
|
|
||||||
result.body.insert(n.body.len - 1, trackingCall)
|
|
||||||
#echo "incomingResponseDecorator: ", result.repr
|
|
||||||
|
|
||||||
macro incomingRequestDecorator(n: untyped): untyped =
|
|
||||||
result = n
|
|
||||||
let (costQuantity, maxQuantity) = n.getCostQuantity
|
|
||||||
|
|
||||||
template acceptStep(quantityExpr, maxQuantity) {.dirty.} =
|
|
||||||
let requestCostQuantity = quantityExpr
|
|
||||||
if requestCostQuantity > maxQuantity:
|
|
||||||
await peer.disconnect(BreachOfProtocol)
|
|
||||||
return
|
|
||||||
|
|
||||||
let lesPeer = peer.state
|
|
||||||
let lesNetwork = peer.networkState
|
|
||||||
|
|
||||||
if not await acceptRequest(lesNetwork, lesPeer,
|
|
||||||
perProtocolMsgId,
|
|
||||||
requestCostQuantity): return
|
|
||||||
|
|
||||||
let zero = result.body[0][0]
|
|
||||||
zero.insert(1, getAst(acceptStep(costQuantity, maxQuantity)))
|
|
||||||
#echo "incomingRequestDecorator: ", result.repr
|
|
||||||
|
|
||||||
template updateBV: BufValueInt =
|
|
||||||
bufValueAfterRequest(lesNetwork, lesPeer,
|
|
||||||
perProtocolMsgId, requestCostQuantity)
|
|
||||||
|
|
||||||
func getValue(values: openArray[KeyValuePair],
|
|
||||||
key: string, T: typedesc): Option[T] =
|
|
||||||
for v in values:
|
|
||||||
if v.key == key:
|
|
||||||
return some(rlp.decode(v.value, T))
|
|
||||||
|
|
||||||
func getRequiredValue(values: openArray[KeyValuePair],
|
|
||||||
key: string, T: typedesc): T =
|
|
||||||
for v in values:
|
|
||||||
if v.key == key:
|
|
||||||
return rlp.decode(v.value, T)
|
|
||||||
|
|
||||||
raise newException(HandshakeError,
|
|
||||||
"Required handshake field " & key & " missing")
|
|
||||||
|
|
||||||
p2pProtocol les(version = lesVersion,
|
|
||||||
peerState = LesPeer,
|
|
||||||
networkState = LesNetwork,
|
|
||||||
outgoingRequestDecorator = outgoingRequestDecorator,
|
|
||||||
incomingRequestDecorator = incomingRequestDecorator,
|
|
||||||
incomingResponseThunkDecorator = incomingResponseDecorator):
|
|
||||||
handshake:
|
|
||||||
proc status(p: Peer, values: openArray[KeyValuePair])
|
|
||||||
|
|
||||||
onPeerConnected do (peer: Peer):
|
|
||||||
let
|
|
||||||
network = peer.network
|
|
||||||
lesPeer = peer.state
|
|
||||||
lesNetwork = peer.networkState
|
|
||||||
status = lesNetwork.getStatus()
|
|
||||||
|
|
||||||
template `=>`(k, v: untyped): untyped =
|
|
||||||
KeyValuePair(key: k, value: rlp.encode(v))
|
|
||||||
|
|
||||||
var lesProperties = @[
|
|
||||||
keyProtocolVersion => lesVersion,
|
|
||||||
keyNetworkId => network.networkId,
|
|
||||||
keyHeadTotalDifficulty => status.difficulty,
|
|
||||||
keyHeadHash => status.blockHash,
|
|
||||||
keyHeadNumber => status.blockNumber,
|
|
||||||
keyGenesisHash => status.genesisHash
|
|
||||||
]
|
|
||||||
|
|
||||||
lesPeer.remoteReqCosts = currentRequestsCosts(lesNetwork, les.protocolInfo)
|
|
||||||
|
|
||||||
if lesNetwork.areWeServingData:
|
|
||||||
lesProperties.add [
|
|
||||||
# keyServeHeaders => nil,
|
|
||||||
keyServeChainSince => 0,
|
|
||||||
keyServeStateSince => 0,
|
|
||||||
# keyRelaysTransactions => nil,
|
|
||||||
keyFlowControlBL => lesNetwork.bufferLimit,
|
|
||||||
keyFlowControlMRR => lesNetwork.minRechargingRate,
|
|
||||||
keyFlowControlMRC => lesPeer.remoteReqCosts
|
|
||||||
]
|
|
||||||
|
|
||||||
if lesNetwork.areWeRequestingData:
|
|
||||||
lesProperties.add(keyAnnounceType => lesNetwork.ourAnnounceType)
|
|
||||||
|
|
||||||
let
|
|
||||||
s = await peer.status(lesProperties, timeout = chronos.seconds(10))
|
|
||||||
peerNetworkId = s.values.getRequiredValue(keyNetworkId, NetworkId)
|
|
||||||
peerGenesisHash = s.values.getRequiredValue(keyGenesisHash, KeccakHash)
|
|
||||||
peerLesVersion = s.values.getRequiredValue(keyProtocolVersion, uint)
|
|
||||||
|
|
||||||
template requireCompatibility(peerVar, localVar, varName: untyped) =
|
|
||||||
if localVar != peerVar:
|
|
||||||
raise newException(HandshakeError,
|
|
||||||
"Incompatibility detected! $1 mismatch ($2 != $3)" %
|
|
||||||
[varName, $localVar, $peerVar])
|
|
||||||
|
|
||||||
requireCompatibility(peerLesVersion, uint(lesVersion), "les version")
|
|
||||||
requireCompatibility(peerNetworkId, network.networkId, "network id")
|
|
||||||
requireCompatibility(peerGenesisHash, status.genesisHash, "genesis hash")
|
|
||||||
|
|
||||||
template `:=`(lhs, key) =
|
|
||||||
lhs = s.values.getRequiredValue(key, type(lhs))
|
|
||||||
|
|
||||||
lesPeer.bestBlockHash := keyHeadHash
|
|
||||||
lesPeer.bestBlockNumber := keyHeadNumber
|
|
||||||
lesPeer.bestDifficulty := keyHeadTotalDifficulty
|
|
||||||
|
|
||||||
let peerAnnounceType = s.values.getValue(keyAnnounceType, AnnounceType)
|
|
||||||
if peerAnnounceType.isSome:
|
|
||||||
lesPeer.isClient = true
|
|
||||||
lesPeer.announceType = peerAnnounceType.get
|
|
||||||
else:
|
|
||||||
lesPeer.announceType = AnnounceType.Simple
|
|
||||||
lesPeer.hasChainSince := keyServeChainSince
|
|
||||||
lesPeer.hasStateSince := keyServeStateSince
|
|
||||||
lesPeer.relaysTransactions := keyRelaysTransactions
|
|
||||||
lesPeer.localFlowState.bufLimit := keyFlowControlBL
|
|
||||||
lesPeer.localFlowState.minRecharge := keyFlowControlMRR
|
|
||||||
lesPeer.localReqCosts := keyFlowControlMRC
|
|
||||||
|
|
||||||
lesNetwork.addPeer lesPeer
|
|
||||||
|
|
||||||
onPeerDisconnected do (peer: Peer, reason: DisconnectionReason) {.gcsafe.}:
|
|
||||||
peer.networkState.removePeer peer.state
|
|
||||||
|
|
||||||
## Header synchronisation
|
|
||||||
##
|
|
||||||
|
|
||||||
proc announce(
|
|
||||||
peer: Peer,
|
|
||||||
headHash: KeccakHash,
|
|
||||||
headNumber: BlockNumber,
|
|
||||||
headTotalDifficulty: DifficultyInt,
|
|
||||||
reorgDepth: BlockNumber,
|
|
||||||
values: openArray[KeyValuePair],
|
|
||||||
announceType: AnnounceType) =
|
|
||||||
|
|
||||||
if peer.state.announceType == AnnounceType.None:
|
|
||||||
error "unexpected announce message", peer
|
|
||||||
return
|
|
||||||
|
|
||||||
if announceType == AnnounceType.Signed:
|
|
||||||
let signature = values.getValue(keyAnnounceSignature, Blob)
|
|
||||||
if signature.isNone:
|
|
||||||
chronicles.error "missing announce signature"
|
|
||||||
return
|
|
||||||
let sig = Signature.fromRaw(signature.get).tryGet()
|
|
||||||
let sigMsg = rlp.encodeList(headHash, headNumber, headTotalDifficulty)
|
|
||||||
let signerKey = recover(sig, sigMsg).tryGet()
|
|
||||||
if signerKey.toNodeId != peer.remote.id:
|
|
||||||
chronicles.error "invalid announce signature"
|
|
||||||
# TODO: should we disconnect this peer?
|
|
||||||
return
|
|
||||||
|
|
||||||
# TODO: handle new block
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc getBlockHeaders(
|
|
||||||
peer: Peer,
|
|
||||||
req: BlocksRequest) {.
|
|
||||||
costQuantity(req.maxResults.int, max = maxHeadersFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
let headers = ctx.getBlockHeaders(req)
|
|
||||||
await response.send(updateBV(), headers)
|
|
||||||
|
|
||||||
proc blockHeaders(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
blocks: openArray[BlockHeader])
|
|
||||||
|
|
||||||
## On-damand data retrieval
|
|
||||||
##
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc getBlockBodies(
|
|
||||||
peer: Peer,
|
|
||||||
blocks: openArray[KeccakHash]) {.
|
|
||||||
costQuantity(blocks.len, max = maxBodiesFetch), gcsafe.} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
let blocks = ctx.getBlockBodies(blocks)
|
|
||||||
await response.send(updateBV(), blocks)
|
|
||||||
|
|
||||||
proc blockBodies(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
bodies: openArray[BlockBody])
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc getReceipts(
|
|
||||||
peer: Peer,
|
|
||||||
hashes: openArray[KeccakHash])
|
|
||||||
{.costQuantity(hashes.len, max = maxReceiptsFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
let receipts = ctx.getReceipts(hashes)
|
|
||||||
await response.send(updateBV(), receipts)
|
|
||||||
|
|
||||||
proc receipts(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
receipts: openArray[Receipt])
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc getProofs(
|
|
||||||
peer: Peer,
|
|
||||||
proofs: openArray[ProofRequest]) {.
|
|
||||||
costQuantity(proofs.len, max = maxProofsFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
let proofs = ctx.getProofs(proofs)
|
|
||||||
await response.send(updateBV(), proofs)
|
|
||||||
|
|
||||||
proc proofs(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
proofs: openArray[Blob])
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc getContractCodes(
|
|
||||||
peer: Peer,
|
|
||||||
reqs: seq[ContractCodeRequest]) {.
|
|
||||||
costQuantity(reqs.len, max = maxCodeFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
let results = ctx.getContractCodes(reqs)
|
|
||||||
await response.send(updateBV(), results)
|
|
||||||
|
|
||||||
proc contractCodes(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
results: seq[Blob])
|
|
||||||
|
|
||||||
nextID 15
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc getHeaderProofs(
|
|
||||||
peer: Peer,
|
|
||||||
reqs: openArray[ProofRequest]) {.
|
|
||||||
costQuantity(reqs.len, max = maxHeaderProofsFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
let proofs = ctx.getHeaderProofs(reqs)
|
|
||||||
await response.send(updateBV(), proofs)
|
|
||||||
|
|
||||||
proc headerProofs(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
proofs: openArray[Blob])
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc getHelperTrieProofs(
|
|
||||||
peer: Peer,
|
|
||||||
reqs: openArray[HelperTrieProofRequest]) {.
|
|
||||||
costQuantity(reqs.len, max = maxProofsFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
var nodes, auxData: seq[Blob]
|
|
||||||
ctx.getHelperTrieProofs(reqs, nodes, auxData)
|
|
||||||
await response.send(updateBV(), nodes, auxData)
|
|
||||||
|
|
||||||
proc helperTrieProofs(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
nodes: seq[Blob],
|
|
||||||
auxData: seq[Blob])
|
|
||||||
|
|
||||||
## Transaction relaying and status retrieval
|
|
||||||
##
|
|
||||||
|
|
||||||
requestResponse:
|
|
||||||
proc sendTxV2(
|
|
||||||
peer: Peer,
|
|
||||||
transactions: openArray[Transaction]) {.
|
|
||||||
costQuantity(transactions.len, max = maxTransactionsFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
|
|
||||||
var results: seq[TransactionStatusMsg]
|
|
||||||
for t in transactions:
|
|
||||||
let hash = t.rlpHash
|
|
||||||
var s = ctx.getTransactionStatus(hash)
|
|
||||||
if s.status == TransactionStatus.Unknown:
|
|
||||||
ctx.addTransactions([t])
|
|
||||||
s = ctx.getTransactionStatus(hash)
|
|
||||||
|
|
||||||
results.add s
|
|
||||||
|
|
||||||
await response.send(updateBV(), results)
|
|
||||||
|
|
||||||
proc getTxStatus(
|
|
||||||
peer: Peer,
|
|
||||||
transactions: openArray[Transaction]) {.
|
|
||||||
costQuantity(transactions.len, max = maxTransactionsFetch).} =
|
|
||||||
|
|
||||||
let ctx = peer.networkState()
|
|
||||||
|
|
||||||
var results: seq[TransactionStatusMsg]
|
|
||||||
for t in transactions:
|
|
||||||
results.add ctx.getTransactionStatus(t.rlpHash)
|
|
||||||
await response.send(updateBV(), results)
|
|
||||||
|
|
||||||
proc txStatus(
|
|
||||||
peer: Peer,
|
|
||||||
bufValue: BufValueInt,
|
|
||||||
transactions: openArray[TransactionStatusMsg])
|
|
||||||
|
|
||||||
proc configureLes*(node: EthereumNode,
|
|
||||||
# Client options:
|
|
||||||
announceType = AnnounceType.Simple,
|
|
||||||
# Server options.
|
|
||||||
# The zero default values indicate that the
|
|
||||||
# LES server will be deactivated.
|
|
||||||
maxReqCount = 0,
|
|
||||||
maxReqCostSum = 0,
|
|
||||||
reqCostTarget = 0) =
|
|
||||||
|
|
||||||
doAssert announceType != AnnounceType.Unspecified or maxReqCount > 0
|
|
||||||
|
|
||||||
var lesNetwork = node.protocolState(les)
|
|
||||||
lesNetwork.ourAnnounceType = announceType
|
|
||||||
initFlowControl(lesNetwork, les.protocolInfo,
|
|
||||||
maxReqCount, maxReqCostSum, reqCostTarget)
|
|
||||||
|
|
||||||
proc configureLesServer*(node: EthereumNode,
|
|
||||||
# Client options:
|
|
||||||
announceType = AnnounceType.Unspecified,
|
|
||||||
# Server options.
|
|
||||||
# The zero default values indicate that the
|
|
||||||
# LES server will be deactivated.
|
|
||||||
maxReqCount = 0,
|
|
||||||
maxReqCostSum = 0,
|
|
||||||
reqCostTarget = 0) =
|
|
||||||
## This is similar to `configureLes`, but with default parameter
|
|
||||||
## values appropriate for a server.
|
|
||||||
node.configureLes(announceType, maxReqCount, maxReqCostSum, reqCostTarget)
|
|
||||||
|
|
||||||
proc persistLesMessageStats*(node: EthereumNode) =
|
|
||||||
persistMessageStats(node.protocolState(les))
|
|
||||||
|
|
|
@ -157,19 +157,9 @@ proc configurationMain*() =
|
||||||
let flags = conf.getProtocolFlags()
|
let flags = conf.getProtocolFlags()
|
||||||
check ProtocolFlag.Eth in flags
|
check ProtocolFlag.Eth in flags
|
||||||
|
|
||||||
let aa = makeConfig(@["--protocols:les"])
|
let bb = makeConfig(@["--protocols:eth"])
|
||||||
let ax = aa.getProtocolFlags()
|
|
||||||
check ProtocolFlag.Les in ax
|
|
||||||
|
|
||||||
let bb = makeConfig(@["--protocols:eth", "--protocols:les"])
|
|
||||||
let bx = bb.getProtocolFlags()
|
let bx = bb.getProtocolFlags()
|
||||||
check ProtocolFlag.Eth in bx
|
check ProtocolFlag.Eth in bx
|
||||||
check ProtocolFlag.Les in bx
|
|
||||||
|
|
||||||
let cc = makeConfig(@["--protocols:eth,les"])
|
|
||||||
let cx = cc.getProtocolFlags()
|
|
||||||
check ProtocolFlag.Eth in cx
|
|
||||||
check ProtocolFlag.Les in cx
|
|
||||||
|
|
||||||
test "bootstrap-node and bootstrap-file":
|
test "bootstrap-node and bootstrap-file":
|
||||||
let conf = makeTestConfig()
|
let conf = makeTestConfig()
|
||||||
|
|
Loading…
Reference in New Issue