nimbus-eth2/beacon_chain/rpc/rpc_utils.nim

112 lines
4.4 KiB
Nim

# beacon_chain
# Copyright (c) 2018-2021 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
{.push raises: [Defect].}
import
std/[strutils, parseutils],
stew/byteutils,
../beacon_node, ../validators/validator_duties,
../consensus_object_pools/[block_pools_types, blockchain_dag],
../spec/datatypes/base,
../spec/[forks, helpers],
../spec/eth2_apis/[rpc_types, eth2_json_rpc_serialization]
export forks, rpc_types, eth2_json_rpc_serialization, blockchain_dag
template raiseNoAltairSupport*() =
raise (ref ValueError)(msg:
"The JSON-RPC interface does not support certain Altair operations due to changes in block structure - see https://nimbus.guide/rest-api.html for full altair support")
template withStateForStateId*(stateId: string, body: untyped): untyped =
let
bsi = node.stateIdToBlockSlotId(stateId)
template isState(state: ForkedHashedBeaconState): bool =
state.matches_block_slot(bsi.bid.root, bsi.slot)
if isState(node.dag.headState):
withStateVars(node.dag.headState):
var cache {.inject, used.}: StateCache
body
else:
let rpcState = assignClone(node.dag.headState)
node.dag.withUpdatedState(rpcState[], bsi) do:
body
do:
raise (ref CatchableError)(msg: "Trying to access pruned state")
proc parseRoot*(str: string): Eth2Digest {.raises: [Defect, ValueError].} =
Eth2Digest(data: hexToByteArray[32](str))
func checkEpochToSlotOverflow*(epoch: Epoch) {.raises: [Defect, ValueError].} =
const maxEpoch = epoch(FAR_FUTURE_SLOT)
if epoch >= maxEpoch:
raise newException(
ValueError, "Requesting epoch for which slot would overflow")
proc doChecksAndGetCurrentHead*(node: BeaconNode, slot: Slot): BlockRef {.raises: [Defect, CatchableError].} =
result = node.dag.head
if not node.isSynced(result):
raise newException(CatchableError, "Cannot fulfill request until node is synced")
# TODO for now we limit the requests arbitrarily by up to 2 epochs into the future
if result.slot + uint64(2 * SLOTS_PER_EPOCH) < slot:
raise newException(CatchableError, "Requesting way ahead of the current head")
proc doChecksAndGetCurrentHead*(node: BeaconNode, epoch: Epoch): BlockRef {.raises: [Defect, CatchableError].} =
checkEpochToSlotOverflow(epoch)
node.doChecksAndGetCurrentHead(epoch.start_slot())
proc parseSlot(slot: string): Slot {.raises: [Defect, CatchableError].} =
if slot.len == 0:
raise newException(ValueError, "Empty slot number not allowed")
var parsed: BiggestUInt
if parseBiggestUInt(slot, parsed) != slot.len:
raise newException(ValueError, "Not a valid slot number")
Slot parsed
proc getBlockSlotIdFromString*(node: BeaconNode, slot: string): BlockSlotId {.raises: [Defect, CatchableError].} =
let parsed = parseSlot(slot)
discard node.doChecksAndGetCurrentHead(parsed)
node.dag.getBlockIdAtSlot(parsed).valueOr:
raise newException(ValueError, "Block not found")
proc getBlockIdFromString*(node: BeaconNode, slot: string): BlockId {.raises: [Defect, CatchableError].} =
let parsed = parseSlot(slot)
discard node.doChecksAndGetCurrentHead(parsed)
let bsid = node.dag.getBlockIdAtSlot(parsed)
if bsid.isSome and bsid.get.isProposed():
bsid.get().bid
else:
raise (ref ValueError)(msg: "Block not found")
proc stateIdToBlockSlotId*(node: BeaconNode, stateId: string): BlockSlotId {.raises: [Defect, CatchableError].} =
case stateId:
of "head":
node.dag.head.bid.atSlot()
of "genesis":
node.dag.genesis.atSlot()
of "finalized":
node.dag.finalizedHead.toBlockSlotId().expect("not nil")
of "justified":
node.dag.head.atEpochStart(
getStateField(
node.dag.headState, current_justified_checkpoint).epoch).
toBlockSlotId().valueOr:
raise (ref ValueError)(msg: "State not found")
else:
if stateId.startsWith("0x"):
let stateRoot = parseRoot(stateId)
if stateRoot == getStateRoot(node.dag.headState):
node.dag.head.bid.atSlot()
else:
# We don't have a state root -> BlockSlot mapping
raise (ref ValueError)(msg: "State not found")
else: # Parse as slot number
node.getBlockSlotIdFromString(stateId)