mirror of
https://github.com/status-im/nimbus-eth2.git
synced 2025-02-10 21:46:32 +00:00
Some upstream repos still need fixes, but this gets us close enough that style hints can be enabled by default. In general, "canonical" spellings are preferred even if they violate nep-1 - this applies in particular to spec-related stuff like `genesis_validators_root` which appears throughout the codebase.
2280 lines
80 KiB
Nim
2280 lines
80 KiB
Nim
# Copyright (c) 2018-2022 Status Research & Development GmbH
|
|
# Licensed and distributed under either of
|
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
|
# * 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 stew/[assign2, results, base10, byteutils], presto/common,
|
|
libp2p/peerid, serialization, json_serialization,
|
|
json_serialization/std/[options, net, sets]
|
|
import ".."/[eth2_ssz_serialization, forks, keystore],
|
|
".."/datatypes/[phase0, altair, bellatrix],
|
|
".."/../validators/slashing_protection_common,
|
|
"."/[rest_types, rest_keymanager_types]
|
|
import nimcrypto/utils as ncrutils
|
|
|
|
export
|
|
eth2_ssz_serialization, results, peerid, common, serialization,
|
|
json_serialization, options, net, sets, rest_types, slashing_protection_common
|
|
|
|
from web3/ethtypes import BlockHash
|
|
export ethtypes.BlockHash
|
|
|
|
Json.createFlavor RestJson
|
|
|
|
const
|
|
DecimalSet = {'0' .. '9'}
|
|
# Base10 (decimal) set of chars
|
|
ValidatorKeySize = RawPubKeySize * 2
|
|
# Size of `ValidatorPubKey` hexadecimal value (without 0x)
|
|
ValidatorSigSize = RawSigSize * 2
|
|
# 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
|
|
code*: uint64
|
|
message*: string
|
|
stacktraces*: Option[seq[string]]
|
|
|
|
RestAttestationError* = object
|
|
code*: uint64
|
|
message*: string
|
|
failures*: seq[RestFailureItem]
|
|
|
|
EncodeTypes* =
|
|
AttesterSlashing |
|
|
ProposerSlashing |
|
|
phase0.SignedBeaconBlock |
|
|
altair.SignedBeaconBlock |
|
|
bellatrix.SignedBeaconBlock |
|
|
SignedVoluntaryExit |
|
|
Web3SignerRequest |
|
|
KeystoresAndSlashingProtection |
|
|
DeleteKeystoresBody |
|
|
ImportRemoteKeystoresBody
|
|
|
|
EncodeArrays* =
|
|
seq[ValidatorIndex] |
|
|
seq[Attestation] |
|
|
seq[SignedAggregateAndProof] |
|
|
seq[RestCommitteeSubscription] |
|
|
seq[RestSyncCommitteeSubscription] |
|
|
seq[RestSyncCommitteeMessage] |
|
|
seq[RestSignedContributionAndProof] |
|
|
seq[RemoteKeystoreInfo]
|
|
|
|
DecodeTypes* =
|
|
DataEnclosedObject |
|
|
DataMetaEnclosedObject |
|
|
DataRootEnclosedObject |
|
|
GetBlockV2Response |
|
|
GetKeystoresResponse |
|
|
GetRemoteKeystoresResponse |
|
|
GetStateV2Response |
|
|
GetStateForkResponse |
|
|
ProduceBlockResponseV2 |
|
|
RestAttestationError |
|
|
RestValidator |
|
|
RestGenericError |
|
|
Web3SignerErrorResponse |
|
|
Web3SignerKeysResponse |
|
|
Web3SignerSignatureResponse |
|
|
Web3SignerStatusResponse
|
|
|
|
# These types may be extended with additional fields in the future.
|
|
# Locally unknown fields are silently ignored when decoding them.
|
|
ExtensibleDecodeTypes* =
|
|
GetSpecResponse |
|
|
GetSpecVCResponse
|
|
|
|
SszDecodeTypes* =
|
|
GetPhase0StateSszResponse |
|
|
GetPhase0BlockSszResponse
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
proc prepareJsonResponse*(t: typedesc[RestApiResponse], d: auto): seq[byte] =
|
|
let res =
|
|
block:
|
|
var default: seq[byte]
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("data", d)
|
|
writer.endRecord()
|
|
stream.getOutput(seq[byte])
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
res
|
|
|
|
proc prepareJsonStringResponse*(t: typedesc[RestApiResponse], d: auto): string =
|
|
let res =
|
|
block:
|
|
var default: string
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.writeValue(d)
|
|
stream.getOutput(string)
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
res
|
|
|
|
proc jsonResponseWRoot*(t: typedesc[RestApiResponse], data: auto,
|
|
dependent_root: Eth2Digest): RestApiResponse =
|
|
let res =
|
|
block:
|
|
var default: seq[byte]
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("dependent_root", dependent_root)
|
|
writer.writeField("data", data)
|
|
writer.endRecord()
|
|
stream.getOutput(seq[byte])
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.response(res, Http200, "application/json")
|
|
|
|
proc jsonResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse =
|
|
let res =
|
|
block:
|
|
var default: seq[byte]
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("data", data)
|
|
writer.endRecord()
|
|
stream.getOutput(seq[byte])
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.response(res, Http200, "application/json")
|
|
|
|
proc jsonResponsePlain*(t: typedesc[RestApiResponse],
|
|
data: auto): RestApiResponse =
|
|
let res =
|
|
block:
|
|
var default: seq[byte]
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.writeValue(data)
|
|
stream.getOutput(seq[byte])
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.response(res, Http200, "application/json")
|
|
|
|
proc jsonResponseWMeta*(t: typedesc[RestApiResponse],
|
|
data: auto, meta: auto): RestApiResponse =
|
|
let res =
|
|
block:
|
|
var default: seq[byte]
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("data", data)
|
|
writer.writeField("meta", meta)
|
|
writer.endRecord()
|
|
stream.getOutput(seq[byte])
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.response(res, Http200, "application/json")
|
|
|
|
proc jsonMsgResponse*(t: typedesc[RestApiResponse],
|
|
msg: string = ""): RestApiResponse =
|
|
let data =
|
|
block:
|
|
var default: seq[byte]
|
|
try:
|
|
var defstrings: seq[string]
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("code", "200")
|
|
writer.writeField("message", msg)
|
|
writer.writeField("stacktraces", defstrings)
|
|
writer.endRecord()
|
|
stream.getOutput(seq[byte])
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.response(data, Http200, "application/json")
|
|
|
|
proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200,
|
|
msg: string = ""): RestApiResponse =
|
|
let data =
|
|
block:
|
|
var default: string
|
|
try:
|
|
var defstrings: seq[string]
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("code", Base10.toString(uint64(status.toInt())))
|
|
writer.writeField("message", msg)
|
|
writer.writeField("stacktraces", defstrings)
|
|
writer.endRecord()
|
|
stream.getOutput(string)
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.error(status, data, "application/json")
|
|
|
|
proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200,
|
|
msg: string = "", stacktrace: string): RestApiResponse =
|
|
let data =
|
|
block:
|
|
var default: string
|
|
try:
|
|
var defstrings: seq[string]
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("code", Base10.toString(uint64(status.toInt())))
|
|
writer.writeField("message", msg)
|
|
if len(stacktrace) > 0:
|
|
writer.writeField("stacktraces", [stacktrace])
|
|
else:
|
|
writer.writeField("stacktraces", defstrings)
|
|
writer.endRecord()
|
|
stream.getOutput(string)
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.error(status, data, "application/json")
|
|
|
|
proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200,
|
|
msg: string = "",
|
|
stacktraces: openArray[string]): RestApiResponse =
|
|
let data =
|
|
block:
|
|
var default: string
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("code", Base10.toString(uint64(status.toInt())))
|
|
writer.writeField("message", msg)
|
|
writer.writeField("stacktraces", stacktraces)
|
|
writer.endRecord()
|
|
stream.getOutput(string)
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
default
|
|
RestApiResponse.error(status, data, "application/json")
|
|
|
|
proc jsonErrorList*(t: typedesc[RestApiResponse],
|
|
status: HttpCode = Http200,
|
|
msg: string = "", failures: auto): RestApiResponse =
|
|
let data =
|
|
block:
|
|
var default: string
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.beginRecord()
|
|
writer.writeField("code", Base10.toString(uint64(status.toInt())))
|
|
writer.writeField("message", msg)
|
|
writer.writeField("failures", failures)
|
|
writer.endRecord()
|
|
stream.getOutput(string)
|
|
except SerializationError:
|
|
default
|
|
except IOError:
|
|
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 =
|
|
to0xHex(data)
|
|
|
|
proc decodeJsonString*[T](t: typedesc[T],
|
|
data: JsonString,
|
|
requireAllFields = true): Result[T, cstring] =
|
|
try:
|
|
ok(RestJson.decode(string(data), T, requireAllFields = requireAllFields))
|
|
except SerializationError:
|
|
err("Unable to deserialize data")
|
|
|
|
## uint64
|
|
proc writeValue*(w: var JsonWriter[RestJson], value: uint64) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(w, Base10.toString(value))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var uint64) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
let res = Base10.decode(uint64, svalue)
|
|
if res.isOk():
|
|
value = res.get()
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error() & ": " & svalue)
|
|
|
|
proc writeValue*(w: var JsonWriter[RestJson], value: uint8) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(w, Base10.toString(value))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var uint8) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
let res = Base10.decode(uint8, svalue)
|
|
if res.isOk():
|
|
value = res.get()
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error() & ": " & svalue)
|
|
|
|
proc writeValue*(w: var JsonWriter[RestJson], value: JustificationBits) {.
|
|
raises: [IOError, Defect].} =
|
|
w.writeValue hexOriginal([uint8(value)])
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var JustificationBits) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let hex = reader.readValue(string)
|
|
try:
|
|
value = JustificationBits(hexToByteArray(hex, 1)[0])
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader,
|
|
"The `justification_bits` value must be a hex string")
|
|
|
|
## UInt256
|
|
proc writeValue*(w: var JsonWriter[RestJson], value: UInt256) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(w, toString(value))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var UInt256) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
try:
|
|
value = parse(svalue, UInt256, 10)
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader,
|
|
"UInt256 value should be a valid decimal string")
|
|
|
|
## Slot
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: Slot) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, Base10.toString(uint64(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var Slot) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
let res = Base10.decode(uint64, svalue)
|
|
if res.isOk():
|
|
value = Slot(res.get())
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## Epoch
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: Epoch) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, Base10.toString(uint64(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var Epoch) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
let res = Base10.decode(uint64, svalue)
|
|
if res.isOk():
|
|
value = Epoch(res.get())
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## ValidatorIndex
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: ValidatorIndex)
|
|
{.raises: [IOError, Defect].} =
|
|
writeValue(writer, Base10.toString(uint64(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var ValidatorIndex)
|
|
{.raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
let res = Base10.decode(uint64, svalue)
|
|
if res.isOk():
|
|
let v = res.get()
|
|
if v < VALIDATOR_REGISTRY_LIMIT:
|
|
value = ValidatorIndex(v)
|
|
else:
|
|
reader.raiseUnexpectedValue(
|
|
"Validator index is bigger then VALIDATOR_REGISTRY_LIMIT")
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: IndexInSyncCommittee)
|
|
{.raises: [IOError, Defect].} =
|
|
writeValue(writer, Base10.toString(distinctBase(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var IndexInSyncCommittee)
|
|
{.raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
let res = Base10.decode(uint64, svalue)
|
|
if res.isOk():
|
|
let v = res.get()
|
|
if v < SYNC_COMMITTEE_SIZE:
|
|
value = IndexInSyncCommittee(v)
|
|
else:
|
|
reader.raiseUnexpectedValue(
|
|
"Index in committee is bigger than SYNC_COMMITTEE_SIZE")
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## RestValidatorIndex
|
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
|
value: RestValidatorIndex) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, Base10.toString(uint64(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var RestValidatorIndex) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let svalue = reader.readValue(string)
|
|
let res = Base10.decode(uint64, svalue)
|
|
if res.isOk():
|
|
let v = res.get()
|
|
value = RestValidatorIndex(v)
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## CommitteeIndex
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: CommitteeIndex) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, value.asUInt64)
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var CommitteeIndex) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var v: uint64
|
|
reader.readValue(v)
|
|
|
|
let res = CommitteeIndex.init(v)
|
|
if res.isOk():
|
|
value = res.get()
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## ValidatorSig
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: ValidatorSig) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(toRaw(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var ValidatorSig) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let hexValue = reader.readValue(string)
|
|
let res = ValidatorSig.fromHex(hexValue)
|
|
if res.isOk():
|
|
value = res.get()
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## TrustedSig
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: TrustedSig) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(toRaw(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var TrustedSig) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let hexValue = reader.readValue(string)
|
|
let res = ValidatorSig.fromHex(hexValue)
|
|
if res.isOk():
|
|
value = cast[TrustedSig](res.get())
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## ValidatorPubKey
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: ValidatorPubKey) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(toRaw(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var ValidatorPubKey) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
let hexValue = reader.readValue(string)
|
|
let res = ValidatorPubKey.fromHex(hexValue)
|
|
if res.isOk():
|
|
value = res.get()
|
|
else:
|
|
reader.raiseUnexpectedValue($res.error())
|
|
|
|
## BitSeq
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var BitSeq) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
value = BitSeq hexToSeqByte(reader.readValue(string))
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader, "A BitSeq value should be a valid hex string")
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: BitSeq) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(value.bytes()))
|
|
|
|
## BitList
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var BitList) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
type T = type(value)
|
|
value = T readValue(reader, BitSeq)
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: BitList) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, BitSeq value)
|
|
|
|
## BitArray
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var BitArray) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
hexToByteArray(readValue(reader, string), value.bytes)
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader,
|
|
"A BitArray value should be a valid hex string")
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: BitArray) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(value.bytes))
|
|
|
|
## BlockHash
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var BlockHash) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
hexToByteArray(reader.readValue(string), distinctBase(value))
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader,
|
|
"BlockHash value should be a valid hex string")
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: BlockHash) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(distinctBase(value)))
|
|
|
|
## Eth2Digest
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var Eth2Digest) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
hexToByteArray(reader.readValue(string), value.data)
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader,
|
|
"Eth2Digest value should be a valid hex string")
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: Eth2Digest) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(value.data))
|
|
|
|
## BloomLogs
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var BloomLogs) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
hexToByteArray(reader.readValue(string), value.data)
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader,
|
|
"BloomLogs value should be a valid hex string")
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: BloomLogs) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(value.data))
|
|
|
|
## HashArray
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var HashArray) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
readValue(reader, value.data)
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: HashArray) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, value.data)
|
|
|
|
## HashList
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var HashList) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
readValue(reader, value.data)
|
|
value.resetCache()
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: HashList) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, value.data)
|
|
|
|
## Eth1Address
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var Eth1Address) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
hexToByteArray(reader.readValue(string), distinctBase(value))
|
|
except ValueError:
|
|
raiseUnexpectedValue(reader,
|
|
"Eth1Address value should be a valid hex string")
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: Eth1Address) {.
|
|
raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(distinctBase(value)))
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: GraffitiBytes)
|
|
{.raises: [IOError, Defect].} =
|
|
writeValue(writer, hexOriginal(distinctBase(value)))
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], T: type GraffitiBytes): T
|
|
{.raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
init(GraffitiBytes, reader.readValue(string))
|
|
except ValueError as err:
|
|
reader.raiseUnexpectedValue err.msg
|
|
|
|
## Version
|
|
proc readValue*(
|
|
reader: var JsonReader[RestJson],
|
|
value: var (Version | ForkDigest | DomainType | GraffitiBytes)) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
try:
|
|
hexToByteArray(reader.readValue(string), distinctBase(value))
|
|
except ValueError:
|
|
raiseUnexpectedValue(
|
|
reader, "Expected a valid hex string with " & $value.len() & " bytes")
|
|
|
|
## ForkedBeaconBlock
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var ForkedBeaconBlock) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var
|
|
version: Option[BeaconBlockFork]
|
|
data: Option[JsonString]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "version":
|
|
if version.isSome():
|
|
reader.raiseUnexpectedField("Multiple version fields found",
|
|
"ForkedBeaconBlock")
|
|
let vres = reader.readValue(string)
|
|
case vres
|
|
of "phase0":
|
|
version = some(BeaconBlockFork.Phase0)
|
|
of "altair":
|
|
version = some(BeaconBlockFork.Altair)
|
|
of "bellatrix":
|
|
version = some(BeaconBlockFork.Bellatrix)
|
|
else:
|
|
reader.raiseUnexpectedValue("Incorrect version field value")
|
|
of "data":
|
|
if data.isSome():
|
|
reader.raiseUnexpectedField("Multiple data fields found",
|
|
"ForkedBeaconBlock")
|
|
data = some(reader.readValue(JsonString))
|
|
else:
|
|
reader.raiseUnexpectedField(fieldName, "ForkedBeaconBlock")
|
|
|
|
if version.isNone():
|
|
reader.raiseUnexpectedValue("Field version is missing")
|
|
if data.isNone():
|
|
reader.raiseUnexpectedValue("Field data is missing")
|
|
|
|
case version.get():
|
|
of BeaconBlockFork.Phase0:
|
|
let res =
|
|
try:
|
|
some(RestJson.decode(string(data.get()), phase0.BeaconBlock,
|
|
requireAllFields = true))
|
|
except SerializationError:
|
|
none[phase0.BeaconBlock]()
|
|
if res.isNone():
|
|
reader.raiseUnexpectedValue("Incorrect phase0 block format")
|
|
value = ForkedBeaconBlock.init(res.get())
|
|
of BeaconBlockFork.Altair:
|
|
let res =
|
|
try:
|
|
some(RestJson.decode(string(data.get()), altair.BeaconBlock,
|
|
requireAllFields = true))
|
|
except SerializationError:
|
|
none[altair.BeaconBlock]()
|
|
if res.isNone():
|
|
reader.raiseUnexpectedValue("Incorrect altair block format")
|
|
value = ForkedBeaconBlock.init(res.get())
|
|
of BeaconBlockFork.Bellatrix:
|
|
let res =
|
|
try:
|
|
some(RestJson.decode(string(data.get()), bellatrix.BeaconBlock,
|
|
requireAllFields = true))
|
|
except SerializationError:
|
|
none[bellatrix.BeaconBlock]()
|
|
if res.isNone():
|
|
reader.raiseUnexpectedValue("Incorrect bellatrix block format")
|
|
value = ForkedBeaconBlock.init(res.get())
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: ForkedBeaconBlock) {.
|
|
raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
case value.kind
|
|
of BeaconBlockFork.Phase0:
|
|
writer.writeField("version", "phase0")
|
|
writer.writeField("data", value.phase0Data)
|
|
of BeaconBlockFork.Altair:
|
|
writer.writeField("version", "altair")
|
|
writer.writeField("data", value.altairData)
|
|
of BeaconBlockFork.Bellatrix:
|
|
writer.writeField("version", "bellatrix")
|
|
writer.writeField("data", value.bellatrixData)
|
|
writer.endRecord()
|
|
|
|
## RestPublishedBeaconBlockBody
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var RestPublishedBeaconBlockBody) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var
|
|
randao_reveal: Option[ValidatorSig]
|
|
eth1_data: Option[Eth1Data]
|
|
graffiti: Option[GraffitiBytes]
|
|
proposer_slashings: Option[
|
|
List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]]
|
|
attester_slashings: Option[
|
|
List[AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]]
|
|
attestations: Option[List[Attestation, Limit MAX_ATTESTATIONS]]
|
|
deposits: Option[List[Deposit, Limit MAX_DEPOSITS]]
|
|
voluntary_exits: Option[
|
|
List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]]
|
|
sync_aggregate: Option[SyncAggregate]
|
|
execution_payload: Option[ExecutionPayload]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "randao_reveal":
|
|
if randao_reveal.isSome():
|
|
reader.raiseUnexpectedField("Multiple `randao_reveal` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
randao_reveal = some(reader.readValue(ValidatorSig))
|
|
of "eth1_data":
|
|
if eth1_data.isSome():
|
|
reader.raiseUnexpectedField("Multiple `eth1_data` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
eth1_data = some(reader.readValue(Eth1Data))
|
|
of "graffiti":
|
|
if graffiti.isSome():
|
|
reader.raiseUnexpectedField("Multiple `graffiti` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
graffiti = some(reader.readValue(GraffitiBytes))
|
|
of "proposer_slashings":
|
|
if proposer_slashings.isSome():
|
|
reader.raiseUnexpectedField(
|
|
"Multiple `proposer_slashings` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
proposer_slashings = some(
|
|
reader.readValue(List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]))
|
|
of "attester_slashings":
|
|
if attester_slashings.isSome():
|
|
reader.raiseUnexpectedField(
|
|
"Multiple `attester_slashings` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
attester_slashings = some(
|
|
reader.readValue(List[AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]))
|
|
of "attestations":
|
|
if attestations.isSome():
|
|
reader.raiseUnexpectedField("Multiple `attestations` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
attestations = some(
|
|
reader.readValue(List[Attestation, Limit MAX_ATTESTATIONS]))
|
|
of "deposits":
|
|
if deposits.isSome():
|
|
reader.raiseUnexpectedField("Multiple `deposits` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
deposits = some(reader.readValue(List[Deposit, Limit MAX_DEPOSITS]))
|
|
of "voluntary_exits":
|
|
if voluntary_exits.isSome():
|
|
reader.raiseUnexpectedField("Multiple `voluntary_exits` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
voluntary_exits = some(
|
|
reader.readValue(List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]))
|
|
of "sync_aggregate":
|
|
if sync_aggregate.isSome():
|
|
reader.raiseUnexpectedField("Multiple `sync_aggregate` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
sync_aggregate = some(reader.readValue(SyncAggregate))
|
|
of "execution_payload":
|
|
if execution_payload.isSome():
|
|
reader.raiseUnexpectedField("Multiple `execution_payload` fields found",
|
|
"RestPublishedBeaconBlockBody")
|
|
execution_payload = some(reader.readValue(ExecutionPayload))
|
|
else:
|
|
# Ignore unknown fields
|
|
discard
|
|
|
|
if randao_reveal.isNone():
|
|
reader.raiseUnexpectedValue("Field `randao_reveal` is missing")
|
|
if eth1_data.isNone():
|
|
reader.raiseUnexpectedValue("Field `eth1_data` is missing")
|
|
if graffiti.isNone():
|
|
reader.raiseUnexpectedValue("Field `graffiti` is missing")
|
|
if proposer_slashings.isNone():
|
|
reader.raiseUnexpectedValue("Field `proposer_slashings` is missing")
|
|
if attester_slashings.isNone():
|
|
reader.raiseUnexpectedValue("Field `attester_slashings` is missing")
|
|
if attestations.isNone():
|
|
reader.raiseUnexpectedValue("Field `attestations` is missing")
|
|
if deposits.isNone():
|
|
reader.raiseUnexpectedValue("Field `deposits` is missing")
|
|
if voluntary_exits.isNone():
|
|
reader.raiseUnexpectedValue("Field `voluntary_exits` is missing")
|
|
|
|
let bodyKind =
|
|
if execution_payload.isSome() and sync_aggregate.isSome():
|
|
BeaconBlockFork.Bellatrix
|
|
elif execution_payload.isNone() and sync_aggregate.isSome():
|
|
BeaconBlockFork.Altair
|
|
else:
|
|
BeaconBlockFork.Phase0
|
|
|
|
case bodyKind
|
|
of BeaconBlockFork.Phase0:
|
|
value = RestPublishedBeaconBlockBody(
|
|
kind: BeaconBlockFork.Phase0,
|
|
phase0Body: phase0.BeaconBlockBody(
|
|
randao_reveal: randao_reveal.get(),
|
|
eth1_data: eth1_data.get(),
|
|
graffiti: graffiti.get(),
|
|
proposer_slashings: proposer_slashings.get(),
|
|
attester_slashings: attester_slashings.get(),
|
|
attestations: attestations.get(),
|
|
deposits: deposits.get(),
|
|
voluntary_exits: voluntary_exits.get()
|
|
)
|
|
)
|
|
of BeaconBlockFork.Altair:
|
|
value = RestPublishedBeaconBlockBody(
|
|
kind: BeaconBlockFork.Altair,
|
|
altairBody: altair.BeaconBlockBody(
|
|
randao_reveal: randao_reveal.get(),
|
|
eth1_data: eth1_data.get(),
|
|
graffiti: graffiti.get(),
|
|
proposer_slashings: proposer_slashings.get(),
|
|
attester_slashings: attester_slashings.get(),
|
|
attestations: attestations.get(),
|
|
deposits: deposits.get(),
|
|
voluntary_exits: voluntary_exits.get(),
|
|
sync_aggregate: sync_aggregate.get()
|
|
)
|
|
)
|
|
of BeaconBlockFork.Bellatrix:
|
|
value = RestPublishedBeaconBlockBody(
|
|
kind: BeaconBlockFork.Bellatrix,
|
|
bellatrixBody: bellatrix.BeaconBlockBody(
|
|
randao_reveal: randao_reveal.get(),
|
|
eth1_data: eth1_data.get(),
|
|
graffiti: graffiti.get(),
|
|
proposer_slashings: proposer_slashings.get(),
|
|
attester_slashings: attester_slashings.get(),
|
|
attestations: attestations.get(),
|
|
deposits: deposits.get(),
|
|
voluntary_exits: voluntary_exits.get(),
|
|
sync_aggregate: sync_aggregate.get(),
|
|
execution_payload: execution_payload.get()
|
|
)
|
|
)
|
|
|
|
## RestPublishedBeaconBlock
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var RestPublishedBeaconBlock) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var
|
|
slot: Option[Slot]
|
|
proposer_index: Option[uint64]
|
|
parent_root: Option[Eth2Digest]
|
|
state_root: Option[Eth2Digest]
|
|
blockBody: Option[RestPublishedBeaconBlockBody]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "slot":
|
|
if slot.isSome():
|
|
reader.raiseUnexpectedField("Multiple `slot` fields found",
|
|
"RestPublishedBeaconBlock")
|
|
slot = some(reader.readValue(Slot))
|
|
of "proposer_index":
|
|
if proposer_index.isSome():
|
|
reader.raiseUnexpectedField("Multiple `proposer_index` fields found",
|
|
"RestPublishedBeaconBlock")
|
|
proposer_index = some(reader.readValue(uint64))
|
|
of "parent_root":
|
|
if parent_root.isSome():
|
|
reader.raiseUnexpectedField("Multiple `parent_root` fields found",
|
|
"RestPublishedBeaconBlock")
|
|
parent_root = some(reader.readValue(Eth2Digest))
|
|
of "state_root":
|
|
if state_root.isSome():
|
|
reader.raiseUnexpectedField("Multiple `state_root` fields found",
|
|
"RestPublishedBeaconBlock")
|
|
state_root = some(reader.readValue(Eth2Digest))
|
|
of "body":
|
|
if blockBody.isSome():
|
|
reader.raiseUnexpectedField("Multiple `body` fields found",
|
|
"RestPublishedBeaconBlock")
|
|
blockBody = some(reader.readValue(RestPublishedBeaconBlockBody))
|
|
else:
|
|
# Ignore unknown fields
|
|
discard
|
|
|
|
if slot.isNone():
|
|
reader.raiseUnexpectedValue("Field `slot` is missing")
|
|
if proposer_index.isNone():
|
|
reader.raiseUnexpectedValue("Field `proposer_index` is missing")
|
|
if parent_root.isNone():
|
|
reader.raiseUnexpectedValue("Field `parent_root` is missing")
|
|
if state_root.isNone():
|
|
reader.raiseUnexpectedValue("Field `state_root` is missing")
|
|
if blockBody.isNone():
|
|
reader.raiseUnexpectedValue("Field `body` is missing")
|
|
|
|
let body = blockBody.get()
|
|
value = RestPublishedBeaconBlock(
|
|
case body.kind
|
|
of BeaconBlockFork.Phase0:
|
|
ForkedBeaconBlock.init(
|
|
phase0.BeaconBlock(
|
|
slot: slot.get(),
|
|
proposer_index: proposer_index.get(),
|
|
parent_root: parent_root.get(),
|
|
state_root: state_root.get(),
|
|
body: body.phase0Body
|
|
)
|
|
)
|
|
of BeaconBlockFork.Altair:
|
|
ForkedBeaconBlock.init(
|
|
altair.BeaconBlock(
|
|
slot: slot.get(),
|
|
proposer_index: proposer_index.get(),
|
|
parent_root: parent_root.get(),
|
|
state_root: state_root.get(),
|
|
body: body.altairBody
|
|
)
|
|
)
|
|
of BeaconBlockFork.Bellatrix:
|
|
ForkedBeaconBlock.init(
|
|
bellatrix.BeaconBlock(
|
|
slot: slot.get(),
|
|
proposer_index: proposer_index.get(),
|
|
parent_root: parent_root.get(),
|
|
state_root: state_root.get(),
|
|
body: body.bellatrixBody
|
|
)
|
|
)
|
|
)
|
|
|
|
## RestPublishedSignedBeaconBlock
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var RestPublishedSignedBeaconBlock) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var signature: Option[ValidatorSig]
|
|
var message: Option[RestPublishedBeaconBlock]
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "message":
|
|
if message.isSome():
|
|
reader.raiseUnexpectedField("Multiple `message` fields found",
|
|
"RestPublishedSignedBeaconBlock")
|
|
message = some(reader.readValue(RestPublishedBeaconBlock))
|
|
of "signature":
|
|
if signature.isSome():
|
|
reader.raiseUnexpectedField("Multiple `signature` fields found",
|
|
"RestPublishedSignedBeaconBlock")
|
|
signature = some(reader.readValue(ValidatorSig))
|
|
else:
|
|
# Ignore unknown fields
|
|
discard
|
|
|
|
if signature.isNone():
|
|
reader.raiseUnexpectedValue("Field `signature` is missing")
|
|
if message.isNone():
|
|
reader.raiseUnexpectedValue("Field `message` is missing")
|
|
|
|
let blck = ForkedBeaconBlock(message.get())
|
|
value = RestPublishedSignedBeaconBlock(
|
|
case blck.kind
|
|
of BeaconBlockFork.Phase0:
|
|
ForkedSignedBeaconBlock.init(
|
|
phase0.SignedBeaconBlock(
|
|
message: blck.phase0Data,
|
|
signature: signature.get()
|
|
)
|
|
)
|
|
of BeaconBlockFork.Altair:
|
|
ForkedSignedBeaconBlock.init(
|
|
altair.SignedBeaconBlock(
|
|
message: blck.altairData,
|
|
signature: signature.get()
|
|
)
|
|
)
|
|
of BeaconBlockFork.Bellatrix:
|
|
ForkedSignedBeaconBlock.init(
|
|
bellatrix.SignedBeaconBlock(
|
|
message: blck.bellatrixData,
|
|
signature: signature.get()
|
|
)
|
|
)
|
|
)
|
|
|
|
## ForkedSignedBeaconBlock
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var ForkedSignedBeaconBlock) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var
|
|
version: Option[BeaconBlockFork]
|
|
data: Option[JsonString]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "version":
|
|
if version.isSome():
|
|
reader.raiseUnexpectedField("Multiple version fields found",
|
|
"ForkedSignedBeaconBlock")
|
|
let vres = reader.readValue(string)
|
|
case vres
|
|
of "phase0":
|
|
version = some(BeaconBlockFork.Phase0)
|
|
of "altair":
|
|
version = some(BeaconBlockFork.Altair)
|
|
of "bellatrix":
|
|
version = some(BeaconBlockFork.Bellatrix)
|
|
else:
|
|
reader.raiseUnexpectedValue("Incorrect version field value")
|
|
of "data":
|
|
if data.isSome():
|
|
reader.raiseUnexpectedField("Multiple data fields found",
|
|
"ForkedSignedBeaconBlock")
|
|
data = some(reader.readValue(JsonString))
|
|
else:
|
|
reader.raiseUnexpectedField(fieldName, "ForkedSignedBeaconBlock")
|
|
|
|
if version.isNone():
|
|
reader.raiseUnexpectedValue("Field version is missing")
|
|
if data.isNone():
|
|
reader.raiseUnexpectedValue("Field data is missing")
|
|
|
|
case version.get():
|
|
of BeaconBlockFork.Phase0:
|
|
let res =
|
|
try:
|
|
some(RestJson.decode(string(data.get()), phase0.SignedBeaconBlock,
|
|
requireAllFields = true))
|
|
except SerializationError:
|
|
none[phase0.SignedBeaconBlock]()
|
|
if res.isNone():
|
|
reader.raiseUnexpectedValue("Incorrect phase0 block format")
|
|
value = ForkedSignedBeaconBlock.init(res.get())
|
|
of BeaconBlockFork.Altair:
|
|
let res =
|
|
try:
|
|
some(RestJson.decode(string(data.get()), altair.SignedBeaconBlock,
|
|
requireAllFields = true))
|
|
except SerializationError:
|
|
none[altair.SignedBeaconBlock]()
|
|
if res.isNone():
|
|
reader.raiseUnexpectedValue("Incorrect altair block format")
|
|
value = ForkedSignedBeaconBlock.init(res.get())
|
|
of BeaconBlockFork.Bellatrix:
|
|
let res =
|
|
try:
|
|
some(RestJson.decode(string(data.get()), bellatrix.SignedBeaconBlock,
|
|
requireAllFields = true))
|
|
except SerializationError:
|
|
none[bellatrix.SignedBeaconBlock]()
|
|
if res.isNone():
|
|
reader.raiseUnexpectedValue("Incorrect bellatrix block format")
|
|
value = ForkedSignedBeaconBlock.init(res.get())
|
|
withBlck(value):
|
|
blck.root = hash_tree_root(blck.message)
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
|
value: ForkedSignedBeaconBlock) {.
|
|
raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
case value.kind
|
|
of BeaconBlockFork.Phase0:
|
|
writer.writeField("version", "phase0")
|
|
writer.writeField("data", value.phase0Data)
|
|
of BeaconBlockFork.Altair:
|
|
writer.writeField("version", "altair")
|
|
writer.writeField("data", value.altairData)
|
|
of BeaconBlockFork.Bellatrix:
|
|
writer.writeField("version", "bellatrix")
|
|
writer.writeField("data", value.bellatrixData)
|
|
writer.endRecord()
|
|
|
|
# ForkedHashedBeaconState is used where a `ForkedBeaconState` normally would
|
|
# be used, mainly because caching the hash early on is easier to do
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var ForkedHashedBeaconState) {.
|
|
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)
|
|
version = case vres
|
|
of "phase0": some(BeaconStateFork.Phase0)
|
|
of "altair": some(BeaconStateFork.Altair)
|
|
of "bellatrix": some(BeaconStateFork.Bellatrix)
|
|
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")
|
|
|
|
# Use a temporary to avoid stack instances and `value` mutation in case of
|
|
# exception
|
|
let
|
|
tmp = (ref ForkedHashedBeaconState)(kind: version.get())
|
|
|
|
template toValue(field: untyped) =
|
|
if tmp[].kind == value.kind:
|
|
assign(value.field, tmp[].field)
|
|
else:
|
|
value = tmp[] # slow, but rare (hopefully)
|
|
value.field.root = hash_tree_root(value.field.data)
|
|
|
|
case version.get():
|
|
of BeaconStateFork.Phase0:
|
|
try:
|
|
tmp[].phase0Data.data = RestJson.decode(
|
|
string(data.get()), phase0.BeaconState, requireAllFields = true)
|
|
except SerializationError:
|
|
reader.raiseUnexpectedValue("Incorrect phase0 beacon state format")
|
|
|
|
toValue(phase0Data)
|
|
of BeaconStateFork.Altair:
|
|
try:
|
|
tmp[].altairData.data = RestJson.decode(
|
|
string(data.get()), altair.BeaconState, requireAllFields = true)
|
|
except SerializationError:
|
|
reader.raiseUnexpectedValue("Incorrect altair beacon state format")
|
|
|
|
toValue(altairData)
|
|
of BeaconStateFork.Bellatrix:
|
|
try:
|
|
tmp[].bellatrixData.data = RestJson.decode(
|
|
string(data.get()), bellatrix.BeaconState, requireAllFields = true)
|
|
except SerializationError:
|
|
reader.raiseUnexpectedValue("Incorrect altair beacon state format")
|
|
toValue(bellatrixData)
|
|
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: ForkedHashedBeaconState) {.
|
|
raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
case value.kind
|
|
of BeaconStateFork.Phase0:
|
|
writer.writeField("version", "phase0")
|
|
writer.writeField("data", value.phase0Data.data)
|
|
of BeaconStateFork.Altair:
|
|
writer.writeField("version", "altair")
|
|
writer.writeField("data", value.altairData.data)
|
|
of BeaconStateFork.Bellatrix:
|
|
writer.writeField("version", "bellatrix")
|
|
writer.writeField("data", value.bellatrixData.data)
|
|
writer.endRecord()
|
|
|
|
# Web3SignerRequest
|
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
|
value: Web3SignerRequest) {.
|
|
raises: [IOError, Defect].} =
|
|
case value.kind
|
|
of Web3SignerRequestKind.AggregationSlot:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "AGGREGATION_SLOT")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("aggregation_slot", value.aggregationSlot)
|
|
of Web3SignerRequestKind.AggregateAndProof:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "AGGREGATE_AND_PROOF")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("aggregate_and_proof", value.aggregateAndProof)
|
|
of Web3SignerRequestKind.Attestation:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "ATTESTATION")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("attestation", value.attestation)
|
|
of Web3SignerRequestKind.Block:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "BLOCK")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("block", value.blck)
|
|
of Web3SignerRequestKind.BlockV2:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "BLOCK_V2")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("beacon_block", value.beaconBlock)
|
|
of Web3SignerRequestKind.Deposit:
|
|
writer.writeField("type", "DEPOSIT")
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("deposit", value.deposit)
|
|
of Web3SignerRequestKind.RandaoReveal:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "RANDAO_REVEAL")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("randao_reveal", value.randaoReveal)
|
|
of Web3SignerRequestKind.VoluntaryExit:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "VOLUNTARY_EXIT")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("voluntary_exit", value.voluntaryExit)
|
|
of Web3SignerRequestKind.SyncCommitteeMessage:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "SYNC_COMMITTEE_MESSAGE")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("sync_committee_message", value.syncCommitteeMessage)
|
|
of Web3SignerRequestKind.SyncCommitteeSelectionProof:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "SYNC_COMMITTEE_SELECTION_PROOF")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("sync_aggregator_selection_data",
|
|
value.syncAggregatorSelectionData)
|
|
of Web3SignerRequestKind.SyncCommitteeContributionAndProof:
|
|
doAssert(value.forkInfo.isSome(),
|
|
"forkInfo should be set for this type of request")
|
|
writer.writeField("type", "SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF")
|
|
writer.writeField("fork_info", value.forkInfo.get())
|
|
if isSome(value.signingRoot):
|
|
writer.writeField("signingRoot", value.signingRoot)
|
|
writer.writeField("contribution_and_proof",
|
|
value.syncCommitteeContributionAndProof)
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var Web3SignerRequest) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var
|
|
requestKind: Option[Web3SignerRequestKind]
|
|
forkInfo: Option[Web3SignerForkInfo]
|
|
signingRoot: Option[Eth2Digest]
|
|
data: Option[JsonString]
|
|
dataName: string
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "type":
|
|
if requestKind.isSome():
|
|
reader.raiseUnexpectedField("Multiple `type` fields found",
|
|
"Web3SignerRequest")
|
|
let vres = reader.readValue(string)
|
|
requestKind = some(
|
|
case vres
|
|
of "AGGREGATION_SLOT":
|
|
Web3SignerRequestKind.AggregationSlot
|
|
of "AGGREGATE_AND_PROOF":
|
|
Web3SignerRequestKind.AggregateAndProof
|
|
of "ATTESTATION":
|
|
Web3SignerRequestKind.Attestation
|
|
of "BLOCK":
|
|
Web3SignerRequestKind.Block
|
|
of "BLOCK_V2":
|
|
Web3SignerRequestKind.BlockV2
|
|
of "DEPOSIT":
|
|
Web3SignerRequestKind.Deposit
|
|
of "RANDAO_REVEAL":
|
|
Web3SignerRequestKind.RandaoReveal
|
|
of "VOLUNTARY_EXIT":
|
|
Web3SignerRequestKind.VoluntaryExit
|
|
of "SYNC_COMMITTEE_MESSAGE":
|
|
Web3SignerRequestKind.SyncCommitteeMessage
|
|
of "SYNC_COMMITTEE_SELECTION_PROOF":
|
|
Web3SignerRequestKind.SyncCommitteeSelectionProof
|
|
of "SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF":
|
|
Web3SignerRequestKind.SyncCommitteeContributionAndProof
|
|
else:
|
|
reader.raiseUnexpectedValue("Unexpected `type` value")
|
|
)
|
|
of "fork_info":
|
|
if forkInfo.isSome():
|
|
reader.raiseUnexpectedField("Multiple `fork_info` fields found",
|
|
"Web3SignerRequest")
|
|
forkInfo = some(reader.readValue(Web3SignerForkInfo))
|
|
of "signingRoot":
|
|
if signingRoot.isSome():
|
|
reader.raiseUnexpectedField("Multiple `signingRoot` fields found",
|
|
"Web3SignerRequest")
|
|
signingRoot = some(reader.readValue(Eth2Digest))
|
|
of "aggregation_slot", "aggregate_and_proof", "block", "beacon_block",
|
|
"randao_reveal", "voluntary_exit", "sync_committee_message",
|
|
"sync_aggregator_selection_data", "contribution_and_proof":
|
|
if data.isSome():
|
|
reader.raiseUnexpectedField("Multiple data fields found",
|
|
"Web3SignerRequest")
|
|
dataName = fieldName
|
|
data = some(reader.readValue(JsonString))
|
|
else:
|
|
# We ignore all unknown fields.
|
|
discard
|
|
|
|
if requestKind.isNone():
|
|
reader.raiseUnexpectedValue("Field `type` is missing")
|
|
|
|
value =
|
|
case requestKind.get()
|
|
of Web3SignerRequestKind.AggregationSlot:
|
|
if dataName != "aggregation_slot":
|
|
reader.raiseUnexpectedValue("Field `aggregation_slot` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(Web3SignerAggregationSlotData,
|
|
data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `aggregation_slot` format")
|
|
res.get()
|
|
Web3SignerRequest(kind: Web3SignerRequestKind.AggregationSlot,
|
|
forkInfo: forkInfo, signingRoot: signingRoot, aggregationSlot: data
|
|
)
|
|
of Web3SignerRequestKind.AggregateAndProof:
|
|
if dataName != "aggregate_and_proof":
|
|
reader.raiseUnexpectedValue("Field `aggregate_and_proof` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(AggregateAndProof, data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `aggregate_and_proof` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.AggregateAndProof,
|
|
forkInfo: forkInfo, signingRoot: signingRoot, aggregateAndProof: data
|
|
)
|
|
of Web3SignerRequestKind.Attestation:
|
|
if dataName != "attestation":
|
|
reader.raiseUnexpectedValue("Field `attestation` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(AttestationData, data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `attestation` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.Attestation,
|
|
forkInfo: forkInfo, signingRoot: signingRoot, attestation: data
|
|
)
|
|
of Web3SignerRequestKind.Block:
|
|
if dataName != "block":
|
|
reader.raiseUnexpectedValue("Field `block` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(phase0.BeaconBlock, data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `block` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.Block,
|
|
forkInfo: forkInfo, signingRoot: signingRoot, blck: data
|
|
)
|
|
of Web3SignerRequestKind.BlockV2:
|
|
if dataName != "beacon_block":
|
|
reader.raiseUnexpectedValue("Field `beacon_block` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(ForkedBeaconBlock, data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `beacon_block` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.BlockV2,
|
|
forkInfo: forkInfo, signingRoot: signingRoot, beaconBlock: data
|
|
)
|
|
of Web3SignerRequestKind.Deposit:
|
|
if dataName != "deposit":
|
|
reader.raiseUnexpectedValue("Field `deposit` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(Web3SignerDepositData, data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `deposit` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.Deposit,
|
|
signingRoot: signingRoot, deposit: data
|
|
)
|
|
of Web3SignerRequestKind.RandaoReveal:
|
|
if dataName != "randao_reveal":
|
|
reader.raiseUnexpectedValue("Field `randao_reveal` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(Web3SignerRandaoRevealData, data.get(),
|
|
true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `randao_reveal` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.RandaoReveal,
|
|
forkInfo: forkInfo, signingRoot: signingRoot, randaoReveal: data
|
|
)
|
|
of Web3SignerRequestKind.VoluntaryExit:
|
|
if dataName != "voluntary_exit":
|
|
reader.raiseUnexpectedValue("Field `voluntary_exit` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(VoluntaryExit, data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `voluntary_exit` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.VoluntaryExit,
|
|
forkInfo: forkInfo, signingRoot: signingRoot, voluntaryExit: data
|
|
)
|
|
of Web3SignerRequestKind.SyncCommitteeMessage:
|
|
if dataName != "sync_committee_message":
|
|
reader.raiseUnexpectedValue(
|
|
"Field `sync_committee_message` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(Web3SignerSyncCommitteeMessageData,
|
|
data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `sync_committee_message` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.SyncCommitteeMessage,
|
|
forkInfo: forkInfo, signingRoot: signingRoot,
|
|
syncCommitteeMessage: data
|
|
)
|
|
of Web3SignerRequestKind.SyncCommitteeSelectionProof:
|
|
if dataName != "sync_aggregator_selection_data":
|
|
reader.raiseUnexpectedValue(
|
|
"Field `sync_aggregator_selection_data` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(SyncAggregatorSelectionData,
|
|
data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `sync_aggregator_selection_data` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.SyncCommitteeSelectionProof,
|
|
forkInfo: forkInfo, signingRoot: signingRoot,
|
|
syncAggregatorSelectionData: data
|
|
)
|
|
of Web3SignerRequestKind.SyncCommitteeContributionAndProof:
|
|
if dataName != "contribution_and_proof":
|
|
reader.raiseUnexpectedValue(
|
|
"Field `contribution_and_proof` is missing")
|
|
if forkInfo.isNone():
|
|
reader.raiseUnexpectedValue("Field `fork_info` is missing")
|
|
let data =
|
|
block:
|
|
let res = decodeJsonString(ContributionAndProof,
|
|
data.get(), true)
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue(
|
|
"Incorrect field `contribution_and_proof` format")
|
|
res.get()
|
|
Web3SignerRequest(
|
|
kind: Web3SignerRequestKind.SyncCommitteeContributionAndProof,
|
|
forkInfo: forkInfo, signingRoot: signingRoot,
|
|
syncCommitteeContributionAndProof: data
|
|
)
|
|
|
|
## RemoteKeystoreStatus
|
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
|
value: RemoteKeystoreStatus) {.raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
writer.writeField("status", $value.status)
|
|
if value.message.isSome():
|
|
writer.writeField("message", value.message.get())
|
|
writer.endRecord()
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var RemoteKeystoreStatus) {.
|
|
raises: [IOError, SerializationError, Defect].} =
|
|
var message: Option[string]
|
|
var status: Option[KeystoreStatus]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "message":
|
|
if message.isSome():
|
|
reader.raiseUnexpectedField("Multiple `message` fields found",
|
|
"RemoteKeystoreStatus")
|
|
message = some(reader.readValue(string))
|
|
of "status":
|
|
if status.isSome():
|
|
reader.raiseUnexpectedField("Multiple `status` fields found",
|
|
"RemoteKeystoreStatus")
|
|
let res = reader.readValue(string)
|
|
status = some(
|
|
case res
|
|
of "error":
|
|
KeystoreStatus.error
|
|
of "not_active":
|
|
KeystoreStatus.notActive
|
|
of "not_found":
|
|
KeystoreStatus.notFound
|
|
of "deleted":
|
|
KeystoreStatus.deleted
|
|
of "duplicate":
|
|
KeystoreStatus.duplicate
|
|
of "imported":
|
|
KeystoreStatus.imported
|
|
else:
|
|
reader.raiseUnexpectedValue("Invalid `status` value")
|
|
)
|
|
else:
|
|
# We ignore all unknown fields.
|
|
discard
|
|
if status.isNone():
|
|
reader.raiseUnexpectedValue("Field `status` is missing")
|
|
value = RemoteKeystoreStatus(status: status.get(), message: message)
|
|
|
|
## ScryptSalt
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var ScryptSalt) {.
|
|
raises: [SerializationError, IOError, Defect].} =
|
|
let res = ncrutils.fromHex(reader.readValue(string))
|
|
if len(res) == 0:
|
|
reader.raiseUnexpectedValue("Invalid scrypt salt value")
|
|
value = ScryptSalt(res)
|
|
|
|
## Pbkdf2Params
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: Pbkdf2Params) {.
|
|
raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
writer.writeField("dklen", JsonString(Base10.toString(value.dklen)))
|
|
writer.writeField("c", JsonString(Base10.toString(value.c)))
|
|
writer.writeField("prf", value.prf)
|
|
writer.writeField("salt", value.salt)
|
|
writer.endRecord()
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var Pbkdf2Params) {.
|
|
raises: [SerializationError, IOError, Defect].} =
|
|
var
|
|
dklen: Option[uint64]
|
|
c: Option[uint64]
|
|
prf: Option[PrfKind]
|
|
salt: Option[Pbkdf2Salt]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "dklen":
|
|
if dklen.isSome():
|
|
reader.raiseUnexpectedField("Multiple `dklen` fields found",
|
|
"Pbkdf2Params")
|
|
dklen = some(reader.readValue(uint64))
|
|
of "c":
|
|
if c.isSome():
|
|
reader.raiseUnexpectedField("Multiple `c` fields found",
|
|
"Pbkdf2Params")
|
|
c = some(reader.readValue(uint64))
|
|
of "prf":
|
|
if prf.isSome():
|
|
reader.raiseUnexpectedField("Multiple `prf` fields found",
|
|
"Pbkdf2Params")
|
|
prf = some(reader.readValue(PrfKind))
|
|
of "salt":
|
|
if salt.isSome():
|
|
reader.raiseUnexpectedField("Multiple `salt` fields found",
|
|
"Pbkdf2Params")
|
|
salt = some(reader.readValue(Pbkdf2Salt))
|
|
else:
|
|
# Ignore unknown field names.
|
|
discard
|
|
|
|
if dklen.isNone():
|
|
reader.raiseUnexpectedValue("Field `dklen` is missing")
|
|
if c.isNone():
|
|
reader.raiseUnexpectedValue("Field `c` is missing")
|
|
if prf.isNone():
|
|
reader.raiseUnexpectedValue("Field `prf` is missing")
|
|
if salt.isNone():
|
|
reader.raiseUnexpectedValue("Field `salt` is missing")
|
|
|
|
value = Pbkdf2Params(
|
|
dklen: dklen.get(),
|
|
c: c.get(),
|
|
prf: prf.get(),
|
|
salt: salt.get()
|
|
)
|
|
|
|
## ScryptParams
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: ScryptParams) {.
|
|
raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
writer.writeField("dklen", JsonString(Base10.toString(value.dklen)))
|
|
writer.writeField("n", JsonString(Base10.toString(uint64(value.n))))
|
|
writer.writeField("p", JsonString(Base10.toString(uint64(value.p))))
|
|
writer.writeField("r", JsonString(Base10.toString(uint64(value.r))))
|
|
writer.writeField("salt", value.salt)
|
|
writer.endRecord()
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var ScryptParams) {.
|
|
raises: [SerializationError, IOError, Defect].} =
|
|
var
|
|
dklen: Option[uint64]
|
|
n, p, r: Option[int]
|
|
salt: Option[ScryptSalt]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "dklen":
|
|
if dklen.isSome():
|
|
reader.raiseUnexpectedField("Multiple `dklen` fields found",
|
|
"ScryptParams")
|
|
dklen = some(reader.readValue(uint64))
|
|
of "n":
|
|
if n.isSome():
|
|
reader.raiseUnexpectedField("Multiple `n` fields found",
|
|
"ScryptParams")
|
|
let res = reader.readValue(int)
|
|
if res < 0:
|
|
reader.raiseUnexpectedValue("Unexpected negative `n` value")
|
|
n = some(res)
|
|
of "p":
|
|
if p.isSome():
|
|
reader.raiseUnexpectedField("Multiple `p` fields found",
|
|
"ScryptParams")
|
|
let res = reader.readValue(int)
|
|
if res < 0:
|
|
reader.raiseUnexpectedValue("Unexpected negative `p` value")
|
|
p = some(res)
|
|
of "r":
|
|
if r.isSome():
|
|
reader.raiseUnexpectedField("Multiple `r` fields found",
|
|
"ScryptParams")
|
|
let res = reader.readValue(int)
|
|
if res < 0:
|
|
reader.raiseUnexpectedValue("Unexpected negative `r` value")
|
|
r = some(res)
|
|
of "salt":
|
|
if salt.isSome():
|
|
reader.raiseUnexpectedField("Multiple `salt` fields found",
|
|
"ScryptParams")
|
|
salt = some(reader.readValue(ScryptSalt))
|
|
else:
|
|
# Ignore unknown field names.
|
|
discard
|
|
|
|
if dklen.isNone():
|
|
reader.raiseUnexpectedValue("Field `dklen` is missing")
|
|
if n.isNone():
|
|
reader.raiseUnexpectedValue("Field `n` is missing")
|
|
if p.isNone():
|
|
reader.raiseUnexpectedValue("Field `p` is missing")
|
|
if r.isNone():
|
|
reader.raiseUnexpectedValue("Field `r` is missing")
|
|
if salt.isNone():
|
|
reader.raiseUnexpectedValue("Field `salt` is missing")
|
|
|
|
value = ScryptParams(
|
|
dklen: dklen.get(),
|
|
n: n.get(), p: p.get(), r: r.get(),
|
|
salt: salt.get()
|
|
)
|
|
|
|
## Keystore
|
|
proc writeValue*(writer: var JsonWriter[RestJson], value: Keystore) {.
|
|
raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
writer.writeField("crypto", value.crypto)
|
|
if not(isNil(value.description)):
|
|
writer.writeField("description", value.description[])
|
|
writer.writeField("pubkey", value.pubkey)
|
|
writer.writeField("path", string(value.path))
|
|
writer.writeField("uuid", value.uuid)
|
|
writer.writeField("version", JsonString(
|
|
Base10.toString(uint64(value.version))))
|
|
writer.endRecord()
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson], value: var Keystore) {.
|
|
raises: [SerializationError, IOError, Defect].} =
|
|
var
|
|
crypto: Option[Crypto]
|
|
description: Option[string]
|
|
pubkey: Option[ValidatorPubKey]
|
|
path: Option[KeyPath]
|
|
uuid: Option[string]
|
|
version: Option[int]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "crypto":
|
|
if crypto.isSome():
|
|
reader.raiseUnexpectedField("Multiple `crypto` fields found",
|
|
"Keystore")
|
|
crypto = some(reader.readValue(Crypto))
|
|
of "description":
|
|
let res = reader.readValue(string)
|
|
if description.isSome():
|
|
description = some(description.get() & "\n" & res)
|
|
else:
|
|
description = some(res)
|
|
of "pubkey":
|
|
if pubkey.isSome():
|
|
reader.raiseUnexpectedField("Multiple `pubkey` fields found",
|
|
"Keystore")
|
|
pubkey = some(reader.readValue(ValidatorPubKey))
|
|
of "path":
|
|
if path.isSome():
|
|
reader.raiseUnexpectedField("Multiple `path` fields found",
|
|
"Keystore")
|
|
let res = validateKeyPath(reader.readValue(string))
|
|
if res.isErr():
|
|
reader.raiseUnexpectedValue("Invalid `path` value")
|
|
path = some(res.get())
|
|
of "uuid":
|
|
if uuid.isSome():
|
|
reader.raiseUnexpectedField("Multiple `uuid` fields found",
|
|
"Keystore")
|
|
uuid = some(reader.readValue(string))
|
|
of "version":
|
|
if version.isSome():
|
|
reader.raiseUnexpectedField("Multiple `version` fields found",
|
|
"Keystore")
|
|
let res = reader.readValue(int)
|
|
if res < 0:
|
|
reader.raiseUnexpectedValue("Unexpected negative `version` value")
|
|
version = some(res)
|
|
else:
|
|
# Ignore unknown field names.
|
|
discard
|
|
|
|
if crypto.isNone():
|
|
reader.raiseUnexpectedValue("Field `crypto` is missing")
|
|
if pubkey.isNone():
|
|
reader.raiseUnexpectedValue("Field `pubkey` is missing")
|
|
if path.isNone():
|
|
reader.raiseUnexpectedValue("Field `path` is missing")
|
|
if uuid.isNone():
|
|
reader.raiseUnexpectedValue("Field `uuid` is missing")
|
|
if version.isNone():
|
|
reader.raiseUnexpectedValue("Field `version` is missing")
|
|
|
|
value = Keystore(
|
|
crypto: crypto.get(),
|
|
pubkey: pubkey.get(),
|
|
path: path.get(),
|
|
uuid: uuid.get(),
|
|
description: if description.isNone(): nil else: newClone(description.get()),
|
|
version: version.get(),
|
|
)
|
|
|
|
## KeystoresAndSlashingProtection
|
|
proc writeValue*(writer: var JsonWriter[RestJson],
|
|
value: KeystoresAndSlashingProtection) {.
|
|
raises: [IOError, Defect].} =
|
|
writer.beginRecord()
|
|
writer.writeField("keystores", value.keystores)
|
|
writer.writeField("passwords", value.passwords)
|
|
if value.slashing_protection.isSome():
|
|
writer.writeField("slashing_protection", value.slashing_protection)
|
|
writer.endRecord()
|
|
|
|
proc readValue*(reader: var JsonReader[RestJson],
|
|
value: var KeystoresAndSlashingProtection) {.
|
|
raises: [SerializationError, IOError, Defect].} =
|
|
var
|
|
keystores: seq[Keystore]
|
|
passwords: seq[string]
|
|
slashing: Option[SPDIR]
|
|
|
|
for fieldName in readObjectFields(reader):
|
|
case fieldName
|
|
of "keystores":
|
|
keystores = reader.readValue(seq[Keystore])
|
|
of "passwords":
|
|
passwords = reader.readValue(seq[string])
|
|
of "slashing_protection":
|
|
if slashing.isSome():
|
|
reader.raiseUnexpectedField(
|
|
"Multiple `slashing_protection` fields found",
|
|
"KeystoresAndSlashingProtection")
|
|
slashing = some(reader.readValue(SPDIR))
|
|
else:
|
|
# Ignore unknown field names.
|
|
discard
|
|
|
|
if len(keystores) == 0:
|
|
reader.raiseUnexpectedValue("Missing `keystores` value")
|
|
if len(passwords) == 0:
|
|
reader.raiseUnexpectedValue("Missing `passwords` value")
|
|
|
|
value = KeystoresAndSlashingProtection(
|
|
keystores: keystores, passwords: passwords, slashing_protection: slashing
|
|
)
|
|
|
|
proc parseRoot(value: string): Result[Eth2Digest, cstring] =
|
|
try:
|
|
ok(Eth2Digest(data: hexToByteArray[32](value)))
|
|
except ValueError:
|
|
err("Unable to decode root value")
|
|
|
|
proc decodeBody*[T](t: typedesc[T],
|
|
body: ContentBody): Result[T, cstring] =
|
|
if body.contentType != "application/json":
|
|
return err("Unsupported content type")
|
|
let data =
|
|
try:
|
|
RestJson.decode(body.data, T)
|
|
except SerializationError as exc:
|
|
return err("Unable to deserialize data")
|
|
except CatchableError:
|
|
return err("Unexpected deserialization error")
|
|
ok(data)
|
|
|
|
proc encodeBytes*[T: EncodeTypes](value: T,
|
|
contentType: string): RestResult[seq[byte]] =
|
|
case contentType
|
|
of "application/json":
|
|
let data =
|
|
block:
|
|
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)
|
|
else:
|
|
err("Content-Type not supported")
|
|
|
|
proc encodeBytes*[T: EncodeArrays](value: T,
|
|
contentType: string): RestResult[seq[byte]] =
|
|
case contentType
|
|
of "application/json":
|
|
let data =
|
|
block:
|
|
try:
|
|
var stream = memoryOutput()
|
|
var writer = JsonWriter[RestJson].init(stream)
|
|
writer.writeArray(value)
|
|
stream.getOutput(seq[byte])
|
|
except IOError:
|
|
return err("Input/output error")
|
|
except SerializationError:
|
|
return err("Serialization error")
|
|
ok(data)
|
|
else:
|
|
err("Content-Type not supported")
|
|
|
|
proc decodeBytes*[T: DecodeTypes](t: typedesc[T], value: openArray[byte],
|
|
contentType: string): RestResult[T] =
|
|
const isExtensibleType = t is ExtensibleDecodeTypes
|
|
case contentType
|
|
of "application/json":
|
|
try:
|
|
ok RestJson.decode(value, T, allowUnknownFields = isExtensibleType)
|
|
except SerializationError:
|
|
err("Serialization error")
|
|
else:
|
|
err("Content-Type not supported")
|
|
|
|
proc decodeBytes*[T: SszDecodeTypes](t: typedesc[T], value: openArray[byte],
|
|
contentType: string, updateRoot = true): RestResult[T] =
|
|
case contentType
|
|
of "application/octet-stream":
|
|
try:
|
|
var v: RestResult[T]
|
|
v.ok(T()) # This optimistically avoids an expensive genericAssign
|
|
readSszBytes(value, v.get(), updateRoot)
|
|
v
|
|
except SerializationError as exc:
|
|
err("Serialization error")
|
|
else:
|
|
err("Content-Type not supported")
|
|
|
|
proc encodeString*(value: string): RestResult[string] =
|
|
ok(value)
|
|
|
|
proc encodeString*(value: Epoch|Slot|CommitteeIndex|SyncSubcommitteeIndex): RestResult[string] =
|
|
ok(Base10.toString(uint64(value)))
|
|
|
|
proc encodeString*(value: ValidatorSig): RestResult[string] =
|
|
ok(hexOriginal(toRaw(value)))
|
|
|
|
proc encodeString*(value: GraffitiBytes): RestResult[string] =
|
|
ok(hexOriginal(distinctBase(value)))
|
|
|
|
proc encodeString*(value: Eth2Digest): RestResult[string] =
|
|
ok(hexOriginal(value.data))
|
|
|
|
proc encodeString*(value: ValidatorIdent): RestResult[string] =
|
|
case value.kind
|
|
of ValidatorQueryKind.Index:
|
|
ok(Base10.toString(uint64(value.index)))
|
|
of ValidatorQueryKind.Key:
|
|
ok(hexOriginal(toRaw(value.key)))
|
|
|
|
proc encodeString*(value: ValidatorPubKey): RestResult[string] =
|
|
ok(hexOriginal(toRaw(value)))
|
|
|
|
proc encodeString*(value: StateIdent): RestResult[string] =
|
|
case value.kind
|
|
of StateQueryKind.Slot:
|
|
ok(Base10.toString(uint64(value.slot)))
|
|
of StateQueryKind.Root:
|
|
ok(hexOriginal(value.root.data))
|
|
of StateQueryKind.Named:
|
|
case value.value
|
|
of StateIdentType.Head:
|
|
ok("head")
|
|
of StateIdentType.Genesis:
|
|
ok("genesis")
|
|
of StateIdentType.Finalized:
|
|
ok("finalized")
|
|
of StateIdentType.Justified:
|
|
ok("justified")
|
|
|
|
proc encodeString*(value: BlockIdent): RestResult[string] =
|
|
case value.kind
|
|
of BlockQueryKind.Slot:
|
|
ok(Base10.toString(uint64(value.slot)))
|
|
of BlockQueryKind.Root:
|
|
ok(hexOriginal(value.root.data))
|
|
of BlockQueryKind.Named:
|
|
case value.value
|
|
of BlockIdentType.Head:
|
|
ok("head")
|
|
of BlockIdentType.Genesis:
|
|
ok("genesis")
|
|
of BlockIdentType.Finalized:
|
|
ok("finalized")
|
|
|
|
proc decodeString*(t: typedesc[PeerStateKind],
|
|
value: string): Result[PeerStateKind, cstring] =
|
|
case value
|
|
of "disconnected":
|
|
ok(PeerStateKind.Disconnected)
|
|
of "connecting":
|
|
ok(PeerStateKind.Connecting)
|
|
of "connected":
|
|
ok(PeerStateKind.Connected)
|
|
of "disconnecting":
|
|
ok(PeerStateKind.Disconnecting)
|
|
else:
|
|
err("Incorrect peer's state value")
|
|
|
|
proc encodeString*(value: PeerStateKind): Result[string, cstring] =
|
|
case value
|
|
of PeerStateKind.Disconnected:
|
|
ok("disconnected")
|
|
of PeerStateKind.Connecting:
|
|
ok("connecting")
|
|
of PeerStateKind.Connected:
|
|
ok("connected")
|
|
of PeerStateKind.Disconnecting:
|
|
ok("disconnecting")
|
|
|
|
proc decodeString*(t: typedesc[PeerDirectKind],
|
|
value: string): Result[PeerDirectKind, cstring] =
|
|
case value
|
|
of "inbound":
|
|
ok(PeerDirectKind.Inbound)
|
|
of "outbound":
|
|
ok(PeerDirectKind.Outbound)
|
|
else:
|
|
err("Incorrect peer's direction value")
|
|
|
|
proc encodeString*(value: PeerDirectKind): Result[string, cstring] =
|
|
case value
|
|
of PeerDirectKind.Inbound:
|
|
ok("inbound")
|
|
of PeerDirectKind.Outbound:
|
|
ok("outbound")
|
|
|
|
proc encodeString*(peerid: PeerId): Result[string, cstring] =
|
|
ok($peerid)
|
|
|
|
proc decodeString*(t: typedesc[EventTopic],
|
|
value: string): Result[EventTopic, cstring] =
|
|
case value
|
|
of "head":
|
|
ok(EventTopic.Head)
|
|
of "block":
|
|
ok(EventTopic.Block)
|
|
of "attestation":
|
|
ok(EventTopic.Attestation)
|
|
of "voluntary_exit":
|
|
ok(EventTopic.VoluntaryExit)
|
|
of "finalized_checkpoint":
|
|
ok(EventTopic.FinalizedCheckpoint)
|
|
of "chain_reorg":
|
|
ok(EventTopic.ChainReorg)
|
|
of "contribution_and_proof":
|
|
ok(EventTopic.ContributionAndProof)
|
|
else:
|
|
err("Incorrect event's topic value")
|
|
|
|
proc decodeString*(t: typedesc[ValidatorSig],
|
|
value: string): Result[ValidatorSig, cstring] =
|
|
if len(value) != ValidatorSigSize + 2:
|
|
return err("Incorrect validator signature value length")
|
|
if value[0] != '0' and value[1] != 'x':
|
|
return err("Incorrect validator signature encoding")
|
|
ValidatorSig.fromHex(value)
|
|
|
|
proc decodeString*(t: typedesc[ValidatorPubKey],
|
|
value: string): Result[ValidatorPubKey, cstring] =
|
|
if len(value) != ValidatorKeySize + 2:
|
|
return err("Incorrect validator's key value length")
|
|
if value[0] != '0' and value[1] != 'x':
|
|
err("Incorrect validator's key encoding")
|
|
else:
|
|
ValidatorPubKey.fromHex(value)
|
|
|
|
proc decodeString*(t: typedesc[GraffitiBytes],
|
|
value: string): Result[GraffitiBytes, cstring] =
|
|
try:
|
|
ok(GraffitiBytes.init(value))
|
|
except ValueError:
|
|
err("Unable to decode graffiti value")
|
|
|
|
proc decodeString*(t: typedesc[string],
|
|
value: string): Result[string, cstring] =
|
|
ok(value)
|
|
|
|
proc decodeString*(t: typedesc[Slot], value: string): Result[Slot, cstring] =
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(Slot(res))
|
|
|
|
proc decodeString*(t: typedesc[Epoch], value: string): Result[Epoch, cstring] =
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(Epoch(res))
|
|
|
|
proc decodeString*(t: typedesc[uint64],
|
|
value: string): Result[uint64, cstring] =
|
|
Base10.decode(uint64, value)
|
|
|
|
proc decodeString*(t: typedesc[StateIdent],
|
|
value: string): Result[StateIdent, cstring] =
|
|
if len(value) > 2:
|
|
if (value[0] == '0') and (value[1] == 'x'):
|
|
if len(value) != RootHashSize + 2:
|
|
err("Incorrect state root value length")
|
|
else:
|
|
let res = ? parseRoot(value)
|
|
ok(StateIdent(kind: StateQueryKind.Root, root: res))
|
|
elif (value[0] in DecimalSet) and (value[1] in DecimalSet):
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(StateIdent(kind: StateQueryKind.Slot, slot: Slot(res)))
|
|
else:
|
|
case value
|
|
of "head":
|
|
ok(StateIdent(kind: StateQueryKind.Named,
|
|
value: StateIdentType.Head))
|
|
of "genesis":
|
|
ok(StateIdent(kind: StateQueryKind.Named,
|
|
value: StateIdentType.Genesis))
|
|
of "finalized":
|
|
ok(StateIdent(kind: StateQueryKind.Named,
|
|
value: StateIdentType.Finalized))
|
|
of "justified":
|
|
ok(StateIdent(kind: StateQueryKind.Named,
|
|
value: StateIdentType.Justified))
|
|
else:
|
|
err("Incorrect state identifier value")
|
|
else:
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(StateIdent(kind: StateQueryKind.Slot, slot: Slot(res)))
|
|
|
|
proc decodeString*(t: typedesc[BlockIdent],
|
|
value: string): Result[BlockIdent, cstring] =
|
|
if len(value) > 2:
|
|
if (value[0] == '0') and (value[1] == 'x'):
|
|
if len(value) != RootHashSize + 2:
|
|
err("Incorrect block root value length")
|
|
else:
|
|
let res = ? parseRoot(value)
|
|
ok(BlockIdent(kind: BlockQueryKind.Root, root: res))
|
|
elif (value[0] in DecimalSet) and (value[1] in DecimalSet):
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(BlockIdent(kind: BlockQueryKind.Slot, slot: Slot(res)))
|
|
else:
|
|
case value
|
|
of "head":
|
|
ok(BlockIdent(kind: BlockQueryKind.Named,
|
|
value: BlockIdentType.Head))
|
|
of "genesis":
|
|
ok(BlockIdent(kind: BlockQueryKind.Named,
|
|
value: BlockIdentType.Genesis))
|
|
of "finalized":
|
|
ok(BlockIdent(kind: BlockQueryKind.Named,
|
|
value: BlockIdentType.Finalized))
|
|
else:
|
|
err("Incorrect block identifier value")
|
|
else:
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(BlockIdent(kind: BlockQueryKind.Slot, slot: Slot(res)))
|
|
|
|
proc decodeString*(t: typedesc[ValidatorIdent],
|
|
value: string): Result[ValidatorIdent, cstring] =
|
|
if len(value) > 2:
|
|
if (value[0] == '0') and (value[1] == 'x'):
|
|
if len(value) != ValidatorKeySize + 2:
|
|
err("Incorrect validator's key value length")
|
|
else:
|
|
let res = ? ValidatorPubKey.fromHex(value)
|
|
ok(ValidatorIdent(kind: ValidatorQueryKind.Key,
|
|
key: res))
|
|
elif (value[0] in DecimalSet) and (value[1] in DecimalSet):
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(ValidatorIdent(kind: ValidatorQueryKind.Index,
|
|
index: RestValidatorIndex(res)))
|
|
else:
|
|
err("Incorrect validator identifier value")
|
|
else:
|
|
let res = ? Base10.decode(uint64, value)
|
|
ok(ValidatorIdent(kind: ValidatorQueryKind.Index,
|
|
index: RestValidatorIndex(res)))
|
|
|
|
proc decodeString*(t: typedesc[PeerId],
|
|
value: string): Result[PeerId, cstring] =
|
|
PeerId.init(value)
|
|
|
|
proc decodeString*(t: typedesc[CommitteeIndex],
|
|
value: string): Result[CommitteeIndex, cstring] =
|
|
let res = ? Base10.decode(uint64, value)
|
|
CommitteeIndex.init(res)
|
|
|
|
proc decodeString*(t: typedesc[SyncSubcommitteeIndex],
|
|
value: string): Result[SyncSubcommitteeIndex, cstring] =
|
|
let res = ? Base10.decode(uint64, value)
|
|
SyncSubcommitteeIndex.init(res)
|
|
|
|
proc decodeString*(t: typedesc[Eth2Digest],
|
|
value: string): Result[Eth2Digest, cstring] =
|
|
if len(value) != RootHashSize + 2:
|
|
return err("Incorrect root value length")
|
|
if value[0] != '0' and value[1] != 'x':
|
|
return err("Incorrect root value encoding")
|
|
parseRoot(value)
|
|
|
|
proc decodeString*(t: typedesc[ValidatorFilter],
|
|
value: string): Result[ValidatorFilter, cstring] =
|
|
case value
|
|
of "pending_initialized":
|
|
ok({ValidatorFilterKind.PendingInitialized})
|
|
of "pending_queued":
|
|
ok({ValidatorFilterKind.PendingQueued})
|
|
of "active_ongoing":
|
|
ok({ValidatorFilterKind.ActiveOngoing})
|
|
of "active_exiting":
|
|
ok({ValidatorFilterKind.ActiveExiting})
|
|
of "active_slashed":
|
|
ok({ValidatorFilterKind.ActiveSlashed})
|
|
of "exited_unslashed":
|
|
ok({ValidatorFilterKind.ExitedUnslashed})
|
|
of "exited_slashed":
|
|
ok({ValidatorFilterKind.ExitedSlashed})
|
|
of "withdrawal_possible":
|
|
ok({ValidatorFilterKind.WithdrawalPossible})
|
|
of "withdrawal_done":
|
|
ok({ValidatorFilterKind.WithdrawalDone})
|
|
of "pending":
|
|
ok({
|
|
ValidatorFilterKind.PendingInitialized,
|
|
ValidatorFilterKind.PendingQueued
|
|
})
|
|
of "active":
|
|
ok({
|
|
ValidatorFilterKind.ActiveOngoing,
|
|
ValidatorFilterKind.ActiveExiting,
|
|
ValidatorFilterKind.ActiveSlashed
|
|
})
|
|
of "exited":
|
|
ok({
|
|
ValidatorFilterKind.ExitedUnslashed,
|
|
ValidatorFilterKind.ExitedSlashed
|
|
})
|
|
of "withdrawal":
|
|
ok({
|
|
ValidatorFilterKind.WithdrawalPossible,
|
|
ValidatorFilterKind.WithdrawalDone
|
|
})
|
|
else:
|
|
err("Incorrect validator state identifier value")
|