SSZ encoded responses for REST API calls. (#2851)
This commit is contained in:
parent
6638476b5f
commit
7ab1856c04
|
@ -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 (
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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* =
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 05c91418be18c1f0507d477182952c1e2f8cb11d
|
||||
Subproject commit 5034f0a5a6772576c78d6ef45dacfda6a6bc60c4
|
|
@ -1 +1 @@
|
|||
Subproject commit e96c6ded2a0f3f3985b638a64d225390e8259801
|
||||
Subproject commit 89d321bc160976978028ddbd13fd7d46c7192034
|
Loading…
Reference in New Issue