serve libp2p protocol for light client sync (#3341)
This extends the `--serve-light-client-data` launch option to serve locally collected light client data via libp2p. Backfill of historic best `LightClientUpdate` is not yet implemented. See https://github.com/ethereum/consensus-specs/pull/2802
This commit is contained in:
parent
70270eeabe
commit
b2b7b0bd56
|
@ -7,6 +7,10 @@
|
||||||
|
|
||||||
{.push raises: [Defect].}
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
|
# References to `vFuture` refer to the pre-release proposal of the libp2p based
|
||||||
|
# light client sync protocol. Conflicting release versions are not in use.
|
||||||
|
# https://github.com/ethereum/consensus-specs/pull/2802
|
||||||
|
|
||||||
import
|
import
|
||||||
options, tables, sets, macros,
|
options, tables, sets, macros,
|
||||||
chronicles, chronos, stew/ranges/bitranges, libp2p/switch,
|
chronicles, chronos, stew/ranges/bitranges, libp2p/switch,
|
||||||
|
@ -21,10 +25,16 @@ logScope:
|
||||||
|
|
||||||
const
|
const
|
||||||
MAX_REQUEST_BLOCKS = 1024
|
MAX_REQUEST_BLOCKS = 1024
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#configuration
|
||||||
|
MAX_REQUEST_LIGHT_CLIENT_UPDATES = 128
|
||||||
|
|
||||||
blockByRootLookupCost = allowedOpsPerSecondCost(50)
|
blockByRootLookupCost = allowedOpsPerSecondCost(50)
|
||||||
blockResponseCost = allowedOpsPerSecondCost(100)
|
blockResponseCost = allowedOpsPerSecondCost(100)
|
||||||
blockByRangeLookupCost = allowedOpsPerSecondCost(20)
|
blockByRangeLookupCost = allowedOpsPerSecondCost(20)
|
||||||
|
lightClientUpdateResponseCost = allowedOpsPerSecondCost(100)
|
||||||
|
lightClientUpdateByRangeLookupCost = allowedOpsPerSecondCost(20)
|
||||||
|
lightClientBootstrapLookupCost = allowedOpsPerSecondCost(5)
|
||||||
|
lightClientBootstrapResponseCost = allowedOpsPerSecondCost(100)
|
||||||
|
|
||||||
type
|
type
|
||||||
StatusMsg* = object
|
StatusMsg* = object
|
||||||
|
@ -439,6 +449,114 @@ p2pProtocol BeaconSync(version = 1,
|
||||||
debug "Block root request done",
|
debug "Block root request done",
|
||||||
peer, roots = blockRoots.len, count, found
|
peer, roots = blockRoots.len, count, found
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#bestlightclientupdatesbyrange
|
||||||
|
proc bestLightClientUpdatesByRange(
|
||||||
|
peer: Peer,
|
||||||
|
startPeriod: SyncCommitteePeriod,
|
||||||
|
reqCount: uint64,
|
||||||
|
response: MultipleChunksResponse[altair.LightClientUpdate])
|
||||||
|
{.async, libp2pProtocol("best_light_client_updates_by_range", 0,
|
||||||
|
isLightClientRequest = true).} =
|
||||||
|
trace "Received LC updates by range request", peer, startPeriod, reqCount
|
||||||
|
if reqCount == 0'u64:
|
||||||
|
raise newException(InvalidInputsError, "Empty range requested")
|
||||||
|
let dag = peer.networkState.dag
|
||||||
|
doAssert dag.serveLightClientData
|
||||||
|
|
||||||
|
let
|
||||||
|
headPeriod = dag.head.slot.sync_committee_period
|
||||||
|
# Limit number of updates in response
|
||||||
|
maxSupportedCount =
|
||||||
|
if startPeriod > headPeriod:
|
||||||
|
0'u64
|
||||||
|
else:
|
||||||
|
min(headPeriod + 1 - startPeriod, MAX_REQUEST_LIGHT_CLIENT_UPDATES)
|
||||||
|
count = min(reqCount, maxSupportedCount)
|
||||||
|
onePastPeriod = startPeriod + count
|
||||||
|
|
||||||
|
peer.updateRequestQuota(count.float * lightClientUpdateByRangeLookupCost)
|
||||||
|
peer.awaitNonNegativeRequestQuota()
|
||||||
|
|
||||||
|
var found = 0
|
||||||
|
for period in startPeriod..<onePastPeriod:
|
||||||
|
let update = dag.getBestLightClientUpdateForPeriod(period)
|
||||||
|
if update.isSome:
|
||||||
|
await response.write(update.get)
|
||||||
|
inc found
|
||||||
|
|
||||||
|
peer.updateRequestQuota(found.float * lightClientUpdateResponseCost)
|
||||||
|
|
||||||
|
debug "LC updates by range request done", peer, startPeriod, count, found
|
||||||
|
|
||||||
|
if found == 0 and count > 0:
|
||||||
|
raise newException(ResourceUnavailableError,
|
||||||
|
"No light client update available")
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#getlatestlightclientupdate
|
||||||
|
proc latestLightClientUpdate(
|
||||||
|
peer: Peer,
|
||||||
|
response: SingleChunkResponse[altair.LightClientUpdate])
|
||||||
|
{.async, libp2pProtocol("latest_light_client_update", 0,
|
||||||
|
isLightClientRequest = true).} =
|
||||||
|
trace "Received latest LC update request", peer
|
||||||
|
let dag = peer.networkState.dag
|
||||||
|
doAssert dag.serveLightClientData
|
||||||
|
|
||||||
|
peer.awaitNonNegativeRequestQuota()
|
||||||
|
|
||||||
|
let update = dag.getLatestLightClientUpdate
|
||||||
|
if update.isSome:
|
||||||
|
await response.send(update.get)
|
||||||
|
else:
|
||||||
|
raise newException(ResourceUnavailableError,
|
||||||
|
"No light client update available")
|
||||||
|
|
||||||
|
peer.updateRequestQuota(lightClientUpdateResponseCost)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#getoptimisticlightclientupdate
|
||||||
|
proc optimisticLightClientUpdate(
|
||||||
|
peer: Peer,
|
||||||
|
response: SingleChunkResponse[OptimisticLightClientUpdate])
|
||||||
|
{.async, libp2pProtocol("optimistic_light_client_update", 0,
|
||||||
|
isLightClientRequest = true).} =
|
||||||
|
trace "Received optimistic LC update request", peer
|
||||||
|
let dag = peer.networkState.dag
|
||||||
|
doAssert dag.serveLightClientData
|
||||||
|
|
||||||
|
peer.awaitNonNegativeRequestQuota()
|
||||||
|
|
||||||
|
let optimistic_update = dag.getOptimisticLightClientUpdate
|
||||||
|
if optimistic_update.isSome:
|
||||||
|
await response.send(optimistic_update.get)
|
||||||
|
else:
|
||||||
|
raise newException(ResourceUnavailableError,
|
||||||
|
"No optimistic light client update available")
|
||||||
|
|
||||||
|
peer.updateRequestQuota(lightClientUpdateResponseCost)
|
||||||
|
|
||||||
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#getlightclientbootstrap
|
||||||
|
proc lightClientBootstrap(
|
||||||
|
peer: Peer,
|
||||||
|
blockRoot: Eth2Digest,
|
||||||
|
response: SingleChunkResponse[altair.LightClientBootstrap])
|
||||||
|
{.async, libp2pProtocol("light_client_bootstrap", 0,
|
||||||
|
isLightClientRequest = true).} =
|
||||||
|
trace "Received LC bootstrap request", peer, blockRoot
|
||||||
|
let dag = peer.networkState.dag
|
||||||
|
doAssert dag.serveLightClientData
|
||||||
|
|
||||||
|
peer.updateRequestQuota(lightClientBootstrapLookupCost)
|
||||||
|
peer.awaitNonNegativeRequestQuota()
|
||||||
|
|
||||||
|
let bootstrap = dag.getLightClientBootstrap(blockRoot)
|
||||||
|
if bootstrap.isOk:
|
||||||
|
await response.send(bootstrap.get)
|
||||||
|
else:
|
||||||
|
raise newException(ResourceUnavailableError,
|
||||||
|
"No light client bootstrap available")
|
||||||
|
|
||||||
|
peer.updateRequestQuota(lightClientBootstrapResponseCost)
|
||||||
|
|
||||||
proc goodbye(peer: Peer,
|
proc goodbye(peer: Peer,
|
||||||
reason: uint64)
|
reason: uint64)
|
||||||
{.async, libp2pProtocol("goodbye", 1).} =
|
{.async, libp2pProtocol("goodbye", 1).} =
|
||||||
|
|
Loading…
Reference in New Issue