REST API: add ssz encoding for publishBlock (#4154)

This commit is contained in:
Eugene Kabanov 2022-09-30 00:00:53 +03:00 committed by GitHub
parent af9ec577d0
commit a845450283
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 122 additions and 9 deletions

View File

@ -769,14 +769,18 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
block: block:
if contentBody.isNone(): if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let body = contentBody.get() let
let res = decodeBody(RestPublishedSignedBeaconBlock, body) body = contentBody.get()
if res.isErr(): version = request.headers.getString("eth-consensus-version")
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError, var
$res.error()) restBlock = decodeBody(RestPublishedSignedBeaconBlock, body,
var forked = ForkedSignedBeaconBlock(res.get()) version).valueOr:
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$error)
forked = ForkedSignedBeaconBlock(restBlock)
if forked.kind != node.dag.cfg.blockForkAtEpoch( if forked.kind != node.dag.cfg.blockForkAtEpoch(
getForkedBlockField(forked, slot).epoch): getForkedBlockField(forked, slot).epoch):
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError) return RestApiResponse.jsonError(Http400, InvalidBlockObjectError)
withBlck(forked): withBlck(forked):

View File

@ -5,7 +5,7 @@
# * 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 std/typetraits import std/[typetraits, strutils]
import stew/[assign2, results, base10, byteutils], presto/common, import stew/[assign2, results, base10, byteutils], presto/common,
libp2p/peerid, serialization, json_serialization, libp2p/peerid, serialization, json_serialization,
json_serialization/std/[options, net, sets], json_serialization/std/[options, net, sets],
@ -85,7 +85,9 @@ type
SignedBlindedBeaconBlock | SignedBlindedBeaconBlock |
SignedValidatorRegistrationV1 | SignedValidatorRegistrationV1 |
SignedVoluntaryExit | SignedVoluntaryExit |
Web3SignerRequest | Web3SignerRequest
EncodeOctetTypes* =
altair.SignedBeaconBlock | altair.SignedBeaconBlock |
bellatrix.SignedBeaconBlock | bellatrix.SignedBeaconBlock |
phase0.SignedBeaconBlock phase0.SignedBeaconBlock
@ -2273,6 +2275,58 @@ proc parseRoot(value: string): Result[Eth2Digest, cstring] =
except ValueError: except ValueError:
err("Unable to decode root value") err("Unable to decode root value")
proc decodeBody*(
t: typedesc[RestPublishedSignedBeaconBlock],
body: ContentBody,
version: string
): Result[RestPublishedSignedBeaconBlock, cstring] =
if body.contentType == ApplicationJsonMediaType:
let data =
try:
RestJson.decode(body.data, RestPublishedSignedBeaconBlock,
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(data)
elif body.contentType == OctetStreamMediaType:
let blockFork = ? BeaconBlockFork.decodeString(version)
case blockFork
of BeaconBlockFork.Phase0:
let blck =
try:
SSZ.decode(body.data, phase0.SignedBeaconBlock)
except SerializationError:
return err("Unable to deserialize data")
except CatchableError:
return err("Unexpected deserialization error")
ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck)))
of BeaconBlockFork.Altair:
let blck =
try:
SSZ.decode(body.data, altair.SignedBeaconBlock)
except SerializationError:
return err("Unable to deserialize data")
except CatchableError:
return err("Unexpected deserialization error")
ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck)))
of BeaconBlockFork.Bellatrix:
let blck =
try:
SSZ.decode(body.data, bellatrix.SignedBeaconBlock)
except SerializationError:
return err("Unable to deserialize data")
except CatchableError:
return err("Unexpected deserialization error")
ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck)))
else:
return err("Unsupported or invalid content media type")
proc decodeBody*[T](t: typedesc[T], proc decodeBody*[T](t: typedesc[T],
body: ContentBody): Result[T, cstring] = body: ContentBody): Result[T, cstring] =
if body.contentType != ApplicationJsonMediaType: if body.contentType != ApplicationJsonMediaType:
@ -2329,6 +2383,33 @@ proc encodeBytes*[T: EncodeArrays](value: T,
else: else:
err("Content-Type not supported") err("Content-Type not supported")
proc encodeBytes*[T: EncodeOctetTypes](
value: T,
contentType: string
): RestResult[seq[byte]] =
case contentType
of "application/json":
let data =
try:
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)
of "application/octet-stream":
let data =
try:
SSZ.encode(value)
except CatchableError:
return err("Serialization error")
ok(data)
else:
err("Content-Type not supported")
proc decodeBytes*[T: DecodeTypes]( proc decodeBytes*[T: DecodeTypes](
t: typedesc[T], t: typedesc[T],
value: openArray[byte], value: openArray[byte],
@ -2698,3 +2779,19 @@ proc decodeString*(t: typedesc[ValidatorFilter],
}) })
else: else:
err("Incorrect validator state identifier value") err("Incorrect validator state identifier value")
proc decodeString*(t: typedesc[BeaconBlockFork],
value: string): Result[BeaconBlockFork, cstring] =
case toLowerAscii(value)
of "phase0": ok(BeaconBlockFork.Phase0)
of "altair": ok(BeaconBlockFork.Altair)
of "bellatrix": ok(BeaconBlockFork.Bellatrix)
else: err("Unsupported or invalid beacon block fork version")
proc decodeString*(t: typedesc[BeaconStateFork],
value: string): Result[BeaconStateFork, cstring] =
case toLowerAscii(value)
of "phase0": ok(BeaconStateFork.Phase0)
of "altair": ok(BeaconStateFork.Altair)
of "bellatrix": ok(BeaconStateFork.Bellatrix)
else: err("Unsupported or invalid beacon state fork version")

View File

@ -116,6 +116,18 @@ proc publishBlock*(body: bellatrix.SignedBeaconBlock): RestPlainResponse {.
meth: MethodPost.} meth: MethodPost.}
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock ## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
proc publishSszBlock*(
client: RestClientRef,
blck: ForkySignedBeaconBlock
): Future[RestPlainResponse] {.async.} =
## https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock
let
consensus = typeof(blck).toFork.toString()
resp = await client.publishBlock(
blck, restContentType = $OctetStreamMediaType,
extraHeaders = @[("eth-consensus-version", consensus)])
return resp
proc getBlockV2Plain*(block_id: BlockIdent): RestPlainResponse {. proc getBlockV2Plain*(block_id: BlockIdent): RestPlainResponse {.
rest, endpoint: "/eth/v2/beacon/blocks/{block_id}", rest, endpoint: "/eth/v2/beacon/blocks/{block_id}",
accept: preferSSZ, accept: preferSSZ,