Add support for POST /eth/v2/beacon/blocks (#5214)
* Add support for POST /eth/v2/beacon/blocks * More descriptive errors * Address review feedback * Return 500 (not 400) for a missing implementation case
This commit is contained in:
parent
9ceed40090
commit
9efd26c2e9
|
@ -894,8 +894,68 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
|
|||
|
||||
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
|
||||
|
||||
# TODO
|
||||
# Add POST /eth/v2/beacon/blocks
|
||||
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
|
||||
router.api(MethodPost, "/eth/v2/beacon/blocks") do (
|
||||
contentBody: Option[ContentBody]) -> RestApiResponse:
|
||||
let res =
|
||||
block:
|
||||
if contentBody.isNone():
|
||||
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
|
||||
if request.headers.getString("broadcast_validation") != "gossip":
|
||||
# TODO (henridf): support 'consensus' and 'consensus_and_equivocation'
|
||||
# broadcast_validation
|
||||
return RestApiResponse.jsonError(
|
||||
Http500, "gossip broadcast_validation only supported")
|
||||
let
|
||||
body = contentBody.get()
|
||||
version = request.headers.getString("eth-consensus-version")
|
||||
var
|
||||
restBlock = decodeBodyJsonOrSsz(RestPublishedSignedBlockContents,
|
||||
body, version).valueOr:
|
||||
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
|
||||
$error)
|
||||
forked = ForkedSignedBeaconBlock.init(restBlock)
|
||||
|
||||
# TODO (henridf): handle broadcast_validation flag
|
||||
if restBlock.kind != node.dag.cfg.consensusForkAtEpoch(
|
||||
getForkedBlockField(forked, slot).epoch):
|
||||
doAssert strictVerification notin node.dag.updateFlags
|
||||
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError)
|
||||
|
||||
case restBlock.kind
|
||||
of ConsensusFork.Phase0:
|
||||
var blck = restBlock.phase0Data
|
||||
blck.root = hash_tree_root(blck.message)
|
||||
await node.router.routeSignedBeaconBlock(blck,
|
||||
Opt.none(SignedBlobSidecars))
|
||||
of ConsensusFork.Altair:
|
||||
var blck = restBlock.altairData
|
||||
blck.root = hash_tree_root(blck.message)
|
||||
await node.router.routeSignedBeaconBlock(blck,
|
||||
Opt.none(SignedBlobSidecars))
|
||||
of ConsensusFork.Bellatrix:
|
||||
var blck = restBlock.bellatrixData
|
||||
blck.root = hash_tree_root(blck.message)
|
||||
await node.router.routeSignedBeaconBlock(blck,
|
||||
Opt.none(SignedBlobSidecars))
|
||||
of ConsensusFork.Capella:
|
||||
var blck = restBlock.capellaData
|
||||
blck.root = hash_tree_root(blck.message)
|
||||
await node.router.routeSignedBeaconBlock(blck,
|
||||
Opt.none(SignedBlobSidecars))
|
||||
of ConsensusFork.Deneb:
|
||||
var blck = restBlock.denebData.signed_block
|
||||
blck.root = hash_tree_root(blck.message)
|
||||
await node.router.routeSignedBeaconBlock(
|
||||
blck, Opt.some(asSeq restBlock.denebData.signed_blob_sidecars))
|
||||
|
||||
if res.isErr():
|
||||
return RestApiResponse.jsonError(
|
||||
Http503, BeaconNodeInSyncError, $res.error())
|
||||
if res.get().isNone():
|
||||
return RestApiResponse.jsonError(Http202, BlockValidationError)
|
||||
|
||||
return RestApiResponse.jsonMsgResponse(BlockValidationSuccess)
|
||||
|
||||
# https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlindedBlock
|
||||
# https://github.com/ethereum/beacon-APIs/blob/v2.4.0/apis/beacon/blocks/blinded_blocks.yaml
|
||||
|
|
|
@ -3027,7 +3027,7 @@ proc decodeBody*(
|
|||
t: typedesc[RestPublishedSignedBlockContents],
|
||||
body: ContentBody,
|
||||
version: string
|
||||
): Result[RestPublishedSignedBlockContents, cstring] =
|
||||
): Result[RestPublishedSignedBlockContents, string] =
|
||||
if body.contentType == ApplicationJsonMediaType:
|
||||
let data =
|
||||
try:
|
||||
|
@ -3043,7 +3043,9 @@ proc decodeBody*(
|
|||
return err("Unexpected deserialization error")
|
||||
ok(data)
|
||||
elif body.contentType == OctetStreamMediaType:
|
||||
let consensusFork = ? ConsensusFork.decodeString(version)
|
||||
let consensusFork =
|
||||
decodeEthConsensusVersion(version).valueOr:
|
||||
return err("Invalid or Unsupported consensus version")
|
||||
case consensusFork
|
||||
of ConsensusFork.Phase0:
|
||||
let blck =
|
||||
|
@ -3116,6 +3118,98 @@ proc decodeBody*[T](t: typedesc[T],
|
|||
return err("Unexpected deserialization error")
|
||||
ok(data)
|
||||
|
||||
proc decodeBodyJsonOrSsz*(
|
||||
t: typedesc[RestPublishedSignedBlockContents],
|
||||
body: ContentBody,
|
||||
version: string
|
||||
): Result[RestPublishedSignedBlockContents, string] =
|
||||
if body.contentType == OctetStreamMediaType:
|
||||
decodeBody(RestPublishedSignedBlockContents, body, version)
|
||||
elif body.contentType == ApplicationJsonMediaType:
|
||||
let consensusFork =
|
||||
decodeEthConsensusVersion(version).valueOr:
|
||||
return err("Invalid or Unsupported consensus version")
|
||||
case consensusFork
|
||||
of ConsensusFork.Phase0:
|
||||
let blck =
|
||||
try:
|
||||
RestJson.decode(body.data, phase0.SignedBeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as exc:
|
||||
debug "Failed to deserialize REST JSON data",
|
||||
err = exc.formatMsg("<data>"),
|
||||
data = string.fromBytes(body.data)
|
||||
return err("Unable to deserialize JSON for fork " &
|
||||
version & ": " & exc.formatMsg("<data>"))
|
||||
except CatchableError as exc:
|
||||
return err("Unexpected JSON deserialization error: " & exc.msg)
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Phase0, phase0Data: blck))
|
||||
of ConsensusFork.Altair:
|
||||
let blck =
|
||||
try:
|
||||
RestJson.decode(body.data, altair.SignedBeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as exc:
|
||||
debug "Failed to deserialize REST JSON data",
|
||||
err = exc.formatMsg("<data>"),
|
||||
data = string.fromBytes(body.data)
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Altair, altairData: blck))
|
||||
of ConsensusFork.Bellatrix:
|
||||
let blck =
|
||||
try:
|
||||
RestJson.decode(body.data, bellatrix.SignedBeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as exc:
|
||||
debug "Failed to deserialize REST JSON data",
|
||||
err = exc.formatMsg("<data>"),
|
||||
data = string.fromBytes(body.data)
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Bellatrix, bellatrixData: blck))
|
||||
of ConsensusFork.Capella:
|
||||
let blck =
|
||||
try:
|
||||
RestJson.decode(body.data, capella.SignedBeaconBlock,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as exc:
|
||||
debug "Failed to deserialize REST JSON data",
|
||||
err = exc.formatMsg("<data>"),
|
||||
data = string.fromBytes(body.data)
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Capella, capellaData: blck))
|
||||
of ConsensusFork.Deneb:
|
||||
let blckContents =
|
||||
try:
|
||||
RestJson.decode(body.data, DenebSignedBlockContents,
|
||||
requireAllFields = true,
|
||||
allowUnknownFields = true)
|
||||
except SerializationError as exc:
|
||||
debug "Failed to deserialize REST JSON data",
|
||||
err = exc.formatMsg("<data>"),
|
||||
data = string.fromBytes(body.data)
|
||||
return err("Unable to deserialize data")
|
||||
except CatchableError:
|
||||
return err("Unexpected deserialization error")
|
||||
ok(RestPublishedSignedBlockContents(
|
||||
kind: ConsensusFork.Deneb, denebData: blckContents))
|
||||
else:
|
||||
return err("Unsupported or invalid content media type")
|
||||
|
||||
|
||||
proc decodeBodyJsonOrSsz*[T](t: typedesc[T],
|
||||
body: ContentBody): Result[T, cstring] =
|
||||
if body.contentType == ApplicationJsonMediaType:
|
||||
|
|
|
@ -184,6 +184,45 @@ proc publishSszBlock*(
|
|||
extraHeaders = @[("eth-consensus-version", consensus)])
|
||||
return resp
|
||||
|
||||
proc publishBlockV2Plain(body: phase0.SignedBeaconBlock): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v2/beacon/blocks",
|
||||
meth: MethodPost.}
|
||||
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
|
||||
|
||||
proc publishBlockV2Plain(body: altair.SignedBeaconBlock): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v2/beacon/blocks",
|
||||
meth: MethodPost.}
|
||||
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
|
||||
|
||||
proc publishBlockV2Plain(body: bellatrix.SignedBeaconBlock): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v2/beacon/blocks",
|
||||
meth: MethodPost.}
|
||||
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
|
||||
|
||||
proc publishBlockV2Plain(body: capella.SignedBeaconBlock): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v2/beacon/blocks",
|
||||
meth: MethodPost.}
|
||||
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
|
||||
|
||||
proc publishBlockV2Plain(body: DenebSignedBlockContents): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v2/beacon/blocks",
|
||||
meth: MethodPost.}
|
||||
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlockV2
|
||||
|
||||
proc publishBlockV2*(
|
||||
client: RestClientRef,
|
||||
blck: phase0.SignedBeaconBlock | altair.SignedBeaconBlock |
|
||||
bellatrix.SignedBeaconBlock | capella.SignedBeaconBlock |
|
||||
deneb.SignedBeaconBlock
|
||||
): Future[RestPlainResponse] {.async} =
|
||||
let
|
||||
consensus = typeof(blck).toFork.toString()
|
||||
resp = await client.publishBlockV2Plain(
|
||||
blck, extraHeaders = @[
|
||||
("eth-consensus-version", consensus),
|
||||
("broadcast_validation", "gossip")])
|
||||
return resp
|
||||
|
||||
proc publishBlindedBlock*(body: phase0.SignedBeaconBlock): RestPlainResponse {.
|
||||
rest, endpoint: "/eth/v1/beacon/blinded_blocks",
|
||||
meth: MethodPost.}
|
||||
|
|
Loading…
Reference in New Issue