Number of REST fixes for Altair. (#2790)

* Fix getForkSchedule call.
Create cache of all configuration endpoints at node startup.
Add prepareJsonResponse() call to create cached responses.
Mark all procedures with `raises`.

* Add getForkSchedule to VC.
Fix getForkSchedule return type for API.
More `raises` annotations.
Fix VC fork_service.nim.

* Use `push raises` instead of inline `raises`.

* Improvements for REST API aggregated attestations and attestations processing.

* Rename eth2_network.sendXXX procedures to eth2_network.broadcastXXX.
Add broadcastBeaconBlock() and broadcastAggregateAndProof().
Fix links to specification in REST API declarations.
Add implementation for v2 getStateV2().
Add validator_duties.sendXXX procedures which not only broadcast data, but also validate it.
Fix JSON-RPC/REST to use new validator_duties.sendXXX procedures instead of own implementations.

* Fix validator_client online nodes count incorrect value.
Fix aggregate and proof attestation could be sent too late.

* Adding timeout for block wait in attestations processing.
Fix compilation errors.

* Attempt to debug aggregate and proofs.

* Fix Beacon AIP to use `sendAttestation`.
Add link comment to produceBlockV2.

* Add debug logs before publish operation for blocks, attestations and aggregated attestations.
Fix attestations publishing issue.

* logging fixes

`indexInCommnittee` already logged in attestation

Co-authored-by: Jacek Sieka <jacek@status.im>
This commit is contained in:
Eugene Kabanov 2021-08-23 13:41:48 +03:00 committed by GitHub
parent 0a61d1112e
commit 66cb18d69b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 774 additions and 441 deletions

View File

@ -2061,8 +2061,8 @@ func forkDigestAtEpoch(node: Eth2Node, epoch: Epoch): ForkDigest =
proc getWallEpoch(node: Eth2Node): Epoch = proc getWallEpoch(node: Eth2Node): Epoch =
node.getBeaconTime().slotOrZero.epoch node.getBeaconTime().slotOrZero.epoch
proc sendAttestation*( proc broadcastAttestation*(node: Eth2Node, subnet_id: SubnetId,
node: Eth2Node, subnet_id: SubnetId, attestation: Attestation) = attestation: Attestation) =
# Regardless of the contents of the attestation, # Regardless of the contents of the attestation,
# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/p2p-interface.md#transitioning-the-gossip # https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/p2p-interface.md#transitioning-the-gossip
# implies that pre-fork, messages using post-fork digests might be # implies that pre-fork, messages using post-fork digests might be
@ -2070,21 +2070,35 @@ proc sendAttestation*(
# timer unsubscription point that means no new pre-fork-forkdigest # timer unsubscription point that means no new pre-fork-forkdigest
# should be sent. # should be sent.
let forkPrefix = node.forkDigestAtEpoch(node.getWallEpoch) let forkPrefix = node.forkDigestAtEpoch(node.getWallEpoch)
node.broadcast( let topic = getAttestationTopic(forkPrefix, subnet_id)
getAttestationTopic(forkPrefix, subnet_id), node.broadcast(topic, attestation)
attestation)
proc sendVoluntaryExit*(node: Eth2Node, exit: SignedVoluntaryExit) = proc broadcastVoluntaryExit*(node: Eth2Node, exit: SignedVoluntaryExit) =
let exitsTopic = getVoluntaryExitsTopic( let exitsTopic = getVoluntaryExitsTopic(
node.forkDigestAtEpoch(node.getWallEpoch)) node.forkDigestAtEpoch(node.getWallEpoch))
node.broadcast(exitsTopic, exit) node.broadcast(exitsTopic, exit)
proc sendAttesterSlashing*(node: Eth2Node, slashing: AttesterSlashing) = proc broadcastAttesterSlashing*(node: Eth2Node, slashing: AttesterSlashing) =
let attesterSlashingsTopic = getAttesterSlashingsTopic( let attesterSlashingsTopic = getAttesterSlashingsTopic(
node.forkDigestAtEpoch(node.getWallEpoch)) node.forkDigestAtEpoch(node.getWallEpoch))
node.broadcast(attesterSlashingsTopic, slashing) node.broadcast(attesterSlashingsTopic, slashing)
proc sendProposerSlashing*(node: Eth2Node, slashing: ProposerSlashing) = proc broadcastProposerSlashing*(node: Eth2Node, slashing: ProposerSlashing) =
let proposerSlashingsTopic = getProposerSlashingsTopic( let proposerSlashingsTopic = getProposerSlashingsTopic(
node.forkDigestAtEpoch(node.getWallEpoch)) node.forkDigestAtEpoch(node.getWallEpoch))
node.broadcast(proposerSlashingsTopic, slashing) node.broadcast(proposerSlashingsTopic, slashing)
proc broadcastAggregateAndProof*(node: Eth2Node,
proof: SignedAggregateAndProof) =
let proofTopic = getAggregateAndProofsTopic(
node.forkDigestAtEpoch(node.getWallEpoch))
node.broadcast(proofTopic, proof)
proc broadcastBeaconBlock*(node: Eth2Node, forked: ForkedSignedBeaconBlock) =
case forked.kind
of BeaconBlockFork.Phase0:
let topic = getBeaconBlocksTopic(node.forkDigests.phase0)
node.broadcast(topic, forked.phase0Block)
of BeaconBlockFork.Altair:
let topic = getBeaconBlocksTopic(node.forkDigests.altair)
node.broadcast(topic, forked.altairBlock)

View File

@ -9,8 +9,7 @@ import
chronicles, chronicles,
nimcrypto/utils as ncrutils, nimcrypto/utils as ncrutils,
../beacon_node_common, ../networking/eth2_network, ../beacon_node_common, ../networking/eth2_network,
../consensus_object_pools/[blockchain_dag, exit_pool], ../consensus_object_pools/[blockchain_dag, exit_pool, spec_cache],
../gossip_processing/gossip_validation,
../validators/validator_duties, ../validators/validator_duties,
../spec/[eth2_merkleization, forks, network], ../spec/[eth2_merkleization, forks, network],
../spec/datatypes/[phase0, altair], ../spec/datatypes/[phase0, altair],
@ -105,7 +104,7 @@ proc getBeaconBlocksTopic(node: BeaconNode, kind: BeaconBlockFork): string =
getBeaconBlocksTopic(node.dag.forkDigests.altair) getBeaconBlocksTopic(node.dag.forkDigests.altair)
proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getGenesis # https://ethereum.github.io/beacon-APIs/#/Beacon/getGenesis
router.api(MethodGet, "/api/eth/v1/beacon/genesis") do () -> RestApiResponse: router.api(MethodGet, "/api/eth/v1/beacon/genesis") do () -> RestApiResponse:
return RestApiResponse.jsonResponse( return RestApiResponse.jsonResponse(
( (
@ -116,7 +115,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
) )
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateRoot # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateRoot
router.api(MethodGet, "/api/eth/v1/beacon/states/{state_id}/root") do ( router.api(MethodGet, "/api/eth/v1/beacon/states/{state_id}/root") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
let bslot = let bslot =
@ -133,7 +132,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonResponse((root: stateRoot)) return RestApiResponse.jsonResponse((root: stateRoot))
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateFork # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateFork
router.api(MethodGet, "/api/eth/v1/beacon/states/{state_id}/fork") do ( router.api(MethodGet, "/api/eth/v1/beacon/states/{state_id}/fork") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
let bslot = let bslot =
@ -159,7 +158,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateFinalityCheckpoints # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateFinalityCheckpoints
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/beacon/states/{state_id}/finality_checkpoints") do ( "/api/eth/v1/beacon/states/{state_id}/finality_checkpoints") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
@ -185,7 +184,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateValidators # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidators
router.api(MethodGet, "/api/eth/v1/beacon/states/{state_id}/validators") do ( router.api(MethodGet, "/api/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:
@ -276,7 +275,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateValidator # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidator
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/beacon/states/{state_id}/validators/{validator_id}") do ( "/api/eth/v1/beacon/states/{state_id}/validators/{validator_id}") do (
state_id: StateIdent, validator_id: ValidatorIdent) -> RestApiResponse: state_id: StateIdent, validator_id: ValidatorIdent) -> RestApiResponse:
@ -353,7 +352,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
ValidatorStatusNotFoundError) ValidatorStatusNotFoundError)
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateValidatorBalances # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidatorBalances
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/beacon/states/{state_id}/validator_balances") do ( "/api/eth/v1/beacon/states/{state_id}/validator_balances") do (
state_id: StateIdent, id: seq[ValidatorIdent]) -> RestApiResponse: state_id: StateIdent, id: seq[ValidatorIdent]) -> RestApiResponse:
@ -427,7 +426,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getEpochCommittees # https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochCommittees
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/beacon/states/{state_id}/committees") do ( "/api/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],
@ -511,7 +510,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockHeaders # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeaders
router.api(MethodGet, "/api/eth/v1/beacon/headers") do ( router.api(MethodGet, "/api/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
@ -568,7 +567,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
] ]
) )
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockHeader # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader
router.api(MethodGet, "/api/eth/v1/beacon/headers/{block_id}") do ( router.api(MethodGet, "/api/eth/v1/beacon/headers/{block_id}") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let bdata = let bdata =
@ -600,10 +599,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
) )
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/publishBlock # https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
router.api(MethodPost, "/api/eth/v1/beacon/blocks") do ( router.api(MethodPost, "/api/eth/v1/beacon/blocks") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let blockData = let forked =
block: block:
if contentBody.isNone(): if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
@ -641,37 +640,14 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError, return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$phase0res.error()) $phase0res.error())
let head = node.dag.head let res = await node.sendBeaconBlock(forked)
if not(node.isSynced(head)): if res.isErr():
return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError)
if not(res.get()):
if head.slot >= blockData.slot():
let blocksTopic = node.getBeaconBlocksTopic(blockData.kind)
withBlck(blockData):
node.network.broadcast(blocksTopic, blck)
return RestApiResponse.jsonError(Http202, BlockValidationError) return RestApiResponse.jsonError(Http202, BlockValidationError)
else: return RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
let res =
when compiles(node.proposeSignedBlock(head, AttachedValidator(),
blockData)):
await node.proposeSignedBlock(head, AttachedValidator(), blockData)
else:
case blockData.kind
of BeaconBlockFork.Phase0:
await node.proposeSignedBlock(head, AttachedValidator(),
blockData.phase0Block)
of BeaconBlockFork.Altair:
head
if res == head: # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlock
let blocksTopic = node.getBeaconBlocksTopic(blockData.kind)
withBlck(blockData):
node.network.broadcast(blocksTopic, blck)
return RestApiResponse.jsonError(Http202, BlockValidationError)
else:
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlock
router.api(MethodGet, "/api/eth/v1/beacon/blocks/{block_id}") do ( router.api(MethodGet, "/api/eth/v1/beacon/blocks/{block_id}") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let bdata = let bdata =
@ -690,7 +666,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
of BeaconBlockFork.Altair: of BeaconBlockFork.Altair:
RestApiResponse.jsonError(Http404, BlockNotFoundError) RestApiResponse.jsonError(Http404, BlockNotFoundError)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockV2 # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockV2
router.api(MethodGet, "/api/eth/v2/beacon/blocks/{block_id}") do ( router.api(MethodGet, "/api/eth/v2/beacon/blocks/{block_id}") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let bdata = let bdata =
@ -713,7 +689,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
(version: "altair", data: bdata.data.altairBlock) (version: "altair", data: bdata.data.altairBlock)
) )
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockRoot # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockRoot
router.api(MethodGet, "/api/eth/v1/beacon/blocks/{block_id}/root") do ( router.api(MethodGet, "/api/eth/v1/beacon/blocks/{block_id}/root") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
let bdata = let bdata =
@ -729,7 +705,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
withBlck(bdata.data): withBlck(bdata.data):
RestApiResponse.jsonResponse((root: blck.root)) RestApiResponse.jsonResponse((root: blck.root))
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getBlockAttestations # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/beacon/blocks/{block_id}/attestations") do ( "/api/eth/v1/beacon/blocks/{block_id}/attestations") do (
block_id: BlockIdent) -> RestApiResponse: block_id: BlockIdent) -> RestApiResponse:
@ -746,7 +722,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
withBlck(bdata.data): withBlck(bdata.data):
RestApiResponse.jsonResponse(blck.message.body.attestations.asSeq()) RestApiResponse.jsonResponse(blck.message.body.attestations.asSeq())
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getPoolAttestations # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations
router.api(MethodGet, "/api/eth/v1/beacon/pool/attestations") do ( router.api(MethodGet, "/api/eth/v1/beacon/pool/attestations") do (
slot: Option[Slot], slot: Option[Slot],
committee_index: Option[CommitteeIndex]) -> RestApiResponse: committee_index: Option[CommitteeIndex]) -> RestApiResponse:
@ -774,7 +750,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
res.add(item) res.add(item)
return RestApiResponse.jsonResponse(res) return RestApiResponse.jsonResponse(res)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/submitPoolAttestations # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations
router.api(MethodPost, "/api/eth/v1/beacon/pool/attestations") do ( router.api(MethodPost, "/api/eth/v1/beacon/pool/attestations") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let attestations = let attestations =
@ -788,13 +764,48 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
$dres.error()) $dres.error())
dres.get() dres.get()
var failures: seq[RestAttestationsFailure] proc processAttestation(a: Attestation): Future[SendResult] {.async.} =
for atindex, attestation in attestations.pairs(): let res = await node.sendAttestation(a)
debug "Attestation for pool", attestation = attestation, if res.isErr():
signature = $attestation.signature return res
if not await node.sendAttestation(attestation): let
failures.add(RestAttestationsFailure( wallTime = node.processor.getCurrentBeaconTime()
index: uint64(atindex), message: "Attestation failed validation")) deadline = a.data.slot.toBeaconTime() +
seconds(int(SECONDS_PER_SLOT div 3))
(delayStr, delaySecs) =
if wallTime < deadline:
("-" & $(deadline - wallTime), -toFloatSeconds(deadline - wallTime))
else:
($(wallTime - deadline), toFloatSeconds(wallTime - deadline))
notice "Attestation sent", attestation = shortLog(a), delay = delayStr
return res
# Since our validation logic supports batch processing, we will submit all
# attestations for validation.
let pending =
block:
var res: seq[Future[SendResult]]
for attestation in attestations:
res.add(processAttestation(attestation))
res
let failures =
block:
var res: seq[RestAttestationsFailure]
await allFutures(pending)
for index, future in pending.pairs():
if future.done():
let fres = future.read()
if fres.isErr():
let failure = RestAttestationsFailure(index: uint64(index),
message: $fres.error())
res.add(failure)
elif future.failed() or future.cancelled():
# This is unexpected failure, so we log the error message.
let exc = future.readError()
let failure = RestAttestationsFailure(index: uint64(index),
message: $exc.msg)
res.add(failure)
res
if len(failures) > 0: if len(failures) > 0:
return RestApiResponse.jsonErrorList(Http400, AttestationValidationError, return RestApiResponse.jsonErrorList(Http400, AttestationValidationError,
@ -802,7 +813,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
else: else:
return RestApiResponse.jsonMsgResponse(AttestationValidationSuccess) return RestApiResponse.jsonMsgResponse(AttestationValidationSuccess)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getPoolAttesterSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttesterSlashings
router.api(MethodGet, "/api/eth/v1/beacon/pool/attester_slashings") do ( router.api(MethodGet, "/api/eth/v1/beacon/pool/attester_slashings") do (
) -> RestApiResponse: ) -> RestApiResponse:
var res: seq[AttesterSlashing] var res: seq[AttesterSlashing]
@ -814,7 +825,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
res.add(item) res.add(item)
return RestApiResponse.jsonResponse(res) return RestApiResponse.jsonResponse(res)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/submitPoolAttesterSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttesterSlashings
router.api(MethodPost, "/api/eth/v1/beacon/pool/attester_slashings") do ( router.api(MethodPost, "/api/eth/v1/beacon/pool/attester_slashings") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let slashing = let slashing =
@ -826,17 +837,15 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidAttesterSlashingObjectError, InvalidAttesterSlashingObjectError,
$dres.error()) $dres.error())
let res = dres.get() dres.get()
let vres = node.exitPool[].validateAttesterSlashing(res) let res = node.sendAttesterSlashing(slashing)
if vres.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
AttesterSlashingValidationError, AttesterSlashingValidationError,
$vres.error()) $res.error())
res
node.network.sendAttesterSlashing(slashing)
return RestApiResponse.jsonMsgResponse(AttesterSlashingValidationSuccess) return RestApiResponse.jsonMsgResponse(AttesterSlashingValidationSuccess)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getPoolProposerSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolProposerSlashings
router.api(MethodGet, "/api/eth/v1/beacon/pool/proposer_slashings") do ( router.api(MethodGet, "/api/eth/v1/beacon/pool/proposer_slashings") do (
) -> RestApiResponse: ) -> RestApiResponse:
var res: seq[ProposerSlashing] var res: seq[ProposerSlashing]
@ -848,7 +857,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
res.add(item) res.add(item)
return RestApiResponse.jsonResponse(res) return RestApiResponse.jsonResponse(res)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/submitPoolProposerSlashings # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolProposerSlashings
router.api(MethodPost, "/api/eth/v1/beacon/pool/proposer_slashings") do ( router.api(MethodPost, "/api/eth/v1/beacon/pool/proposer_slashings") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let slashing = let slashing =
@ -860,17 +869,15 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidProposerSlashingObjectError, InvalidProposerSlashingObjectError,
$dres.error()) $dres.error())
let res = dres.get() dres.get()
let vres = node.exitPool[].validateProposerSlashing(res) let res = node.sendProposerSlashing(slashing)
if vres.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
ProposerSlashingValidationError, ProposerSlashingValidationError,
$vres.error()) $res.error())
res
node.network.sendProposerSlashing(slashing)
return RestApiResponse.jsonMsgResponse(ProposerSlashingValidationSuccess) return RestApiResponse.jsonMsgResponse(ProposerSlashingValidationSuccess)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/getPoolVoluntaryExits # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolVoluntaryExits
router.api(MethodGet, "/api/eth/v1/beacon/pool/voluntary_exits") do ( router.api(MethodGet, "/api/eth/v1/beacon/pool/voluntary_exits") do (
) -> RestApiResponse: ) -> RestApiResponse:
var res: seq[SignedVoluntaryExit] var res: seq[SignedVoluntaryExit]
@ -882,7 +889,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
res.add(item) res.add(item)
return RestApiResponse.jsonResponse(res) return RestApiResponse.jsonResponse(res)
# https://ethereum.github.io/eth2.0-APIs/#/Beacon/submitPoolVoluntaryExit # https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolVoluntaryExit
router.api(MethodPost, "/api/eth/v1/beacon/pool/voluntary_exits") do ( router.api(MethodPost, "/api/eth/v1/beacon/pool/voluntary_exits") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let exit = let exit =
@ -894,14 +901,12 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
InvalidVoluntaryExitObjectError, InvalidVoluntaryExitObjectError,
$dres.error()) $dres.error())
let res = dres.get() dres.get()
let vres = node.exitPool[].validateVoluntaryExit(res) let res = node.sendVoluntaryExit(exit)
if vres.isErr(): if res.isErr():
return RestApiResponse.jsonError(Http400, return RestApiResponse.jsonError(Http400,
VoluntaryExitValidationError, VoluntaryExitValidationError,
$vres.error()) $res.error())
res
node.network.sendVoluntaryExit(exit)
return RestApiResponse.jsonMsgResponse(VoluntaryExitValidationSuccess) return RestApiResponse.jsonMsgResponse(VoluntaryExitValidationSuccess)
router.redirect( router.redirect(

View File

@ -4,164 +4,169 @@
# * 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).
# at your option. This file may not be copied, modified, or distributed except according to those terms. # at your option. This file may not be copied, modified, or distributed except according to those terms.
import import stew/[endians2, base10], presto, chronicles,
stew/[endians2, base10], nimcrypto/utils as ncrutils
presto, import ".."/beacon_node_common,
chronicles, ".."/eth1/eth1_monitor,
nimcrypto/utils as ncrutils, ".."/spec/forks,
../beacon_node_common, ../eth1/eth1_monitor, "."/rest_utils
../spec/forks,
./rest_utils
logScope: topics = "rest_config" logScope: topics = "rest_config"
proc installConfigApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installConfigApiHandlers*(router: var RestRouter, node: BeaconNode) =
let
cachedForkSchedule =
RestApiResponse.prepareJsonResponse(getForkSchedule(node.dag.cfg))
cachedConfigSpec =
RestApiResponse.prepareJsonResponse(
(
CONFIG_NAME:
const_preset,
MAX_COMMITTEES_PER_SLOT:
Base10.toString(MAX_COMMITTEES_PER_SLOT),
TARGET_COMMITTEE_SIZE:
Base10.toString(TARGET_COMMITTEE_SIZE),
MAX_VALIDATORS_PER_COMMITTEE:
Base10.toString(MAX_VALIDATORS_PER_COMMITTEE),
MIN_PER_EPOCH_CHURN_LIMIT:
Base10.toString(node.dag.cfg.MIN_PER_EPOCH_CHURN_LIMIT),
CHURN_LIMIT_QUOTIENT:
Base10.toString(node.dag.cfg.CHURN_LIMIT_QUOTIENT),
SHUFFLE_ROUND_COUNT:
Base10.toString(SHUFFLE_ROUND_COUNT),
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
Base10.toString(node.dag.cfg.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT),
MIN_GENESIS_TIME:
Base10.toString(node.dag.cfg.MIN_GENESIS_TIME),
HYSTERESIS_QUOTIENT:
Base10.toString(HYSTERESIS_QUOTIENT),
HYSTERESIS_DOWNWARD_MULTIPLIER:
Base10.toString(HYSTERESIS_DOWNWARD_MULTIPLIER),
HYSTERESIS_UPWARD_MULTIPLIER:
Base10.toString(HYSTERESIS_UPWARD_MULTIPLIER),
SAFE_SLOTS_TO_UPDATE_JUSTIFIED:
Base10.toString(SAFE_SLOTS_TO_UPDATE_JUSTIFIED),
ETH1_FOLLOW_DISTANCE:
Base10.toString(node.dag.cfg.ETH1_FOLLOW_DISTANCE),
TARGET_AGGREGATORS_PER_COMMITTEE:
Base10.toString(TARGET_AGGREGATORS_PER_COMMITTEE),
RANDOM_SUBNETS_PER_VALIDATOR:
Base10.toString(RANDOM_SUBNETS_PER_VALIDATOR),
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION:
Base10.toString(EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION),
SECONDS_PER_ETH1_BLOCK:
Base10.toString(node.dag.cfg.SECONDS_PER_ETH1_BLOCK),
DEPOSIT_CHAIN_ID:
Base10.toString(uint64(node.dag.cfg.DEPOSIT_CHAIN_ID)),
DEPOSIT_NETWORK_ID:
Base10.toString(uint64(node.dag.cfg.DEPOSIT_NETWORK_ID)),
DEPOSIT_CONTRACT_ADDRESS:
$node.dag.cfg.DEPOSIT_CONTRACT_ADDRESS,
MIN_DEPOSIT_AMOUNT:
Base10.toString(MIN_DEPOSIT_AMOUNT),
MAX_EFFECTIVE_BALANCE:
Base10.toString(MAX_EFFECTIVE_BALANCE),
EJECTION_BALANCE:
Base10.toString(node.dag.cfg.EJECTION_BALANCE),
EFFECTIVE_BALANCE_INCREMENT:
Base10.toString(EFFECTIVE_BALANCE_INCREMENT),
GENESIS_FORK_VERSION:
"0x" & $node.dag.cfg.GENESIS_FORK_VERSION,
BLS_WITHDRAWAL_PREFIX:
"0x" & ncrutils.toHex([BLS_WITHDRAWAL_PREFIX]),
GENESIS_DELAY:
Base10.toString(node.dag.cfg.GENESIS_DELAY),
SECONDS_PER_SLOT:
Base10.toString(uint64(SECONDS_PER_SLOT)),
MIN_ATTESTATION_INCLUSION_DELAY:
Base10.toString(MIN_ATTESTATION_INCLUSION_DELAY),
SLOTS_PER_EPOCH:
Base10.toString(SLOTS_PER_EPOCH),
MIN_SEED_LOOKAHEAD:
Base10.toString(MIN_SEED_LOOKAHEAD),
MAX_SEED_LOOKAHEAD:
Base10.toString(MAX_SEED_LOOKAHEAD),
EPOCHS_PER_ETH1_VOTING_PERIOD:
Base10.toString(EPOCHS_PER_ETH1_VOTING_PERIOD),
SLOTS_PER_HISTORICAL_ROOT:
Base10.toString(SLOTS_PER_HISTORICAL_ROOT),
MIN_VALIDATOR_WITHDRAWABILITY_DELAY:
Base10.toString(node.dag.cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY),
SHARD_COMMITTEE_PERIOD:
Base10.toString(node.dag.cfg.SHARD_COMMITTEE_PERIOD),
MIN_EPOCHS_TO_INACTIVITY_PENALTY:
Base10.toString(MIN_EPOCHS_TO_INACTIVITY_PENALTY),
EPOCHS_PER_HISTORICAL_VECTOR:
Base10.toString(EPOCHS_PER_HISTORICAL_VECTOR),
EPOCHS_PER_SLASHINGS_VECTOR:
Base10.toString(EPOCHS_PER_SLASHINGS_VECTOR),
HISTORICAL_ROOTS_LIMIT:
Base10.toString(HISTORICAL_ROOTS_LIMIT),
VALIDATOR_REGISTRY_LIMIT:
Base10.toString(VALIDATOR_REGISTRY_LIMIT),
BASE_REWARD_FACTOR:
Base10.toString(BASE_REWARD_FACTOR),
WHISTLEBLOWER_REWARD_QUOTIENT:
Base10.toString(WHISTLEBLOWER_REWARD_QUOTIENT),
PROPOSER_REWARD_QUOTIENT:
Base10.toString(PROPOSER_REWARD_QUOTIENT),
INACTIVITY_PENALTY_QUOTIENT:
Base10.toString(INACTIVITY_PENALTY_QUOTIENT),
MIN_SLASHING_PENALTY_QUOTIENT:
Base10.toString(MIN_SLASHING_PENALTY_QUOTIENT),
PROPORTIONAL_SLASHING_MULTIPLIER:
Base10.toString(PROPORTIONAL_SLASHING_MULTIPLIER),
MAX_PROPOSER_SLASHINGS:
Base10.toString(MAX_PROPOSER_SLASHINGS),
MAX_ATTESTER_SLASHINGS:
Base10.toString(MAX_ATTESTER_SLASHINGS),
MAX_ATTESTATIONS:
Base10.toString(MAX_ATTESTATIONS),
MAX_DEPOSITS:
Base10.toString(MAX_DEPOSITS),
MAX_VOLUNTARY_EXITS:
Base10.toString(MAX_VOLUNTARY_EXITS),
DOMAIN_BEACON_PROPOSER:
"0x" & ncrutils.toHex(uint32(DOMAIN_BEACON_PROPOSER).toBytesLE()),
DOMAIN_BEACON_ATTESTER:
"0x" & ncrutils.toHex(uint32(DOMAIN_BEACON_ATTESTER).toBytesLE()),
DOMAIN_RANDAO:
"0x" & ncrutils.toHex(uint32(DOMAIN_RANDAO).toBytesLE()),
DOMAIN_DEPOSIT:
"0x" & ncrutils.toHex(uint32(DOMAIN_DEPOSIT).toBytesLE()),
DOMAIN_VOLUNTARY_EXIT:
"0x" & ncrutils.toHex(uint32(DOMAIN_VOLUNTARY_EXIT).toBytesLE()),
DOMAIN_SELECTION_PROOF:
"0x" & ncrutils.toHex(uint32(DOMAIN_SELECTION_PROOF).toBytesLE()),
DOMAIN_AGGREGATE_AND_PROOF:
"0x" & ncrutils.toHex(uint32(DOMAIN_AGGREGATE_AND_PROOF).toBytesLE())
)
)
cachedDepositContract =
RestApiResponse.prepareJsonResponse(
(
chain_id: $node.dag.cfg.DEPOSIT_CHAIN_ID,
address: $node.dag.cfg.DEPOSIT_CONTRACT_ADDRESS
)
)
# https://ethereum.github.io/beacon-APIs/#/Config/getForkSchedule
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/config/fork_schedule") do () -> RestApiResponse: "/api/eth/v1/config/fork_schedule") do () -> RestApiResponse:
# TODO: Implemenation needs a fix, when forks infrastructure will be return RestApiResponse.response(cachedForkSchedule, Http200,
# established. "application/json")
return RestApiResponse.jsonResponse(
[getStateField(node.dag.headState.data, fork)]
)
# https://ethereum.github.io/beacon-APIs/#/Config/getSpec
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/config/spec") do () -> RestApiResponse: "/api/eth/v1/config/spec") do () -> RestApiResponse:
return RestApiResponse.jsonResponse( return RestApiResponse.response(cachedConfigSpec, Http200,
( "application/json")
CONFIG_NAME:
const_preset,
MAX_COMMITTEES_PER_SLOT:
Base10.toString(MAX_COMMITTEES_PER_SLOT),
TARGET_COMMITTEE_SIZE:
Base10.toString(TARGET_COMMITTEE_SIZE),
MAX_VALIDATORS_PER_COMMITTEE:
Base10.toString(MAX_VALIDATORS_PER_COMMITTEE),
MIN_PER_EPOCH_CHURN_LIMIT:
Base10.toString(node.dag.cfg.MIN_PER_EPOCH_CHURN_LIMIT),
CHURN_LIMIT_QUOTIENT:
Base10.toString(node.dag.cfg.CHURN_LIMIT_QUOTIENT),
SHUFFLE_ROUND_COUNT:
Base10.toString(SHUFFLE_ROUND_COUNT),
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT:
Base10.toString(
node.dag.cfg.MIN_GENESIS_ACTIVE_VALIDATOR_COUNT
),
MIN_GENESIS_TIME:
Base10.toString(node.dag.cfg.MIN_GENESIS_TIME),
HYSTERESIS_QUOTIENT:
Base10.toString(HYSTERESIS_QUOTIENT),
HYSTERESIS_DOWNWARD_MULTIPLIER:
Base10.toString(HYSTERESIS_DOWNWARD_MULTIPLIER),
HYSTERESIS_UPWARD_MULTIPLIER:
Base10.toString(HYSTERESIS_UPWARD_MULTIPLIER),
SAFE_SLOTS_TO_UPDATE_JUSTIFIED:
Base10.toString(SAFE_SLOTS_TO_UPDATE_JUSTIFIED),
ETH1_FOLLOW_DISTANCE:
Base10.toString(node.dag.cfg.ETH1_FOLLOW_DISTANCE),
TARGET_AGGREGATORS_PER_COMMITTEE:
Base10.toString(TARGET_AGGREGATORS_PER_COMMITTEE),
RANDOM_SUBNETS_PER_VALIDATOR:
Base10.toString(RANDOM_SUBNETS_PER_VALIDATOR),
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION:
Base10.toString(EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION),
SECONDS_PER_ETH1_BLOCK:
Base10.toString(node.dag.cfg.SECONDS_PER_ETH1_BLOCK),
DEPOSIT_CHAIN_ID:
Base10.toString(uint64(node.dag.cfg.DEPOSIT_CHAIN_ID)),
DEPOSIT_NETWORK_ID:
Base10.toString(uint64(node.dag.cfg.DEPOSIT_NETWORK_ID)),
DEPOSIT_CONTRACT_ADDRESS:
$node.dag.cfg.DEPOSIT_CONTRACT_ADDRESS,
MIN_DEPOSIT_AMOUNT:
Base10.toString(MIN_DEPOSIT_AMOUNT),
MAX_EFFECTIVE_BALANCE:
Base10.toString(MAX_EFFECTIVE_BALANCE),
EJECTION_BALANCE:
Base10.toString(node.dag.cfg.EJECTION_BALANCE),
EFFECTIVE_BALANCE_INCREMENT:
Base10.toString(EFFECTIVE_BALANCE_INCREMENT),
GENESIS_FORK_VERSION:
"0x" & $node.dag.cfg.GENESIS_FORK_VERSION,
BLS_WITHDRAWAL_PREFIX:
"0x" & ncrutils.toHex([BLS_WITHDRAWAL_PREFIX]),
GENESIS_DELAY:
Base10.toString(node.dag.cfg.GENESIS_DELAY),
SECONDS_PER_SLOT:
Base10.toString(uint64(SECONDS_PER_SLOT)),
MIN_ATTESTATION_INCLUSION_DELAY:
Base10.toString(MIN_ATTESTATION_INCLUSION_DELAY),
SLOTS_PER_EPOCH:
Base10.toString(SLOTS_PER_EPOCH),
MIN_SEED_LOOKAHEAD:
Base10.toString(MIN_SEED_LOOKAHEAD),
MAX_SEED_LOOKAHEAD:
Base10.toString(MAX_SEED_LOOKAHEAD),
EPOCHS_PER_ETH1_VOTING_PERIOD:
Base10.toString(EPOCHS_PER_ETH1_VOTING_PERIOD),
SLOTS_PER_HISTORICAL_ROOT:
Base10.toString(SLOTS_PER_HISTORICAL_ROOT),
MIN_VALIDATOR_WITHDRAWABILITY_DELAY:
Base10.toString(
node.dag.cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY),
SHARD_COMMITTEE_PERIOD:
Base10.toString(node.dag.cfg.SHARD_COMMITTEE_PERIOD),
MIN_EPOCHS_TO_INACTIVITY_PENALTY:
Base10.toString(MIN_EPOCHS_TO_INACTIVITY_PENALTY),
EPOCHS_PER_HISTORICAL_VECTOR:
Base10.toString(EPOCHS_PER_HISTORICAL_VECTOR),
EPOCHS_PER_SLASHINGS_VECTOR:
Base10.toString(EPOCHS_PER_SLASHINGS_VECTOR),
HISTORICAL_ROOTS_LIMIT:
Base10.toString(HISTORICAL_ROOTS_LIMIT),
VALIDATOR_REGISTRY_LIMIT:
Base10.toString(VALIDATOR_REGISTRY_LIMIT),
BASE_REWARD_FACTOR:
Base10.toString(BASE_REWARD_FACTOR),
WHISTLEBLOWER_REWARD_QUOTIENT:
Base10.toString(WHISTLEBLOWER_REWARD_QUOTIENT),
PROPOSER_REWARD_QUOTIENT:
Base10.toString(PROPOSER_REWARD_QUOTIENT),
INACTIVITY_PENALTY_QUOTIENT:
Base10.toString(INACTIVITY_PENALTY_QUOTIENT),
MIN_SLASHING_PENALTY_QUOTIENT:
Base10.toString(MIN_SLASHING_PENALTY_QUOTIENT),
PROPORTIONAL_SLASHING_MULTIPLIER:
Base10.toString(PROPORTIONAL_SLASHING_MULTIPLIER),
MAX_PROPOSER_SLASHINGS:
Base10.toString(MAX_PROPOSER_SLASHINGS),
MAX_ATTESTER_SLASHINGS:
Base10.toString(MAX_ATTESTER_SLASHINGS),
MAX_ATTESTATIONS:
Base10.toString(MAX_ATTESTATIONS),
MAX_DEPOSITS:
Base10.toString(MAX_DEPOSITS),
MAX_VOLUNTARY_EXITS:
Base10.toString(MAX_VOLUNTARY_EXITS),
DOMAIN_BEACON_PROPOSER:
"0x" & ncrutils.toHex(uint32(DOMAIN_BEACON_PROPOSER).toBytesLE()),
DOMAIN_BEACON_ATTESTER:
"0x" & ncrutils.toHex(uint32(DOMAIN_BEACON_ATTESTER).toBytesLE()),
DOMAIN_RANDAO:
"0x" & ncrutils.toHex(uint32(DOMAIN_RANDAO).toBytesLE()),
DOMAIN_DEPOSIT:
"0x" & ncrutils.toHex(uint32(DOMAIN_DEPOSIT).toBytesLE()),
DOMAIN_VOLUNTARY_EXIT:
"0x" & ncrutils.toHex(uint32(DOMAIN_VOLUNTARY_EXIT).toBytesLE()),
DOMAIN_SELECTION_PROOF:
"0x" & ncrutils.toHex(uint32(DOMAIN_SELECTION_PROOF).toBytesLE()),
DOMAIN_AGGREGATE_AND_PROOF:
"0x" & ncrutils.toHex(uint32(DOMAIN_AGGREGATE_AND_PROOF).toBytesLE())
)
)
# https://ethereum.github.io/beacon-APIs/#/Config/getDepositContract
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/config/deposit_contract") do () -> RestApiResponse: "/api/eth/v1/config/deposit_contract") do () -> RestApiResponse:
return RestApiResponse.jsonResponse( return RestApiResponse.response(cachedDepositContract, Http200,
( "application/json")
chain_id: $node.dag.cfg.DEPOSIT_CHAIN_ID,
address: $node.dag.cfg.DEPOSIT_CONTRACT_ADDRESS
)
)
router.redirect( router.redirect(
MethodGet, MethodGet,

View File

@ -1,13 +1,13 @@
import import std/sequtils
std/sequtils, import presto, chronicles
presto, import ".."/[version, beacon_node_common],
chronicles, ".."/spec/forks,
../version, ../beacon_node_common, "."/rest_utils
./rest_utils
logScope: topics = "rest_debug" 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
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/debug/beacon/states/{state_id}") do ( "/api/eth/v1/debug/beacon/states/{state_id}") do (
state_id: StateIdent) -> RestApiResponse: state_id: StateIdent) -> RestApiResponse:
@ -22,9 +22,40 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
$bres.error()) $bres.error())
bres.get() bres.get()
node.withStateForBlockSlot(bslot): node.withStateForBlockSlot(bslot):
return RestApiResponse.jsonResponse(stateData.data.hbsPhase0.data) case stateData.data.beaconStateFork
of BeaconStateFork.forkPhase0:
return RestApiResponse.jsonResponse(stateData.data.hbsPhase0.data)
of BeaconStateFork.forkAltair:
return RestApiResponse.jsonError(Http404, StateNotFoundError)
return RestApiResponse.jsonError(Http500, InternalServerError) return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/beacon-APIs/#/Debug/getStateV2
router.api(MethodGet,
"/api/eth/v2/debug/beacon/states/{state_id}") do (
state_id: StateIdent) -> RestApiResponse:
let bslot =
block:
if state_id.isErr():
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
$state_id.error())
let bres = node.getBlockSlot(state_id.get())
if bres.isErr():
return RestApiResponse.jsonError(Http404, StateNotFoundError,
$bres.error())
bres.get()
node.withStateForBlockSlot(bslot):
case stateData.data.beaconStateFork
of BeaconStateFork.forkPhase0:
return RestApiResponse.jsonResponse(
(version: "phase0", data: stateData.data.hbsPhase0.data)
)
of BeaconStateFork.forkAltair:
return RestApiResponse.jsonResponse(
(version: "altair", data: stateData.data.hbsAltair.data)
)
return RestApiResponse.jsonError(Http500, InternalServerError)
# https://ethereum.github.io/beacon-APIs/#/Debug/getDebugChainHeads
router.api(MethodGet, router.api(MethodGet,
"/api/eth/v1/debug/beacon/heads") do () -> RestApiResponse: "/api/eth/v1/debug/beacon/heads") do () -> RestApiResponse:
return RestApiResponse.jsonResponse( return RestApiResponse.jsonResponse(
@ -41,6 +72,11 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
"/eth/v1/debug/beacon/heads", "/eth/v1/debug/beacon/heads",
"/api/eth/v1/debug/beacon/heads" "/api/eth/v1/debug/beacon/heads"
) )
router.redirect(
MethodGet,
"/eth/v2/debug/beacon/heads",
"/api/eth/v2/debug/beacon/heads"
)
proc getDebugChainHeads*(): RestResponse[GetDebugChainHeadsResponse] {. proc getDebugChainHeads*(): RestResponse[GetDebugChainHeadsResponse] {.
rest, endpoint: "/eth/v1/debug/beacon/heads", rest, endpoint: "/eth/v1/debug/beacon/heads",

View File

@ -49,6 +49,7 @@ proc validateEventTopics(events: seq[EventTopic]): Result[EventTopics,
ok(res) ok(res)
proc installEventApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installEventApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/#/Events/eventstream
router.api(MethodGet, "/api/eth/v1/events") do ( router.api(MethodGet, "/api/eth/v1/events") do (
topics: seq[EventTopic]) -> RestApiResponse: topics: seq[EventTopic]) -> RestApiResponse:
# TODO (cheatfate): This call is not fully implemented yet, because there # TODO (cheatfate): This call is not fully implemented yet, because there

View File

@ -126,6 +126,7 @@ proc getP2PAddresses(node: BeaconNode): Option[seq[string]] =
return some(addresses) return some(addresses)
proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/#/Node/getNetworkIdentity
router.api(MethodGet, "/api/eth/v1/node/identity") do () -> RestApiResponse: router.api(MethodGet, "/api/eth/v1/node/identity") do () -> RestApiResponse:
let discoveryAddresses = let discoveryAddresses =
block: block:
@ -156,6 +157,7 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
) )
# https://ethereum.github.io/beacon-APIs/#/Node/getPeers
router.api(MethodGet, "/api/eth/v1/node/peers") do ( router.api(MethodGet, "/api/eth/v1/node/peers") do (
state: seq[PeerStateKind], state: seq[PeerStateKind],
direction: seq[PeerDirectKind]) -> RestApiResponse: direction: seq[PeerDirectKind]) -> RestApiResponse:
@ -198,6 +200,7 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
res.add(peer) res.add(peer)
return RestApiResponse.jsonResponseWMeta(res, (count: uint64(len(res)))) return RestApiResponse.jsonResponseWMeta(res, (count: uint64(len(res))))
# https://ethereum.github.io/beacon-APIs/#/Node/getPeerCount
router.api(MethodGet, "/api/eth/v1/node/peer_count") do () -> RestApiResponse: router.api(MethodGet, "/api/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():
@ -214,6 +217,7 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
discard discard
return RestApiResponse.jsonResponse(res) return RestApiResponse.jsonResponse(res)
# https://ethereum.github.io/beacon-APIs/#/Node/getPeer
router.api(MethodGet, "/api/eth/v1/node/peers/{peer_id}") do ( router.api(MethodGet, "/api/eth/v1/node/peers/{peer_id}") do (
peer_id: PeerID) -> RestApiResponse: peer_id: PeerID) -> RestApiResponse:
let peer = let peer =
@ -237,14 +241,17 @@ proc installNodeApiHandlers*(router: var RestRouter, node: BeaconNode) =
) )
) )
# https://ethereum.github.io/beacon-APIs/#/Node/getNodeVersion
router.api(MethodGet, "/api/eth/v1/node/version") do () -> RestApiResponse: router.api(MethodGet, "/api/eth/v1/node/version") do () -> RestApiResponse:
return RestApiResponse.jsonResponse( return RestApiResponse.jsonResponse(
(version: "Nimbus/" & fullVersionStr) (version: "Nimbus/" & fullVersionStr)
) )
# https://ethereum.github.io/beacon-APIs/#/Node/getSyncingStatus
router.api(MethodGet, "/api/eth/v1/node/syncing") do () -> RestApiResponse: router.api(MethodGet, "/api/eth/v1/node/syncing") do () -> RestApiResponse:
return RestApiResponse.jsonResponse(node.syncManager.getInfo()) return RestApiResponse.jsonResponse(node.syncManager.getInfo())
# https://ethereum.github.io/beacon-APIs/#/Node/getHealth
router.api(MethodGet, "/api/eth/v1/node/health") do () -> RestApiResponse: router.api(MethodGet, "/api/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.

View File

@ -11,7 +11,6 @@ import
../spec/datatypes/[phase0], ../spec/datatypes/[phase0],
../beacon_node_common, ../networking/eth2_network, ../beacon_node_common, ../networking/eth2_network,
../consensus_object_pools/[blockchain_dag, spec_cache, attestation_pool], ../consensus_object_pools/[blockchain_dag, spec_cache, attestation_pool],
../gossip_processing/gossip_validation,
../validators/validator_duties, ../validators/validator_duties,
../spec/[forks, network], ../spec/[forks, network],
./rest_utils ./rest_utils
@ -19,7 +18,7 @@ import
logScope: topics = "rest_validatorapi" logScope: topics = "rest_validatorapi"
proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/eth2.0-APIs/#/Validator/getAttesterDuties # https://ethereum.github.io/beacon-APIs/#/Validator/getAttesterDuties
router.api(MethodPost, "/api/eth/v1/validator/duties/attester/{epoch}") do ( router.api(MethodPost, "/api/eth/v1/validator/duties/attester/{epoch}") do (
epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse: epoch: Epoch, contentBody: Option[ContentBody]) -> RestApiResponse:
let indexList = let indexList =
@ -98,7 +97,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
res res
return RestApiResponse.jsonResponseWRoot(duties, droot) return RestApiResponse.jsonResponseWRoot(duties, droot)
# https://ethereum.github.io/eth2.0-APIs/#/Validator/getProposerDuties # https://ethereum.github.io/beacon-APIs/#/Validator/getProposerDuties
router.api(MethodGet, "/api/eth/v1/validator/duties/proposer/{epoch}") do ( router.api(MethodGet, "/api/eth/v1/validator/duties/proposer/{epoch}") do (
epoch: Epoch) -> RestApiResponse: epoch: Epoch) -> RestApiResponse:
let qepoch = let qepoch =
@ -142,7 +141,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
res res
return RestApiResponse.jsonResponseWRoot(duties, droot) return RestApiResponse.jsonResponseWRoot(duties, droot)
# https://ethereum.github.io/eth2.0-APIs/#/Validator/produceBlock # https://ethereum.github.io/beacon-APIs/#/Validator/produceBlock
router.api(MethodGet, "/api/eth/v1/validator/blocks/{slot}") do ( router.api(MethodGet, "/api/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:
@ -204,6 +203,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
of BeaconBlockFork.Altair: of BeaconBlockFork.Altair:
return RestApiResponse.jsonError(Http400, BlockProduceError) return RestApiResponse.jsonError(Http400, BlockProduceError)
# https://ethereum.github.io/beacon-APIs/#/Validator/produceBlockV2
router.api(MethodGet, "/api/eth/v2/validator/blocks/{slot}") do ( router.api(MethodGet, "/api/eth/v2/validator/blocks/{slot}") do (
slot: Slot, randao_reveal: Option[ValidatorSig], slot: Slot, randao_reveal: Option[ValidatorSig],
graffiti: Option[GraffitiBytes]) -> RestApiResponse: graffiti: Option[GraffitiBytes]) -> RestApiResponse:
@ -271,7 +271,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
(version: "altair", data: message.altairBlock.message) (version: "altair", data: message.altairBlock.message)
) )
# https://ethereum.github.io/eth2.0-APIs/#/Validator/produceAttestationData # https://ethereum.github.io/beacon-APIs/#/Validator/produceAttestationData
router.api(MethodGet, "/api/eth/v1/validator/attestation_data") do ( router.api(MethodGet, "/api/eth/v1/validator/attestation_data") do (
slot: Option[Slot], slot: Option[Slot],
committee_index: Option[CommitteeIndex]) -> RestApiResponse: committee_index: Option[CommitteeIndex]) -> RestApiResponse:
@ -307,7 +307,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
makeAttestationData(epochRef, qhead.atSlot(qslot), qindex) makeAttestationData(epochRef, qhead.atSlot(qslot), qindex)
return RestApiResponse.jsonResponse(adata) return RestApiResponse.jsonResponse(adata)
# https://ethereum.github.io/eth2.0-APIs/#/Validator/getAggregatedAttestation # https://ethereum.github.io/beacon-APIs/#/Validator/getAggregatedAttestation
router.api(MethodGet, "/api/eth/v1/validator/aggregate_attestation") do ( router.api(MethodGet, "/api/eth/v1/validator/aggregate_attestation") do (
attestation_data_root: Option[Eth2Digest], attestation_data_root: Option[Eth2Digest],
slot: Option[Slot]) -> RestApiResponse: slot: Option[Slot]) -> RestApiResponse:
@ -339,7 +339,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
res.get() res.get()
return RestApiResponse.jsonResponse(attestation) return RestApiResponse.jsonResponse(attestation)
# https://ethereum.github.io/eth2.0-APIs/#/Validator/publishAggregateAndProofs # https://ethereum.github.io/beacon-APIs/#/Validator/publishAggregateAndProofs
router.api(MethodPost, "/api/eth/v1/validator/aggregate_and_proofs") do ( router.api(MethodPost, "/api/eth/v1/validator/aggregate_and_proofs") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:
let proofs = let proofs =
@ -352,25 +352,28 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) =
InvalidAggregateAndProofObjectError, InvalidAggregateAndProofObjectError,
$dres.error()) $dres.error())
dres.get() dres.get()
# Since our validation logic supports batch processing, we will submit all
for item in proofs: # aggregated attestations for validation.
let wallTime = node.processor.getCurrentBeaconTime() var pending =
let res = await node.attestationPool.validateAggregate( block:
node.processor.batchCrypto, item, wallTime var res: seq[Future[SendResult]]
) for proof in proofs:
if res.isErr(): res.add(node.sendAggregateAndProof(proof))
return RestApiResponse.jsonError(Http400, res
AggregateAndProofValidationError, await allFutures(pending)
$res.error()) for future in pending:
node.network.broadcast( if future.done():
getAggregateAndProofsTopic(node.dag.forkDigests.phase0), item) let res = future.read()
notice "Aggregated attestation sent", if res.isErr():
attestation = shortLog(item.message.aggregate), return RestApiResponse.jsonError(Http400,
signature = shortLog(item.signature) AggregateAndProofValidationError,
$res.error())
else:
return RestApiResponse.jsonError(Http500,
"Unexpected server failure, while sending aggregate and proof")
return RestApiResponse.jsonMsgResponse(AggregateAndProofValidationSuccess) return RestApiResponse.jsonMsgResponse(AggregateAndProofValidationSuccess)
# https://ethereum.github.io/eth2.0-APIs/#/Validator/prepareBeaconCommitteeSubnet # https://ethereum.github.io/beacon-APIs/#/Validator/prepareBeaconCommitteeSubnet
router.api(MethodPost, router.api(MethodPost,
"/api/eth/v1/validator/beacon_committee_subscriptions") do ( "/api/eth/v1/validator/beacon_committee_subscriptions") do (
contentBody: Option[ContentBody]) -> RestApiResponse: contentBody: Option[ContentBody]) -> RestApiResponse:

View File

@ -16,7 +16,6 @@ import
../beacon_node_common, ../beacon_node_common,
../networking/eth2_network, ../networking/eth2_network,
../validators/validator_duties, ../validators/validator_duties,
../gossip_processing/gossip_validation,
../consensus_object_pools/blockchain_dag, ../consensus_object_pools/blockchain_dag,
../spec/[eth2_merkleization, forks, network], ../spec/[eth2_merkleization, forks, network],
../spec/datatypes/[phase0], ../spec/datatypes/[phase0],
@ -470,7 +469,8 @@ proc installBeaconApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
rpcServer.rpc("post_v1_beacon_pool_attestations") do ( rpcServer.rpc("post_v1_beacon_pool_attestations") do (
attestation: Attestation) -> bool: attestation: Attestation) -> bool:
return await node.sendAttestation(attestation) let res = await node.sendAttestation(attestation)
return res.isOk()
rpcServer.rpc("get_v1_beacon_pool_attester_slashings") do ( rpcServer.rpc("get_v1_beacon_pool_attester_slashings") do (
) -> seq[AttesterSlashing]: ) -> seq[AttesterSlashing]:
@ -485,14 +485,8 @@ proc installBeaconApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
rpcServer.rpc("post_v1_beacon_pool_attester_slashings") do ( rpcServer.rpc("post_v1_beacon_pool_attester_slashings") do (
slashing: AttesterSlashing) -> bool: slashing: AttesterSlashing) -> bool:
if isNil(node.exitPool): let res = node.sendAttesterSlashing(slashing)
raise newException(CatchableError, "Exit pool is not yet available!") return res.isOk()
let validity = node.exitPool[].validateAttesterSlashing(slashing)
if validity.isOk:
node.network.sendAttesterSlashing(slashing)
else:
raise newException(CatchableError, $(validity.error[1]))
return true
rpcServer.rpc("get_v1_beacon_pool_proposer_slashings") do ( rpcServer.rpc("get_v1_beacon_pool_proposer_slashings") do (
) -> seq[ProposerSlashing]: ) -> seq[ProposerSlashing]:
@ -507,14 +501,8 @@ proc installBeaconApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
rpcServer.rpc("post_v1_beacon_pool_proposer_slashings") do ( rpcServer.rpc("post_v1_beacon_pool_proposer_slashings") do (
slashing: ProposerSlashing) -> bool: slashing: ProposerSlashing) -> bool:
if isNil(node.exitPool): let res = node.sendProposerSlashing(slashing)
raise newException(CatchableError, "Exit pool is not yet available!") return res.isOk()
let validity = node.exitPool[].validateProposerSlashing(slashing)
if validity.isOk:
node.network.sendProposerSlashing(slashing)
else:
raise newException(CatchableError, $(validity.error[1]))
return true
rpcServer.rpc("get_v1_beacon_pool_voluntary_exits") do ( rpcServer.rpc("get_v1_beacon_pool_voluntary_exits") do (
) -> seq[SignedVoluntaryExit]: ) -> seq[SignedVoluntaryExit]:
@ -529,11 +517,5 @@ proc installBeaconApiHandlers*(rpcServer: RpcServer, node: BeaconNode) {.
rpcServer.rpc("post_v1_beacon_pool_voluntary_exits") do ( rpcServer.rpc("post_v1_beacon_pool_voluntary_exits") do (
exit: SignedVoluntaryExit) -> bool: exit: SignedVoluntaryExit) -> bool:
if isNil(node.exitPool): let res = node.sendVoluntaryExit(exit)
raise newException(CatchableError, "Exit pool is not yet available!") return res.isOk()
let validity = node.exitPool[].validateVoluntaryExit(exit)
if validity.isOk:
node.network.sendVoluntaryExit(exit)
else:
raise newException(CatchableError, $(validity.error[1]))
return true

View File

@ -59,85 +59,144 @@ type
RestAttestationError | RestAttestationError |
RestGenericError RestGenericError
proc jsonResponseWRoot*(t: typedesc[RestApiResponse], {.push raises: [Defect].}
data: auto,
dependent_root: Eth2Digest): RestApiResponse =
var stream = memoryOutput()
var writer = JsonWriter[RestJson].init(stream)
writer.beginRecord()
writer.writeField("dependent_root", dependent_root)
writer.writeField("data", data)
writer.endRecord()
RestApiResponse.response(stream.getOutput(seq[byte]), Http200,
"application/json")
proc jsonResponse*(t: typedesc[RestApiResponse], proc prepareJsonResponse*(t: typedesc[RestApiResponse], d: auto): seq[byte] =
data: auto): RestApiResponse = let res =
var stream = memoryOutput() block:
var writer = JsonWriter[RestJson].init(stream) var default: seq[byte]
writer.beginRecord() try:
writer.writeField("data", data) var stream = memoryOutput()
writer.endRecord() var writer = JsonWriter[RestJson].init(stream)
RestApiResponse.response(stream.getOutput(seq[byte]), Http200, writer.beginRecord()
"application/json") writer.writeField("data", d)
writer.endRecord()
stream.getOutput(seq[byte])
except SerializationError:
default
except IOError:
default
res
proc jsonResponseWRoot*(t: typedesc[RestApiResponse], data: auto,
dependent_root: Eth2Digest): RestApiResponse =
let res =
block:
var default: seq[byte]
try:
var stream = memoryOutput()
var writer = JsonWriter[RestJson].init(stream)
writer.beginRecord()
writer.writeField("dependent_root", dependent_root)
writer.writeField("data", data)
writer.endRecord()
stream.getOutput(seq[byte])
except SerializationError:
default
except IOError:
default
RestApiResponse.response(res, Http200, "application/json")
proc jsonResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse =
let res =
block:
var default: seq[byte]
try:
var stream = memoryOutput()
var writer = JsonWriter[RestJson].init(stream)
writer.beginRecord()
writer.writeField("data", data)
writer.endRecord()
stream.getOutput(seq[byte])
except SerializationError:
default
except IOError:
default
RestApiResponse.response(res, Http200, "application/json")
proc jsonResponseWMeta*(t: typedesc[RestApiResponse], proc jsonResponseWMeta*(t: typedesc[RestApiResponse],
data: auto, meta: auto): RestApiResponse = data: auto, meta: auto): RestApiResponse =
var stream = memoryOutput() let res =
var writer = JsonWriter[RestJson].init(stream) block:
writer.beginRecord() var default: seq[byte]
writer.writeField("data", data) try:
writer.writeField("meta", meta) var stream = memoryOutput()
writer.endRecord() var writer = JsonWriter[RestJson].init(stream)
RestApiResponse.response(stream.getOutput(seq[byte]), Http200, writer.beginRecord()
"application/json") writer.writeField("data", data)
writer.writeField("meta", meta)
writer.endRecord()
stream.getOutput(seq[byte])
except SerializationError:
default
except IOError:
default
RestApiResponse.response(res, Http200, "application/json")
proc jsonMsgResponse*(t: typedesc[RestApiResponse], proc jsonMsgResponse*(t: typedesc[RestApiResponse],
msg: string = ""): RestApiResponse = msg: string = ""): RestApiResponse =
let data = let data =
block: block:
var default: seq[string] var default: seq[byte]
var stream = memoryOutput() try:
var writer = JsonWriter[RestJson].init(stream) var defstrings: seq[string]
writer.beginRecord() var stream = memoryOutput()
writer.writeField("code", "200") var writer = JsonWriter[RestJson].init(stream)
writer.writeField("message", msg) writer.beginRecord()
writer.writeField("stacktrace", default) writer.writeField("code", "200")
writer.endRecord() writer.writeField("message", msg)
stream.getOutput(seq[byte]) writer.writeField("stacktrace", defstrings)
writer.endRecord()
stream.getOutput(seq[byte])
except SerializationError:
default
except IOError:
default
RestApiResponse.response(data, Http200, "application/json") RestApiResponse.response(data, Http200, "application/json")
proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200, proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200,
msg: string = ""): RestApiResponse = msg: string = ""): RestApiResponse =
let data = let data =
block: block:
var default: seq[string] var default: string
var stream = memoryOutput() try:
var writer = JsonWriter[RestJson].init(stream) var defstrings: seq[string]
writer.beginRecord() var stream = memoryOutput()
writer.writeField("code", Base10.toString(uint64(status.toInt()))) var writer = JsonWriter[RestJson].init(stream)
writer.writeField("message", msg) writer.beginRecord()
writer.writeField("stacktrace", default) writer.writeField("code", Base10.toString(uint64(status.toInt())))
writer.endRecord() writer.writeField("message", msg)
stream.getOutput(string) writer.writeField("stacktrace", defstrings)
writer.endRecord()
stream.getOutput(string)
except SerializationError:
default
except IOError:
default
RestApiResponse.error(status, data, "application/json") RestApiResponse.error(status, data, "application/json")
proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200, proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200,
msg: string = "", stacktrace: string): RestApiResponse = msg: string = "", stacktrace: string): RestApiResponse =
let data = let data =
block: block:
var default: seq[string] var default: string
var stream = memoryOutput() try:
var writer = JsonWriter[RestJson].init(stream) var defstrings: seq[string]
writer.beginRecord() var stream = memoryOutput()
writer.writeField("code", Base10.toString(uint64(status.toInt()))) var writer = JsonWriter[RestJson].init(stream)
writer.writeField("message", msg) writer.beginRecord()
if len(stacktrace) > 0: writer.writeField("code", Base10.toString(uint64(status.toInt())))
writer.writeField("stacktrace", [stacktrace]) writer.writeField("message", msg)
else: if len(stacktrace) > 0:
writer.writeField("stacktrace", default) writer.writeField("stacktrace", [stacktrace])
writer.endRecord() else:
stream.getOutput(string) writer.writeField("stacktrace", defstrings)
writer.endRecord()
stream.getOutput(string)
except SerializationError:
default
except IOError:
default
RestApiResponse.error(status, data, "application/json") RestApiResponse.error(status, data, "application/json")
proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200, proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200,
@ -145,14 +204,20 @@ proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200,
stacktraces: openarray[string]): RestApiResponse = stacktraces: openarray[string]): RestApiResponse =
let data = let data =
block: block:
var stream = memoryOutput() var default: string
var writer = JsonWriter[RestJson].init(stream) try:
writer.beginRecord() var stream = memoryOutput()
writer.writeField("code", Base10.toString(uint64(status.toInt()))) var writer = JsonWriter[RestJson].init(stream)
writer.writeField("message", msg) writer.beginRecord()
writer.writeField("stacktrace", stacktraces) writer.writeField("code", Base10.toString(uint64(status.toInt())))
writer.endRecord() writer.writeField("message", msg)
stream.getOutput(string) writer.writeField("stacktrace", stacktraces)
writer.endRecord()
stream.getOutput(string)
except SerializationError:
default
except IOError:
default
RestApiResponse.error(status, data, "application/json") RestApiResponse.error(status, data, "application/json")
proc jsonErrorList*(t: typedesc[RestApiResponse], proc jsonErrorList*(t: typedesc[RestApiResponse],
@ -160,21 +225,28 @@ proc jsonErrorList*(t: typedesc[RestApiResponse],
msg: string = "", failures: auto): RestApiResponse = msg: string = "", failures: auto): RestApiResponse =
let data = let data =
block: block:
var stream = memoryOutput() var default: string
var writer = JsonWriter[RestJson].init(stream) try:
writer.beginRecord() var stream = memoryOutput()
writer.writeField("code", Base10.toString(uint64(status.toInt()))) var writer = JsonWriter[RestJson].init(stream)
writer.writeField("message", msg) writer.beginRecord()
writer.writeField("failures", failures) writer.writeField("code", Base10.toString(uint64(status.toInt())))
writer.endRecord() writer.writeField("message", msg)
stream.getOutput(string) writer.writeField("failures", failures)
writer.endRecord()
stream.getOutput(string)
except SerializationError:
default
except IOError:
default
RestApiResponse.error(status, data, "application/json") RestApiResponse.error(status, data, "application/json")
template hexOriginal(data: openarray[byte]): string = template hexOriginal(data: openarray[byte]): string =
"0x" & ncrutils.toHex(data, true) "0x" & ncrutils.toHex(data, true)
## uint64 ## uint64
proc writeValue*(w: var JsonWriter[RestJson], value: uint64) = proc writeValue*(w: var JsonWriter[RestJson], value: uint64) {.
raises: [IOError, Defect].} =
writeValue(w, Base10.toString(value)) writeValue(w, Base10.toString(value))
proc readValue*(reader: var JsonReader[RestJson], value: var uint64) {. proc readValue*(reader: var JsonReader[RestJson], value: var uint64) {.
@ -187,7 +259,8 @@ proc readValue*(reader: var JsonReader[RestJson], value: var uint64) {.
reader.raiseUnexpectedValue($res.error()) reader.raiseUnexpectedValue($res.error())
## byte ## byte
proc writeValue*(w: var JsonWriter[RestJson], value: byte) = proc writeValue*(w: var JsonWriter[RestJson], value: byte) {.
raises: [IOError, Defect].} =
var data: array[1, byte] var data: array[1, byte]
data[0] = value data[0] = value
writeValue(w, hexOriginal(data)) writeValue(w, hexOriginal(data))
@ -203,7 +276,8 @@ proc readValue*(reader: var JsonReader[RestJson], value: var byte) {.
"byte value should be a valid hex string") "byte value should be a valid hex string")
## DomainType ## DomainType
proc writeValue*(w: var JsonWriter[RestJson], value: DomainType) = proc writeValue*(w: var JsonWriter[RestJson], value: DomainType) {.
raises: [IOError, Defect].} =
writeValue(w, hexOriginal(uint32(value).toBytesLE())) writeValue(w, hexOriginal(uint32(value).toBytesLE()))
proc readValue*(reader: var JsonReader[RestJson], value: var DomainType) {. proc readValue*(reader: var JsonReader[RestJson], value: var DomainType) {.
@ -353,11 +427,13 @@ proc writeValue*(writer: var JsonWriter[RestJson], value: BitSeq) {.
writeValue(writer, hexOriginal(value.bytes())) writeValue(writer, hexOriginal(value.bytes()))
## BitList ## BitList
proc readValue*(reader: var JsonReader[RestJson], value: var BitList) = proc readValue*(reader: var JsonReader[RestJson], value: var BitList) {.
raises: [IOError, SerializationError, Defect].} =
type T = type(value) type T = type(value)
value = T readValue(reader, BitSeq) value = T readValue(reader, BitSeq)
proc writeValue*(writer: var JsonWriter[RestJson], value: BitList) = proc writeValue*(writer: var JsonWriter[RestJson], value: BitList) {.
raises: [IOError, Defect].} =
writeValue(writer, BitSeq value) writeValue(writer, BitSeq value)
## Eth2Digest ## Eth2Digest
@ -489,13 +565,21 @@ RestJson.useCustomSerialization(phase0.BeaconState.justification_bits):
writer.writeValue "0x" & toHex([value]) writer.writeValue "0x" & toHex([value])
proc encodeBytes*[T: EncodeTypes](value: T, proc encodeBytes*[T: EncodeTypes](value: T,
contentType: string): RestResult[seq[byte]] = contentType: string): RestResult[seq[byte]] =
case contentType case contentType
of "application/json": of "application/json":
var stream = memoryOutput() let data =
var writer = JsonWriter[RestJson].init(stream) block:
writer.writeValue(value) try:
ok(stream.getOutput(seq[byte])) var stream = memoryOutput()
var writer = JsonWriter[RestJson].init(stream)
writer.writeValue(value)
stream.getOutput(seq[byte])
except IOError:
return err("Input/output error")
except SerializationError:
return err("Serialization error")
ok(data)
else: else:
err("Content-Type not supported") err("Content-Type not supported")
@ -503,10 +587,18 @@ proc encodeBytes*[T: EncodeArrays](value: T,
contentType: string): RestResult[seq[byte]] = contentType: string): RestResult[seq[byte]] =
case contentType case contentType
of "application/json": of "application/json":
var stream = memoryOutput() let data =
var writer = JsonWriter[RestJson].init(stream) block:
writer.writeArray(value) try:
ok(stream.getOutput(seq[byte])) var stream = memoryOutput()
var writer = JsonWriter[RestJson].init(stream)
writer.writeArray(value)
stream.getOutput(seq[byte])
except IOError:
return err("Input/output error")
except SerializationError:
return err("Serialization error")
ok(data)
else: else:
err("Content-Type not supported") err("Content-Type not supported")

View File

@ -277,7 +277,7 @@ type
GetDebugChainHeadsResponse* = DataEnclosedObject[seq[RestChainHead]] GetDebugChainHeadsResponse* = DataEnclosedObject[seq[RestChainHead]]
GetDepositContractResponse* = DataEnclosedObject[RestDepositContract] GetDepositContractResponse* = DataEnclosedObject[RestDepositContract]
GetEpochCommitteesResponse* = DataEnclosedObject[RestGenesis] GetEpochCommitteesResponse* = DataEnclosedObject[RestGenesis]
GetForkScheduleResponse* = DataEnclosedObject[Fork] GetForkScheduleResponse* = DataEnclosedObject[seq[Fork]]
GetGenesisResponse* = DataEnclosedObject[RestGenesis] GetGenesisResponse* = DataEnclosedObject[RestGenesis]
GetNetworkIdentityResponse* = DataEnclosedObject[RestNetworkIdentity] GetNetworkIdentityResponse* = DataEnclosedObject[RestNetworkIdentity]
GetPeerCountResponse* = DataMetaEnclosedObject[RestPeerCount] GetPeerCountResponse* = DataMetaEnclosedObject[RestPeerCount]

View File

@ -297,3 +297,11 @@ proc nextForkEpochAtEpoch*(cfg: RuntimeConfig, epoch: Epoch): Epoch =
cfg.ALTAIR_FORK_EPOCH cfg.ALTAIR_FORK_EPOCH
else: else:
FAR_FUTURE_EPOCH FAR_FUTURE_EPOCH
func getForkSchedule*(cfg: RuntimeConfig): array[2, Fork] =
## This procedure returns list of known and/or scheduled forks.
##
## This procedure is used by HTTP REST framework and validator client.
##
## NOTE: Update this procedure when new fork will be scheduled.
[cfg.genesisFork(), cfg.altairFork()]

View File

@ -343,9 +343,10 @@ template firstSuccessTimeout*(vc: ValidatorClientRef, respType: typedesc,
RestBeaconNodeStatus.NotSynced, RestBeaconNodeStatus.NotSynced,
RestBeaconNodeStatus.Uninitalized} RestBeaconNodeStatus.Uninitalized}
let offlineNodes = vc.beaconNodes.filterIt(it.status in offlineMask) let offlineNodes = vc.beaconNodes.filterIt(it.status in offlineMask)
let onlineNodesCount = len(vc.beaconNodes) - len(offlineNodes)
warn "No working beacon nodes available, refreshing nodes status", warn "No working beacon nodes available, refreshing nodes status",
online_nodes = len(onlineNodes), offline_nodes = len(offlineNodes) online_nodes = onlineNodesCount, offline_nodes = len(offlineNodes)
var checkFut = vc.checkNodes(offlineMask) var checkFut = vc.checkNodes(offlineMask)
@ -475,6 +476,30 @@ proc getAttesterDuties*(vc: ValidatorClientRef, epoch: Epoch,
raise newException(ValidatorApiError, "Unable to retrieve attester duties") raise newException(ValidatorApiError, "Unable to retrieve attester duties")
proc getForkSchedule*(vc: ValidatorClientRef): Future[seq[Fork]] {.async.} =
logScope: request = "getForkSchedule"
vc.firstSuccessTimeout(RestResponse[GetForkScheduleResponse], SlotDuration,
getForkSchedule(it)):
if apiResponse.isErr():
debug "Unable to retrieve head state's fork", endpoint = node,
error = apiResponse.error()
RestBeaconNodeStatus.Offline
else:
let response = apiResponse.get()
case response.status
of 200:
debug "Received successfull response", endpoint = node
return response.data.data
of 500:
debug "Received internal error response",
response_code = response.status, endpoint = node
RestBeaconNodeStatus.Offline
else:
debug "Received unexpected error response",
response_code = response.status, endpoint = node
RestBeaconNodeStatus.Offline
raise newException(ValidatorApiError, "Unable to retrieve fork schedule")
proc getHeadStateFork*(vc: ValidatorClientRef): Future[Fork] {.async.} = proc getHeadStateFork*(vc: ValidatorClientRef): Future[Fork] {.async.} =
logScope: request = "getHeadStateFork" logScope: request = "getHeadStateFork"
let stateIdent = StateIdent.init(StateIdentType.Head) let stateIdent = StateIdent.init(StateIdentType.Head)

View File

@ -27,6 +27,7 @@ proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
let signingRoot = let signingRoot =
compute_attestation_root(fork, vc.beaconGenesis.genesis_validators_root, compute_attestation_root(fork, vc.beaconGenesis.genesis_validators_root,
adata) adata)
let attestationRoot = adata.hash_tree_root()
let vindex = validator.index.get() let vindex = validator.index.get()
let notSlashable = vc.attachedValidators.slashingProtection let notSlashable = vc.attachedValidators.slashingProtection
@ -45,6 +46,11 @@ proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
Natural(duty.data.validator_committee_index), Natural(duty.data.validator_committee_index),
fork, vc.beaconGenesis.genesis_validators_root) fork, vc.beaconGenesis.genesis_validators_root)
debug "Sending attestation", attestation = shortLog(attestation),
validator = shortLog(validator), validator_index = vindex,
attestation_root = shortLog(attestationRoot),
delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3))
let res = let res =
try: try:
await vc.submitPoolAttestations(@[attestation]) await vc.submitPoolAttestations(@[attestation])
@ -63,19 +69,17 @@ proc serveAttestation(service: AttestationServiceRef, adata: AttestationData,
return false return false
let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3)) let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3))
let indexInCommittee = duty.data.validator_committee_index
if res: if res:
notice "Attestation published", attestation = shortLog(attestation), notice "Attestation published", attestation = shortLog(attestation),
validator = shortLog(validator), validator = shortLog(validator),
validator_index = vindex, validator_index = vindex,
delay = delay, delay = delay,
indexInCommittee = indexInCommittee attestation_root = attestationRoot
else: else:
warn "Attestation was not accepted by beacon node", warn "Attestation was not accepted by beacon node",
attestation = shortLog(attestation), attestation = shortLog(attestation),
validator = shortLog(validator), validator = shortLog(validator),
validator_index = vindex, delay = delay, validator_index = vindex, delay = delay
indexInCommittee = indexInCommittee
return res return res
proc serveAggregateAndProof*(service: AttestationServiceRef, proc serveAggregateAndProof*(service: AttestationServiceRef,
@ -94,6 +98,13 @@ proc serveAggregateAndProof*(service: AttestationServiceRef,
let aggregationSlot = proof.aggregate.data.slot let aggregationSlot = proof.aggregate.data.slot
let vindex = validator.index.get() let vindex = validator.index.get()
debug "Sending aggregated attestation",
attestation = shortLog(signedProof.message.aggregate),
validator = shortLog(validator), validator_index = vindex,
aggregationSlot = aggregationSlot,
delay = vc.getDelay(seconds((int64(SECONDS_PER_SLOT) div 3) * 2))
let res = let res =
try: try:
await vc.publishAggregateAndProofs(@[signedProof]) await vc.publishAggregateAndProofs(@[signedProof])
@ -277,24 +288,22 @@ proc publishAttestationsAndAggregates(service: AttestationServiceRef,
committee_index: CommitteeIndex, committee_index: CommitteeIndex,
duties: seq[DutyAndProof]) {.async.} = duties: seq[DutyAndProof]) {.async.} =
let vc = service.client let vc = service.client
let aggregateTime =
# chronos.Duration substraction could not return negative value, in such
# case it will return `ZeroDuration`.
vc.beaconClock.durationToNextSlot() - seconds(int64(SECONDS_PER_SLOT) div 3)
# Waiting for blocks to be published before attesting. # Waiting for blocks to be published before attesting.
# TODO (cheatfate): Here should be present timeout.
let startTime = Moment.now() let startTime = Moment.now()
await vc.waitForBlockPublished(slot) try:
let dur = Moment.now() - startTime let timeout = seconds(int64(SECONDS_PER_SLOT) div 3) # 4.seconds in mainnet
debug "Block proposal awaited", slot = slot, duration = dur await vc.waitForBlockPublished(slot).wait(timeout)
let dur = Moment.now() - startTime
debug "Block proposal awaited", slot = slot, duration = dur
except AsyncTimeoutError:
let dur = Moment.now() - startTime
debug "Block was not produced in time", slot = slot, duration = dur
block: block:
let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3)) let delay = vc.getDelay(seconds(int64(SECONDS_PER_SLOT) div 3))
debug "Producing attestations", delay = delay, slot = slot, debug "Producing attestations", delay = delay, slot = slot,
committee_index = committee_index, committee_index = committee_index,
duties_count = len(duties) duties_count = len(duties)
let ad = let ad =
try: try:
await service.produceAndPublishAttestations(slot, committee_index, duties) await service.produceAndPublishAttestations(slot, committee_index, duties)
@ -308,6 +317,10 @@ proc publishAttestationsAndAggregates(service: AttestationServiceRef,
err_name = exc.name, err_msg = exc.msg err_name = exc.name, err_msg = exc.msg
return return
let aggregateTime =
# chronos.Duration substraction could not return negative value, in such
# case it will return `ZeroDuration`.
vc.beaconClock.durationToNextSlot() - seconds(int64(SECONDS_PER_SLOT) div 3)
if aggregateTime != ZeroDuration: if aggregateTime != ZeroDuration:
await sleepAsync(aggregateTime) await sleepAsync(aggregateTime)

View File

@ -53,6 +53,11 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
let signedBlock = let signedBlock =
phase0.SignedBeaconBlock(message: beaconBlock, root: blockRoot, phase0.SignedBeaconBlock(message: beaconBlock, root: blockRoot,
signature: signature) signature: signature)
debug "Sending block", blck = shortLog(signedBlock.message),
signature = shortLog(signature), blockRoot = shortLog(blockRoot),
validator = shortLog(validator)
let res = let res =
try: try:
await vc.publishBlock(signedBlock) await vc.publishBlock(signedBlock)
@ -69,15 +74,15 @@ proc publishBlock(vc: ValidatorClientRef, currentSlot, slot: Slot,
return return
if res: if res:
notice "Block published", blck = shortLog(signedBlock.message), notice "Block published", blck = shortLog(signedBlock.message),
blockRoot = shortLog(blockRoot), validator = shortLog(validator), blockRoot = shortLog(blockRoot), validator = shortLog(validator),
validator_index = validator.index.get() validator_index = validator.index.get()
else: else:
warn "Block was not accepted by beacon node", warn "Block was not accepted by beacon node",
blck = shortLog(signedBlock.message), blck = shortLog(signedBlock.message),
blockRoot = shortLog(blockRoot), blockRoot = shortLog(blockRoot),
validator = shortLog(validator), validator = shortLog(validator),
validator_index = validator.index.get(), validator_index = validator.index.get(),
wall_slot = currentSlot wall_slot = currentSlot
else: else:
warn "Slashing protection activated for block proposal", warn "Slashing protection activated for block proposal",
blck = shortLog(beaconBlock), blockRoot = shortLog(blockRoot), blck = shortLog(beaconBlock), blockRoot = shortLog(blockRoot),

View File

@ -1,23 +1,70 @@
import common, api import std/algorithm
import chronicles import chronicles
import common, api
logScope: service = "fork_service" logScope: service = "fork_service"
proc pollForFork(vc: ValidatorClientRef) {.async.} = proc validateForkSchedule(forks: openarray[Fork]): bool {.raises: [Defect].} =
let fork = # Check if `forks` list is linked list.
try: var current_version = forks[0].current_version
await vc.getHeadStateFork() for index, item in forks.pairs():
except ValidatorApiError as exc: if index > 0:
error "Unable to retrieve head state's fork", reason = exc.msg if item.previous_version != current_version:
return return false
except CatchableError as exc: else:
error "Unexpected error occured while getting fork information", if item.previous_version != item.current_version:
err_name = exc.name, err_msg = exc.msg return false
return current_version = item.current_version
true
if vc.fork.isNone() or vc.fork.get() != fork: proc getCurrentFork(forks: openarray[Fork],
vc.fork = some(fork) epoch: Epoch): Result[Fork, cstring] {.raises: [Defect].} =
notice "Fork update success", fork = fork proc cmp(x, y: Fork): int {.closure.} =
if uint64(x.epoch) == uint64(y.epoch): return 0
if uint64(x.epoch) < uint64(y.epoch): return -1
return 1
let sortedForks = sorted(forks, cmp)
if len(sortedForks) == 0:
return err("Empty fork schedule")
if not(validateForkSchedule(sortedForks)):
return err("Invalid fork schedule")
var res: Fork
for item in sortedForks:
res = item
if item.epoch > epoch:
break
ok(res)
proc pollForFork(vc: ValidatorClientRef) {.async.} =
let sres = vc.getCurrentSlot()
if sres.isSome():
let
currentSlot = sres.get()
currentEpoch = currentSlot.epoch()
let forks =
try:
await vc.getForkSchedule()
except ValidatorApiError as exc:
error "Unable to retrieve fork schedule", reason = exc.msg
return
except CatchableError as exc:
error "Unexpected error occured while getting fork information",
err_name = exc.name, err_msg = exc.msg
return
let fork =
block:
let res = getCurrentFork(forks, currentEpoch)
if res.isErr():
error "Invalid fork schedule received", reason = res.error()
return
res.get()
if vc.fork.isNone() or (vc.fork.get() != fork):
vc.fork = some(fork)
notice "Fork update succeeded", fork = fork
proc waitForNextEpoch(service: ForkServiceRef) {.async.} = proc waitForNextEpoch(service: ForkServiceRef) {.async.} =
let vc = service.client let vc = service.client

View File

@ -54,6 +54,10 @@ declarePublicGauge(attached_validator_balance_total,
logScope: topics = "beacval" logScope: topics = "beacval"
type
SendResult* = Result[void, cstring]
SendBlockResult* = Result[bool, cstring]
proc findValidator(validators: auto, pubKey: ValidatorPubKey): proc findValidator(validators: auto, pubKey: ValidatorPubKey):
Option[ValidatorIndex] = Option[ValidatorIndex] =
let idx = validators.findIt(it.pubKey == pubKey) let idx = validators.findIt(it.pubKey == pubKey)
@ -170,7 +174,7 @@ proc sendAttestation*(
return case ok return case ok
of ValidationResult.Accept: of ValidationResult.Accept:
node.network.sendAttestation(subnet_id, attestation) node.network.broadcastAttestation(subnet_id, attestation)
beacon_attestations_sent.inc() beacon_attestations_sent.inc()
true true
else: else:
@ -179,22 +183,6 @@ proc sendAttestation*(
result = $ok result = $ok
false false
proc sendAttestation*(node: BeaconNode, attestation: Attestation): Future[bool] =
# For the validator API, which doesn't supply the subnet id.
let attestationBlck =
node.dag.getRef(attestation.data.beacon_block_root)
if attestationBlck.isNil:
debug "Attempt to send attestation without corresponding block"
return
let
epochRef = node.dag.getEpochRef(
attestationBlck, attestation.data.target.epoch)
subnet_id = compute_subnet_for_attestation(
get_committee_count_per_slot(epochRef), attestation.data.slot,
attestation.data.index.CommitteeIndex)
node.sendAttestation(attestation, subnet_id, checkSignature = true)
proc createAndSendAttestation(node: BeaconNode, proc createAndSendAttestation(node: BeaconNode,
fork: Fork, fork: Fork,
genesis_validators_root: Eth2Digest, genesis_validators_root: Eth2Digest,
@ -228,9 +216,9 @@ proc createAndSendAttestation(node: BeaconNode,
else: else:
($(wallTime - deadline), toFloatSeconds(wallTime - deadline)) ($(wallTime - deadline), toFloatSeconds(wallTime - deadline))
notice "Attestation sent", attestation = shortLog(attestation), notice "Attestation sent",
validator = shortLog(validator), delay = delayStr, attestation = shortLog(attestation), validator = shortLog(validator),
indexInCommittee = indexInCommittee delay = delayStr
beacon_attestation_sent_delay.observe(delaySecs) beacon_attestation_sent_delay.observe(delaySecs)
except CatchableError as exc: except CatchableError as exc:
@ -731,3 +719,105 @@ proc handleValidatorDuties*(node: BeaconNode, lastSlot, slot: Slot) {.async.} =
let finalizedEpochRef = node.dag.getFinalizedEpochRef() let finalizedEpochRef = node.dag.getFinalizedEpochRef()
discard node.eth1Monitor.trackFinalizedState( discard node.eth1Monitor.trackFinalizedState(
finalizedEpochRef.eth1_data, finalizedEpochRef.eth1_deposit_index) finalizedEpochRef.eth1_data, finalizedEpochRef.eth1_deposit_index)
proc sendAttestation*(node: BeaconNode,
attestation: Attestation): Future[SendResult] {.async.} =
# REST/JSON-RPC API helper procedure.
let attestationBlock =
block:
let res = node.dag.getRef(attestation.data.beacon_block_root)
if isNil(res):
debug "Attempt to send attestation without corresponding block",
attestation = shortLog(attestation)
return SendResult.err(
"Attempt to send attestation without corresponding block")
res
let
epochRef = node.dag.getEpochRef(
attestationBlock, attestation.data.target.epoch)
subnet_id = compute_subnet_for_attestation(
get_committee_count_per_slot(epochRef), attestation.data.slot,
attestation.data.index.CommitteeIndex)
res = await node.sendAttestation(attestation, subnet_id,
checkSignature = true)
if not(res):
return SendResult.err("Attestation failed validation")
return SendResult.ok()
proc sendAggregateAndProof*(node: BeaconNode,
proof: SignedAggregateAndProof): Future[SendResult] {.
async.} =
# REST/JSON-RPC API helper procedure.
let res = await node.processor.aggregateValidator(proof)
case res
of ValidationResult.Accept:
node.network.broadcastAggregateAndProof(proof)
return SendResult.ok()
else:
notice "Aggregate and proof failed validation",
proof = shortLog(proof.message.aggregate), result = $res
return SendResult.err("Aggregate and proof failed validation")
proc sendVoluntaryExit*(node: BeaconNode,
exit: SignedVoluntaryExit): SendResult =
# REST/JSON-RPC API helper procedure.
let res = node.processor[].voluntaryExitValidator(exit)
case res
of ValidationResult.Accept:
node.network.broadcastVoluntaryExit(exit)
ok()
else:
notice "Voluntary exit request failed validation",
exit = shortLog(exit.message), result = $res
err("Voluntary exit request failed validation")
proc sendAttesterSlashing*(node: BeaconNode,
slashing: AttesterSlashing): SendResult =
# REST/JSON-RPC API helper procedure.
let res = node.processor[].attesterSlashingValidator(slashing)
case res
of ValidationResult.Accept:
node.network.broadcastAttesterSlashing(slashing)
ok()
else:
notice "Attester slashing request failed validation",
slashing = shortLog(slashing), result = $res
err("Attester slashing request failed validation")
proc sendProposerSlashing*(node: BeaconNode,
slashing: ProposerSlashing): SendResult =
# REST/JSON-RPC API helper procedure.
let res = node.processor[].proposerSlashingValidator(slashing)
case res
of ValidationResult.Accept:
node.network.broadcastProposerSlashing(slashing)
else:
notice "Proposer slashing request failed validation",
slashing = shortLog(slashing), result = $res
return SendResult.err("Proposer slashing request failed validation")
proc sendBeaconBlock*(node: BeaconNode, forked: ForkedSignedBeaconBlock
): Future[SendBlockResult] {.async.} =
# REST/JSON-RPC API helper procedure.
let head = node.dag.head
if not(node.isSynced(head)):
return SendBlockResult.err("Beacon node is currently syncing")
if head.slot >= forked.slot():
node.network.broadcastBeaconBlock(forked)
return SendBlockResult.ok(false)
let res =
case forked.kind
of BeaconBlockFork.Phase0:
await node.proposeSignedBlock(head, AttachedValidator(),
forked.phase0Block)
of BeaconBlockFork.Altair:
# TODO altair-transition
# await node.proposeSignedBlock(head, AttachedValidator(),
# forked.altairBlock)
head
if res == head:
# `res == head` means failure, in such case we need to broadcast block
# manually because of the specification.
node.network.broadcastBeaconBlock(forked)
return SendBlockResult.ok(false)
return SendBlockResult.ok(true)