Adopt asyncraises guarantees to most of the REST API handlers. (#5803)

* Adopt asyncraises guarantees to most of the REST API handlers.
Bump presto.

* Fix copyright year.

---------

Co-authored-by: Etan Kissling <etan@status.im>
This commit is contained in:
Eugene Kabanov 2024-01-20 18:06:28 +02:00 committed by GitHub
parent 9acb7b99fe
commit 525b994e7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 403 additions and 382 deletions

View File

@ -129,14 +129,17 @@ proc toString*(kind: ValidatorFilterKind): string =
proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4881.md # https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4881.md
router.api(MethodGet, "/eth/v1/beacon/deposit_snapshot") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/beacon/deposit_snapshot") do (
) -> RestApiResponse:
let snapshot = node.db.getDepositTreeSnapshot().valueOr: let snapshot = node.db.getDepositTreeSnapshot().valueOr:
# This can happen in a very short window after the client is started, but the # This can happen in a very short window after the client is started,
# snapshot record still haven't been upgraded in the database. Returning 404 # but the snapshot record still haven't been upgraded in the database.
# should be easy to handle for the clients - they just need to retry. # Returning 404 should be easy to handle for the clients - they just need
return RestApiResponse.jsonError(Http404, NoFinalizedSnapshotAvailableError) # to retry.
return RestApiResponse.jsonError(Http404,
NoFinalizedSnapshotAvailableError)
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
RestDepositSnapshot( RestDepositSnapshot(
finalized: snapshot.depositContractState.branch, finalized: snapshot.depositContractState.branch,
deposit_root: snapshot.getDepositRoot(), deposit_root: snapshot.getDepositRoot(),
@ -145,8 +148,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
execution_block_height: snapshot.blockHeight)) execution_block_height: snapshot.blockHeight))
# https://ethereum.github.io/beacon-APIs/#/Beacon/getGenesis # https://ethereum.github.io/beacon-APIs/#/Beacon/getGenesis
router.api(MethodGet, "/eth/v1/beacon/genesis") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/beacon/genesis") do () -> RestApiResponse:
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
( (
genesis_time: getStateField(node.dag.headState, genesis_time), genesis_time: getStateField(node.dag.headState, genesis_time),
genesis_validators_root: genesis_validators_root:
@ -156,7 +159,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateRoot # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateRoot
router.api(MethodGet, "/eth/v1/beacon/states/{state_id}/root") do ( router.api2(MethodGet, "/eth/v1/beacon/states/{state_id}/root") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -177,10 +180,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.dag.isFinalized(bslot.bid) node.dag.isFinalized(bslot.bid)
) )
return RestApiResponse.jsonError(Http404, StateNotFoundError) RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateFork # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateFork
router.api(MethodGet, "/eth/v1/beacon/states/{state_id}/fork") do ( router.api2(MethodGet, "/eth/v1/beacon/states/{state_id}/fork") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -207,11 +210,12 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.getStateOptimistic(state), node.getStateOptimistic(state),
node.dag.isFinalized(bslot.bid) node.dag.isFinalized(bslot.bid)
) )
return RestApiResponse.jsonError(Http404, StateNotFoundError)
RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateFinalityCheckpoints # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateFinalityCheckpoints
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/states/{state_id}/finality_checkpoints") do ( "/eth/v1/beacon/states/{state_id}/finality_checkpoints") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -238,7 +242,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.getStateOptimistic(state), node.getStateOptimistic(state),
node.dag.isFinalized(bslot.bid) node.dag.isFinalized(bslot.bid)
) )
return RestApiResponse.jsonError(Http404, StateNotFoundError)
RestApiResponse.jsonError(Http404, StateNotFoundError)
proc getIndices( proc getIndices(
node: BeaconNode, node: BeaconNode,
@ -369,7 +374,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
RestApiResponse.jsonError(Http404, StateNotFoundError) RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidators # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidators
router.api(MethodGet, "/eth/v1/beacon/states/{state_id}/validators") do ( router.api2(MethodGet, "/eth/v1/beacon/states/{state_id}/validators") do (
state_id: StateIdent, id: seq[ValidatorIdent], state_id: StateIdent, id: seq[ValidatorIdent],
status: seq[ValidatorFilter]) -> RestApiResponse: status: seq[ValidatorFilter]) -> RestApiResponse:
let let
@ -404,7 +409,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
getValidators(node, bslot, validatorsMask, validatorIds) getValidators(node, bslot, validatorsMask, validatorIds)
# https://ethereum.github.io/beacon-APIs/#/Beacon/postStateValidators # https://ethereum.github.io/beacon-APIs/#/Beacon/postStateValidators
router.api(MethodPost, "/eth/v1/beacon/states/{state_id}/validators") do ( router.api2(MethodPost, "/eth/v1/beacon/states/{state_id}/validators") do (
state_id: StateIdent, contentBody: Option[ContentBody]) -> RestApiResponse: state_id: StateIdent, contentBody: Option[ContentBody]) -> RestApiResponse:
let let
(validatorIds, validatorsMask) = (validatorIds, validatorsMask) =
@ -431,8 +436,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
getValidators(node, bslot, validatorsMask, validatorIds) getValidators(node, bslot, validatorsMask, validatorIds)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidator # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidator
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/states/{state_id}/validators/{validator_id}") do ( "/eth/v1/beacon/states/{state_id}/validators/{validator_id}") do (
state_id: StateIdent, validator_id: ValidatorIdent) -> RestApiResponse: state_id: StateIdent, validator_id: ValidatorIdent) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -465,7 +470,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
of ValidatorQueryKind.Index: of ValidatorQueryKind.Index:
let vres = vid.index.toValidatorIndex() let vres = vid.index.toValidatorIndex()
if vres.isErr(): if vres.isErr():
case vres.error() case vres.error
of ValidatorIndexError.TooHighValue: of ValidatorIndexError.TooHighValue:
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
TooHighValidatorIndexValueError) TooHighValidatorIndexValueError)
@ -493,11 +498,12 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.getStateOptimistic(state), node.getStateOptimistic(state),
node.dag.isFinalized(bslot.bid) node.dag.isFinalized(bslot.bid)
) )
return RestApiResponse.jsonError(Http404, StateNotFoundError)
RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidatorBalances # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidatorBalances
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/states/{state_id}/validator_balances") do ( "/eth/v1/beacon/states/{state_id}/validator_balances") do (
state_id: StateIdent, id: seq[ValidatorIdent]) -> RestApiResponse: state_id: StateIdent, id: seq[ValidatorIdent]) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -522,8 +528,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
getBalances(node, bslot, validatorIds) getBalances(node, bslot, validatorIds)
# https://ethereum.github.io/beacon-APIs/#/Beacon/postStateValidatorBalances # https://ethereum.github.io/beacon-APIs/#/Beacon/postStateValidatorBalances
router.api(MethodPost, router.api2(MethodPost,
"/eth/v1/beacon/states/{state_id}/validator_balances") do ( "/eth/v1/beacon/states/{state_id}/validator_balances") do (
state_id: StateIdent, contentBody: Option[ContentBody]) -> RestApiResponse: state_id: StateIdent, contentBody: Option[ContentBody]) -> RestApiResponse:
let let
validatorIds = validatorIds =
@ -546,8 +552,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
getBalances(node, bslot, validatorIds) getBalances(node, bslot, validatorIds)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochCommittees # https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochCommittees
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/states/{state_id}/committees") do ( "/eth/v1/beacon/states/{state_id}/committees") do (
state_id: StateIdent, epoch: Option[Epoch], index: Option[CommitteeIndex], state_id: StateIdent, epoch: Option[Epoch], index: Option[CommitteeIndex],
slot: Option[Slot]) -> RestApiResponse: slot: Option[Slot]) -> RestApiResponse:
let let
@ -567,7 +573,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let repoch = epoch.get() let repoch = epoch.get()
if repoch.isErr(): if repoch.isErr():
return RestApiResponse.jsonError(Http400, InvalidEpochValueError, return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
$repoch.error()) $repoch.error)
let res = repoch.get() let res = repoch.get()
if res > bslot.slot.epoch + MIN_SEED_LOOKAHEAD: if res > bslot.slot.epoch + MIN_SEED_LOOKAHEAD:
@ -590,7 +596,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if rindex.isErr(): if rindex.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidCommitteeIndexValueError, InvalidCommitteeIndexValueError,
$rindex.error()) $rindex.error)
some(rindex.get()) some(rindex.get())
else: else:
none[CommitteeIndex]() none[CommitteeIndex]()
@ -599,12 +605,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let rslot = slot.get() let rslot = slot.get()
if rslot.isErr(): if rslot.isErr():
return RestApiResponse.jsonError(Http400, InvalidSlotValueError, return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
$rslot.error()) $rslot.error)
let res = rslot.get() let res = rslot.get()
if vepoch.isSome(): if vepoch.isSome():
if res.epoch != vepoch.get(): if res.epoch != vepoch.get():
return RestApiResponse.jsonError(Http400, InvalidSlotValueError, return RestApiResponse.jsonError(
"Slot does not match requested epoch") Http400, InvalidSlotValueError,
"Slot does not match requested epoch")
else: else:
if res.epoch > bslot.slot.epoch + 1: if res.epoch > bslot.slot.epoch + 1:
return RestApiResponse.jsonError( return RestApiResponse.jsonError(
@ -620,6 +627,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
some(res) some(res)
else: else:
none[Slot]() none[Slot]()
node.withStateForBlockSlotId(bslot): node.withStateForBlockSlotId(bslot):
proc getCommittee(slot: Slot, proc getCommittee(slot: Slot,
index: CommitteeIndex): RestBeaconStatesCommittees = index: CommitteeIndex): RestBeaconStatesCommittees =
@ -660,11 +668,11 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.dag.isFinalized(bslot.bid) node.dag.isFinalized(bslot.bid)
) )
return RestApiResponse.jsonError(Http404, StateNotFoundError) RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochSyncCommittees # https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochSyncCommittees
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/states/{state_id}/sync_committees") do ( "/eth/v1/beacon/states/{state_id}/sync_committees") do (
state_id: StateIdent, epoch: Option[Epoch]) -> RestApiResponse: state_id: StateIdent, epoch: Option[Epoch]) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -683,7 +691,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let repoch = epoch.get() let repoch = epoch.get()
if repoch.isErr(): if repoch.isErr():
return RestApiResponse.jsonError(Http400, InvalidEpochValueError, return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
$repoch.error()) $repoch.error)
let res = repoch.get() let res = repoch.get()
if res > MaxEpoch: if res > MaxEpoch:
return RestApiResponse.jsonError(Http400, EpochOverflowValueError) return RestApiResponse.jsonError(Http400, EpochOverflowValueError)
@ -702,7 +710,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let res = syncCommitteeParticipants(state, qepoch) let res = syncCommitteeParticipants(state, qepoch)
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
$res.error()) $res.error)
let kres = res.get() let kres = res.get()
if len(kres) == 0: if len(kres) == 0:
return RestApiResponse.jsonError(Http500, InternalServerError, return RestApiResponse.jsonError(Http500, InternalServerError,
@ -742,12 +750,12 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.dag.isFinalized(bslot.bid) node.dag.isFinalized(bslot.bid)
) )
return RestApiResponse.jsonError(Http404, StateNotFoundError) RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getStateRandao # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getStateRandao
# https://github.com/ethereum/beacon-APIs/blob/b3c4defa238aaa74bf22aa602aa1b24b68a4c78e/apis/beacon/states/randao.yaml # https://github.com/ethereum/beacon-APIs/blob/b3c4defa238aaa74bf22aa602aa1b24b68a4c78e/apis/beacon/states/randao.yaml
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/states/{state_id}/randao") do ( "/eth/v1/beacon/states/{state_id}/randao") do (
state_id: StateIdent, epoch: Option[Epoch]) -> RestApiResponse: state_id: StateIdent, epoch: Option[Epoch]) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -766,7 +774,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let repoch = epoch.get() let repoch = epoch.get()
if repoch.isErr(): if repoch.isErr():
return RestApiResponse.jsonError(Http400, InvalidEpochValueError, return RestApiResponse.jsonError(Http400, InvalidEpochValueError,
$repoch.error()) $repoch.error)
let res = repoch.get() let res = repoch.get()
if res > MaxEpoch: if res > MaxEpoch:
return RestApiResponse.jsonError(Http400, EpochOverflowValueError) return RestApiResponse.jsonError(Http400, EpochOverflowValueError)
@ -801,10 +809,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.dag.isFinalized(bslot.bid) node.dag.isFinalized(bslot.bid)
) )
return RestApiResponse.jsonError(Http404, StateNotFoundError) RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeaders # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeaders
router.api(MethodGet, "/eth/v1/beacon/headers") do ( router.api2(MethodGet, "/eth/v1/beacon/headers") do (
slot: Option[Slot], parent_root: Option[Eth2Digest]) -> RestApiResponse: slot: Option[Slot], parent_root: Option[Eth2Digest]) -> RestApiResponse:
# TODO (cheatfate): This call is incomplete, because structure # TODO (cheatfate): This call is incomplete, because structure
# of database do not allow to query blocks by `parent_root`. # of database do not allow to query blocks by `parent_root`.
@ -813,7 +821,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let rslot = slot.get() let rslot = slot.get()
if rslot.isErr(): if rslot.isErr():
return RestApiResponse.jsonError(Http400, InvalidSlotValueError, return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
$rslot.error()) $rslot.error)
rslot.get() rslot.get()
else: else:
node.dag.head.slot node.dag.head.slot
@ -822,32 +830,31 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let rroot = parent_root.get() let rroot = parent_root.get()
if rroot.isErr(): if rroot.isErr():
return RestApiResponse.jsonError(Http400, InvalidParentRootValueError, return RestApiResponse.jsonError(Http400, InvalidParentRootValueError,
$rroot.error()) $rroot.error)
return RestApiResponse.jsonError(Http500, NoImplementationError) return RestApiResponse.jsonError(Http500, NoImplementationError)
let bdata = node.getForkedBlock(BlockIdent.init(qslot)).valueOr: let bdata = node.getForkedBlock(BlockIdent.init(qslot)).valueOr:
return RestApiResponse.jsonError(Http404, BlockNotFoundError) return RestApiResponse.jsonError(Http404, BlockNotFoundError)
return withBlck(bdata):
withBlck(bdata): let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot)
let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot) RestApiResponse.jsonResponseFinalized(
RestApiResponse.jsonResponseFinalized( [
[ (
( root: forkyBlck.root,
root: forkyBlck.root, canonical: node.dag.isCanonical(bid),
canonical: node.dag.isCanonical(bid), header: (
header: ( message: forkyBlck.toBeaconBlockHeader,
message: forkyBlck.toBeaconBlockHeader, signature: forkyBlck.signature
signature: forkyBlck.signature
)
) )
], )
node.getBlockOptimistic(bdata), ],
node.dag.isFinalized(bid) node.getBlockOptimistic(bdata),
) node.dag.isFinalized(bid)
)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader
router.api(MethodGet, "/eth/v1/beacon/headers/{block_id}") do ( router.api2(MethodGet, "/eth/v1/beacon/headers/{block_id}") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let let
bid = block_id.valueOr: bid = block_id.valueOr:
@ -857,21 +864,20 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
bdata = node.getForkedBlock(bid).valueOr: bdata = node.getForkedBlock(bid).valueOr:
return RestApiResponse.jsonError(Http404, BlockNotFoundError) return RestApiResponse.jsonError(Http404, BlockNotFoundError)
return withBlck(bdata):
withBlck(bdata): let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot)
let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot) RestApiResponse.jsonResponseFinalized(
RestApiResponse.jsonResponseFinalized( (
( root: forkyBlck.root,
root: forkyBlck.root, canonical: node.dag.isCanonical(bid),
canonical: node.dag.isCanonical(bid), header: (
header: ( message: forkyBlck.toBeaconBlockHeader,
message: forkyBlck.toBeaconBlockHeader, signature: forkyBlck.signature
signature: forkyBlck.signature )
) ),
), node.getBlockOptimistic(bdata),
node.getBlockOptimistic(bdata), node.dag.isFinalized(bid)
node.dag.isFinalized(bid) )
)
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock # https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
router.api(MethodPost, "/eth/v1/beacon/blocks") do ( router.api(MethodPost, "/eth/v1/beacon/blocks") do (
@ -924,11 +930,11 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError( return RestApiResponse.jsonError(
Http503, BeaconNodeInSyncError, $res.error()) Http503, BeaconNodeInSyncError, $res.error)
if res.get().isNone(): if res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError) return RestApiResponse.jsonError(Http202, BlockValidationError)
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess) RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2 # https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
router.api(MethodPost, "/eth/v2/beacon/blocks") do ( router.api(MethodPost, "/eth/v2/beacon/blocks") do (
@ -1000,11 +1006,11 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError( return RestApiResponse.jsonError(
Http503, BeaconNodeInSyncError, $res.error()) Http503, BeaconNodeInSyncError, $res.error)
if res.get().isNone(): if res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError) return RestApiResponse.jsonError(Http202, BlockValidationError)
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess) RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock # https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
# https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/beacon/blocks/blinded_blocks.yaml # https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/beacon/blocks/blinded_blocks.yaml
@ -1046,7 +1052,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError( return RestApiResponse.jsonError(
Http500, InternalServerError, $res.error()) Http500, InternalServerError, $res.error)
if res.get().isNone(): if res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError) return RestApiResponse.jsonError(Http202, BlockValidationError)
@ -1076,20 +1082,20 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError( return RestApiResponse.jsonError(
Http503, BeaconNodeInSyncError, $res.error()) Http503, BeaconNodeInSyncError, $res.error)
elif res.get().isNone(): elif res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError) return RestApiResponse.jsonError(Http202, BlockValidationError)
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess) RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlock # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlock
router.api(MethodGet, "/eth/v1/beacon/blocks/{block_id}") do ( router.api2(MethodGet, "/eth/v1/beacon/blocks/{block_id}") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
return RestApiResponse.jsonError( RestApiResponse.jsonError(
Http410, DeprecatedRemovalBeaconBlocksDebugStateV1) Http410, DeprecatedRemovalBeaconBlocksDebugStateV1)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockV2 # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockV2
router.api(MethodGet, "/eth/v2/beacon/blocks/{block_id}") do ( router.api2(MethodGet, "/eth/v2/beacon/blocks/{block_id}") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let let
blockIdent = block_id.valueOr: blockIdent = block_id.valueOr:
@ -1105,31 +1111,31 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError) return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
res.get() res.get()
return
if contentType == sszMediaType:
var data: seq[byte]
if not node.dag.getBlockSSZ(bid, data):
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
let if contentType == sszMediaType:
fork = node.dag.cfg.consensusForkAtEpoch(bid.slot.epoch) var data: seq[byte]
headers = [("eth-consensus-version", fork.toString())] if not node.dag.getBlockSSZ(bid, data):
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
RestApiResponse.sszResponsePlain(data, headers) let
elif contentType == jsonMediaType: fork = node.dag.cfg.consensusForkAtEpoch(bid.slot.epoch)
let bdata = node.dag.getForkedBlock(bid).valueOr: headers = [("eth-consensus-version", fork.toString())]
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
RestApiResponse.jsonResponseBlock( RestApiResponse.sszResponsePlain(data, headers)
bdata.asSigned(), elif contentType == jsonMediaType:
node.getBlockOptimistic(bdata), let bdata = node.dag.getForkedBlock(bid).valueOr:
node.dag.isFinalized(bid) return RestApiResponse.jsonError(Http404, BlockNotFoundError)
)
else: RestApiResponse.jsonResponseBlock(
RestApiResponse.jsonError(Http500, InvalidAcceptError) bdata.asSigned(),
node.getBlockOptimistic(bdata),
node.dag.isFinalized(bid)
)
else:
RestApiResponse.jsonError(Http500, InvalidAcceptError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockRoot # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockRoot
router.api(MethodGet, "/eth/v1/beacon/blocks/{block_id}/root") do ( router.api2(MethodGet, "/eth/v1/beacon/blocks/{block_id}/root") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let let
blockIdent = block_id.valueOr: blockIdent = block_id.valueOr:
@ -1142,14 +1148,14 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
bdata = node.dag.getForkedBlock(bid).valueOr: bdata = node.dag.getForkedBlock(bid).valueOr:
return RestApiResponse.jsonError(Http404, BlockNotFoundError) return RestApiResponse.jsonError(Http404, BlockNotFoundError)
return RestApiResponse.jsonResponseFinalized( RestApiResponse.jsonResponseFinalized(
(root: bid.root), (root: bid.root),
node.getBlockOptimistic(bdata), node.getBlockOptimistic(bdata),
node.dag.isFinalized(bid) node.dag.isFinalized(bid)
) )
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/blocks/{block_id}/attestations") do ( "/eth/v1/beacon/blocks/{block_id}/attestations") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let let
@ -1159,17 +1165,16 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
bdata = node.getForkedBlock(blockIdent).valueOr: bdata = node.getForkedBlock(blockIdent).valueOr:
return RestApiResponse.jsonError(Http404, BlockNotFoundError) return RestApiResponse.jsonError(Http404, BlockNotFoundError)
return withBlck(bdata):
withBlck(bdata): let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot)
let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot) RestApiResponse.jsonResponseFinalized(
RestApiResponse.jsonResponseFinalized( forkyBlck.message.body.attestations.asSeq(),
forkyBlck.message.body.attestations.asSeq(), node.getBlockOptimistic(bdata),
node.getBlockOptimistic(bdata), node.dag.isFinalized(bid)
node.dag.isFinalized(bid) )
)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations
router.api(MethodGet, "/eth/v1/beacon/pool/attestations") do ( router.api2(MethodGet, "/eth/v1/beacon/pool/attestations") do (
slot: Option[Slot], slot: Option[Slot],
committee_index: Option[CommitteeIndex]) -> RestApiResponse: committee_index: Option[CommitteeIndex]) -> RestApiResponse:
let vindex = let vindex =
@ -1178,7 +1183,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if rindex.isErr(): if rindex.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidCommitteeIndexValueError, InvalidCommitteeIndexValueError,
$rindex.error()) $rindex.error)
Opt.some(rindex.get()) Opt.some(rindex.get())
else: else:
Opt.none(CommitteeIndex) Opt.none(CommitteeIndex)
@ -1187,17 +1192,17 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
let rslot = slot.get() let rslot = slot.get()
if rslot.isErr(): if rslot.isErr():
return RestApiResponse.jsonError(Http400, InvalidSlotValueError, return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
$rslot.error()) $rslot.error)
Opt.some(rslot.get()) Opt.some(rslot.get())
else: else:
Opt.none(Slot) Opt.none(Slot)
var res: seq[Attestation] var res: seq[Attestation]
for item in node.attestationPool[].attestations(vslot, vindex): for item in node.attestationPool[].attestations(vslot, vindex):
res.add(item) res.add(item)
return RestApiResponse.jsonResponse(res) RestApiResponse.jsonResponse(res)
# https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations
router.api(MethodPost, "/eth/v1/beacon/pool/attestations") do ( router.api2(MethodPost, "/eth/v1/beacon/pool/attestations") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let attestations = let attestations =
block: block:
@ -1207,7 +1212,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if dres.isErr(): if dres.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidAttestationObjectError, InvalidAttestationObjectError,
$dres.error()) $dres.error)
dres.get() dres.get()
# Since our validation logic supports batch processing, we will submit all # Since our validation logic supports batch processing, we will submit all
@ -1224,29 +1229,29 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
await allFutures(pending) await allFutures(pending)
for index, future in pending: for index, future in pending:
if future.completed(): if future.completed():
let fres = future.read() let fres = future.value()
if fres.isErr(): if fres.isErr():
let failure = RestIndexedErrorMessageItem(index: index, let failure = RestIndexedErrorMessageItem(index: index,
message: $fres.error()) message: $fres.error)
res.add(failure) res.add(failure)
elif future.failed() or future.cancelled(): elif future.failed() or future.cancelled():
# This is unexpected failure, so we log the error message. # This is unexpected failure, so we log the error message.
let exc = future.readError() let exc = future.error()
let failure = RestIndexedErrorMessageItem(index: index, let failure = RestIndexedErrorMessageItem(index: index,
message: $exc.msg) message: $exc.msg)
res.add(failure) res.add(failure)
res res
if len(failures) > 0: if len(failures) > 0:
return RestApiResponse.jsonErrorList(Http400, AttestationValidationError, RestApiResponse.jsonErrorList(Http400, AttestationValidationError,
failures) failures)
else: else:
return RestApiResponse.jsonMsgResponse(AttestationValidationSuccess) RestApiResponse.jsonMsgResponse(AttestationValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttesterSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttesterSlashings
router.api(MethodGet, "/eth/v1/beacon/pool/attester_slashings") do ( router.api2(MethodGet, "/eth/v1/beacon/pool/attester_slashings") do (
) -> RestApiResponse: ) -> RestApiResponse:
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
toSeq(node.validatorChangePool.attester_slashings)) toSeq(node.validatorChangePool.attester_slashings))
# https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttesterSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttesterSlashings
@ -1260,19 +1265,19 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if dres.isErr(): if dres.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidAttesterSlashingObjectError, InvalidAttesterSlashingObjectError,
$dres.error()) $dres.error)
dres.get() dres.get()
let res = await node.router.routeAttesterSlashing(slashing) let res = await node.router.routeAttesterSlashing(slashing)
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
AttesterSlashingValidationError, AttesterSlashingValidationError,
$res.error()) $res.error)
return RestApiResponse.jsonMsgResponse(AttesterSlashingValidationSuccess) RestApiResponse.jsonMsgResponse(AttesterSlashingValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolProposerSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolProposerSlashings
router.api(MethodGet, "/eth/v1/beacon/pool/proposer_slashings") do ( router.api2(MethodGet, "/eth/v1/beacon/pool/proposer_slashings") do (
) -> RestApiResponse: ) -> RestApiResponse:
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
toSeq(node.validatorChangePool.proposer_slashings)) toSeq(node.validatorChangePool.proposer_slashings))
# https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolProposerSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolProposerSlashings
@ -1286,55 +1291,58 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if dres.isErr(): if dres.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidProposerSlashingObjectError, InvalidProposerSlashingObjectError,
$dres.error()) $dres.error)
dres.get() dres.get()
let res = await node.router.routeProposerSlashing(slashing) let res = await node.router.routeProposerSlashing(slashing)
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
ProposerSlashingValidationError, ProposerSlashingValidationError,
$res.error()) $res.error)
return RestApiResponse.jsonMsgResponse(ProposerSlashingValidationSuccess) RestApiResponse.jsonMsgResponse(ProposerSlashingValidationSuccess)
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getPoolBLSToExecutionChanges # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getPoolBLSToExecutionChanges
# https://github.com/ethereum/beacon-APIs/blob/86850001845df9163da5ae9605dbf15cd318d5d0/apis/beacon/pool/bls_to_execution_changes.yaml # https://github.com/ethereum/beacon-APIs/blob/86850001845df9163da5ae9605dbf15cd318d5d0/apis/beacon/pool/bls_to_execution_changes.yaml
router.api(MethodGet, "/eth/v1/beacon/pool/bls_to_execution_changes") do ( router.api2(MethodGet, "/eth/v1/beacon/pool/bls_to_execution_changes") do (
) -> RestApiResponse: ) -> RestApiResponse:
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
toSeq(node.validatorChangePool.bls_to_execution_changes_gossip) & toSeq(node.validatorChangePool.bls_to_execution_changes_gossip) &
toSeq(node.validatorChangePool.bls_to_execution_changes_api)) toSeq(node.validatorChangePool.bls_to_execution_changes_api))
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/submitPoolBLSToExecutionChange # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/submitPoolBLSToExecutionChange
# https://github.com/ethereum/beacon-APIs/blob/86850001845df9163da5ae9605dbf15cd318d5d0/apis/beacon/pool/bls_to_execution_changes.yaml # https://github.com/ethereum/beacon-APIs/blob/86850001845df9163da5ae9605dbf15cd318d5d0/apis/beacon/pool/bls_to_execution_changes.yaml
router.api(MethodPost, "/eth/v1/beacon/pool/bls_to_execution_changes") do ( router.api2(MethodPost, "/eth/v1/beacon/pool/bls_to_execution_changes") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
if node.currentSlot().epoch() < node.dag.cfg.CAPELLA_FORK_EPOCH: if node.currentSlot().epoch() < node.dag.cfg.CAPELLA_FORK_EPOCH:
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidBlsToExecutionChangeObjectError, InvalidBlsToExecutionChangeObjectError,
"Attempt to add to BLS to execution change pool pre-Capella") "Attempt to add to BLS to execution change pool pre-Capella")
let bls_to_execution_changes = let
block: bls_to_execution_changes =
if contentBody.isNone(): block:
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) if contentBody.isNone():
let dres = decodeBody(seq[SignedBLSToExecutionChange], contentBody.get()) return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
if dres.isErr(): let dres =
return RestApiResponse.jsonError(Http400, decodeBody(seq[SignedBLSToExecutionChange], contentBody.get())
InvalidBlsToExecutionChangeObjectError, if dres.isErr():
$dres.error()) return RestApiResponse.jsonError(
dres.get() Http400, InvalidBlsToExecutionChangeObjectError, $dres.error)
let res = await allFinished(mapIt( dres.get()
bls_to_execution_changes, node.router.routeBlsToExecutionChange(it))) pending = mapIt(bls_to_execution_changes,
for individual_res in res: node.router.routeBlsToExecutionChange(it))
doAssert individual_res.finished()
if individual_res.failed(): await allFutures(pending)
for future in pending:
if future.failed() or future.cancelled():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
BlsToExecutionChangeValidationError, BlsToExecutionChangeValidationError,
$individual_res.error[].msg) $future.error().msg)
let fut_result = individual_res.read() let res = future.value()
if fut_result.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
BlsToExecutionChangeValidationError, BlsToExecutionChangeValidationError,
$fut_result.error()) $res.error)
return RestApiResponse.jsonMsgResponse(BlsToExecutionChangeValidationSuccess) RestApiResponse.jsonMsgResponse(BlsToExecutionChangeValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolSyncCommitteeSignatures # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolSyncCommitteeSignatures
router.api(MethodPost, "/eth/v1/beacon/pool/sync_committees") do ( router.api(MethodPost, "/eth/v1/beacon/pool/sync_committees") do (
@ -1357,18 +1365,17 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
for index, item in results: for index, item in results:
if item.isErr(): if item.isErr():
res.add(RestIndexedErrorMessageItem(index: index, res.add(RestIndexedErrorMessageItem(index: index,
message: $item.error())) message: $item.error))
res res
if len(failures) > 0: if len(failures) > 0:
return RestApiResponse.jsonErrorList(Http400, RestApiResponse.jsonErrorList(
SyncCommitteeMessageValidationError, Http400, SyncCommitteeMessageValidationError, failures)
failures)
else: else:
return RestApiResponse.jsonMsgResponse( RestApiResponse.jsonMsgResponse(
SyncCommitteeMessageValidationSuccess) SyncCommitteeMessageValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolVoluntaryExits # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolVoluntaryExits
router.api(MethodGet, "/eth/v1/beacon/pool/voluntary_exits") do ( router.api2(MethodGet, "/eth/v1/beacon/pool/voluntary_exits") do (
) -> RestApiResponse: ) -> RestApiResponse:
return RestApiResponse.jsonResponse( return RestApiResponse.jsonResponse(
toSeq(node.validatorChangePool.voluntary_exits)) toSeq(node.validatorChangePool.voluntary_exits))
@ -1384,18 +1391,17 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if dres.isErr(): if dres.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidVoluntaryExitObjectError, InvalidVoluntaryExitObjectError,
$dres.error()) $dres.error)
dres.get() dres.get()
let res = await node.router.routeSignedVoluntaryExit(exit) let res = await node.router.routeSignedVoluntaryExit(exit)
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(
VoluntaryExitValidationError, Http400, VoluntaryExitValidationError, $res.error)
$res.error())
return RestApiResponse.jsonMsgResponse(VoluntaryExitValidationSuccess) return RestApiResponse.jsonMsgResponse(VoluntaryExitValidationSuccess)
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.4.2#/Beacon/getBlobSidecars # https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.4.2#/Beacon/getBlobSidecars
# https://github.com/ethereum/beacon-APIs/blob/v2.4.2/apis/beacon/blob_sidecars/blob_sidecars.yaml # https://github.com/ethereum/beacon-APIs/blob/v2.4.2/apis/beacon/blob_sidecars/blob_sidecars.yaml
router.api(MethodGet, "/eth/v1/beacon/blob_sidecars/{block_id}") do ( router.api2(MethodGet, "/eth/v1/beacon/blob_sidecars/{block_id}") do (
block_id: BlockIdent, indices: seq[uint64]) -> RestApiResponse: block_id: BlockIdent, indices: seq[uint64]) -> RestApiResponse:
let let
blockIdent = block_id.valueOr: blockIdent = block_id.valueOr:
@ -1429,12 +1435,11 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if node.dag.db.getBlobSidecar(bid.root, blobIndex, blobSidecar[]): if node.dag.db.getBlobSidecar(bid.root, blobIndex, blobSidecar[]):
discard data[].add blobSidecar[] discard data[].add blobSidecar[]
return if contentType == sszMediaType:
if contentType == sszMediaType: RestApiResponse.sszResponse(
RestApiResponse.sszResponse( data[], headers = [("eth-consensus-version",
data[], headers = [("eth-consensus-version", node.dag.cfg.consensusForkAtEpoch(bid.slot.epoch).toString())])
node.dag.cfg.consensusForkAtEpoch(bid.slot.epoch).toString())]) elif contentType == jsonMediaType:
elif contentType == jsonMediaType: RestApiResponse.jsonResponse(data)
RestApiResponse.jsonResponse(data) else:
else: RestApiResponse.jsonError(Http500, InvalidAcceptError)
RestApiResponse.jsonError(Http500, InvalidAcceptError)

View File

@ -1,5 +1,5 @@
# beacon_chain # beacon_chain
# Copyright (c) 2023 Status Research & Development GmbH # Copyright (c) 2023-2024 Status Research & Development GmbH
# Licensed and distributed under either of # Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT). # * 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). # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@ -17,7 +17,8 @@ logScope: topics = "rest_builderapi"
proc installBuilderApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installBuilderApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.4.0#/Builder/getNextWithdrawals # https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.4.0#/Builder/getNextWithdrawals
# https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/builder/states/expected_withdrawals.yaml # https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/builder/states/expected_withdrawals.yaml
router.api(MethodGet, "/eth/v1/builder/states/{state_id}/expected_withdrawals") do ( router.api2(MethodGet,
"/eth/v1/builder/states/{state_id}/expected_withdrawals") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
let let
sid = state_id.valueOr: sid = state_id.valueOr:
@ -41,4 +42,4 @@ proc installBuilderApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError( return RestApiResponse.jsonError(
Http400, "The specified state is not a capella state") Http400, "The specified state is not a capella state")
return RestApiResponse.jsonError(Http404, StateNotFoundError) RestApiResponse.jsonError(Http404, StateNotFoundError)

View File

@ -326,19 +326,16 @@ proc installConfigApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
# https://ethereum.github.io/beacon-APIs/#/Config/getForkSchedule # https://ethereum.github.io/beacon-APIs/#/Config/getForkSchedule
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/config/fork_schedule") do () -> RestApiResponse: "/eth/v1/config/fork_schedule") do () -> RestApiResponse:
return RestApiResponse.response(cachedForkSchedule, Http200, RestApiResponse.response(cachedForkSchedule, Http200, "application/json")
"application/json")
# https://ethereum.github.io/beacon-APIs/#/Config/getSpec # https://ethereum.github.io/beacon-APIs/#/Config/getSpec
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/config/spec") do () -> RestApiResponse: "/eth/v1/config/spec") do () -> RestApiResponse:
return RestApiResponse.response(cachedConfigSpec, Http200, RestApiResponse.response(cachedConfigSpec, Http200, "application/json")
"application/json")
# https://ethereum.github.io/beacon-APIs/#/Config/getDepositContract # https://ethereum.github.io/beacon-APIs/#/Config/getDepositContract
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/config/deposit_contract") do () -> RestApiResponse: "/eth/v1/config/deposit_contract") do () -> RestApiResponse:
return RestApiResponse.response(cachedDepositContract, Http200, RestApiResponse.response(cachedDepositContract, Http200, "application/json")
"application/json")

View File

@ -19,15 +19,15 @@ logScope: topics = "rest_debug"
proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/#/Debug/getState # https://ethereum.github.io/beacon-APIs/#/Debug/getState
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/debug/beacon/states/{state_id}") do ( "/eth/v1/debug/beacon/states/{state_id}") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
return RestApiResponse.jsonError( RestApiResponse.jsonError(
Http410, DeprecatedRemovalBeaconBlocksDebugStateV1) Http410, DeprecatedRemovalBeaconBlocksDebugStateV1)
# https://ethereum.github.io/beacon-APIs/#/Debug/getStateV2 # https://ethereum.github.io/beacon-APIs/#/Debug/getStateV2
router.api(MethodGet, router.api2(MethodGet,
"/eth/v2/debug/beacon/states/{state_id}") do ( "/eth/v2/debug/beacon/states/{state_id}") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
let bslot = let bslot =
block: block:
@ -46,43 +46,40 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError) return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
res.get() res.get()
node.withStateForBlockSlotId(bslot): node.withStateForBlockSlotId(bslot):
return return
if contentType == jsonMediaType: if contentType == jsonMediaType:
RestApiResponse.jsonResponseState( RestApiResponse.jsonResponseState(
state, state, node.getStateOptimistic(state))
node.getStateOptimistic(state)
)
elif contentType == sszMediaType: elif contentType == sszMediaType:
let headers = [("eth-consensus-version", state.kind.toString())] let headers = [("eth-consensus-version", state.kind.toString())]
withState(state): withState(state):
RestApiResponse.sszResponse(forkyState.data, headers) RestApiResponse.sszResponse(forkyState.data, headers)
else: else:
RestApiResponse.jsonError(Http500, InvalidAcceptError) RestApiResponse.jsonError(Http500, InvalidAcceptError)
return RestApiResponse.jsonError(Http404, StateNotFoundError)
RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Debug/getDebugChainHeads # https://ethereum.github.io/beacon-APIs/#/Debug/getDebugChainHeads
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/debug/beacon/heads") do () -> RestApiResponse: "/eth/v1/debug/beacon/heads") do () -> RestApiResponse:
return RestApiResponse.jsonError( RestApiResponse.jsonError(
Http410, DeprecatedRemovalGetDebugChainHeadsV1) Http410, DeprecatedRemovalGetDebugChainHeadsV1)
# https://ethereum.github.io/beacon-APIs/#/Debug/getDebugChainHeadsV2 # https://ethereum.github.io/beacon-APIs/#/Debug/getDebugChainHeadsV2
router.api(MethodGet, router.api2(MethodGet,
"/eth/v2/debug/beacon/heads") do () -> RestApiResponse: "/eth/v2/debug/beacon/heads") do () -> RestApiResponse:
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
node.dag.heads.mapIt( node.dag.heads.mapIt(
( (root: it.root, slot: it.slot,
root: it.root, execution_optimistic: not it.executionValid)
slot: it.slot,
execution_optimistic: not it.executionValid
)
) )
) )
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Debug/getDebugForkChoice # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Debug/getDebugForkChoice
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/debug/fork_choice") do () -> RestApiResponse: "/eth/v1/debug/fork_choice") do () -> RestApiResponse:
template forkChoice: auto = node.attestationPool[].forkChoice template forkChoice: auto = node.attestationPool[].forkChoice
var response = GetForkChoiceResponse( var response = GetForkChoiceResponse(
@ -135,4 +132,4 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
best_child: item.bestChild, best_child: item.bestChild,
bestDescendant: item.bestDescendant)) bestDescendant: item.bestDescendant))
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)

View File

@ -85,7 +85,7 @@ proc eventHandler*[T](response: HttpResponseRef,
proc installEventApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installEventApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/#/Events/eventstream # https://ethereum.github.io/beacon-APIs/#/Events/eventstream
router.api(MethodGet, "/eth/v1/events") do ( router.api2(MethodGet, "/eth/v1/events") do (
topics: seq[EventTopic]) -> RestApiResponse: topics: seq[EventTopic]) -> RestApiResponse:
let eventTopics = let eventTopics =
block: block:
@ -174,7 +174,10 @@ proc installEventApiHandlers*(router: var RestRouter, node: BeaconNode) =
res.add(handler) res.add(handler)
res res
discard await one(handlers) try:
discard await race(handlers)
except ValueError:
raiseAssert "There should be more than one event handler at this point!"
# One of the handlers finished, it means that connection has been droped, so # One of the handlers finished, it means that connection has been droped, so
# we cancelling all other handlers. # we cancelling all other handlers.
let pending = let pending =

View File

@ -142,17 +142,17 @@ proc handleAddRemoteValidatorReq(host: KeymanagerHost,
proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) = proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
# https://ethereum.github.io/keymanager-APIs/#/Keymanager/ListKeys # https://ethereum.github.io/keymanager-APIs/#/Keymanager/ListKeys
router.api(MethodGet, "/eth/v1/keystores") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/keystores") do () -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
let response = GetKeystoresResponse( let response = GetKeystoresResponse(
data: listLocalValidators(host.validatorPool[])) data: listLocalValidators(host.validatorPool[]))
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# https://ethereum.github.io/keymanager-APIs/#/Keymanager/ImportKeystores # https://ethereum.github.io/keymanager-APIs/#/Keymanager/ImportKeystores
router.api(MethodPost, "/eth/v1/keystores") do ( router.api2(MethodPost, "/eth/v1/keystores") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
@ -167,14 +167,30 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
if request.slashing_protection.isSome(): if request.slashing_protection.isSome():
let slashing_protection = request.slashing_protection.get() let slashing_protection = request.slashing_protection.get()
let nodeSPDIR = toSPDIR(host.validatorPool[].slashingProtection) let nodeSPDIR =
try:
toSPDIR(host.validatorPool[].slashingProtection)
except IOError as exc:
return keymanagerApiError(
Http500, "Internal server error; " & $exc.msg)
if nodeSPDIR.metadata.genesis_validators_root.Eth2Digest != if nodeSPDIR.metadata.genesis_validators_root.Eth2Digest !=
slashing_protection.metadata.genesis_validators_root.Eth2Digest: slashing_protection.metadata.genesis_validators_root.Eth2Digest:
return keymanagerApiError(Http400, return keymanagerApiError(Http400,
"The slashing protection database and imported file refer to " & "The slashing protection database and imported file refer to " &
"different blockchains.") "different blockchains.")
let res = inclSPDIR(host.validatorPool[].slashingProtection, let res =
slashing_protection) try:
inclSPDIR(host.validatorPool[].slashingProtection,
slashing_protection)
except SerializationError as exc:
return keymanagerApiError(
Http500, "Internal server error; Failed to import slashing " &
"protection data, reason: " &
exc.formatMsg("slashing_protection"))
except IOError as exc:
return keymanagerApiError(
Http500, "Internal server error; Failed to import slashing " &
"protection data, reason: " & $exc.msg)
if res == siFailure: if res == siFailure:
return keymanagerApiError(Http500, return keymanagerApiError(Http500,
"Internal server error; Failed to import slashing protection data") "Internal server error; Failed to import slashing protection data")
@ -202,10 +218,10 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
response.data.add( response.data.add(
RequestItemStatus(status: $KeystoreStatus.imported)) RequestItemStatus(status: $KeystoreStatus.imported))
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# https://ethereum.github.io/keymanager-APIs/#/Keymanager/DeleteKeys # https://ethereum.github.io/keymanager-APIs/#/Keymanager/DeleteKeys
router.api(MethodDelete, "/eth/v1/keystores") do ( router.api2(MethodDelete, "/eth/v1/keystores") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
@ -221,7 +237,12 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
var var
response: DeleteKeystoresResponse response: DeleteKeystoresResponse
nodeSPDIR = toSPDIR(host.validatorPool[].slashingProtection) nodeSPDIR =
try:
toSPDIR(host.validatorPool[].slashingProtection)
except IOError as exc:
return keymanagerApiError(
Http500, "Internal server error; " & $exc.msg)
# Hash table to keep the removal status of all keys form request # Hash table to keep the removal status of all keys form request
keysAndDeleteStatus = initTable[PubKeyBytes, RequestItemStatus]() keysAndDeleteStatus = initTable[PubKeyBytes, RequestItemStatus]()
responseSPDIR: SPDIR responseSPDIR: SPDIR
@ -263,23 +284,24 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
foundKeystore.status = $KeystoreStatus.notActive foundKeystore.status = $KeystoreStatus.notActive
for index, key in keys: for index, key in keys:
response.data.add(keysAndDeleteStatus[key.blob.PubKey0x.PubKeyBytes]) response.data.add(
keysAndDeleteStatus.getOrDefault(key.blob.PubKey0x.PubKeyBytes))
response.slashing_protection = RestJson.encode(responseSPDIR) response.slashing_protection = RestJson.encode(responseSPDIR)
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/ListRemoteKeys # https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/ListRemoteKeys
router.api(MethodGet, "/eth/v1/remotekeys") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/remotekeys") do () -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
let response = GetRemoteKeystoresResponse( let response = GetRemoteKeystoresResponse(
data: listRemoteValidators(host.validatorPool[])) data: listRemoteValidators(host.validatorPool[]))
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/ImportRemoteKeys # https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/ImportRemoteKeys
router.api(MethodPost, "/eth/v1/remotekeys") do ( router.api2(MethodPost, "/eth/v1/remotekeys") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
@ -307,10 +329,10 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
response.data.add handleAddRemoteValidatorReq(host, keystore) response.data.add handleAddRemoteValidatorReq(host, keystore)
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/DeleteRemoteKeys # https://ethereum.github.io/keymanager-APIs/#/Remote%20Key%20Manager/DeleteRemoteKeys
router.api(MethodDelete, "/eth/v1/remotekeys") do ( router.api2(MethodDelete, "/eth/v1/remotekeys") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
@ -327,10 +349,10 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
var response: DeleteRemoteKeystoresResponse var response: DeleteRemoteKeystoresResponse
for index, key in keys: for index, key in keys:
response.data.add handleRemoveValidatorReq(host, key) response.data.add handleRemoveValidatorReq(host, key)
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/ListFeeRecipient # https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/ListFeeRecipient
router.api(MethodGet, "/eth/v1/validator/{pubkey}/feerecipient") do ( router.api2(MethodGet, "/eth/v1/validator/{pubkey}/feerecipient") do (
pubkey: ValidatorPubKey) -> RestApiResponse: pubkey: ValidatorPubKey) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
@ -344,7 +366,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
ethaddress = host.getSuggestedFeeRecipient( ethaddress = host.getSuggestedFeeRecipient(
pubkey, perValidatorDefaultFeeRecipient) pubkey, perValidatorDefaultFeeRecipient)
return if ethaddress.isOk: if ethaddress.isOk:
RestApiResponse.jsonResponse(ListFeeRecipientResponse( RestApiResponse.jsonResponse(ListFeeRecipientResponse(
pubkey: pubkey, pubkey: pubkey,
ethaddress: ethaddress.get)) ethaddress: ethaddress.get))
@ -356,14 +378,14 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
keymanagerApiError(Http500, "Error reading fee recipient file") keymanagerApiError(Http500, "Error reading fee recipient file")
# https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/SetFeeRecipient # https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/SetFeeRecipient
router.api(MethodPost, "/eth/v1/validator/{pubkey}/feerecipient") do ( router.api2(MethodPost, "/eth/v1/validator/{pubkey}/feerecipient") do (
pubkey: ValidatorPubKey, pubkey: ValidatorPubKey,
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
let let
pubkey= pubkey.valueOr: pubkey = pubkey.valueOr:
return keymanagerApiError(Http400, InvalidValidatorPublicKey) return keymanagerApiError(Http400, InvalidValidatorPublicKey)
feeRecipientReq = feeRecipientReq =
block: block:
@ -376,14 +398,14 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
status = host.setFeeRecipient(pubkey, feeRecipientReq.ethaddress) status = host.setFeeRecipient(pubkey, feeRecipientReq.ethaddress)
return if status.isOk: if status.isOk:
RestApiResponse.response("", Http202, "text/plain") RestApiResponse.response("", Http202, "text/plain")
else: else:
keymanagerApiError( keymanagerApiError(
Http500, "Failed to set fee recipient: " & status.error) Http500, "Failed to set fee recipient: " & status.error)
# https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/DeleteFeeRecipient # https://ethereum.github.io/keymanager-APIs/#/Fee%20Recipient/DeleteFeeRecipient
router.api(MethodDelete, "/eth/v1/validator/{pubkey}/feerecipient") do ( router.api2(MethodDelete, "/eth/v1/validator/{pubkey}/feerecipient") do (
pubkey: ValidatorPubKey) -> RestApiResponse: pubkey: ValidatorPubKey) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
@ -393,15 +415,15 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
return keymanagerApiError(Http400, InvalidValidatorPublicKey) return keymanagerApiError(Http400, InvalidValidatorPublicKey)
res = host.removeFeeRecipientFile(pubkey) res = host.removeFeeRecipientFile(pubkey)
return if res.isOk: if res.isOk:
RestApiResponse.response("", Http204, "text/plain") RestApiResponse.response("", Http204, "text/plain")
else: else:
keymanagerApiError( keymanagerApiError(
Http500, "Failed to remove fee recipient file: " & res.error) Http500, "Failed to remove fee recipient file: " & res.error)
# https://ethereum.github.io/keymanager-APIs/#/Gas%20Limit/getGasLimit # https://ethereum.github.io/keymanager-APIs/#/Gas%20Limit/getGasLimit
router.api(MethodGet, "/eth/v1/validator/{pubkey}/gas_limit") do ( router.api2(MethodGet, "/eth/v1/validator/{pubkey}/gas_limit") do (
pubkey: ValidatorPubKey) -> RestApiResponse: pubkey: ValidatorPubKey) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
@ -411,7 +433,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
return keymanagerApiError(Http400, InvalidValidatorPublicKey) return keymanagerApiError(Http400, InvalidValidatorPublicKey)
gasLimit = host.getSuggestedGasLimit(pubkey) gasLimit = host.getSuggestedGasLimit(pubkey)
return if gasLimit.isOk: if gasLimit.isOk:
RestApiResponse.jsonResponse(GetValidatorGasLimitResponse( RestApiResponse.jsonResponse(GetValidatorGasLimitResponse(
pubkey: pubkey, pubkey: pubkey,
gas_limit: gasLimit.get)) gas_limit: gasLimit.get))
@ -423,9 +445,9 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
keymanagerApiError(Http500, "Error reading gas limit file") keymanagerApiError(Http500, "Error reading gas limit file")
# https://ethereum.github.io/keymanager-APIs/#/Gas%20Limit/setGasLimit # https://ethereum.github.io/keymanager-APIs/#/Gas%20Limit/setGasLimit
router.api(MethodPost, "/eth/v1/validator/{pubkey}/gas_limit") do ( router.api2(MethodPost, "/eth/v1/validator/{pubkey}/gas_limit") do (
pubkey: ValidatorPubKey, pubkey: ValidatorPubKey,
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
@ -443,15 +465,15 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
status = host.setGasLimit(pubkey, gasLimitReq.gas_limit) status = host.setGasLimit(pubkey, gasLimitReq.gas_limit)
return if status.isOk: if status.isOk:
RestApiResponse.response("", Http202, "text/plain") RestApiResponse.response("", Http202, "text/plain")
else: else:
keymanagerApiError( keymanagerApiError(
Http500, "Failed to set gas limit: " & status.error) Http500, "Failed to set gas limit: " & status.error)
# https://ethereum.github.io/keymanager-APIs/#/Gas%20Limit/deleteGasLimit # https://ethereum.github.io/keymanager-APIs/#/Gas%20Limit/deleteGasLimit
router.api(MethodDelete, "/eth/v1/validator/{pubkey}/gas_limit") do ( router.api2(MethodDelete, "/eth/v1/validator/{pubkey}/gas_limit") do (
pubkey: ValidatorPubKey) -> RestApiResponse: pubkey: ValidatorPubKey) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
@ -460,7 +482,7 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
return keymanagerApiError(Http400, InvalidValidatorPublicKey) return keymanagerApiError(Http400, InvalidValidatorPublicKey)
res = host.removeGasLimitFile(pubkey) res = host.removeGasLimitFile(pubkey)
return if res.isOk: if res.isOk:
RestApiResponse.response("", Http204, "text/plain") RestApiResponse.response("", Http204, "text/plain")
else: else:
keymanagerApiError( keymanagerApiError(
@ -468,17 +490,18 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
# TODO: These URLs will be changed once we submit a proposal for # TODO: These URLs will be changed once we submit a proposal for
# /eth/v2/remotekeys that supports distributed keys. # /eth/v2/remotekeys that supports distributed keys.
router.api(MethodGet, "/eth/v1/remotekeys/distributed") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/remotekeys/distributed") do (
) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
return authErrorResponse authStatus.error return authErrorResponse authStatus.error
let response = GetDistributedKeystoresResponse( let response = GetDistributedKeystoresResponse(
data: listRemoteDistributedValidators(host.validatorPool[])) data: listRemoteDistributedValidators(host.validatorPool[]))
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# TODO: These URLs will be changed once we submit a proposal for # TODO: These URLs will be changed once we submit a proposal for
# /eth/v2/remotekeys that supports distributed keys. # /eth/v2/remotekeys that supports distributed keys.
router.api(MethodPost, "/eth/v1/remotekeys/distributed") do ( router.api2(MethodPost, "/eth/v1/remotekeys/distributed") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
@ -504,9 +527,9 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
) )
response.data.add handleAddRemoteValidatorReq(host, keystore) response.data.add handleAddRemoteValidatorReq(host, keystore)
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
router.api(MethodDelete, "/eth/v1/remotekeys/distributed") do ( router.api2(MethodDelete, "/eth/v1/remotekeys/distributed") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let authStatus = checkAuthorization(request, host) let authStatus = checkAuthorization(request, host)
if authStatus.isErr(): if authStatus.isErr():
@ -524,10 +547,10 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
for index, key in keys: for index, key in keys:
response.data.add handleRemoveValidatorReq(host, key) response.data.add handleRemoveValidatorReq(host, key)
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)
# https://ethereum.github.io/keymanager-APIs/?urls.primaryName=dev#/Voluntary%20Exit/signVoluntaryExit # https://ethereum.github.io/keymanager-APIs/?urls.primaryName=dev#/Voluntary%20Exit/signVoluntaryExit
router.api(MethodPost, "/eth/v1/validator/{pubkey}/voluntary_exit") do ( router.api2(MethodPost, "/eth/v1/validator/{pubkey}/voluntary_exit") do (
pubkey: ValidatorPubKey, epoch: Option[Epoch], pubkey: ValidatorPubKey, epoch: Option[Epoch],
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
@ -575,4 +598,4 @@ proc installKeymanagerHandlers*(router: var RestRouter, host: KeymanagerHost) =
message: voluntaryExit, message: voluntaryExit,
signature: signature signature: signature
) )
return RestApiResponse.jsonResponse(response) RestApiResponse.jsonResponse(response)

View File

@ -15,8 +15,8 @@ logScope: topics = "rest_light_client"
proc installLightClientApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installLightClientApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientBootstrap # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientBootstrap
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/light_client/bootstrap/{block_root}") do ( "/eth/v1/beacon/light_client/bootstrap/{block_root}") do (
block_root: Eth2Digest) -> RestApiResponse: block_root: Eth2Digest) -> RestApiResponse:
doAssert node.dag.lcDataStore.serve doAssert node.dag.lcDataStore.serve
let contentType = let contentType =
@ -51,8 +51,8 @@ proc installLightClientApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http404, LCBootstrapUnavailable) return RestApiResponse.jsonError(Http404, LCBootstrapUnavailable)
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientUpdatesByRange # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientUpdatesByRange
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/light_client/updates") do ( "/eth/v1/beacon/light_client/updates") do (
start_period: Option[SyncCommitteePeriod], count: Option[uint64] start_period: Option[SyncCommitteePeriod], count: Option[uint64]
) -> RestApiResponse: ) -> RestApiResponse:
doAssert node.dag.lcDataStore.serve doAssert node.dag.lcDataStore.serve
@ -116,8 +116,8 @@ proc installLightClientApiHandlers*(router: var RestRouter, node: BeaconNode) =
RestApiResponse.jsonError(Http500, InvalidAcceptError) RestApiResponse.jsonError(Http500, InvalidAcceptError)
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientFinalityUpdate # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientFinalityUpdate
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/light_client/finality_update") do ( "/eth/v1/beacon/light_client/finality_update") do (
) -> RestApiResponse: ) -> RestApiResponse:
doAssert node.dag.lcDataStore.serve doAssert node.dag.lcDataStore.serve
let contentType = let contentType =
@ -147,8 +147,8 @@ proc installLightClientApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http404, LCFinUpdateUnavailable) return RestApiResponse.jsonError(Http404, LCFinUpdateUnavailable)
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientOptimisticUpdate # https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getLightClientOptimisticUpdate
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/beacon/light_client/optimistic_update") do ( "/eth/v1/beacon/light_client/optimistic_update") do (
) -> RestApiResponse: ) -> RestApiResponse:
doAssert node.dag.lcDataStore.serve doAssert node.dag.lcDataStore.serve
let contentType = let contentType =

View File

@ -152,16 +152,16 @@ proc toNode(v: PubSubPeer, backoff: Moment): RestPubSubPeer =
) )
proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
router.api(MethodGet, "/nimbus/v1/beacon/head") do () -> RestApiResponse: router.api2(MethodGet, "/nimbus/v1/beacon/head") do () -> RestApiResponse:
return RestApiResponse.jsonResponse(node.dag.head.slot) RestApiResponse.jsonResponse(node.dag.head.slot)
router.api(MethodGet, "/nimbus/v1/chain/head") do() -> RestApiResponse: router.api2(MethodGet, "/nimbus/v1/chain/head") do() -> RestApiResponse:
let let
head = node.dag.head head = node.dag.head
finalized = getStateField(node.dag.headState, finalized_checkpoint) finalized = getStateField(node.dag.headState, finalized_checkpoint)
justified = justified =
getStateField(node.dag.headState, current_justified_checkpoint) getStateField(node.dag.headState, current_justified_checkpoint)
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
( (
head_slot: head.slot, head_slot: head.slot,
head_block_root: head.root.data.toHex(), head_block_root: head.root.data.toHex(),
@ -172,26 +172,26 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
) )
router.api(MethodGet, "/nimbus/v1/syncmanager/status") do ( router.api2(MethodGet, "/nimbus/v1/syncmanager/status") do (
) -> RestApiResponse: ) -> RestApiResponse:
return RestApiResponse.jsonResponse(node.syncManager.inProgress) RestApiResponse.jsonResponse(node.syncManager.inProgress)
router.api(MethodGet, "/nimbus/v1/node/peerid") do ( router.api2(MethodGet, "/nimbus/v1/node/peerid") do (
) -> RestApiResponse: ) -> RestApiResponse:
return RestApiResponse.jsonResponse((peerid: $node.network.peerId())) RestApiResponse.jsonResponse((peerid: $node.network.peerId()))
router.api(MethodGet, "/nimbus/v1/node/version") do ( router.api2(MethodGet, "/nimbus/v1/node/version") do (
) -> RestApiResponse: ) -> RestApiResponse:
return RestApiResponse.jsonResponse((version: "Nimbus/" & fullVersionStr)) RestApiResponse.jsonResponse((version: "Nimbus/" & fullVersionStr))
router.api(MethodGet, "/nimbus/v1/network/ids") do ( router.api2(MethodGet, "/nimbus/v1/network/ids") do (
) -> RestApiResponse: ) -> RestApiResponse:
var res: seq[PeerId] var res: seq[PeerId]
for peerId, peer in node.network.peerPool: for peerId, peer in node.network.peerPool:
res.add(peerId) res.add(peerId)
return RestApiResponse.jsonResponse((peerids: res)) RestApiResponse.jsonResponse((peerids: res))
router.api(MethodGet, "/nimbus/v1/network/peers") do ( router.api2(MethodGet, "/nimbus/v1/network/peers") do (
) -> RestApiResponse: ) -> RestApiResponse:
var res: seq[RestSimplePeer] var res: seq[RestSimplePeer]
for id, peer in node.network.peerPool: for id, peer in node.network.peerPool:
@ -202,9 +202,9 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
score: peer.score score: peer.score
) )
) )
return RestApiResponse.jsonResponse((peers: res)) RestApiResponse.jsonResponse((peers: res))
router.api(MethodPost, "/nimbus/v1/graffiti") do ( router.api2(MethodPost, "/nimbus/v1/graffiti") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
if contentBody.isNone: if contentBody.isNone:
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
@ -225,20 +225,19 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, InvalidGraffitiBytesValue, return RestApiResponse.jsonError(Http400, InvalidGraffitiBytesValue,
$graffitiBytes.error()) $graffitiBytes.error())
node.graffitiBytes = graffitiBytes.get() node.graffitiBytes = graffitiBytes.get()
return RestApiResponse.jsonResponse((result: true)) RestApiResponse.jsonResponse((result: true))
elif body.contentType == TextPlainMediaType: elif body.contentType == TextPlainMediaType:
return node.setGraffitiAux body.strData() node.setGraffitiAux body.strData()
elif body.contentType == UrlEncodedMediaType: elif body.contentType == UrlEncodedMediaType:
return node.setGraffitiAux decodeUrl(body.strData()) node.setGraffitiAux decodeUrl(body.strData())
else: else:
return RestApiResponse.jsonError(Http400, "Unsupported content type: " & RestApiResponse.jsonError(
$body.contentType) Http400, "Unsupported content type: " & $body.contentType)
router.api(MethodGet, "/nimbus/v1/graffiti") do ( router.api2(MethodGet, "/nimbus/v1/graffiti") do () -> RestApiResponse:
) -> RestApiResponse: RestApiResponse.jsonResponse(node.graffitiBytes)
return RestApiResponse.jsonResponse(node.graffitiBytes)
router.api(MethodPost, "/nimbus/v1/chronicles/settings") do ( router.api2(MethodPost, "/nimbus/v1/chronicles/settings") do (
log_level: Option[string]) -> RestApiResponse: log_level: Option[string]) -> RestApiResponse:
if log_level.isSome(): if log_level.isSome():
let level = let level =
@ -249,15 +248,17 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
$res.error()) $res.error())
res.get() res.get()
{.gcsafe.}: {.gcsafe.}:
updateLogLevel(level) try:
return RestApiResponse.jsonResponse((result: true)) updateLogLevel(level)
except ValueError:
return RestApiResponse.jsonResponse((result: false))
RestApiResponse.jsonResponse((result: true))
router.api(MethodGet, "/nimbus/v1/eth1/chain") do ( router.api2(MethodGet, "/nimbus/v1/eth1/chain") do () -> RestApiResponse:
) -> RestApiResponse:
let res = mapIt(node.elManager.eth1ChainBlocks, it) let res = mapIt(node.elManager.eth1ChainBlocks, it)
return RestApiResponse.jsonResponse(res) RestApiResponse.jsonResponse(res)
router.api(MethodGet, "/nimbus/v1/eth1/proposal_data") do ( router.api2(MethodGet, "/nimbus/v1/eth1/proposal_data") do (
) -> RestApiResponse: ) -> RestApiResponse:
let wallSlot = node.beaconClock.now.slotOrZero let wallSlot = node.beaconClock.now.slotOrZero
let head = let head =
@ -279,7 +280,7 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
do: do:
return RestApiResponse.jsonError(Http400, PrunedStateError) return RestApiResponse.jsonError(Http400, PrunedStateError)
router.api(MethodGet, "/nimbus/v1/debug/chronos/futures") do ( router.api2(MethodGet, "/nimbus/v1/debug/chronos/futures") do (
) -> RestApiResponse: ) -> RestApiResponse:
when defined(chronosFutureTracking): when defined(chronosFutureTracking):
var res: seq[RestFutureInfo] var res: seq[RestFutureInfo]
@ -299,12 +300,12 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
state: $item.state state: $item.state
) )
) )
return RestApiResponse.jsonResponse(res) RestApiResponse.jsonResponse(res)
else: else:
return RestApiResponse.jsonError(Http503, RestApiResponse.jsonError(Http503,
"Compile with '-d:chronosFutureTracking' to get this request working") "Compile with '-d:chronosFutureTracking' to get this request working")
router.api(MethodGet, "/nimbus/v1/debug/chronos/metrics") do ( router.api2(MethodGet, "/nimbus/v1/debug/chronos/metrics") do (
) -> RestApiResponse: ) -> RestApiResponse:
template getCount(name: string): uint64 = template getCount(name: string): uint64 =
@ -329,9 +330,9 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
http_body_readers: getCount(HttpBodyReaderTrackerName), http_body_readers: getCount(HttpBodyReaderTrackerName),
http_body_writers: getCount(HttpBodyWriterTrackerName) http_body_writers: getCount(HttpBodyWriterTrackerName)
) )
return RestApiResponse.jsonResponse(res) RestApiResponse.jsonResponse(res)
router.api(MethodGet, "/nimbus/v1/debug/chronos/restserver/connections") do ( router.api2(MethodGet, "/nimbus/v1/debug/chronos/restserver/connections") do (
) -> RestApiResponse: ) -> RestApiResponse:
var res: seq[RestConnectionInfo] var res: seq[RestConnectionInfo]
for connection in node.restServer.server.getConnections(): for connection in node.restServer.server.getConnections():
@ -378,9 +379,9 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
since_create: sinceCreate since_create: sinceCreate
) )
) )
return RestApiResponse.jsonResponse(res) RestApiResponse.jsonResponse(res)
router.api(MethodPost, "/nimbus/v1/validator/activity/{epoch}") do ( router.api2(MethodPost, "/nimbus/v1/validator/activity/{epoch}") do (
epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse: epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse:
let indexList = let indexList =
block: block:
@ -441,11 +442,10 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
active: node.attestationPool[].validatorSeenAtEpoch(qepoch, it) active: node.attestationPool[].validatorSeenAtEpoch(qepoch, it)
) )
) )
return RestApiResponse.jsonResponse(response) RestApiResponse.jsonResponse(response)
router.api(MethodGet, "/nimbus/v1/debug/gossip/peers") do ( router.api2(MethodGet, "/nimbus/v1/debug/gossip/peers") do (
) -> RestApiResponse: ) -> RestApiResponse:
let gossipPeers = let gossipPeers =
block: block:
var res: seq[tuple[topic: string, peers: seq[RestPubSubPeer]]] var res: seq[tuple[topic: string, peers: seq[RestPubSubPeer]]]
@ -496,7 +496,7 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
for peerId, peer in node.network.pubsub.peers: for peerId, peer in node.network.pubsub.peers:
peers.add(RestPeerStatus(peerId: peerId, connected: peer.connected)) peers.add(RestPeerStatus(peerId: peerId, connected: peer.connected))
peers peers
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
( (
gossip_peers: gossipPeers, gossip_peers: gossipPeers,
mesh_peers: meshPeers, mesh_peers: meshPeers,
@ -506,7 +506,7 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
) )
router.api(MethodPost, "/nimbus/v1/timesync") do ( router.api2(MethodPost, "/nimbus/v1/timesync") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let let
timestamp2 = getTimestamp() timestamp2 = getTimestamp()
@ -528,4 +528,4 @@ proc installNimbusApiHandlers*(router: var RestRouter, node: BeaconNode) =
timestamp3: getTimestamp(), timestamp3: getTimestamp(),
delay: uint64(delay.nanoseconds) delay: uint64(delay.nanoseconds)
) )
return RestApiResponse.jsonResponsePlain(response) RestApiResponse.jsonResponsePlain(response)

View File

@ -140,7 +140,7 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
RestApiResponse.prepareJsonResponse((version: "Nimbus/" & fullVersionStr)) RestApiResponse.prepareJsonResponse((version: "Nimbus/" & fullVersionStr))
# https://ethereum.github.io/beacon-APIs/#/Node/getNetworkIdentity # https://ethereum.github.io/beacon-APIs/#/Node/getNetworkIdentity
router.api(MethodGet, "/eth/v1/node/identity") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/node/identity") do () -> RestApiResponse:
let discoveryAddresses = let discoveryAddresses =
block: block:
let res = node.getDiscoveryAddresses() let res = node.getDiscoveryAddresses()
@ -157,7 +157,7 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
else: else:
newSeq[string]() newSeq[string]()
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
( (
peer_id: $node.network.peerId(), peer_id: $node.network.peerId(),
enr: node.network.enrRecord().toURI(), enr: node.network.enrRecord().toURI(),
@ -172,7 +172,7 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
# https://ethereum.github.io/beacon-APIs/#/Node/getPeers # https://ethereum.github.io/beacon-APIs/#/Node/getPeers
router.api(MethodGet, "/eth/v1/node/peers") do ( router.api2(MethodGet, "/eth/v1/node/peers") do (
state: seq[PeerStateKind], state: seq[PeerStateKind],
direction: seq[PeerDirectKind]) -> RestApiResponse: direction: seq[PeerDirectKind]) -> RestApiResponse:
let connectionMask = let connectionMask =
@ -208,11 +208,10 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
proto: node.network.switch.peerStore[ProtoVersionBook][peer.peerId] proto: node.network.switch.peerStore[ProtoVersionBook][peer.peerId]
) )
res.add(peer) res.add(peer)
return RestApiResponse.jsonResponseWMeta(res, RestApiResponse.jsonResponseWMeta(res, (count: RestNumeric(len(res))))
(count: RestNumeric(len(res))))
# https://ethereum.github.io/beacon-APIs/#/Node/getPeerCount # https://ethereum.github.io/beacon-APIs/#/Node/getPeerCount
router.api(MethodGet, "/eth/v1/node/peer_count") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/node/peer_count") do () -> RestApiResponse:
var res: RestNodePeerCount var res: RestNodePeerCount
for item in node.network.peers.values(): for item in node.network.peers.values():
case item.connectionState case item.connectionState
@ -226,10 +225,10 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
inc(res.disconnected) inc(res.disconnected)
of ConnectionState.None: of ConnectionState.None:
discard discard
return RestApiResponse.jsonResponse(res) RestApiResponse.jsonResponse(res)
# https://ethereum.github.io/beacon-APIs/#/Node/getPeer # https://ethereum.github.io/beacon-APIs/#/Node/getPeer
router.api(MethodGet, "/eth/v1/node/peers/{peer_id}") do ( router.api2(MethodGet, "/eth/v1/node/peers/{peer_id}") do (
peer_id: PeerId) -> RestApiResponse: peer_id: PeerId) -> RestApiResponse:
let peer = let peer =
block: block:
@ -240,25 +239,26 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
if isNil(res): if isNil(res):
return RestApiResponse.jsonError(Http404, PeerNotFoundError) return RestApiResponse.jsonError(Http404, PeerNotFoundError)
res res
return RestApiResponse.jsonResponse( RestApiResponse.jsonResponse(
( (
peer_id: $peer.peerId, peer_id: $peer.peerId,
enr: if peer.enr.isSome(): peer.enr.get().toURI() else: "", enr: if peer.enr.isSome(): peer.enr.get().toURI() else: "",
last_seen_p2p_address: getLastSeenAddress(node, peer.peerId), last_seen_p2p_address: getLastSeenAddress(node, peer.peerId),
state: peer.connectionState.toString(), state: peer.connectionState.toString(),
direction: peer.direction.toString(), direction: peer.direction.toString(),
agent: node.network.switch.peerStore[AgentBook][peer.peerId], # Fields `agent` and `proto` are not agent: node.network.switch.peerStore[AgentBook][peer.peerId],
proto: node.network.switch.peerStore[ProtoVersionBook][peer.peerId] # part of specification # Fields `agent` and `proto` are not part of specification
proto: node.network.switch.peerStore[ProtoVersionBook][peer.peerId]
# Fields `agent` and `proto` are not part of specification
) )
) )
# https://ethereum.github.io/beacon-APIs/#/Node/getNodeVersion # https://ethereum.github.io/beacon-APIs/#/Node/getNodeVersion
router.api(MethodGet, "/eth/v1/node/version") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/node/version") do () -> RestApiResponse:
return RestApiResponse.response(cachedVersion, Http200, RestApiResponse.response(cachedVersion, Http200, "application/json")
"application/json")
# https://ethereum.github.io/beacon-APIs/#/Node/getSyncingStatus # https://ethereum.github.io/beacon-APIs/#/Node/getSyncingStatus
router.api(MethodGet, "/eth/v1/node/syncing") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/node/syncing") do () -> RestApiResponse:
let let
wallSlot = node.beaconClock.now().slotOrZero() wallSlot = node.beaconClock.now().slotOrZero()
headSlot = node.dag.head.slot headSlot = node.dag.head.slot
@ -284,10 +284,10 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
is_syncing: isSyncing, is_optimistic: isOptimistic, is_syncing: isSyncing, is_optimistic: isOptimistic,
el_offline: elOffline el_offline: elOffline
) )
return RestApiResponse.jsonResponse(info) RestApiResponse.jsonResponse(info)
# https://ethereum.github.io/beacon-APIs/#/Node/getHealth # https://ethereum.github.io/beacon-APIs/#/Node/getHealth
router.api(MethodGet, "/eth/v1/node/health") do () -> RestApiResponse: router.api2(MethodGet, "/eth/v1/node/health") do () -> RestApiResponse:
# TODO: Add ability to detect node's issues and return 503 error according # TODO: Add ability to detect node's issues and return 503 error according
# to specification. # to specification.
let status = let status =
@ -295,4 +295,4 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
Http206 Http206
else: else:
Http200 Http200
return RestApiResponse.response("", status, contentType = "") RestApiResponse.response("", status, contentType = "")

View File

@ -24,7 +24,7 @@ logScope: topics = "rest_validatorapi"
proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/#/Validator/getAttesterDuties # https://ethereum.github.io/beacon-APIs/#/Validator/getAttesterDuties
router.api(MethodPost, "/eth/v1/validator/duties/attester/{epoch}") do ( router.api2(MethodPost, "/eth/v1/validator/duties/attester/{epoch}") do (
epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse: epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse:
let indexList = let indexList =
block: block:
@ -107,11 +107,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
shufflingRef.attester_dependent_slot, shufflingRef.attester_dependent_slot,
shufflingRef.attester_dependent_root) shufflingRef.attester_dependent_root)
return RestApiResponse.jsonResponseWRoot( RestApiResponse.jsonResponseWRoot(
duties, shufflingRef.attester_dependent_root, optimistic) duties, shufflingRef.attester_dependent_root, optimistic)
# https://ethereum.github.io/beacon-APIs/#/Validator/getProposerDuties # https://ethereum.github.io/beacon-APIs/#/Validator/getProposerDuties
router.api(MethodGet, "/eth/v1/validator/duties/proposer/{epoch}") do ( router.api2(MethodGet, "/eth/v1/validator/duties/proposer/{epoch}") do (
epoch: Epoch) -> RestApiResponse: epoch: Epoch) -> RestApiResponse:
let qepoch = let qepoch =
block: block:
@ -159,11 +159,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
epochRef.proposer_dependent_slot, epochRef.proposer_dependent_slot,
epochRef.proposer_dependent_root) epochRef.proposer_dependent_root)
return RestApiResponse.jsonResponseWRoot( RestApiResponse.jsonResponseWRoot(
duties, epochRef.proposer_dependent_root, optimistic) duties, epochRef.proposer_dependent_root, optimistic)
# https://ethereum.github.io/beacon-APIs/#/Validator/getSyncCommitteeDuties # https://ethereum.github.io/beacon-APIs/#/Validator/getSyncCommitteeDuties
router.api(MethodPost, "/eth/v1/validator/duties/sync/{epoch}") do ( router.api2(MethodPost, "/eth/v1/validator/duties/sync/{epoch}") do (
epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse: epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse:
let indexList = let indexList =
block: block:
@ -311,13 +311,13 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
let res = emptyResponse() let res = emptyResponse()
return RestApiResponse.jsonResponseWOpt(res, execOpt = Opt.some(false)) return RestApiResponse.jsonResponseWOpt(res, execOpt = Opt.some(false))
return RestApiResponse.jsonError(Http404, StateNotFoundError) RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Validator/produceBlock # https://ethereum.github.io/beacon-APIs/#/Validator/produceBlock
router.api(MethodGet, "/eth/v1/validator/blocks/{slot}") do ( router.api2(MethodGet, "/eth/v1/validator/blocks/{slot}") do (
slot: Slot, randao_reveal: Option[ValidatorSig], slot: Slot, randao_reveal: Option[ValidatorSig],
graffiti: Option[GraffitiBytes]) -> RestApiResponse: graffiti: Option[GraffitiBytes]) -> RestApiResponse:
return RestApiResponse.jsonError( RestApiResponse.jsonError(
Http410, DeprecatedRemovalValidatorBlocksV1) Http410, DeprecatedRemovalValidatorBlocksV1)
# https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV2 # https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV2
@ -691,7 +691,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
raiseAssert "preferredContentType() returns invalid content type" raiseAssert "preferredContentType() returns invalid content type"
# https://ethereum.github.io/beacon-APIs/#/Validator/produceAttestationData # https://ethereum.github.io/beacon-APIs/#/Validator/produceAttestationData
router.api(MethodGet, "/eth/v1/validator/attestation_data") do ( router.api2(MethodGet, "/eth/v1/validator/attestation_data") do (
slot: Option[Slot], slot: Option[Slot],
committee_index: Option[CommitteeIndex]) -> RestApiResponse: committee_index: Option[CommitteeIndex]) -> RestApiResponse:
let adata = let adata =
@ -751,10 +751,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
let epochRef = node.dag.getEpochRef(qhead, qslot.epoch, true).valueOr: let epochRef = node.dag.getEpochRef(qhead, qslot.epoch, true).valueOr:
return RestApiResponse.jsonError(Http400, PrunedStateError, $error) return RestApiResponse.jsonError(Http400, PrunedStateError, $error)
makeAttestationData(epochRef, qhead.atSlot(qslot), qindex) makeAttestationData(epochRef, qhead.atSlot(qslot), qindex)
return RestApiResponse.jsonResponse(adata) RestApiResponse.jsonResponse(adata)
# https://ethereum.github.io/beacon-APIs/#/Validator/getAggregatedAttestation # https://ethereum.github.io/beacon-APIs/#/Validator/getAggregatedAttestation
router.api(MethodGet, "/eth/v1/validator/aggregate_attestation") do ( router.api2(MethodGet, "/eth/v1/validator/aggregate_attestation") do (
attestation_data_root: Option[Eth2Digest], attestation_data_root: Option[Eth2Digest],
slot: Option[Slot]) -> RestApiResponse: slot: Option[Slot]) -> RestApiResponse:
let attestation = let attestation =
@ -784,10 +784,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
UnableToGetAggregatedAttestationError) UnableToGetAggregatedAttestationError)
res.get() res.get()
return RestApiResponse.jsonResponse(attestation) RestApiResponse.jsonResponse(attestation)
# https://ethereum.github.io/beacon-APIs/#/Validator/publishAggregateAndProofs # https://ethereum.github.io/beacon-APIs/#/Validator/publishAggregateAndProofs
router.api(MethodPost, "/eth/v1/validator/aggregate_and_proofs") do ( router.api2(MethodPost, "/eth/v1/validator/aggregate_and_proofs") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let proofs = let proofs =
block: block:
@ -810,7 +810,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
await allFutures(pending) await allFutures(pending)
for future in pending: for future in pending:
if future.completed(): if future.completed():
let res = future.read() let res = future.value()
if res.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
AggregateAndProofValidationError, AggregateAndProofValidationError,
@ -818,11 +818,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
else: else:
return RestApiResponse.jsonError(Http500, return RestApiResponse.jsonError(Http500,
"Unexpected server failure, while sending aggregate and proof") "Unexpected server failure, while sending aggregate and proof")
return RestApiResponse.jsonMsgResponse(AggregateAndProofValidationSuccess) RestApiResponse.jsonMsgResponse(AggregateAndProofValidationSuccess)
# https://ethereum.github.io/beacon-APIs/#/Validator/prepareBeaconCommitteeSubnet # https://ethereum.github.io/beacon-APIs/#/Validator/prepareBeaconCommitteeSubnet
router.api(MethodPost, router.api2(MethodPost,
"/eth/v1/validator/beacon_committee_subscriptions") do ( "/eth/v1/validator/beacon_committee_subscriptions") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let requests = let requests =
block: block:
@ -898,11 +898,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.validatorMonitor[].addAutoMonitor( node.validatorMonitor[].addAutoMonitor(
validator_pubkey, ValidatorIndex(request.validator_index)) validator_pubkey, ValidatorIndex(request.validator_index))
return RestApiResponse.jsonMsgResponse(BeaconCommitteeSubscriptionSuccess) RestApiResponse.jsonMsgResponse(BeaconCommitteeSubscriptionSuccess)
# https://ethereum.github.io/beacon-APIs/#/Validator/prepareSyncCommitteeSubnets # https://ethereum.github.io/beacon-APIs/#/Validator/prepareSyncCommitteeSubnets
router.api(MethodPost, router.api2(MethodPost,
"/eth/v1/validator/sync_committee_subscriptions") do ( "/eth/v1/validator/sync_committee_subscriptions") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let subscriptions = let subscriptions =
block: block:
@ -939,11 +939,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.validatorMonitor[].addAutoMonitor( node.validatorMonitor[].addAutoMonitor(
validator_pubkey, ValidatorIndex(item.validator_index)) validator_pubkey, ValidatorIndex(item.validator_index))
return RestApiResponse.jsonMsgResponse(SyncCommitteeSubscriptionSuccess) RestApiResponse.jsonMsgResponse(SyncCommitteeSubscriptionSuccess)
# https://ethereum.github.io/beacon-APIs/#/Validator/produceSyncCommitteeContribution # https://ethereum.github.io/beacon-APIs/#/Validator/produceSyncCommitteeContribution
router.api(MethodGet, router.api2(MethodGet,
"/eth/v1/validator/sync_committee_contribution") do ( "/eth/v1/validator/sync_committee_contribution") do (
slot: Option[Slot], subcommittee_index: Option[SyncSubCommitteeIndex], slot: Option[Slot], subcommittee_index: Option[SyncSubCommitteeIndex],
beacon_block_root: Option[Eth2Digest]) -> RestApiResponse: beacon_block_root: Option[Eth2Digest]) -> RestApiResponse:
let qslot = block: let qslot = block:
@ -1003,11 +1003,11 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
qslot, blck.bid, qindex, contribution) qslot, blck.bid, qindex, contribution)
if not(res): if not(res):
return RestApiResponse.jsonError(Http400, ProduceContributionError) return RestApiResponse.jsonError(Http400, ProduceContributionError)
return RestApiResponse.jsonResponse(contribution) RestApiResponse.jsonResponse(contribution)
# https://ethereum.github.io/beacon-APIs/#/Validator/publishContributionAndProofs # https://ethereum.github.io/beacon-APIs/#/Validator/publishContributionAndProofs
router.api(MethodPost, router.api2(MethodPost,
"/eth/v1/validator/contribution_and_proofs") do ( "/eth/v1/validator/contribution_and_proofs") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let proofs = let proofs =
block: block:
@ -1033,31 +1033,28 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
await allFutures(pending) await allFutures(pending)
for index, future in pending: for index, future in pending:
if future.completed(): if future.completed():
let fres = future.read() let fres = future.value()
if fres.isErr(): if fres.isErr():
let failure = RestIndexedErrorMessageItem(index: index, let failure = RestIndexedErrorMessageItem(index: index,
message: $fres.error()) message: $fres.error())
res.add(failure) res.add(failure)
elif future.failed() or future.cancelled(): elif future.failed() or future.cancelled():
# This is unexpected failure, so we log the error message. # This is unexpected failure, so we log the error message.
let exc = future.readError() let exc = future.error()
let failure = RestIndexedErrorMessageItem(index: index, let failure = RestIndexedErrorMessageItem(index: index,
message: $exc.msg) message: $exc.msg)
res.add(failure) res.add(failure)
res res
if len(failures) > 0: if len(failures) > 0:
return RestApiResponse.jsonErrorList(Http400, RestApiResponse.jsonErrorList(
ContributionAndProofValidationError, Http400, ContributionAndProofValidationError, failures)
failures)
else: else:
return RestApiResponse.jsonMsgResponse( RestApiResponse.jsonMsgResponse(ContributionAndProofValidationSuccess)
ContributionAndProofValidationSuccess
)
# https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/prepareBeaconProposer # https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/prepareBeaconProposer
router.api(MethodPost, router.api2(MethodPost,
"/eth/v1/validator/prepare_beacon_proposer") do ( "/eth/v1/validator/prepare_beacon_proposer") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let let
body = body =
@ -1087,12 +1084,12 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
numUpdatedFeeRecipients = numUpdated, numUpdatedFeeRecipients = numUpdated,
numRefreshedFeeRecipients = numRefreshed numRefreshedFeeRecipients = numRefreshed
return RestApiResponse.response("", Http200, "text/plain") RestApiResponse.response("", Http200, "text/plain")
# https://ethereum.github.io/beacon-APIs/#/Validator/registerValidator # https://ethereum.github.io/beacon-APIs/#/Validator/registerValidator
# https://github.com/ethereum/beacon-APIs/blob/v2.3.0/apis/validator/register_validator.yaml # https://github.com/ethereum/beacon-APIs/blob/v2.3.0/apis/validator/register_validator.yaml
router.api(MethodPost, router.api2(MethodPost,
"/eth/v1/validator/register_validator") do ( "/eth/v1/validator/register_validator") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let let
body = body =
@ -1114,10 +1111,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.externalBuilderRegistrations[signedValidatorRegistration.message.pubkey] = node.externalBuilderRegistrations[signedValidatorRegistration.message.pubkey] =
signedValidatorRegistration signedValidatorRegistration
return RestApiResponse.response("", Http200, "text/plain") RestApiResponse.response("", Http200, "text/plain")
# https://ethereum.github.io/beacon-APIs/#/Validator/getLiveness # https://ethereum.github.io/beacon-APIs/#/Validator/getLiveness
router.api(MethodPost, "/eth/v1/validator/liveness/{epoch}") do ( router.api2(MethodPost, "/eth/v1/validator/liveness/{epoch}") do (
epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse: epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse:
let let
qepoch = qepoch =
@ -1183,10 +1180,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
is_live: node.attestationPool[].validatorSeenAtEpoch(qepoch, it) is_live: node.attestationPool[].validatorSeenAtEpoch(qepoch, it)
) )
) )
return RestApiResponse.jsonResponse(response) RestApiResponse.jsonResponse(response)
# https://github.com/ethereum/beacon-APIs/blob/f087fbf2764e657578a6c29bdf0261b36ee8db1e/apis/validator/beacon_committee_selections.yaml # https://github.com/ethereum/beacon-APIs/blob/f087fbf2764e657578a6c29bdf0261b36ee8db1e/apis/validator/beacon_committee_selections.yaml
router.api(MethodPost, "/eth/v1/validator/beacon_committee_selections") do ( router.api2(MethodPost, "/eth/v1/validator/beacon_committee_selections") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
# "Consensus clients need not support this endpoint and may return a 501." # "Consensus clients need not support this endpoint and may return a 501."
# https://github.com/ethereum/beacon-APIs/pull/224: "This endpoint need not # https://github.com/ethereum/beacon-APIs/pull/224: "This endpoint need not
@ -1194,11 +1191,10 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
# able to use it when a feature flag is turned on, the intercepting # able to use it when a feature flag is turned on, the intercepting
# middleware can handle and swallow the request. I suggest a CL either # middleware can handle and swallow the request. I suggest a CL either
# returns 501 Not Implemented [or] 400 Bad Request." # returns 501 Not Implemented [or] 400 Bad Request."
return RestApiResponse.jsonError( RestApiResponse.jsonError(Http501, AggregationSelectionNotImplemented)
Http501, AggregationSelectionNotImplemented)
# https://github.com/ethereum/beacon-APIs/blob/f087fbf2764e657578a6c29bdf0261b36ee8db1e/apis/validator/sync_committee_selections.yaml # https://github.com/ethereum/beacon-APIs/blob/f087fbf2764e657578a6c29bdf0261b36ee8db1e/apis/validator/sync_committee_selections.yaml
router.api(MethodPost, "/eth/v1/validator/sync_committee_selections") do ( router.api2(MethodPost, "/eth/v1/validator/sync_committee_selections") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
# "Consensus clients need not support this endpoint and may return a 501." # "Consensus clients need not support this endpoint and may return a 501."
# https://github.com/ethereum/beacon-APIs/pull/224: "This endpoint need not # https://github.com/ethereum/beacon-APIs/pull/224: "This endpoint need not
@ -1206,5 +1202,4 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
# able to use it when a feature flag is turned on, the intercepting # able to use it when a feature flag is turned on, the intercepting
# middleware can handle and swallow the request. I suggest a CL either # middleware can handle and swallow the request. I suggest a CL either
# returns 501 Not Implemented [or] 400 Bad Request." # returns 501 Not Implemented [or] 400 Bad Request."
return RestApiResponse.jsonError( RestApiResponse.jsonError(Http501, AggregationSelectionNotImplemented)
Http501, AggregationSelectionNotImplemented)