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.
|
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
|
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* {.
|
discv5Enabled* {.
|
||||||
desc: "Enable Discovery v5"
|
desc: "Enable Discovery v5"
|
||||||
defaultValue: true
|
defaultValue: true
|
||||||
|
|
|
@ -1692,26 +1692,23 @@ proc pruneStateCachesDAG*(dag: ChainDAGRef) =
|
||||||
statePruneDur = statePruneTick - startTick,
|
statePruneDur = statePruneTick - startTick,
|
||||||
epochRefPruneDur = epochRefPruneTick - statePruneTick
|
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 =
|
proc loadExecutionBlockRoot*(dag: ChainDAGRef, blck: BlockRef): Eth2Digest =
|
||||||
if dag.cfg.blockForkAtEpoch(blck.bid.slot.epoch) < BeaconBlockFork.Bellatrix:
|
if blck.executionBlockRoot.isNone:
|
||||||
return ZERO_HASH
|
blck.executionBlockRoot = some dag.loadExecutionBlockRoot(blck.bid)
|
||||||
|
blck.executionBlockRoot.unsafeGet
|
||||||
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
|
|
||||||
|
|
||||||
proc updateHead*(
|
proc updateHead*(
|
||||||
dag: ChainDAGRef,
|
dag: ChainDAGRef,
|
||||||
|
|
|
@ -552,6 +552,50 @@ func nodeIsViableForHead(self: ProtoArray, node: ProtoNode): bool =
|
||||||
(self.checkpoints.finalized.epoch == GENESIS_EPOCH)
|
(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
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
# Sanity checks on internal private procedures
|
# Sanity checks on internal private procedures
|
||||||
|
|
|
@ -11,6 +11,8 @@ import ".."/beacon_node,
|
||||||
".."/spec/forks,
|
".."/spec/forks,
|
||||||
"."/[rest_utils, state_ttl_cache]
|
"."/[rest_utils, state_ttl_cache]
|
||||||
|
|
||||||
|
from ../fork_choice/proto_array import ProtoArrayItem, items
|
||||||
|
|
||||||
export rest_utils
|
export rest_utils
|
||||||
|
|
||||||
logScope: topics = "rest_debug"
|
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
|
# Legacy URLS - Nimbus <= 1.5.5 used to expose the REST API with an additional
|
||||||
# `/api` path component
|
# `/api` path component
|
||||||
router.redirect(
|
router.redirect(
|
||||||
|
|
Loading…
Reference in New Issue