Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion beacon_chain/nimbus_beacon_node.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2254,7 +2254,7 @@ func connectedPeersCount(node: BeaconNode): int =

proc installRestHandlers(restServer: RestServerRef, node: BeaconNode) =
restServer.router.installBeaconApiHandlers(node)
restServer.router.installBuilderApiHandlers(node)
restServer.router.installBuilderApiHandlers()
restServer.router.installConfigApiHandlers(node)
restServer.router.installDebugApiHandlers(node)
restServer.router.installEventApiHandlers(node)
Expand Down
248 changes: 9 additions & 239 deletions beacon_chain/rpc/rest_beacon_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -188,11 +188,9 @@ proc handleDataSidecarRequest*[
](node, mediaType, block_id, indices, DataSidecarsType.maxLen.uint64)

proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4881.md
router.api2(MethodGet, "/eth/v1/beacon/deposit_snapshot") do (
) -> RestApiResponse:
return RestApiResponse.jsonError(Http404,
NoFinalizedSnapshotAvailableError)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/#/Beacon/getGenesis
router.api2(MethodGet, "/eth/v1/beacon/genesis") do () -> RestApiResponse:
Expand Down Expand Up @@ -1008,56 +1006,9 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.dag.isFinalized(bid)
)

# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
router.api(MethodPost, "/eth/v1/beacon/blocks") do (
contentBody: Option[ContentBody]) -> RestApiResponse:
let res =
block:
if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let
body = contentBody.get()
currentEpochFork =
node.dag.cfg.consensusForkAtEpoch(node.currentSlot().epoch())
rawVersion = request.headers.getString("eth-consensus-version")

# The V1 endpoint doesn't require the version to be specified but the
# only fork which works is the current gossip fork. Either it can use
# and broadcast a block in that fork or that broadcast will not prove
# useful anyway, so allow it to fail at the decoding stage.
version =
if rawVersion == "":
currentEpochFork.toString
else:
rawVersion

let restBlock = decodeBody(
RestPublishedSignedBlockContents, body, version).valueOr:
return RestApiResponse.jsonError(error)

withForkyBlck(restBlock):
if restBlock.kind != node.dag.cfg.consensusForkAtEpoch(
forkyBlck.message.slot.epoch):
doAssert strictVerification notin node.dag.updateFlags
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError)

when consensusFork in [ConsensusFork.Deneb, ConsensusFork.Electra]:
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.some(
forkyBlck.create_blob_sidecars(kzg_proofs, blobs)),
Opt.none(seq[fulu.DataColumnSidecar]), checkValidator = true)
else:
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.none(seq[BlobSidecar]),
Opt.none(seq[fulu.DataColumnSidecar]), checkValidator = true)

if res.isErr():
return RestApiResponse.jsonError(
Http503, BeaconNodeInSyncError, $res.error)
if res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError)

RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
router.api(MethodPost, "/eth/v2/beacon/blocks") do (
Expand Down Expand Up @@ -1169,96 +1120,9 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
else:
respondSszOrJson(toSignedBlindedBeaconBlock(forkyBlck), consensusFork)

# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
# https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/beacon/blocks/blinded_blocks.yaml
router.api(MethodPost, "/eth/v1/beacon/blinded_blocks") do (
contentBody: Option[ContentBody]) -> RestApiResponse:
## Instructs the beacon node to use the components of the
## `SignedBlindedBeaconBlock` to construct and publish a
## `SignedBeaconBlock` by swapping out the transactions_root for the
## corresponding full list of transactions. The beacon node should
## broadcast a newly constructed `SignedBeaconBlock` to the beacon network,
## to be included in the beacon chain. The beacon node is not required to
## validate the signed `BeaconBlock`, and a successful response (20X) only
## indicates that the broadcast has been successful.
if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)

let
currentEpochFork =
node.dag.cfg.consensusForkAtEpoch(node.currentSlot().epoch())
rawVersion = request.headers.getString("eth-consensus-version")
body = contentBody.get()

# The V1 endpoint doesn't require the version to be specified but the
# only fork which works is the current gossip fork. Either it can use
# and broadcast a block in that fork or that broadcast will not prove
# useful anyway, so allow it to fail at the decoding stage.
version =
if rawVersion == "":
currentEpochFork.toString
else:
rawVersion

if (body.contentType == OctetStreamMediaType) and
(currentEpochFork.toString != version):
return RestApiResponse.jsonError(Http400, BlockIncorrectFork)

withConsensusFork(currentEpochFork):
when consensusFork == ConsensusFork.Gloas:
debugGloasComment ""
return RestApiResponse.jsonError(
Http400, $consensusFork & " builder API unsupported")
elif consensusFork >= ConsensusFork.Electra:
let
restBlock = decodeBodyJsonOrSsz(
consensusFork.SignedBlindedBeaconBlock, body).valueOr:
return RestApiResponse.jsonError(error)
payloadBuilderClient = node.getPayloadBuilderClient(
restBlock.message.proposer_index).valueOr:
return RestApiResponse.jsonError(
Http400, "Unable to initialize payload builder client: " & $error)
res = await node.unblindAndRouteBlockMEV(
payloadBuilderClient, restBlock)

if res.isErr():
return RestApiResponse.jsonError(
Http500, InternalServerError, $res.error)
if res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError)

return RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
elif consensusFork >= ConsensusFork.Bellatrix:
return RestApiResponse.jsonError(
Http400, $consensusFork & " builder API unsupported")
else:
# Pre-Bellatrix, this endpoint will accept a `SignedBeaconBlock`.
#
# This is mostly the same as /eth/v1/beacon/blocks for phase 0 and
# altair.
var
restBlock = decodeBody(
RestPublishedSignedBeaconBlock, body, version).valueOr:
return RestApiResponse.jsonError(error)
forked = ForkedSignedBeaconBlock(restBlock)

if forked.kind != node.dag.cfg.consensusForkAtEpoch(
getForkedBlockField(forked, slot).epoch):
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError)

let res = withBlck(forked):
forkyBlck.root = hash_tree_root(forkyBlck.message)
await node.router.routeSignedBeaconBlock(
forkyBlck, Opt.none(seq[BlobSidecar]),
Opt.none(seq[fulu.DataColumnSidecar]), checkValidator = true)

if res.isErr():
return RestApiResponse.jsonError(
Http503, BeaconNodeInSyncError, $res.error)
elif res.get().isNone():
return RestApiResponse.jsonError(Http202, BlockValidationError)

RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlockV2
router.api(MethodPost, "/eth/v2/beacon/blinded_blocks") do (
Expand Down Expand Up @@ -1410,24 +1274,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.dag.isFinalized(bid)
)

# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations
router.api2(MethodGet,
"/eth/v1/beacon/blocks/{block_id}/attestations") do (
"/eth/v1/beacon/blocks/{block_id}/attestations") do (
block_id: BlockIdent) -> RestApiResponse:
let
blockIdent = block_id.valueOr:
return RestApiResponse.jsonError(Http400, InvalidBlockIdValueError,
$error)
bdata = node.getForkedBlock(blockIdent).valueOr:
return RestApiResponse.jsonError(Http404, BlockNotFoundError)

withBlck(bdata):
let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot)
RestApiResponse.jsonResponseFinalized(
forkyBlck.message.body.attestations.asSeq(),
node.getBlockOptimistic(bdata),
node.dag.isFinalized(bid)
)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getBlockAttestationsV2
router.api2(MethodGet,
Expand All @@ -1448,33 +1298,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
node.dag.isFinalized(bid),
consensusFork, node.hasRestAllowedOrigin)

# https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations
router.api2(MethodGet, "/eth/v1/beacon/pool/attestations") do (
slot: Option[Slot],
committee_index: Option[CommitteeIndex]) -> RestApiResponse:
let vindex =
if committee_index.isSome():
let rindex = committee_index.get()
if rindex.isErr():
return RestApiResponse.jsonError(Http400,
InvalidCommitteeIndexValueError,
$rindex.error)
Opt.some(rindex.get())
else:
Opt.none(CommitteeIndex)
let vslot =
if slot.isSome():
let rslot = slot.get()
if rslot.isErr():
return RestApiResponse.jsonError(Http400, InvalidSlotValueError,
$rslot.error)
Opt.some(rslot.get())
else:
Opt.none(Slot)
var res: seq[phase0.Attestation]
for item in node.attestationPool[].attestations(vslot, vindex):
res.add(item)
RestApiResponse.jsonResponse(res)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getPoolAttestationsV2
router.api2(MethodGet, "/eth/v2/beacon/pool/attestations") do (
Expand Down Expand Up @@ -1515,48 +1342,9 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
toSeq(node.attestationPool[].electraAttestations(vslot, vindex)),
consensusFork, node.hasRestAllowedOrigin)

# https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations
router.api2(MethodPost, "/eth/v1/beacon/pool/attestations") do (
contentBody: Option[ContentBody]) -> RestApiResponse:
let attestations =
block:
if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let dres = decodeBody(seq[phase0.Attestation], contentBody.get())
if dres.isErr():
return RestApiResponse.jsonError(Http400,
InvalidAttestationObjectError,
$dres.error)
dres.get()

# Since our validation logic supports batch processing, we will submit all
# attestations for validation.
let pending =
mapIt(attestations, node.router.routeAttestation(it))
let failures =
block:
var res: seq[RestIndexedErrorMessageItem]
await allFutures(pending)
for index, future in pending:
if future.completed():
let fres = future.value()
if fres.isErr():
let failure = RestIndexedErrorMessageItem(index: 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.error()
let failure = RestIndexedErrorMessageItem(index: index,
message: $exc.msg)
res.add(failure)
res

if len(failures) > 0:
RestApiResponse.jsonErrorList(Http400, AttestationValidationError,
failures)
else:
RestApiResponse.jsonMsgResponse(AttestationValidationSuccess)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/submitPoolAttestationsV2
router.api2(MethodPost, "/eth/v2/beacon/pool/attestations") do (
Expand Down Expand Up @@ -1614,31 +1402,13 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
else:
RestApiResponse.jsonMsgResponse(AttestationValidationSuccess)

# https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttesterSlashings
router.api2(MethodGet, "/eth/v1/beacon/pool/attester_slashings") do (
) -> RestApiResponse:
RestApiResponse.jsonResponse(
toSeq(node.validatorChangePool.phase0_attester_slashings))
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttesterSlashings
router.api(MethodPost, "/eth/v1/beacon/pool/attester_slashings") do (
contentBody: Option[ContentBody]) -> RestApiResponse:
let slashing =
block:
if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let dres = decodeBody(phase0.AttesterSlashing, contentBody.get())
if dres.isErr():
return RestApiResponse.jsonError(Http400,
InvalidAttesterSlashingObjectError,
$dres.error)
dres.get()
let res = await node.router.routeAttesterSlashing(slashing)
if res.isErr():
return RestApiResponse.jsonError(Http400,
AttesterSlashingValidationError,
$res.error)
RestApiResponse.jsonMsgResponse(AttesterSlashingValidationSuccess)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)

# https://ethereum.github.io/beacon-APIs/?urls.primaryName=dev#/Beacon/getPoolAttesterSlashingsV2
router.api2(MethodGet, "/eth/v2/beacon/pool/attester_slashings") do (
Expand Down
33 changes: 3 additions & 30 deletions beacon_chain/rpc/rest_builder_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,14 @@

{.push raises: [], gcsafe.}

import
./rest_utils,
./state_ttl_cache,
../beacon_node
import ./rest_utils

export rest_utils

logScope: topics = "rest_builderapi"

proc installBuilderApiHandlers*(router: var RestRouter, node: BeaconNode) =
# https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.4.0#/Builder/getNextWithdrawals
# https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/builder/states/expected_withdrawals.yaml
proc installBuilderApiHandlers*(router: var RestRouter) =
router.api2(MethodGet,
"/eth/v1/builder/states/{state_id}/expected_withdrawals") do (
state_id: StateIdent) -> RestApiResponse:
let
sid = state_id.valueOr:
return RestApiResponse.jsonError(Http400, InvalidStateIdValueError,
$error)
bslot = node.getBlockSlotId(sid).valueOr:
if sid.kind == StateQueryKind.Root:
# TODO (cheatfate): Its impossible to retrieve state by `state_root`
# in current version of database.
return RestApiResponse.jsonError(Http500, NoImplementationError)
return RestApiResponse.jsonError(Http404, StateNotFoundError,
$error)

node.withStateForBlockSlotId(bslot):
withState(state):
when consensusFork >= ConsensusFork.Capella:
return RestApiResponse.jsonResponseWOpt(
get_expected_withdrawals(forkyState.data),
node.getStateOptimistic(state))
else:
return RestApiResponse.jsonError(
Http400, "The specified state is not a capella state")

RestApiResponse.jsonError(Http404, StateNotFoundError)
RestApiResponse.jsonError(Http410, DeprecatedRemovalElectra)
7 changes: 7 additions & 0 deletions beacon_chain/rpc/rest_constants.nim
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,13 @@ const
DeprecatedRemovalValidatorBlocksV2* =
"v2/validator/blocks/{slot} was deprecated, removed, and replaced " &
"by produceBlockV3: https://github.com/ethereum/beacon-APIs/pull/466"
DeprecatedRemovalElectra* =
"v1/beacon/deposit_snapshot, /eth/v1/beacon/blocks/{block_id}/attestations," &
"v1/beacon/pool/attestations, v1/beacon/pool/attester_slashings, " &
"v1/validator/aggregate_attestation, v1/validator/aggregate_and_proofs, " &
"v1/beacon/blocks, v1/beacon/blinded_blocks, and " &
"v1/builder/states/{state_id}/expected_withdrawals have been removed: " &
"https://github.com/ethereum/beacon-APIs/pull/549"
BlockIncorrectFork* =
"Block has incorrect fork"
ValidatorNotActive* =
Expand Down
Loading
Loading