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:
Etan Kissling 2022-03-22 21:23:36 +01:00 committed by GitHub
parent 70270eeabe
commit b2b7b0bd56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 118 additions and 0 deletions

View File

@ -7,6 +7,10 @@
{.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
options, tables, sets, macros,
chronicles, chronos, stew/ranges/bitranges, libp2p/switch,
@ -21,10 +25,16 @@ logScope:
const
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)
blockResponseCost = allowedOpsPerSecondCost(100)
blockByRangeLookupCost = allowedOpsPerSecondCost(20)
lightClientUpdateResponseCost = allowedOpsPerSecondCost(100)
lightClientUpdateByRangeLookupCost = allowedOpsPerSecondCost(20)
lightClientBootstrapLookupCost = allowedOpsPerSecondCost(5)
lightClientBootstrapResponseCost = allowedOpsPerSecondCost(100)
type
StatusMsg* = object
@ -439,6 +449,114 @@ p2pProtocol BeaconSync(version = 1,
debug "Block root request done",
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,
reason: uint64)
{.async, libp2pProtocol("goodbye", 1).} =