2022-06-19 05:57:52 +00:00
|
|
|
# beacon_chain
|
2024-01-06 14:26:56 +00:00
|
|
|
# Copyright (c) 2021-2024 Status Research & Development GmbH
|
2022-06-19 05:57:52 +00:00
|
|
|
# Licensed and distributed under either of
|
|
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
|
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|
|
|
|
2023-01-20 14:14:37 +00:00
|
|
|
{.push raises: [].}
|
2022-06-19 05:57:52 +00:00
|
|
|
|
|
|
|
import chronicles
|
|
|
|
import ../beacon_node,
|
|
|
|
./rest_utils
|
|
|
|
|
|
|
|
logScope: topics = "rest_light_client"
|
|
|
|
|
|
|
|
proc installLightClientApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
2022-10-13 00:16:49 +00:00
|
|
|
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientBootstrap
|
2022-06-19 05:57:52 +00:00
|
|
|
router.api(MethodGet,
|
2022-10-13 00:16:49 +00:00
|
|
|
"/eth/v1/beacon/light_client/bootstrap/{block_root}") do (
|
2022-06-19 05:57:52 +00:00
|
|
|
block_root: Eth2Digest) -> RestApiResponse:
|
2022-06-24 14:57:50 +00:00
|
|
|
doAssert node.dag.lcDataStore.serve
|
2022-10-04 11:38:09 +00:00
|
|
|
let contentType =
|
|
|
|
block:
|
|
|
|
let res = preferredContentType(jsonMediaType,
|
|
|
|
sszMediaType)
|
|
|
|
if res.isErr():
|
|
|
|
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
|
|
|
|
res.get()
|
|
|
|
|
2022-06-19 05:57:52 +00:00
|
|
|
let vroot = block:
|
|
|
|
if block_root.isErr():
|
|
|
|
return RestApiResponse.jsonError(Http400, InvalidBlockRootValueError,
|
|
|
|
$block_root.error())
|
|
|
|
block_root.get()
|
|
|
|
|
|
|
|
let bootstrap = node.dag.getLightClientBootstrap(vroot)
|
2023-01-12 17:11:38 +00:00
|
|
|
withForkyBootstrap(bootstrap):
|
2023-01-14 21:19:50 +00:00
|
|
|
when lcDataFork > LightClientDataFork.None:
|
2023-01-12 17:11:38 +00:00
|
|
|
let
|
|
|
|
contextEpoch = forkyBootstrap.contextEpoch
|
2023-02-16 09:32:12 +00:00
|
|
|
contextFork = node.dag.cfg.consensusForkAtEpoch(contextEpoch)
|
2023-01-12 17:11:38 +00:00
|
|
|
return
|
|
|
|
if contentType == sszMediaType:
|
|
|
|
let headers = [("eth-consensus-version", contextFork.toString())]
|
|
|
|
RestApiResponse.sszResponse(forkyBootstrap, headers)
|
|
|
|
elif contentType == jsonMediaType:
|
|
|
|
RestApiResponse.jsonResponseWVersion(forkyBootstrap, contextFork)
|
|
|
|
else:
|
|
|
|
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
2022-10-04 11:38:09 +00:00
|
|
|
else:
|
2023-01-12 17:11:38 +00:00
|
|
|
return RestApiResponse.jsonError(Http404, LCBootstrapUnavailable)
|
2022-10-04 11:38:09 +00:00
|
|
|
|
2022-10-13 00:16:49 +00:00
|
|
|
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientUpdatesByRange
|
2022-06-19 05:57:52 +00:00
|
|
|
router.api(MethodGet,
|
2022-10-13 00:16:49 +00:00
|
|
|
"/eth/v1/beacon/light_client/updates") do (
|
2022-06-19 05:57:52 +00:00
|
|
|
start_period: Option[SyncCommitteePeriod], count: Option[uint64]
|
|
|
|
) -> RestApiResponse:
|
2022-06-24 14:57:50 +00:00
|
|
|
doAssert node.dag.lcDataStore.serve
|
2022-10-04 11:38:09 +00:00
|
|
|
let contentType =
|
|
|
|
block:
|
|
|
|
let res = preferredContentType(jsonMediaType,
|
|
|
|
sszMediaType)
|
|
|
|
if res.isErr():
|
|
|
|
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
|
|
|
|
res.get()
|
|
|
|
|
2022-06-19 05:57:52 +00:00
|
|
|
let vstart = block:
|
|
|
|
if start_period.isNone():
|
|
|
|
return RestApiResponse.jsonError(Http400, MissingStartPeriodValueError)
|
|
|
|
let rstart = start_period.get()
|
|
|
|
if rstart.isErr():
|
|
|
|
return RestApiResponse.jsonError(Http400, InvalidSyncPeriodError,
|
|
|
|
$rstart.error())
|
|
|
|
rstart.get()
|
|
|
|
let vcount = block:
|
|
|
|
if count.isNone():
|
|
|
|
return RestApiResponse.jsonError(Http400, MissingCountValueError)
|
|
|
|
let rcount = count.get()
|
|
|
|
if rcount.isErr():
|
|
|
|
return RestApiResponse.jsonError(Http400, InvalidCountError,
|
|
|
|
$rcount.error())
|
|
|
|
rcount.get()
|
|
|
|
let
|
|
|
|
headPeriod = node.dag.head.slot.sync_committee_period
|
2022-10-04 11:38:09 +00:00
|
|
|
# Limit number of updates in response
|
2022-06-19 05:57:52 +00:00
|
|
|
maxSupportedCount =
|
|
|
|
if vstart > headPeriod:
|
|
|
|
0'u64
|
|
|
|
else:
|
|
|
|
min(headPeriod + 1 - vstart, MAX_REQUEST_LIGHT_CLIENT_UPDATES)
|
|
|
|
numPeriods = min(vcount, maxSupportedCount)
|
|
|
|
onePastPeriod = vstart + numPeriods
|
|
|
|
|
2023-01-12 17:11:38 +00:00
|
|
|
var updates =
|
|
|
|
newSeqOfCap[RestVersioned[ForkedLightClientUpdate]](numPeriods)
|
2022-06-19 05:57:52 +00:00
|
|
|
for period in vstart..<onePastPeriod:
|
2023-01-12 17:11:38 +00:00
|
|
|
let
|
|
|
|
update = node.dag.getLightClientUpdateForPeriod(period)
|
|
|
|
contextEpoch = withForkyUpdate(update):
|
2023-01-14 21:19:50 +00:00
|
|
|
when lcDataFork > LightClientDataFork.None:
|
2023-01-12 17:11:38 +00:00
|
|
|
forkyUpdate.contextEpoch
|
|
|
|
else:
|
|
|
|
continue
|
2023-02-16 09:32:12 +00:00
|
|
|
contextFork = node.dag.cfg.consensusForkAtEpoch(contextEpoch)
|
2023-01-12 17:11:38 +00:00
|
|
|
updates.add RestVersioned[ForkedLightClientUpdate](
|
|
|
|
data: update,
|
|
|
|
jsonVersion: contextFork,
|
2023-03-11 14:39:29 +00:00
|
|
|
sszContext: node.dag.forkDigests[].atConsensusFork(contextFork))
|
2022-10-04 11:38:09 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
if contentType == sszMediaType:
|
2023-01-12 17:11:38 +00:00
|
|
|
RestApiResponse.sszResponseVersioned(updates)
|
2022-10-04 11:38:09 +00:00
|
|
|
elif contentType == jsonMediaType:
|
2023-01-12 17:11:38 +00:00
|
|
|
RestApiResponse.jsonResponseVersioned(updates)
|
2022-10-04 11:38:09 +00:00
|
|
|
else:
|
|
|
|
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
2022-06-19 05:57:52 +00:00
|
|
|
|
2022-10-13 00:16:49 +00:00
|
|
|
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientFinalityUpdate
|
2022-06-19 05:57:52 +00:00
|
|
|
router.api(MethodGet,
|
2022-10-13 00:16:49 +00:00
|
|
|
"/eth/v1/beacon/light_client/finality_update") do (
|
2022-06-19 05:57:52 +00:00
|
|
|
) -> RestApiResponse:
|
2022-06-24 14:57:50 +00:00
|
|
|
doAssert node.dag.lcDataStore.serve
|
2022-10-04 11:38:09 +00:00
|
|
|
let contentType =
|
|
|
|
block:
|
|
|
|
let res = preferredContentType(jsonMediaType,
|
|
|
|
sszMediaType)
|
|
|
|
if res.isErr():
|
|
|
|
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
|
|
|
|
res.get()
|
|
|
|
|
2022-06-19 05:57:52 +00:00
|
|
|
let finality_update = node.dag.getLightClientFinalityUpdate()
|
2023-01-12 17:11:38 +00:00
|
|
|
withForkyFinalityUpdate(finality_update):
|
2023-01-14 21:19:50 +00:00
|
|
|
when lcDataFork > LightClientDataFork.None:
|
2023-01-12 17:11:38 +00:00
|
|
|
let
|
|
|
|
contextEpoch = forkyFinalityUpdate.contextEpoch
|
2023-02-16 09:32:12 +00:00
|
|
|
contextFork = node.dag.cfg.consensusForkAtEpoch(contextEpoch)
|
2023-01-12 17:11:38 +00:00
|
|
|
return
|
|
|
|
if contentType == sszMediaType:
|
|
|
|
let headers = [("eth-consensus-version", contextFork.toString())]
|
|
|
|
RestApiResponse.sszResponse(forkyFinalityUpdate, headers)
|
|
|
|
elif contentType == jsonMediaType:
|
|
|
|
RestApiResponse.jsonResponseWVersion(
|
|
|
|
forkyFinalityUpdate, contextFork)
|
|
|
|
else:
|
|
|
|
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
2022-10-04 11:38:09 +00:00
|
|
|
else:
|
2023-01-12 17:11:38 +00:00
|
|
|
return RestApiResponse.jsonError(Http404, LCFinUpdateUnavailable)
|
2022-10-04 11:38:09 +00:00
|
|
|
|
2022-10-13 00:16:49 +00:00
|
|
|
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientOptimisticUpdate
|
2022-06-19 05:57:52 +00:00
|
|
|
router.api(MethodGet,
|
2022-10-13 00:16:49 +00:00
|
|
|
"/eth/v1/beacon/light_client/optimistic_update") do (
|
2022-06-19 05:57:52 +00:00
|
|
|
) -> RestApiResponse:
|
2022-06-24 14:57:50 +00:00
|
|
|
doAssert node.dag.lcDataStore.serve
|
2022-10-04 11:38:09 +00:00
|
|
|
let contentType =
|
|
|
|
block:
|
|
|
|
let res = preferredContentType(jsonMediaType,
|
|
|
|
sszMediaType)
|
|
|
|
if res.isErr():
|
|
|
|
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
|
|
|
|
res.get()
|
|
|
|
|
2022-06-19 05:57:52 +00:00
|
|
|
let optimistic_update = node.dag.getLightClientOptimisticUpdate()
|
2023-01-12 17:11:38 +00:00
|
|
|
withForkyOptimisticUpdate(optimistic_update):
|
2023-01-14 21:19:50 +00:00
|
|
|
when lcDataFork > LightClientDataFork.None:
|
2023-01-12 17:11:38 +00:00
|
|
|
let
|
|
|
|
contextEpoch = forkyOptimisticUpdate.contextEpoch
|
2023-02-16 09:32:12 +00:00
|
|
|
contextFork = node.dag.cfg.consensusForkAtEpoch(contextEpoch)
|
2023-01-12 17:11:38 +00:00
|
|
|
return
|
|
|
|
if contentType == sszMediaType:
|
|
|
|
let headers = [("eth-consensus-version", contextFork.toString())]
|
|
|
|
RestApiResponse.sszResponse(forkyOptimisticUpdate, headers)
|
|
|
|
elif contentType == jsonMediaType:
|
|
|
|
RestApiResponse.jsonResponseWVersion(
|
|
|
|
forkyOptimisticUpdate, contextFork)
|
|
|
|
else:
|
|
|
|
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
2022-10-04 11:38:09 +00:00
|
|
|
else:
|
2023-01-12 17:11:38 +00:00
|
|
|
return RestApiResponse.jsonError(Http404, LCOptUpdateUnavailable)
|