Skip to content

Commit

Permalink
REST API: add ssz encoding for publishBlock (#4154)
Browse files Browse the repository at this point in the history
  • Loading branch information
cheatfate authored Sep 29, 2022
1 parent af9ec57 commit a845450
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 9 deletions.
18 changes: 11 additions & 7 deletions beacon_chain/rpc/rest_beacon_api.nim
Original file line number Diff line number Diff line change
Expand Up @@ -769,14 +769,18 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
block:
if contentBody.isNone():
return RestApiResponse.jsonError(Http400, EmptyRequestBodyError)
let body = contentBody.get()
let res = decodeBody(RestPublishedSignedBeaconBlock, body)
if res.isErr():
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$res.error())
var forked = ForkedSignedBeaconBlock(res.get())
let
body = contentBody.get()
version = request.headers.getString("eth-consensus-version")
var
restBlock = decodeBody(RestPublishedSignedBeaconBlock, body,
version).valueOr:
return RestApiResponse.jsonError(Http400, InvalidBlockObjectError,
$error)
forked = ForkedSignedBeaconBlock(restBlock)

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

withBlck(forked):
Expand Down
101 changes: 99 additions & 2 deletions beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# * 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.

import std/typetraits
import std/[typetraits, strutils]
import stew/[assign2, results, base10, byteutils], presto/common,
libp2p/peerid, serialization, json_serialization,
json_serialization/std/[options, net, sets],
Expand Down Expand Up @@ -85,7 +85,9 @@ type
SignedBlindedBeaconBlock |
SignedValidatorRegistrationV1 |
SignedVoluntaryExit |
Web3SignerRequest |
Web3SignerRequest

EncodeOctetTypes* =
altair.SignedBeaconBlock |
bellatrix.SignedBeaconBlock |
phase0.SignedBeaconBlock
Expand Down Expand Up @@ -2273,6 +2275,58 @@ proc parseRoot(value: string): Result[Eth2Digest, cstring] =
except ValueError:
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],
body: ContentBody): Result[T, cstring] =
if body.contentType != ApplicationJsonMediaType:
Expand Down Expand Up @@ -2329,6 +2383,33 @@ proc encodeBytes*[T: EncodeArrays](value: T,
else:
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](
t: typedesc[T],
value: openArray[byte],
Expand Down Expand Up @@ -2698,3 +2779,19 @@ proc decodeString*(t: typedesc[ValidatorFilter],
})
else:
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")
12 changes: 12 additions & 0 deletions beacon_chain/spec/eth2_apis/rest_beacon_calls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ proc publishBlock*(body: bellatrix.SignedBeaconBlock): RestPlainResponse {.
meth: MethodPost.}
## 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 {.
rest, endpoint: "/eth/v2/beacon/blocks/{block_id}",
accept: preferSSZ,
Expand Down

0 comments on commit a845450

Please sign in to comment.