SSZ encoded responses for REST API calls. (#2851)

This commit is contained in:
Eugene Kabanov 2021-09-16 16:32:32 +03:00 committed by GitHub
parent 6638476b5f
commit 7ab1856c04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 208 additions and 34 deletions

View File

@ -653,10 +653,23 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr():
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
res.get()
let contentType =
block:
let res = preferredContentType("application/octet-stream",
"application/json")
if res.isErr():
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
res.get()
return
case bdata.data.kind
of BeaconBlockFork.Phase0:
RestApiResponse.jsonResponse(bdata.data.phase0Block)
case contentType
of "application/octet-stream":
RestApiResponse.sszResponse(bdata.data.phase0Block)
of "application/json":
RestApiResponse.jsonResponse(bdata.data.phase0Block)
else:
RestApiResponse.jsonError(Http500, InvalidAcceptError)
of BeaconBlockFork.Altair:
RestApiResponse.jsonError(Http404, BlockNotFoundError)
@ -672,7 +685,25 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) =
if res.isErr():
return RestApiResponse.jsonError(Http404, BlockNotFoundError)
res.get()
return RestApiResponse.jsonResponsePlain(bdata.data.asSigned())
let contentType =
block:
let res = preferredContentType("application/octet-stream",
"application/json")
if res.isErr():
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
res.get()
return
case contentType
of "application/octet-stream":
case bdata.data.kind
of BeaconBlockFork.Phase0:
RestApiResponse.sszResponse(bdata.data.phase0Block)
of BeaconBlockFork.Altair:
RestApiResponse.sszResponse(bdata.data.altairBlock)
of "application/json":
RestApiResponse.jsonResponsePlain(bdata.data.asSigned())
else:
RestApiResponse.jsonError(Http500, InvalidAcceptError)
# https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockRoot
router.api(MethodGet, "/api/eth/v1/beacon/blocks/{block_id}/root") do (

View File

@ -21,13 +21,27 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http404, StateNotFoundError,
$bres.error())
bres.get()
let contentType =
block:
let res = preferredContentType("application/octet-stream",
"application/json")
if res.isErr():
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
res.get()
node.withStateForBlockSlot(bslot):
case stateData.data.beaconStateFork
of BeaconStateFork.forkPhase0:
return RestApiResponse.jsonResponse(stateData.data.hbsPhase0.data)
of BeaconStateFork.forkAltair:
return RestApiResponse.jsonError(Http404, StateNotFoundError)
return RestApiResponse.jsonError(Http500, InternalServerError)
return
case stateData.data.beaconStateFork
of BeaconStateFork.forkPhase0:
case contentType
of "application/octet-stream":
RestApiResponse.sszResponse(stateData.data.hbsPhase0.data)
of "application/json":
RestApiResponse.jsonResponse(stateData.data.hbsPhase0.data)
else:
RestApiResponse.jsonError(Http500, InvalidAcceptError)
of BeaconStateFork.forkAltair:
RestApiResponse.jsonError(Http404, StateNotFoundError)
return RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Debug/getStateV2
router.api(MethodGet,
@ -43,17 +57,28 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
return RestApiResponse.jsonError(Http404, StateNotFoundError,
$bres.error())
bres.get()
let contentType =
block:
let res = preferredContentType("application/octet-stream",
"application/json")
if res.isErr():
return RestApiResponse.jsonError(Http406, ContentNotAcceptableError)
res.get()
node.withStateForBlockSlot(bslot):
case stateData.data.beaconStateFork
of BeaconStateFork.forkPhase0:
return RestApiResponse.jsonResponse(
(version: "phase0", data: stateData.data.hbsPhase0.data)
)
of BeaconStateFork.forkAltair:
return RestApiResponse.jsonResponse(
(version: "altair", data: stateData.data.hbsAltair.data)
)
return RestApiResponse.jsonError(Http500, InternalServerError)
return
case contentType
of "application/json":
RestApiResponse.jsonResponsePlain(
ForkedBeaconState.init(stateData.data))
of "application/octet-stream":
case stateData.data.beaconStateFork
of BeaconStateFork.forkPhase0:
RestApiResponse.sszResponse(stateData.data.hbsPhase0.data)
of BeaconStateFork.forkAltair:
RestApiResponse.sszResponse(stateData.data.hbsAltair.data)
else:
RestApiResponse.jsonError(Http500, InvalidAcceptError)
return RestApiResponse.jsonError(Http404, StateNotFoundError)
# https://ethereum.github.io/beacon-APIs/#/Debug/getDebugChainHeads
router.api(MethodGet,
@ -69,11 +94,11 @@ proc installDebugApiHandlers*(router: var RestRouter, node: BeaconNode) =
)
router.redirect(
MethodGet,
"/eth/v1/debug/beacon/heads",
"/api/eth/v1/debug/beacon/heads"
"/eth/v2/debug/beacon/states/{state_id}",
"/api/eth/v2/debug/beacon/states/{state_id}"
)
router.redirect(
MethodGet,
"/eth/v2/debug/beacon/heads",
"/api/eth/v2/debug/beacon/heads"
"/eth/v1/debug/beacon/heads",
"/api/eth/v1/debug/beacon/heads"
)

View File

@ -135,6 +135,10 @@ const
"Peer not found"
InvalidLogLevelValueError* =
"Invalid log level value error"
ContentNotAcceptableError* =
"Could not find out accepted content type"
InvalidAcceptError* =
"Incorrect accept response type"
InternalServerError* =
"Internal server error"
NoImplementationError* =

View File

@ -4,16 +4,14 @@
# * 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],
stew/[results, base10, byteutils, endians2],
presto/common,
libp2p/peerid,
serialization,
json_serialization, json_serialization/std/[options, net],
nimcrypto/utils as ncrutils,
".."/forks, ".."/datatypes/[phase0, altair, merge],
"."/rest_types
import std/typetraits
import stew/[results, base10, byteutils, endians2], presto/common,
libp2p/peerid, serialization,
json_serialization, json_serialization/std/[options, net],
nimcrypto/utils as ncrutils
import ".."/forks, ".."/datatypes/[phase0, altair, merge],
".."/".."/ssz/ssz_serialization,
"."/rest_types
export
results, peerid, common, serialization, json_serialization, options, net,
@ -30,6 +28,10 @@ const
# Size of `ValidatorSig` hexadecimal value (without 0x)
RootHashSize = sizeof(Eth2Digest) * 2
# Size of `xxx_root` hexadecimal value (without 0x)
Phase0Version =
[byte('p'), byte('h'), byte('a'), byte('s'), byte('e'), byte('0')]
AltairVersion =
[byte('a'), byte('l'), byte('t'), byte('a'), byte('i'), byte('r')]
type
RestGenericError* = object
@ -262,6 +264,21 @@ proc jsonErrorList*(t: typedesc[RestApiResponse],
default
RestApiResponse.error(status, data, "application/json")
proc sszResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse =
let res =
block:
var default: seq[byte]
try:
var stream = memoryOutput()
var writer = SszWriter.init(stream)
writer.writeValue(data)
stream.getOutput(seq[byte])
except SerializationError:
default
except IOError:
default
RestApiResponse.response(res, Http200, "application/octet-stream")
template hexOriginal(data: openarray[byte]): string =
"0x" & ncrutils.toHex(data, true)
@ -692,6 +709,85 @@ proc writeValue*(writer: var JsonWriter[RestJson],
writer.writeField("data", value.altairBlock)
writer.endRecord()
# ForkedBeaconState
proc readValue*(reader: var JsonReader[RestJson],
value: var ForkedBeaconState) {.
raises: [IOError, SerializationError, Defect].} =
var
version: Option[BeaconStateFork]
data: Option[JsonString]
for fieldName in readObjectFields(reader):
case fieldName
of "version":
if version.isSome():
reader.raiseUnexpectedField("Multiple version fields found",
"ForkedBeaconState")
let vres = reader.readValue(string)
case vres
of "phase0":
version = some(BeaconStateFork.forkPhase0)
of "altair":
version = some(BeaconStateFork.forkAltair)
else:
reader.raiseUnexpectedValue("Incorrect version field value")
of "data":
if data.isSome():
reader.raiseUnexpectedField("Multiple data fields found",
"ForkedBeaconState")
data = some(reader.readValue(JsonString))
else:
reader.raiseUnexpectedField(fieldName, "ForkedBeaconState")
if version.isNone():
reader.raiseUnexpectedValue("Field version is missing")
if data.isNone():
reader.raiseUnexpectedValue("Field data is missing")
case version.get():
of BeaconStateFork.forkPhase0:
let res =
try:
some(RestJson.decode(string(data.get()), phase0.BeaconState,
requireAllFields = true))
except SerializationError:
none[phase0.BeaconState]()
if res.isNone():
reader.raiseUnexpectedValue("Incorrect phase0 beacon state format")
value = ForkedBeaconState.init(res.get())
of BeaconStateFork.forkAltair:
let res =
try:
some(RestJson.decode(string(data.get()), altair.BeaconState,
requireAllFields = true))
except SerializationError:
none[altair.BeaconState]()
if res.isNone():
reader.raiseUnexpectedValue("Incorrect altair beacon state format")
value = ForkedBeaconState.init(res.get())
proc writeValue*(writer: var JsonWriter[RestJson], value: ForkedBeaconState) {.
raises: [IOError, Defect].} =
writer.beginRecord()
case value.beaconStateFork
of BeaconStateFork.forkPhase0:
writer.writeField("version", "phase0")
writer.writeField("data", value.bsPhase0)
of BeaconStateFork.forkAltair:
writer.writeField("version", "altair")
writer.writeField("data", value.bsAltair)
writer.endRecord()
template toSszType*(v: BeaconBlockFork): auto =
case v
of BeaconBlockFork.Phase0: Phase0Version
of BeaconBlockFork.Altair: AltairVersion
template toSszType*(v: BeaconStateFork): auto =
case v
of BeaconStateFork.forkPhase0: Phase0Version
of BeaconStateFork.forkAltair: AltairVersion
proc parseRoot(value: string): Result[Eth2Digest, cstring] =
try:
ok(Eth2Digest(data: hexToByteArray[32](value)))

View File

@ -27,6 +27,11 @@ type
of forkPhase0: hbsPhase0*: phase0.HashedBeaconState
of forkAltair: hbsAltair*: altair.HashedBeaconState
ForkedBeaconState* = object
case beaconStateFork*: BeaconStateFork
of forkPhase0: bsPhase0*: phase0.BeaconState
of forkAltair: bsAltair*: altair.BeaconState
BeaconBlockFork* {.pure.} = enum
Phase0
Altair
@ -69,6 +74,19 @@ template init*(T: type ForkedSignedBeaconBlock, blck: phase0.SignedBeaconBlock):
template init*(T: type ForkedSignedBeaconBlock, blck: altair.SignedBeaconBlock): T =
T(kind: BeaconBlockFork.Altair, altairBlock: blck)
template init*(T: type ForkedBeaconState, state: phase0.BeaconState): T =
T(beaconStateFork: BeaconStateFork.forkPhase0, bsPhase0: state)
template init*(T: type ForkedBeaconState, state: altair.BeaconState): T =
T(beaconStateFork: BeaconStateFork.forkAltair, bsAltair: state)
template init*(T: type ForkedBeaconState, state: ForkedHashedBeaconState): T =
case state.beaconStateFork
of BeaconStateFork.forkPhase0:
T(beaconStateFork: BeaconStateFork.forkPhase0,
bsPhase0: state.hbsPhase0.data)
of BeaconStateFork.forkAltair:
T(beaconStateFork: BeaconStateFork.forkAltair,
bsAltair: state.hbsAltair.data)
template init*(T: type ForkedSignedBeaconBlock, forked: ForkedBeaconBlock,
blockRoot: Eth2Digest, signature: ValidatorSig): T =
case forked.kind

2
vendor/nim-chronos vendored

@ -1 +1 @@
Subproject commit 05c91418be18c1f0507d477182952c1e2f8cb11d
Subproject commit 5034f0a5a6772576c78d6ef45dacfda6a6bc60c4

2
vendor/nim-presto vendored

@ -1 +1 @@
Subproject commit e96c6ded2a0f3f3985b638a64d225390e8259801
Subproject commit 89d321bc160976978028ddbd13fd7d46c7192034