Add the `execution_optimistic` flag to REST API responses. (#3780)
* Initial commit * Make `events` API spec compliant. * Add `Eth-Consensus-Version` in responses. * Bump chronos to get redirect with headers working. * Add `is_optimistic` field and handling to syncing RestSyncInfo.
This commit is contained in:
parent
c24c737866
commit
eb6b7affee
|
@ -38,7 +38,7 @@ type
|
||||||
RpcServer* = RpcHttpServer
|
RpcServer* = RpcHttpServer
|
||||||
|
|
||||||
EventBus* = object
|
EventBus* = object
|
||||||
blocksQueue*: AsyncEventQueue[ForkedTrustedSignedBeaconBlock]
|
blocksQueue*: AsyncEventQueue[EventBeaconBlockObject]
|
||||||
headQueue*: AsyncEventQueue[HeadChangeInfoObject]
|
headQueue*: AsyncEventQueue[HeadChangeInfoObject]
|
||||||
reorgQueue*: AsyncEventQueue[ReorgInfoObject]
|
reorgQueue*: AsyncEventQueue[ReorgInfoObject]
|
||||||
finUpdateQueue*: AsyncEventQueue[altair.LightClientFinalityUpdate]
|
finUpdateQueue*: AsyncEventQueue[altair.LightClientFinalityUpdate]
|
||||||
|
|
|
@ -294,6 +294,7 @@ type
|
||||||
epoch_transition*: bool
|
epoch_transition*: bool
|
||||||
previous_duty_dependent_root*: Eth2Digest
|
previous_duty_dependent_root*: Eth2Digest
|
||||||
current_duty_dependent_root*: Eth2Digest
|
current_duty_dependent_root*: Eth2Digest
|
||||||
|
optimistic* {.serializedFieldName: "execution_optimistic".}: Option[bool]
|
||||||
|
|
||||||
ReorgInfoObject* = object
|
ReorgInfoObject* = object
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
|
@ -302,11 +303,18 @@ type
|
||||||
new_head_block*: Eth2Digest
|
new_head_block*: Eth2Digest
|
||||||
old_head_state*: Eth2Digest
|
old_head_state*: Eth2Digest
|
||||||
new_head_state*: Eth2Digest
|
new_head_state*: Eth2Digest
|
||||||
|
optimistic* {.serializedFieldName: "execution_optimistic".}: Option[bool]
|
||||||
|
|
||||||
FinalizationInfoObject* = object
|
FinalizationInfoObject* = object
|
||||||
block_root* {.serializedFieldName: "block".}: Eth2Digest
|
block_root* {.serializedFieldName: "block".}: Eth2Digest
|
||||||
state_root* {.serializedFieldName: "state".}: Eth2Digest
|
state_root* {.serializedFieldName: "state".}: Eth2Digest
|
||||||
epoch*: Epoch
|
epoch*: Epoch
|
||||||
|
optimistic* {.serializedFieldName: "execution_optimistic".}: Option[bool]
|
||||||
|
|
||||||
|
EventBeaconBlockObject* = object
|
||||||
|
slot*: Slot
|
||||||
|
block_root* {.serializedFieldName: "block".}: Eth2Digest
|
||||||
|
optimistic* {.serializedFieldName: "execution_optimistic".}: Option[bool]
|
||||||
|
|
||||||
template head*(dag: ChainDAGRef): BlockRef = dag.headState.blck
|
template head*(dag: ChainDAGRef): BlockRef = dag.headState.blck
|
||||||
|
|
||||||
|
@ -355,34 +363,48 @@ func blockRef*(key: KeyedBlockRef): BlockRef =
|
||||||
|
|
||||||
func init*(t: typedesc[HeadChangeInfoObject], slot: Slot, blockRoot: Eth2Digest,
|
func init*(t: typedesc[HeadChangeInfoObject], slot: Slot, blockRoot: Eth2Digest,
|
||||||
stateRoot: Eth2Digest, epochTransition: bool,
|
stateRoot: Eth2Digest, epochTransition: bool,
|
||||||
previousDutyDepRoot: Eth2Digest,
|
previousDutyDepRoot: Eth2Digest, currentDutyDepRoot: Eth2Digest,
|
||||||
currentDutyDepRoot: Eth2Digest): HeadChangeInfoObject =
|
optimistic: Option[bool]): HeadChangeInfoObject =
|
||||||
HeadChangeInfoObject(
|
HeadChangeInfoObject(
|
||||||
slot: slot,
|
slot: slot,
|
||||||
block_root: blockRoot,
|
block_root: blockRoot,
|
||||||
state_root: stateRoot,
|
state_root: stateRoot,
|
||||||
epoch_transition: epochTransition,
|
epoch_transition: epochTransition,
|
||||||
previous_duty_dependent_root: previousDutyDepRoot,
|
previous_duty_dependent_root: previousDutyDepRoot,
|
||||||
current_duty_dependent_root: currentDutyDepRoot
|
current_duty_dependent_root: currentDutyDepRoot,
|
||||||
|
optimistic: optimistic
|
||||||
)
|
)
|
||||||
|
|
||||||
func init*(t: typedesc[ReorgInfoObject], slot: Slot, depth: uint64,
|
func init*(t: typedesc[ReorgInfoObject], slot: Slot, depth: uint64,
|
||||||
oldHeadBlockRoot: Eth2Digest, newHeadBlockRoot: Eth2Digest,
|
oldHeadBlockRoot: Eth2Digest, newHeadBlockRoot: Eth2Digest,
|
||||||
oldHeadStateRoot: Eth2Digest,
|
oldHeadStateRoot: Eth2Digest, newHeadStateRoot: Eth2Digest,
|
||||||
newHeadStateRoot: Eth2Digest): ReorgInfoObject =
|
optimistic: Option[bool]): ReorgInfoObject =
|
||||||
ReorgInfoObject(
|
ReorgInfoObject(
|
||||||
slot: slot,
|
slot: slot,
|
||||||
depth: depth,
|
depth: depth,
|
||||||
old_head_block: oldHeadBlockRoot,
|
old_head_block: oldHeadBlockRoot,
|
||||||
new_head_block: newHeadBlockRoot,
|
new_head_block: newHeadBlockRoot,
|
||||||
old_head_state: oldHeadStateRoot,
|
old_head_state: oldHeadStateRoot,
|
||||||
new_head_state: newHeadStateRoot
|
new_head_state: newHeadStateRoot,
|
||||||
|
optimistic: optimistic
|
||||||
)
|
)
|
||||||
|
|
||||||
func init*(t: typedesc[FinalizationInfoObject], blockRoot: Eth2Digest,
|
func init*(t: typedesc[FinalizationInfoObject], blockRoot: Eth2Digest,
|
||||||
stateRoot: Eth2Digest, epoch: Epoch): FinalizationInfoObject =
|
stateRoot: Eth2Digest, epoch: Epoch,
|
||||||
|
optimistic: Option[bool]): FinalizationInfoObject =
|
||||||
FinalizationInfoObject(
|
FinalizationInfoObject(
|
||||||
block_root: blockRoot,
|
block_root: blockRoot,
|
||||||
state_root: stateRoot,
|
state_root: stateRoot,
|
||||||
epoch: epoch
|
epoch: epoch,
|
||||||
|
optimistic: optimistic
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init*(t: typedesc[EventBeaconBlockObject],
|
||||||
|
v: ForkedTrustedSignedBeaconBlock,
|
||||||
|
optimistic: Option[bool]): EventBeaconBlockObject =
|
||||||
|
withBlck(v):
|
||||||
|
EventBeaconBlockObject(
|
||||||
|
slot: blck.message.slot,
|
||||||
|
block_root: blck.root,
|
||||||
|
optimistic: optimistic
|
||||||
|
)
|
||||||
|
|
|
@ -1531,6 +1531,13 @@ proc pruneStateCachesDAG*(dag: ChainDAGRef) =
|
||||||
statePruneDur = statePruneTick - startTick,
|
statePruneDur = statePruneTick - startTick,
|
||||||
epochRefPruneDur = epochRefPruneTick - statePruneTick
|
epochRefPruneDur = epochRefPruneTick - statePruneTick
|
||||||
|
|
||||||
|
template getHeadStateMergeComplete*(dag: ChainDAGRef): bool =
|
||||||
|
withState(dag.headState):
|
||||||
|
when stateFork >= BeaconStateFork.Bellatrix:
|
||||||
|
is_merge_transition_complete(state.data)
|
||||||
|
else:
|
||||||
|
false
|
||||||
|
|
||||||
proc updateHead*(
|
proc updateHead*(
|
||||||
dag: ChainDAGRef,
|
dag: ChainDAGRef,
|
||||||
newHead: BlockRef,
|
newHead: BlockRef,
|
||||||
|
@ -1563,16 +1570,9 @@ proc updateHead*(
|
||||||
error "Cannot update head to block without parent"
|
error "Cannot update head to block without parent"
|
||||||
return
|
return
|
||||||
|
|
||||||
template getHeadStateMergeComplete(): bool =
|
|
||||||
withState(dag.headState):
|
|
||||||
when stateFork >= BeaconStateFork.Bellatrix:
|
|
||||||
is_merge_transition_complete(state.data)
|
|
||||||
else:
|
|
||||||
false
|
|
||||||
|
|
||||||
let
|
let
|
||||||
lastHeadStateRoot = getStateRoot(dag.headState)
|
lastHeadStateRoot = getStateRoot(dag.headState)
|
||||||
lastHeadMergeComplete = getHeadStateMergeComplete()
|
lastHeadMergeComplete = dag.getHeadStateMergeComplete()
|
||||||
|
|
||||||
# Start off by making sure we have the right state - updateState will try
|
# Start off by making sure we have the right state - updateState will try
|
||||||
# to use existing in-memory states to make this smooth
|
# to use existing in-memory states to make this smooth
|
||||||
|
@ -1589,7 +1589,7 @@ proc updateHead*(
|
||||||
|
|
||||||
dag.head = newHead
|
dag.head = newHead
|
||||||
|
|
||||||
if getHeadStateMergeComplete() and not lastHeadMergeComplete:
|
if dag.getHeadStateMergeComplete() and not lastHeadMergeComplete:
|
||||||
dag.vanityLogs.onMergeTransitionBlock()
|
dag.vanityLogs.onMergeTransitionBlock()
|
||||||
|
|
||||||
dag.db.putHeadBlock(newHead.root)
|
dag.db.putHeadBlock(newHead.root)
|
||||||
|
@ -1622,10 +1622,15 @@ proc updateHead*(
|
||||||
finalized = shortLog(getStateField(dag.headState, finalized_checkpoint))
|
finalized = shortLog(getStateField(dag.headState, finalized_checkpoint))
|
||||||
|
|
||||||
if not(isNil(dag.onReorgHappened)):
|
if not(isNil(dag.onReorgHappened)):
|
||||||
let data = ReorgInfoObject.init(dag.head.slot, uint64(ancestorDepth),
|
let
|
||||||
lastHead.root, newHead.root,
|
# TODO (cheatfate): Proper implementation required
|
||||||
lastHeadStateRoot,
|
optimistic =
|
||||||
getStateRoot(dag.headState))
|
if dag.getHeadStateMergeComplete(): some(false) else: none[bool]()
|
||||||
|
data = ReorgInfoObject.init(dag.head.slot, uint64(ancestorDepth),
|
||||||
|
lastHead.root, newHead.root,
|
||||||
|
lastHeadStateRoot,
|
||||||
|
getStateRoot(dag.headState),
|
||||||
|
optimistic)
|
||||||
dag.onReorgHappened(data)
|
dag.onReorgHappened(data)
|
||||||
|
|
||||||
# A reasonable criterion for "reorganizations of the chain"
|
# A reasonable criterion for "reorganizations of the chain"
|
||||||
|
@ -1646,10 +1651,13 @@ proc updateHead*(
|
||||||
depRoot = withState(dag.headState): state.proposer_dependent_root
|
depRoot = withState(dag.headState): state.proposer_dependent_root
|
||||||
prevDepRoot = withState(dag.headState): state.attester_dependent_root
|
prevDepRoot = withState(dag.headState): state.attester_dependent_root
|
||||||
epochTransition = (finalizedHead != dag.finalizedHead)
|
epochTransition = (finalizedHead != dag.finalizedHead)
|
||||||
let data = HeadChangeInfoObject.init(dag.head.slot, dag.head.root,
|
# TODO (cheatfate): Proper implementation required
|
||||||
getStateRoot(dag.headState),
|
optimistic =
|
||||||
epochTransition, depRoot,
|
if dag.getHeadStateMergeComplete(): some(false) else: none[bool]()
|
||||||
prevDepRoot)
|
data = HeadChangeInfoObject.init(dag.head.slot, dag.head.root,
|
||||||
|
getStateRoot(dag.headState),
|
||||||
|
epochTransition, depRoot,
|
||||||
|
prevDepRoot, optimistic)
|
||||||
dag.onHeadChanged(data)
|
dag.onHeadChanged(data)
|
||||||
|
|
||||||
withState(dag.headState):
|
withState(dag.headState):
|
||||||
|
@ -1708,11 +1716,13 @@ proc updateHead*(
|
||||||
int(dag.finalizedHead.slot mod SLOTS_PER_HISTORICAL_ROOT)]
|
int(dag.finalizedHead.slot mod SLOTS_PER_HISTORICAL_ROOT)]
|
||||||
else:
|
else:
|
||||||
Eth2Digest() # The thing that finalized was >8192 blocks old?
|
Eth2Digest() # The thing that finalized was >8192 blocks old?
|
||||||
|
# TODO (cheatfate): Proper implementation required
|
||||||
|
let optimistic =
|
||||||
|
if dag.getHeadStateMergeComplete(): some(false) else: none[bool]()
|
||||||
|
|
||||||
let data = FinalizationInfoObject.init(
|
let data = FinalizationInfoObject.init(
|
||||||
dag.finalizedHead.blck.root,
|
dag.finalizedHead.blck.root, stateRoot, dag.finalizedHead.slot.epoch,
|
||||||
stateRoot,
|
optimistic)
|
||||||
dag.finalizedHead.slot.epoch)
|
|
||||||
dag.onFinHappened(dag, data)
|
dag.onFinHappened(dag, data)
|
||||||
|
|
||||||
proc isInitialized*(T: type ChainDAGRef, db: BeaconChainDB): Result[void, cstring] =
|
proc isInitialized*(T: type ChainDAGRef, db: BeaconChainDB): Result[void, cstring] =
|
||||||
|
|
|
@ -152,10 +152,17 @@ proc loadChainDag(
|
||||||
eventBus: EventBus,
|
eventBus: EventBus,
|
||||||
validatorMonitor: ref ValidatorMonitor,
|
validatorMonitor: ref ValidatorMonitor,
|
||||||
networkGenesisValidatorsRoot: Option[Eth2Digest]): ChainDAGRef =
|
networkGenesisValidatorsRoot: Option[Eth2Digest]): ChainDAGRef =
|
||||||
|
var dag: ChainDAGRef
|
||||||
info "Loading block DAG from database", path = config.databaseDir
|
info "Loading block DAG from database", path = config.databaseDir
|
||||||
|
|
||||||
proc onBlockAdded(data: ForkedTrustedSignedBeaconBlock) =
|
proc onBlockAdded(data: ForkedTrustedSignedBeaconBlock) =
|
||||||
eventBus.blocksQueue.emit(data)
|
# TODO (cheatfate): Proper implementation required
|
||||||
|
let optimistic =
|
||||||
|
if isNil(dag):
|
||||||
|
none[bool]()
|
||||||
|
else:
|
||||||
|
if dag.getHeadStateMergeComplete(): some(false) else: none[bool]()
|
||||||
|
eventBus.blocksQueue.emit(EventBeaconBlockObject.init(data, optimistic))
|
||||||
proc onHeadChanged(data: HeadChangeInfoObject) =
|
proc onHeadChanged(data: HeadChangeInfoObject) =
|
||||||
eventBus.headQueue.emit(data)
|
eventBus.headQueue.emit(data)
|
||||||
proc onChainReorg(data: ReorgInfoObject) =
|
proc onChainReorg(data: ReorgInfoObject) =
|
||||||
|
@ -175,14 +182,17 @@ proc loadChainDag(
|
||||||
onLightClientOptimisticUpdateCb =
|
onLightClientOptimisticUpdateCb =
|
||||||
if config.lightClientDataServe.get: onLightClientOptimisticUpdate
|
if config.lightClientDataServe.get: onLightClientOptimisticUpdate
|
||||||
else: nil
|
else: nil
|
||||||
dag = ChainDAGRef.init(
|
|
||||||
cfg, db, validatorMonitor, chainDagFlags, config.eraDir,
|
dag = ChainDAGRef.init(
|
||||||
onBlockAdded, onHeadChanged, onChainReorg,
|
cfg, db, validatorMonitor, chainDagFlags, config.eraDir,
|
||||||
onLCFinalityUpdateCb = onLightClientFinalityUpdateCb,
|
onBlockAdded, onHeadChanged, onChainReorg,
|
||||||
onLCOptimisticUpdateCb = onLightClientOptimisticUpdateCb,
|
onLCFinalityUpdateCb = onLightClientFinalityUpdateCb,
|
||||||
lightClientDataServe = config.lightClientDataServe.get,
|
onLCOptimisticUpdateCb = onLightClientOptimisticUpdateCb,
|
||||||
lightClientDataImportMode = config.lightClientDataImportMode.get,
|
lightClientDataServe = config.lightClientDataServe.get,
|
||||||
vanityLogs = getPandas(detectTTY(config.logStdout)))
|
lightClientDataImportMode = config.lightClientDataImportMode.get,
|
||||||
|
vanityLogs = getPandas(detectTTY(config.logStdout)))
|
||||||
|
|
||||||
|
let
|
||||||
databaseGenesisValidatorsRoot =
|
databaseGenesisValidatorsRoot =
|
||||||
getStateField(dag.headState, genesis_validators_root)
|
getStateField(dag.headState, genesis_validators_root)
|
||||||
|
|
||||||
|
@ -371,7 +381,7 @@ proc init*(T: type BeaconNode,
|
||||||
|
|
||||||
let
|
let
|
||||||
eventBus = EventBus(
|
eventBus = EventBus(
|
||||||
blocksQueue: newAsyncEventQueue[ForkedTrustedSignedBeaconBlock](),
|
blocksQueue: newAsyncEventQueue[EventBeaconBlockObject](),
|
||||||
headQueue: newAsyncEventQueue[HeadChangeInfoObject](),
|
headQueue: newAsyncEventQueue[HeadChangeInfoObject](),
|
||||||
reorgQueue: newAsyncEventQueue[ReorgInfoObject](),
|
reorgQueue: newAsyncEventQueue[ReorgInfoObject](),
|
||||||
finUpdateQueue: newAsyncEventQueue[altair.LightClientFinalityUpdate](),
|
finUpdateQueue: newAsyncEventQueue[altair.LightClientFinalityUpdate](),
|
||||||
|
|
|
@ -128,7 +128,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
$error)
|
$error)
|
||||||
|
|
||||||
node.withStateForBlockSlotId(bslot):
|
node.withStateForBlockSlotId(bslot):
|
||||||
return RestApiResponse.jsonResponse((root: stateRoot))
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
|
(root: stateRoot),
|
||||||
|
node.getStateOptimistic(state)
|
||||||
|
)
|
||||||
|
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
|
@ -148,12 +151,16 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
$error)
|
$error)
|
||||||
|
|
||||||
node.withStateForBlockSlotId(bslot):
|
node.withStateForBlockSlotId(bslot):
|
||||||
return RestApiResponse.jsonResponse(
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
(
|
(
|
||||||
previous_version: getStateField(state, fork).previous_version,
|
previous_version:
|
||||||
current_version: getStateField(state, fork).current_version,
|
getStateField(state, fork).previous_version,
|
||||||
epoch: getStateField(state, fork).epoch
|
current_version:
|
||||||
)
|
getStateField(state, fork).current_version,
|
||||||
|
epoch:
|
||||||
|
getStateField(state, fork).epoch
|
||||||
|
),
|
||||||
|
node.getStateOptimistic(state)
|
||||||
)
|
)
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
|
@ -171,15 +178,19 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
# in current version of database.
|
# in current version of database.
|
||||||
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
return RestApiResponse.jsonError(Http500, NoImplementationError)
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
return RestApiResponse.jsonError(Http404, StateNotFoundError,
|
||||||
$error)
|
$error)
|
||||||
|
|
||||||
node.withStateForBlockSlotId(bslot):
|
node.withStateForBlockSlotId(bslot):
|
||||||
return RestApiResponse.jsonResponse(
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
(
|
(
|
||||||
previous_justified: getStateField(state, previous_justified_checkpoint),
|
previous_justified:
|
||||||
current_justified: getStateField(state, current_justified_checkpoint),
|
getStateField(state, previous_justified_checkpoint),
|
||||||
finalized: getStateField(state, finalized_checkpoint)
|
current_justified:
|
||||||
)
|
getStateField(state, current_justified_checkpoint),
|
||||||
|
finalized:
|
||||||
|
getStateField(state, finalized_checkpoint)
|
||||||
|
),
|
||||||
|
node.getStateOptimistic(state)
|
||||||
)
|
)
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
|
@ -303,7 +314,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
res.add(RestValidator.init(index, balance, toString(status),
|
res.add(RestValidator.init(index, balance, toString(status),
|
||||||
validator))
|
validator))
|
||||||
res
|
res
|
||||||
return RestApiResponse.jsonResponse(response)
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
|
response,
|
||||||
|
node.getStateOptimistic(state)
|
||||||
|
)
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidator
|
# https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidator
|
||||||
|
@ -365,8 +379,9 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
ValidatorStatusNotFoundError,
|
ValidatorStatusNotFoundError,
|
||||||
$sres.get())
|
$sres.get())
|
||||||
toString(sres.get())
|
toString(sres.get())
|
||||||
return RestApiResponse.jsonResponse(
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
RestValidator.init(vindex, balance, status, validator)
|
RestValidator.init(vindex, balance, status, validator),
|
||||||
|
node.getStateOptimistic(state)
|
||||||
)
|
)
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
|
@ -454,7 +469,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
let balance = getStateField(state, balances).item(index)
|
let balance = getStateField(state, balances).item(index)
|
||||||
res.add(RestValidatorBalance.init(index, balance))
|
res.add(RestValidatorBalance.init(index, balance))
|
||||||
res
|
res
|
||||||
return RestApiResponse.jsonResponse(response)
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
|
response,
|
||||||
|
node.getStateOptimistic(state)
|
||||||
|
)
|
||||||
|
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
|
@ -567,7 +585,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
else:
|
else:
|
||||||
forSlot(vslot.get(), vindex, res)
|
forSlot(vslot.get(), vindex, res)
|
||||||
|
|
||||||
return RestApiResponse.jsonResponse(res)
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
|
res,
|
||||||
|
node.getStateOptimistic(state)
|
||||||
|
)
|
||||||
|
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
|
@ -644,8 +665,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
offset.inc(length)
|
offset.inc(length)
|
||||||
res
|
res
|
||||||
|
|
||||||
return RestApiResponse.jsonResponse(RestEpochSyncCommittee(
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
validators: indices, validator_aggregates: aggregates)
|
RestEpochSyncCommittee(validators: indices,
|
||||||
|
validator_aggregates: aggregates),
|
||||||
|
node.getStateOptimistic(state)
|
||||||
)
|
)
|
||||||
|
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
@ -677,7 +700,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
|
|
||||||
return
|
return
|
||||||
withBlck(bdata):
|
withBlck(bdata):
|
||||||
RestApiResponse.jsonResponse(
|
RestApiResponse.jsonResponseWOpt(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
root: blck.root,
|
root: blck.root,
|
||||||
|
@ -688,7 +711,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
signature: blck.signature
|
signature: blck.signature
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
]
|
],
|
||||||
|
node.getBlockOptimistic(bdata)
|
||||||
)
|
)
|
||||||
|
|
||||||
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader
|
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader
|
||||||
|
@ -704,7 +728,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
|
|
||||||
return
|
return
|
||||||
withBlck(bdata):
|
withBlck(bdata):
|
||||||
RestApiResponse.jsonResponse(
|
RestApiResponse.jsonResponseWOpt(
|
||||||
(
|
(
|
||||||
root: blck.root,
|
root: blck.root,
|
||||||
canonical: node.dag.isCanonical(
|
canonical: node.dag.isCanonical(
|
||||||
|
@ -713,7 +737,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
message: blck.toBeaconBlockHeader,
|
message: blck.toBeaconBlockHeader,
|
||||||
signature: blck.signature
|
signature: blck.signature
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
|
node.getBlockOptimistic(bdata)
|
||||||
)
|
)
|
||||||
|
|
||||||
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
|
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
|
||||||
|
@ -812,12 +837,25 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
if not node.dag.getBlockSSZ(bid, data):
|
if not node.dag.getBlockSSZ(bid, data):
|
||||||
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
|
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
|
||||||
|
|
||||||
RestApiResponse.response(data, Http200, $sszMediaType)
|
let
|
||||||
|
fork = node.dag.cfg.blockForkAtEpoch(bid.slot.epoch)
|
||||||
|
headers = [("eth-consensus-version", fork.toString())]
|
||||||
|
|
||||||
|
RestApiResponse.response(data, Http200, $sszMediaType,
|
||||||
|
headers = headers)
|
||||||
elif contentType == jsonMediaType:
|
elif contentType == jsonMediaType:
|
||||||
let bdata = node.dag.getForkedBlock(bid).valueOr:
|
let bdata = node.dag.getForkedBlock(bid).valueOr:
|
||||||
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
|
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
|
||||||
|
|
||||||
RestApiResponse.jsonResponsePlain(bdata.asSigned())
|
let
|
||||||
|
fork = node.dag.cfg.blockForkAtEpoch(bid.slot.epoch)
|
||||||
|
headers = [("eth-consensus-version", fork.toString())]
|
||||||
|
|
||||||
|
RestApiResponse.jsonResponseBlock(
|
||||||
|
bdata.asSigned(),
|
||||||
|
node.getBlockOptimistic(bdata),
|
||||||
|
headers
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
||||||
|
|
||||||
|
@ -832,7 +870,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
bid = node.getBlockId(blockIdent).valueOr:
|
bid = node.getBlockId(blockIdent).valueOr:
|
||||||
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
|
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
|
||||||
|
|
||||||
return RestApiResponse.jsonResponse((root: bid.root))
|
bdata = node.dag.getForkedBlock(bid).valueOr:
|
||||||
|
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
|
||||||
|
|
||||||
|
return RestApiResponse.jsonResponseWOpt(
|
||||||
|
(root: bid.root),
|
||||||
|
node.getBlockOptimistic(bdata)
|
||||||
|
)
|
||||||
|
|
||||||
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations
|
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations
|
||||||
router.api(MethodGet,
|
router.api(MethodGet,
|
||||||
|
@ -848,7 +892,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
|
|
||||||
return
|
return
|
||||||
withBlck(bdata):
|
withBlck(bdata):
|
||||||
RestApiResponse.jsonResponse(blck.message.body.attestations.asSeq())
|
RestApiResponse.jsonResponseWOpt(
|
||||||
|
blck.message.body.attestations.asSeq(),
|
||||||
|
node.getBlockOptimistic(bdata)
|
||||||
|
)
|
||||||
|
|
||||||
# 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.api(MethodGet, "/eth/v1/beacon/pool/attestations") do (
|
||||||
|
|
|
@ -42,7 +42,7 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
case state.kind
|
case state.kind
|
||||||
of BeaconStateFork.Phase0:
|
of BeaconStateFork.Phase0:
|
||||||
if contentType == sszMediaType:
|
if contentType == sszMediaType:
|
||||||
RestApiResponse.sszResponse(state.phase0Data.data)
|
RestApiResponse.sszResponse(state.phase0Data.data, [])
|
||||||
elif contentType == jsonMediaType:
|
elif contentType == jsonMediaType:
|
||||||
RestApiResponse.jsonResponse(state.phase0Data.data)
|
RestApiResponse.jsonResponse(state.phase0Data.data)
|
||||||
else:
|
else:
|
||||||
|
@ -75,10 +75,14 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
node.withStateForBlockSlotId(bslot):
|
node.withStateForBlockSlotId(bslot):
|
||||||
return
|
return
|
||||||
if contentType == jsonMediaType:
|
if contentType == jsonMediaType:
|
||||||
RestApiResponse.jsonResponsePlain(state)
|
RestApiResponse.jsonResponseState(
|
||||||
|
state,
|
||||||
|
node.getStateOptimistic(state)
|
||||||
|
)
|
||||||
elif contentType == sszMediaType:
|
elif contentType == sszMediaType:
|
||||||
|
let headers = [("eth-consensus-version", state.kind.toString())]
|
||||||
withState(state):
|
withState(state):
|
||||||
RestApiResponse.sszResponse(state.data)
|
RestApiResponse.sszResponse(state.data, headers)
|
||||||
else:
|
else:
|
||||||
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
RestApiResponse.jsonError(Http500, InvalidAcceptError)
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
@ -90,6 +94,19 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
node.dag.heads.mapIt((root: it.root, slot: it.slot))
|
node.dag.heads.mapIt((root: it.root, slot: it.slot))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# https://ethereum.github.io/beacon-APIs/#/Debug/getDebugChainHeadsV2
|
||||||
|
router.api(MethodGet,
|
||||||
|
"/eth/v2/debug/beacon/heads") do () -> RestApiResponse:
|
||||||
|
return RestApiResponse.jsonResponse(
|
||||||
|
node.dag.heads.mapIt(
|
||||||
|
(
|
||||||
|
root: it.root,
|
||||||
|
slot: it.slot,
|
||||||
|
execution_optimistic: node.getBlockRefOptimistic(it)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# 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(
|
||||||
|
@ -107,3 +124,8 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
"/api/eth/v1/debug/beacon/heads",
|
"/api/eth/v1/debug/beacon/heads",
|
||||||
"/eth/v1/debug/beacon/heads"
|
"/eth/v1/debug/beacon/heads"
|
||||||
)
|
)
|
||||||
|
router.redirect(
|
||||||
|
MethodGet,
|
||||||
|
"/api/eth/v2/debug/beacon/heads",
|
||||||
|
"/eth/v2/debug/beacon/heads"
|
||||||
|
)
|
||||||
|
|
|
@ -53,12 +53,7 @@ proc eventHandler*[T](response: HttpResponseRef,
|
||||||
empty
|
empty
|
||||||
|
|
||||||
for event in events:
|
for event in events:
|
||||||
let jsonRes =
|
let jsonRes = RestApiResponse.prepareJsonStringResponse(event)
|
||||||
when T is ForkedTrustedSignedBeaconBlock:
|
|
||||||
let blockInfo = RestBlockInfo.init(event)
|
|
||||||
RestApiResponse.prepareJsonStringResponse(blockInfo)
|
|
||||||
else:
|
|
||||||
RestApiResponse.prepareJsonStringResponse(event)
|
|
||||||
|
|
||||||
exitLoop =
|
exitLoop =
|
||||||
if response.state != HttpResponseState.Sending:
|
if response.state != HttpResponseState.Sending:
|
||||||
|
|
|
@ -257,13 +257,21 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
wallSlot = node.beaconClock.now().slotOrZero()
|
wallSlot = node.beaconClock.now().slotOrZero()
|
||||||
headSlot = node.dag.head.slot
|
headSlot = node.dag.head.slot
|
||||||
distance = wallSlot - headSlot
|
distance = wallSlot - headSlot
|
||||||
|
isSyncing =
|
||||||
|
if isNil(node.syncManager):
|
||||||
|
false
|
||||||
|
else:
|
||||||
|
node.syncManager.inProgress
|
||||||
|
isOptimistic =
|
||||||
|
if node.dag.getHeadStateMergeComplete():
|
||||||
|
# TODO (cheatfate): Proper implementation required
|
||||||
|
some(false)
|
||||||
|
else:
|
||||||
|
none[bool]()
|
||||||
|
|
||||||
info = RestSyncInfo(
|
info = RestSyncInfo(
|
||||||
head_slot: headSlot, sync_distance: distance,
|
head_slot: headSlot, sync_distance: distance,
|
||||||
is_syncing:
|
is_syncing: isSyncing, is_optimistic: isOptimistic
|
||||||
if isNil(node.syncManager):
|
|
||||||
false
|
|
||||||
else:
|
|
||||||
node.syncManager.inProgress
|
|
||||||
)
|
)
|
||||||
return RestApiResponse.jsonResponse(info)
|
return RestApiResponse.jsonResponse(info)
|
||||||
|
|
||||||
|
|
|
@ -272,6 +272,40 @@ func keysToIndices*(cacheTable: var Table[ValidatorPubKey, ValidatorIndex],
|
||||||
proc getRouter*(allowedOrigin: Option[string]): RestRouter =
|
proc getRouter*(allowedOrigin: Option[string]): RestRouter =
|
||||||
RestRouter.init(validate, allowedOrigin = allowedOrigin)
|
RestRouter.init(validate, allowedOrigin = allowedOrigin)
|
||||||
|
|
||||||
|
proc getStateOptimistic*(node: BeaconNode,
|
||||||
|
state: ForkedHashedBeaconState): Option[bool] =
|
||||||
|
if node.dag.getHeadStateMergeComplete():
|
||||||
|
case state.kind
|
||||||
|
of BeaconStateFork.Phase0, BeaconStateFork.Altair:
|
||||||
|
some[bool](false)
|
||||||
|
of BeaconStateFork.Bellatrix:
|
||||||
|
# TODO (cheatfate): Proper implementation required.
|
||||||
|
some[bool](false)
|
||||||
|
else:
|
||||||
|
none[bool]()
|
||||||
|
|
||||||
|
proc getBlockOptimistic*(node: BeaconNode,
|
||||||
|
blck: ForkedTrustedSignedBeaconBlock |
|
||||||
|
ForkedSignedBeaconBlock): Option[bool] =
|
||||||
|
if node.dag.getHeadStateMergeComplete():
|
||||||
|
case blck.kind
|
||||||
|
of BeaconBlockFork.Phase0, BeaconBlockFork.Altair:
|
||||||
|
some[bool](false)
|
||||||
|
of BeaconBlockFork.Bellatrix:
|
||||||
|
# TODO (cheatfate): Proper implementation required.
|
||||||
|
some[bool](false)
|
||||||
|
else:
|
||||||
|
none[bool]()
|
||||||
|
|
||||||
|
proc getBlockRefOptimistic*(node: BeaconNode, blck: BlockRef): bool =
|
||||||
|
let blck = node.dag.getForkedBlock(blck.bid).get()
|
||||||
|
case blck.kind
|
||||||
|
of BeaconBlockFork.Phase0, BeaconBlockFork.Altair:
|
||||||
|
false
|
||||||
|
of BeaconBlockFork.Bellatrix:
|
||||||
|
# TODO (cheatfate): Proper implementation required.
|
||||||
|
false
|
||||||
|
|
||||||
const
|
const
|
||||||
jsonMediaType* = MediaType.init("application/json")
|
jsonMediaType* = MediaType.init("application/json")
|
||||||
sszMediaType* = MediaType.init("application/octet-stream")
|
sszMediaType* = MediaType.init("application/octet-stream")
|
||||||
|
|
|
@ -96,8 +96,13 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
res
|
res
|
||||||
|
|
||||||
|
# TODO (cheatfate): Proper implementation required
|
||||||
|
let optimistic =
|
||||||
|
if node.dag.getHeadStateMergeComplete(): some(false) else: none[bool]()
|
||||||
|
|
||||||
return RestApiResponse.jsonResponseWRoot(
|
return RestApiResponse.jsonResponseWRoot(
|
||||||
duties, epochRef.attester_dependent_root)
|
duties, epochRef.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.api(MethodGet, "/eth/v1/validator/duties/proposer/{epoch}") do (
|
||||||
|
@ -142,8 +147,13 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
res
|
res
|
||||||
|
|
||||||
|
# TODO (cheatfate): Proper implementation required
|
||||||
|
let optimistic =
|
||||||
|
if node.dag.getHeadStateMergeComplete(): some(false) else: none[bool]()
|
||||||
|
|
||||||
return RestApiResponse.jsonResponseWRoot(
|
return RestApiResponse.jsonResponseWRoot(
|
||||||
duties, epochRef.proposer_dependent_root)
|
duties, epochRef.proposer_dependent_root, optimistic)
|
||||||
|
|
||||||
router.api(MethodPost, "/eth/v1/validator/duties/sync/{epoch}") do (
|
router.api(MethodPost, "/eth/v1/validator/duties/sync/{epoch}") do (
|
||||||
epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse:
|
epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||||
|
@ -226,6 +236,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
headSyncPeriod = sync_committee_period(headEpoch)
|
headSyncPeriod = sync_committee_period(headEpoch)
|
||||||
|
|
||||||
if qSyncPeriod == headSyncPeriod:
|
if qSyncPeriod == headSyncPeriod:
|
||||||
|
let optimistic = node.getStateOptimistic(node.dag.headState)
|
||||||
let res = withState(node.dag.headState):
|
let res = withState(node.dag.headState):
|
||||||
when stateFork >= BeaconStateFork.Altair:
|
when stateFork >= BeaconStateFork.Altair:
|
||||||
produceResponse(indexList,
|
produceResponse(indexList,
|
||||||
|
@ -233,8 +244,9 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
state.data.validators.asSeq)
|
state.data.validators.asSeq)
|
||||||
else:
|
else:
|
||||||
emptyResponse()
|
emptyResponse()
|
||||||
return RestApiResponse.jsonResponse(res)
|
return RestApiResponse.jsonResponseWOpt(res, optimistic)
|
||||||
elif qSyncPeriod == (headSyncPeriod + 1):
|
elif qSyncPeriod == (headSyncPeriod + 1):
|
||||||
|
let optimistic = node.getStateOptimistic(node.dag.headState)
|
||||||
let res = withState(node.dag.headState):
|
let res = withState(node.dag.headState):
|
||||||
when stateFork >= BeaconStateFork.Altair:
|
when stateFork >= BeaconStateFork.Altair:
|
||||||
produceResponse(indexList,
|
produceResponse(indexList,
|
||||||
|
@ -242,7 +254,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
state.data.validators.asSeq)
|
state.data.validators.asSeq)
|
||||||
else:
|
else:
|
||||||
emptyResponse()
|
emptyResponse()
|
||||||
return RestApiResponse.jsonResponse(res)
|
return RestApiResponse.jsonResponseWOpt(res, optimistic)
|
||||||
elif qSyncPeriod > headSyncPeriod:
|
elif qSyncPeriod > headSyncPeriod:
|
||||||
# The requested epoch may still be too far in the future.
|
# The requested epoch may still be too far in the future.
|
||||||
if not(node.isSynced(node.dag.head)):
|
if not(node.isSynced(node.dag.head)):
|
||||||
|
@ -265,6 +277,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
node.withStateForBlockSlotId(bsi):
|
node.withStateForBlockSlotId(bsi):
|
||||||
|
let optimistic = node.getStateOptimistic(state)
|
||||||
let res = withState(state):
|
let res = withState(state):
|
||||||
when stateFork >= BeaconStateFork.Altair:
|
when stateFork >= BeaconStateFork.Altair:
|
||||||
produceResponse(indexList,
|
produceResponse(indexList,
|
||||||
|
@ -272,7 +285,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
||||||
state.data.validators.asSeq)
|
state.data.validators.asSeq)
|
||||||
else:
|
else:
|
||||||
emptyResponse()
|
emptyResponse()
|
||||||
return RestApiResponse.jsonResponse(res)
|
return RestApiResponse.jsonResponseWOpt(res, optimistic)
|
||||||
|
|
||||||
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
return RestApiResponse.jsonError(Http404, StateNotFoundError)
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import stew/[assign2, results, base10, byteutils], presto/common,
|
||||||
json_serialization/std/[options, net, sets],
|
json_serialization/std/[options, net, sets],
|
||||||
chronicles
|
chronicles
|
||||||
import ".."/[eth2_ssz_serialization, forks, keystore],
|
import ".."/[eth2_ssz_serialization, forks, keystore],
|
||||||
|
".."/../consensus_object_pools/block_pools_types,
|
||||||
".."/datatypes/[phase0, altair, bellatrix],
|
".."/datatypes/[phase0, altair, bellatrix],
|
||||||
".."/mev/bellatrix_mev,
|
".."/mev/bellatrix_mev,
|
||||||
".."/../validators/slashing_protection_common,
|
".."/../validators/slashing_protection_common,
|
||||||
|
@ -187,7 +188,8 @@ proc prepareJsonStringResponse*(t: typedesc[RestApiResponse], d: auto): string =
|
||||||
res
|
res
|
||||||
|
|
||||||
proc jsonResponseWRoot*(t: typedesc[RestApiResponse], data: auto,
|
proc jsonResponseWRoot*(t: typedesc[RestApiResponse], data: auto,
|
||||||
dependent_root: Eth2Digest): RestApiResponse =
|
dependent_root: Eth2Digest,
|
||||||
|
execOpt: Option[bool]): RestApiResponse =
|
||||||
let res =
|
let res =
|
||||||
block:
|
block:
|
||||||
var default: seq[byte]
|
var default: seq[byte]
|
||||||
|
@ -196,6 +198,8 @@ proc jsonResponseWRoot*(t: typedesc[RestApiResponse], data: auto,
|
||||||
var writer = JsonWriter[RestJson].init(stream)
|
var writer = JsonWriter[RestJson].init(stream)
|
||||||
writer.beginRecord()
|
writer.beginRecord()
|
||||||
writer.writeField("dependent_root", dependent_root)
|
writer.writeField("dependent_root", dependent_root)
|
||||||
|
if execOpt.isSome():
|
||||||
|
writer.writeField("execution_optimistic", execOpt.get())
|
||||||
writer.writeField("data", data)
|
writer.writeField("data", data)
|
||||||
writer.endRecord()
|
writer.endRecord()
|
||||||
stream.getOutput(seq[byte])
|
stream.getOutput(seq[byte])
|
||||||
|
@ -222,6 +226,84 @@ proc jsonResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse =
|
||||||
default
|
default
|
||||||
RestApiResponse.response(res, Http200, "application/json")
|
RestApiResponse.response(res, Http200, "application/json")
|
||||||
|
|
||||||
|
proc jsonResponseBlock*(t: typedesc[RestApiResponse],
|
||||||
|
data: ForkedSignedBeaconBlock,
|
||||||
|
execOpt: Option[bool],
|
||||||
|
headers: openArray[tuple[key: string, value: string]]
|
||||||
|
): RestApiResponse =
|
||||||
|
let res =
|
||||||
|
block:
|
||||||
|
var default: seq[byte]
|
||||||
|
try:
|
||||||
|
var stream = memoryOutput()
|
||||||
|
var writer = JsonWriter[RestJson].init(stream)
|
||||||
|
writer.beginRecord()
|
||||||
|
writer.writeField("version", data.kind.toString())
|
||||||
|
if execOpt.isSome():
|
||||||
|
writer.writeField("execution_optimistic", execOpt.get())
|
||||||
|
withBlck(data):
|
||||||
|
writer.writeField("data", blck)
|
||||||
|
writer.endRecord()
|
||||||
|
stream.getOutput(seq[byte])
|
||||||
|
except SerializationError:
|
||||||
|
default
|
||||||
|
except IOError:
|
||||||
|
default
|
||||||
|
RestApiResponse.response(res, Http200, "application/json",
|
||||||
|
headers = headers)
|
||||||
|
|
||||||
|
proc jsonResponseState*(t: typedesc[RestApiResponse],
|
||||||
|
forkedState: ForkedHashedBeaconState,
|
||||||
|
execOpt: Option[bool]): RestApiResponse =
|
||||||
|
let
|
||||||
|
headers = [("eth-consensus-version", forkedState.kind.toString())]
|
||||||
|
res =
|
||||||
|
block:
|
||||||
|
var default: seq[byte]
|
||||||
|
try:
|
||||||
|
var stream = memoryOutput()
|
||||||
|
var writer = JsonWriter[RestJson].init(stream)
|
||||||
|
writer.beginRecord()
|
||||||
|
writer.writeField("version", forkedState.kind.toString())
|
||||||
|
if execOpt.isSome():
|
||||||
|
writer.writeField("execution_optimistic", execOpt.get())
|
||||||
|
# TODO (cheatfate): Unable to use `forks.withState()` template here
|
||||||
|
# because of compiler issues and some kind of generic sandwich.
|
||||||
|
case forkedState.kind
|
||||||
|
of BeaconStateFork.Bellatrix:
|
||||||
|
writer.writeField("data", forkedState.bellatrixData.data)
|
||||||
|
of BeaconStateFork.Altair:
|
||||||
|
writer.writeField("data", forkedState.altairData.data)
|
||||||
|
of BeaconStateFork.Phase0:
|
||||||
|
writer.writeField("data", forkedState.phase0Data.data)
|
||||||
|
writer.endRecord()
|
||||||
|
stream.getOutput(seq[byte])
|
||||||
|
except SerializationError:
|
||||||
|
default
|
||||||
|
except IOError:
|
||||||
|
default
|
||||||
|
RestApiResponse.response(res, Http200, "application/json", headers = headers)
|
||||||
|
|
||||||
|
proc jsonResponseWOpt*(t: typedesc[RestApiResponse], data: auto,
|
||||||
|
execOpt: Option[bool]): RestApiResponse =
|
||||||
|
let res =
|
||||||
|
block:
|
||||||
|
var default: seq[byte]
|
||||||
|
try:
|
||||||
|
var stream = memoryOutput()
|
||||||
|
var writer = JsonWriter[RestJson].init(stream)
|
||||||
|
writer.beginRecord()
|
||||||
|
if execOpt.isSome():
|
||||||
|
writer.writeField("execution_optimistic", execOpt.get())
|
||||||
|
writer.writeField("data", data)
|
||||||
|
writer.endRecord()
|
||||||
|
stream.getOutput(seq[byte])
|
||||||
|
except SerializationError:
|
||||||
|
default
|
||||||
|
except IOError:
|
||||||
|
default
|
||||||
|
RestApiResponse.response(res, Http200, "application/json")
|
||||||
|
|
||||||
proc jsonResponsePlain*(t: typedesc[RestApiResponse],
|
proc jsonResponsePlain*(t: typedesc[RestApiResponse],
|
||||||
data: auto): RestApiResponse =
|
data: auto): RestApiResponse =
|
||||||
let res =
|
let res =
|
||||||
|
@ -365,7 +447,9 @@ proc jsonErrorList*(t: typedesc[RestApiResponse],
|
||||||
default
|
default
|
||||||
RestApiResponse.error(status, data, "application/json")
|
RestApiResponse.error(status, data, "application/json")
|
||||||
|
|
||||||
proc sszResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse =
|
proc sszResponse*(t: typedesc[RestApiResponse], data: auto,
|
||||||
|
headers: openArray[tuple[key: string, value: string]]
|
||||||
|
): RestApiResponse =
|
||||||
let res =
|
let res =
|
||||||
block:
|
block:
|
||||||
var default: seq[byte]
|
var default: seq[byte]
|
||||||
|
@ -378,7 +462,8 @@ proc sszResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse =
|
||||||
default
|
default
|
||||||
except IOError:
|
except IOError:
|
||||||
default
|
default
|
||||||
RestApiResponse.response(res, Http200, "application/octet-stream")
|
RestApiResponse.response(res, Http200, "application/octet-stream",
|
||||||
|
headers = headers)
|
||||||
|
|
||||||
template hexOriginal(data: openArray[byte]): string =
|
template hexOriginal(data: openArray[byte]): string =
|
||||||
to0xHex(data)
|
to0xHex(data)
|
||||||
|
@ -2012,6 +2097,68 @@ proc dump*(value: KeystoresAndSlashingProtection): string {.
|
||||||
writer.writeValue(value)
|
writer.writeValue(value)
|
||||||
stream.getOutput(string)
|
stream.getOutput(string)
|
||||||
|
|
||||||
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
||||||
|
value: HeadChangeInfoObject) {.
|
||||||
|
raises: [IOError, Defect].} =
|
||||||
|
writer.beginRecord()
|
||||||
|
writer.writeField("slot", value.slot)
|
||||||
|
writer.writeField("block", value.block_root)
|
||||||
|
writer.writeField("state", value.state_root)
|
||||||
|
writer.writeField("epoch_transition", value.epoch_transition)
|
||||||
|
writer.writeField("previous_duty_dependent_root",
|
||||||
|
value.previous_duty_dependent_root)
|
||||||
|
writer.writeField("current_duty_dependent_root",
|
||||||
|
value.current_duty_dependent_root)
|
||||||
|
if value.optimistic.isSome():
|
||||||
|
writer.writeField("execution_optimistic", value.optimistic.get())
|
||||||
|
writer.endRecord()
|
||||||
|
|
||||||
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
||||||
|
value: ReorgInfoObject) {.
|
||||||
|
raises: [IOError, Defect].} =
|
||||||
|
writer.beginRecord()
|
||||||
|
writer.writeField("slot", value.slot)
|
||||||
|
writer.writeField("depth", value.depth)
|
||||||
|
writer.writeField("old_head_block", value.old_head_block)
|
||||||
|
writer.writeField("new_head_block", value.new_head_block)
|
||||||
|
writer.writeField("old_head_state", value.old_head_state)
|
||||||
|
writer.writeField("new_head_state", value.new_head_state)
|
||||||
|
if value.optimistic.isSome():
|
||||||
|
writer.writeField("execution_optimistic", value.optimistic.get())
|
||||||
|
writer.endRecord()
|
||||||
|
|
||||||
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
||||||
|
value: FinalizationInfoObject) {.
|
||||||
|
raises: [IOError, Defect].} =
|
||||||
|
writer.beginRecord()
|
||||||
|
writer.writeField("block", value.block_root)
|
||||||
|
writer.writeField("state", value.state_root)
|
||||||
|
writer.writeField("epoch", value.epoch)
|
||||||
|
if value.optimistic.isSome():
|
||||||
|
writer.writeField("execution_optimistic", value.optimistic.get())
|
||||||
|
writer.endRecord()
|
||||||
|
|
||||||
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
||||||
|
value: EventBeaconBlockObject) {.
|
||||||
|
raises: [IOError, Defect].} =
|
||||||
|
writer.beginRecord()
|
||||||
|
writer.writeField("slot", value.slot)
|
||||||
|
writer.writeField("block", value.block_root)
|
||||||
|
if value.optimistic.isSome():
|
||||||
|
writer.writeField("execution_optimistic", value.optimistic.get())
|
||||||
|
writer.endRecord()
|
||||||
|
|
||||||
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
||||||
|
value: RestSyncInfo) {.
|
||||||
|
raises: [IOError, Defect].} =
|
||||||
|
writer.beginRecord()
|
||||||
|
writer.writeField("head_slot", value.head_slot)
|
||||||
|
writer.writeField("sync_distance", value.sync_distance)
|
||||||
|
writer.writeField("is_syncing", value.is_syncing)
|
||||||
|
if value.is_optimistic.isSome():
|
||||||
|
writer.writeField("is_optimistic", value.is_optimistic.get())
|
||||||
|
writer.endRecord()
|
||||||
|
|
||||||
proc parseRoot(value: string): Result[Eth2Digest, cstring] =
|
proc parseRoot(value: string): Result[Eth2Digest, cstring] =
|
||||||
try:
|
try:
|
||||||
ok(Eth2Digest(data: hexToByteArray[32](value)))
|
ok(Eth2Digest(data: hexToByteArray[32](value)))
|
||||||
|
|
|
@ -210,6 +210,7 @@ type
|
||||||
head_slot*: Slot
|
head_slot*: Slot
|
||||||
sync_distance*: uint64
|
sync_distance*: uint64
|
||||||
is_syncing*: bool
|
is_syncing*: bool
|
||||||
|
is_optimistic*: Option[bool]
|
||||||
|
|
||||||
RestPeerCount* = object
|
RestPeerCount* = object
|
||||||
disconnected*: uint64
|
disconnected*: uint64
|
||||||
|
|
|
@ -249,6 +249,24 @@ template init*(T: type ForkedTrustedSignedBeaconBlock, blck: altair.TrustedSigne
|
||||||
template init*(T: type ForkedTrustedSignedBeaconBlock, blck: bellatrix.TrustedSignedBeaconBlock): T =
|
template init*(T: type ForkedTrustedSignedBeaconBlock, blck: bellatrix.TrustedSignedBeaconBlock): T =
|
||||||
T(kind: BeaconBlockFork.Bellatrix, bellatrixData: blck)
|
T(kind: BeaconBlockFork.Bellatrix, bellatrixData: blck)
|
||||||
|
|
||||||
|
template toString*(kind: BeaconBlockFork): string =
|
||||||
|
case kind
|
||||||
|
of BeaconBlockFork.Phase0:
|
||||||
|
"phase0"
|
||||||
|
of BeaconBlockFork.Altair:
|
||||||
|
"altair"
|
||||||
|
of BeaconBlockFork.Bellatrix:
|
||||||
|
"bellatrix"
|
||||||
|
|
||||||
|
template toString*(kind: BeaconStateFork): string =
|
||||||
|
case kind
|
||||||
|
of BeaconStateFork.Phase0:
|
||||||
|
"phase0"
|
||||||
|
of BeaconStateFork.Altair:
|
||||||
|
"altair"
|
||||||
|
of BeaconStateFork.Bellatrix:
|
||||||
|
"bellatrix"
|
||||||
|
|
||||||
template toFork*[T:
|
template toFork*[T:
|
||||||
phase0.SignedBeaconBlock |
|
phase0.SignedBeaconBlock |
|
||||||
phase0.SigVerifiedSignedBeaconBlock |
|
phase0.SigVerifiedSignedBeaconBlock |
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 61fbbc551208aca182ff810661bdf37b08a377cd
|
Subproject commit 2a5095505f771610f9559d2e774b2a9561f01101
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1dba6dd6f466cd4e7b793b0e473c237ce453d82a
|
Subproject commit 6e36cbc474e075ea9a64e7e1657082c0ec80f4e6
|
Loading…
Reference in New Issue