REST API: add ssz encoding for publishBlock (#4154)
This commit is contained in:
parent
af9ec577d0
commit
a845450283
|
@ -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):
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue