add REST endpoint for fork choice context (#4042)
Implements a proposed REST endpoint for analyzing fork choice behaviour. See https://github.com/ethereum/beacon-APIs/pull/232
This commit is contained in:
parent
613f4a9a50
commit
574b84f96f
|
@ -484,6 +484,12 @@ type
|
|||
defaultValue: true # the use of the nimbus_signing_process binary by default will be delayed until async I/O over stdin/stdout is developed for the child process.
|
||||
name: "in-process-validators" .}: bool
|
||||
|
||||
debugForkChoice* {.
|
||||
hidden
|
||||
desc: "Enable debug API for fork choice (https://github.com/ethereum/beacon-APIs/pull/232)"
|
||||
defaultValue: false
|
||||
name: "debug-fork-choice" .}: bool
|
||||
|
||||
discv5Enabled* {.
|
||||
desc: "Enable Discovery v5"
|
||||
defaultValue: true
|
||||
|
|
|
@ -1692,26 +1692,23 @@ proc pruneStateCachesDAG*(dag: ChainDAGRef) =
|
|||
statePruneDur = statePruneTick - startTick,
|
||||
epochRefPruneDur = epochRefPruneTick - statePruneTick
|
||||
|
||||
proc loadExecutionBlockRoot*(dag: ChainDAGRef, bid: BlockId): Eth2Digest =
|
||||
if dag.cfg.blockForkAtEpoch(bid.slot.epoch) < BeaconBlockFork.Bellatrix:
|
||||
return ZERO_HASH
|
||||
|
||||
let blockData = dag.getForkedBlock(bid).valueOr:
|
||||
return ZERO_HASH
|
||||
|
||||
withBlck(blockData):
|
||||
when stateFork >= BeaconStateFork.Bellatrix:
|
||||
blck.message.body.execution_payload.block_hash
|
||||
else:
|
||||
ZERO_HASH
|
||||
|
||||
proc loadExecutionBlockRoot*(dag: ChainDAGRef, blck: BlockRef): Eth2Digest =
|
||||
if dag.cfg.blockForkAtEpoch(blck.bid.slot.epoch) < BeaconBlockFork.Bellatrix:
|
||||
return ZERO_HASH
|
||||
|
||||
if blck.executionBlockRoot.isSome:
|
||||
return blck.executionBlockRoot.get
|
||||
|
||||
let blockData = dag.getForkedBlock(blck.bid).valueOr:
|
||||
blck.executionBlockRoot = some ZERO_HASH
|
||||
return ZERO_HASH
|
||||
|
||||
let executionBlockRoot =
|
||||
withBlck(blockData):
|
||||
when stateFork >= BeaconStateFork.Bellatrix:
|
||||
blck.message.body.execution_payload.block_hash
|
||||
else:
|
||||
ZERO_HASH
|
||||
blck.executionBlockRoot = some executionBlockRoot
|
||||
|
||||
executionBlockRoot
|
||||
if blck.executionBlockRoot.isNone:
|
||||
blck.executionBlockRoot = some dag.loadExecutionBlockRoot(blck.bid)
|
||||
blck.executionBlockRoot.unsafeGet
|
||||
|
||||
proc updateHead*(
|
||||
dag: ChainDAGRef,
|
||||
|
|
|
@ -552,6 +552,50 @@ func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool =
|
|||
(self.checkpoints.finalized.epoch == GENESIS_EPOCH)
|
||||
)
|
||||
|
||||
# Diagnostics
|
||||
# ----------------------------------------------------------------------
|
||||
# Helpers to dump internal state
|
||||
|
||||
type ProtoArrayItem* = object
|
||||
root*: Eth2Digest
|
||||
parent*: Eth2Digest
|
||||
checkpoints*: FinalityCheckpoints
|
||||
unrealized*: Option[FinalityCheckpoints]
|
||||
weight*: int64
|
||||
bestChild*: Eth2Digest
|
||||
bestDescendant*: Eth2Digest
|
||||
|
||||
func root(self: ProtoNodes, logicalIdx: Option[Index]): Eth2Digest =
|
||||
if logicalIdx.isNone:
|
||||
return ZERO_HASH
|
||||
let node = self[logicalIdx.unsafeGet]
|
||||
if node.isNone:
|
||||
return ZERO_HASH
|
||||
node.unsafeGet.root
|
||||
|
||||
iterator items*(self: ProtoArray): ProtoArrayItem =
|
||||
## Iterate over all nodes known by fork choice.
|
||||
doAssert self.indices.len == self.nodes.len
|
||||
for nodePhysicalIdx, node in self.nodes.buf:
|
||||
if node.root.isZero:
|
||||
continue
|
||||
|
||||
let unrealized = block:
|
||||
let nodeLogicalIdx = nodePhysicalIdx + self.nodes.offset
|
||||
if self.currentEpochTips.hasKey(nodeLogicalIdx):
|
||||
some self.currentEpochTips.unsafeGet(nodeLogicalIdx)
|
||||
else:
|
||||
none(FinalityCheckpoints)
|
||||
|
||||
yield ProtoArrayItem(
|
||||
root: node.root,
|
||||
parent: self.nodes.root(node.parent),
|
||||
checkpoints: node.checkpoints,
|
||||
unrealized: unrealized,
|
||||
weight: node.weight,
|
||||
bestChild: self.nodes.root(node.bestChild),
|
||||
bestDescendant: self.nodes.root(node.bestDescendant))
|
||||
|
||||
# Sanity checks
|
||||
# ----------------------------------------------------------------------
|
||||
# Sanity checks on internal private procedures
|
||||
|
|
|
@ -11,6 +11,8 @@ import ".."/beacon_node,
|
|||
".."/spec/forks,
|
||||
"."/[rest_utils, state_ttl_cache]
|
||||
|
||||
from ../fork_choice/proto_array import ProtoArrayItem, items
|
||||
|
||||
export rest_utils
|
||||
|
||||
logScope: topics = "rest_debug"
|
||||
|
@ -107,6 +109,74 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
)
|
||||
)
|
||||
|
||||
# https://github.com/ethereum/beacon-APIs/pull/232
|
||||
if node.config.debugForkChoice:
|
||||
router.api(MethodGet,
|
||||
"/eth/v1/debug/fork_choice") do () -> RestApiResponse:
|
||||
type
|
||||
ForkChoiceResponseExtraData = object
|
||||
justified_root: Eth2Digest
|
||||
finalized_root: Eth2Digest
|
||||
u_justified_checkpoint: Option[Checkpoint]
|
||||
u_finalized_checkpoint: Option[Checkpoint]
|
||||
best_child: Eth2Digest
|
||||
best_descendant: Eth2Digest
|
||||
|
||||
ForkChoiceResponse = object
|
||||
slot: Slot
|
||||
block_root: Eth2Digest
|
||||
parent_root: Eth2Digest
|
||||
justified_epoch: Epoch
|
||||
finalized_epoch: Epoch
|
||||
weight: uint64
|
||||
execution_optimistic: bool
|
||||
execution_payload_root: Eth2Digest
|
||||
extra_data: Option[ForkChoiceResponseExtraData]
|
||||
|
||||
var responses: seq[ForkChoiceResponse]
|
||||
for item in node.attestationPool[].forkChoice.backend.proto_array:
|
||||
let
|
||||
bid = node.dag.getBlockId(item.root)
|
||||
slot =
|
||||
if bid.isOk:
|
||||
bid.unsafeGet.slot
|
||||
else:
|
||||
FAR_FUTURE_SLOT
|
||||
executionPayloadRoot =
|
||||
if bid.isOk:
|
||||
node.dag.loadExecutionBlockRoot(bid.unsafeGet)
|
||||
else:
|
||||
ZERO_HASH
|
||||
unrealized = item.unrealized.get(item.checkpoints)
|
||||
u_justified_checkpoint =
|
||||
if unrealized.justified != item.checkpoints.justified:
|
||||
some unrealized.justified
|
||||
else:
|
||||
none(Checkpoint)
|
||||
u_finalized_checkpoint =
|
||||
if unrealized.finalized != item.checkpoints.finalized:
|
||||
some unrealized.finalized
|
||||
else:
|
||||
none(Checkpoint)
|
||||
|
||||
responses.add ForkChoiceResponse(
|
||||
slot: slot,
|
||||
block_root: item.root,
|
||||
parent_root: item.parent,
|
||||
justified_epoch: item.checkpoints.justified.epoch,
|
||||
finalized_epoch: item.checkpoints.finalized.epoch,
|
||||
weight: cast[uint64](item.weight),
|
||||
execution_optimistic: node.dag.is_optimistic(item.root),
|
||||
execution_payload_root: executionPayloadRoot,
|
||||
extra_data: some ForkChoiceResponseExtraData(
|
||||
justified_root: item.checkpoints.justified.root,
|
||||
finalized_root: item.checkpoints.finalized.root,
|
||||
u_justified_checkpoint: u_justified_checkpoint,
|
||||
u_finalized_checkpoint: u_finalized_checkpoint,
|
||||
best_child: item.bestChild,
|
||||
bestDescendant: item.bestDescendant))
|
||||
return RestApiResponse.jsonResponse(responses)
|
||||
|
||||
# Legacy URLS - Nimbus <= 1.5.5 used to expose the REST API with an additional
|
||||
# `/api` path component
|
||||
router.redirect(
|
||||
|
|
Loading…
Reference in New Issue