From 4fb95d000d08b914f1a14c7967cf262314904ad8 Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Wed, 27 Sep 2023 17:45:33 +0300 Subject: [PATCH] REST server fixes and improvements. (#5422) * Move from Option[T] to Opt[T] usage. * Add `finalized` flag. * Fix compilation issue. * Http415 error code for some REST API calls. Introduce more comprehensive error reporting for block calls. Deprecate decodeEthConsensusVersion() function. * Bump http-utils. * Fix copyright year. * Fix serialization issue. * Address review comments. * Post rebase fixes. --- .../consensus_object_pools/blockchain_dag.nim | 8 +- beacon_chain/deposits.nim | 2 +- beacon_chain/libnimbus_lc/libnimbus_lc.nim | 8 +- beacon_chain/rpc/rest_beacon_api.nim | 129 +-- beacon_chain/rpc/rest_key_management_api.nim | 2 +- beacon_chain/rpc/rest_utils.nim | 42 +- beacon_chain/rpc/rest_validator_api.nim | 2 +- .../eth2_apis/eth2_rest_serialization.nim | 758 ++++++++++-------- .../spec/eth2_apis/rest_keymanager_types.nim | 6 +- .../eth2_apis/rest_light_client_calls.nim | 6 +- beacon_chain/spec/eth2_apis/rest_types.nim | 84 +- tests/test_serialization.nim | 15 +- vendor/nim-http-utils | 2 +- 13 files changed, 609 insertions(+), 455 deletions(-) diff --git a/beacon_chain/consensus_object_pools/blockchain_dag.nim b/beacon_chain/consensus_object_pools/blockchain_dag.nim index 9abbbaca5..cd643d34a 100644 --- a/beacon_chain/consensus_object_pools/blockchain_dag.nim +++ b/beacon_chain/consensus_object_pools/blockchain_dag.nim @@ -339,11 +339,17 @@ proc getForkedBlock*( dag.db.getForkedBlock(root) func isCanonical*(dag: ChainDAGRef, bid: BlockId): bool = - ## Return true iff the given `bid` is part of the history selected by `dag.head` + ## Returns `true` if the given `bid` is part of the history selected by + ## `dag.head`. let current = dag.getBlockIdAtSlot(bid.slot).valueOr: return false # We don't know, so .. return current.bid == bid +func isFinalized*(dag: ChainDAGRef, bid: BlockId): bool = + ## Returns `true` if the given `bid` is part of the finalized history + ## selected by `dag.finalizedHead`. + dag.isCanonical(bid) and (bid.slot <= dag.finalizedHead.slot) + func parent*(dag: ChainDAGRef, bid: BlockId): Opt[BlockId] = if bid.slot == 0: return err() diff --git a/beacon_chain/deposits.nim b/beacon_chain/deposits.nim index 01f4bd1c9..686813cd8 100644 --- a/beacon_chain/deposits.nim +++ b/beacon_chain/deposits.nim @@ -322,7 +322,7 @@ proc restValidatorExit(config: BeaconNodeConf) {.async.} = else: hadErrors = true let responseError = try: - Json.decode(response.data, RestErrorMessage) + RestJson.decode(response.data, RestErrorMessage) except CatchableError as exc: error "Failed to decode invalid error server response on " & "`submitPoolVoluntaryExit` request", reason = exc.msg diff --git a/beacon_chain/libnimbus_lc/libnimbus_lc.nim b/beacon_chain/libnimbus_lc/libnimbus_lc.nim index c25f16b9a..6204a0244 100644 --- a/beacon_chain/libnimbus_lc/libnimbus_lc.nim +++ b/beacon_chain/libnimbus_lc/libnimbus_lc.nim @@ -148,7 +148,7 @@ proc ETHBeaconStateCreateFromSsz( ## * https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.2/specs/capella/beacon-chain.md#beaconstate ## * https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.2/configs/README.md let - consensusFork = decodeEthConsensusVersion($consensusVersion).valueOr: + consensusFork = ConsensusFork.decodeString($consensusVersion).valueOr: return nil state = ForkedHashedBeaconState.new() try: @@ -328,7 +328,7 @@ proc ETHLightClientStoreCreateFromBootstrap( ## * https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.2/specs/phase0/weak-subjectivity.md#weak-subjectivity-period let mediaType = MediaType.init($mediaType) - consensusFork = decodeEthConsensusVersion($consensusVersion).valueOr: + consensusFork = ConsensusFork.decodeString($consensusVersion).valueOr: return nil var bootstrap = try: @@ -603,7 +603,7 @@ proc ETHLightClientStoreProcessFinalityUpdate( finUpdateBytes.toOpenArray(0, numFinUpdateBytes - 1), Opt.none(ConsensusFork), cfg[]) else: - let consensusFork = decodeEthConsensusVersion( + let consensusFork = ConsensusFork.decodeString( $consensusVersion).valueOr: return 1 ForkedLightClientFinalityUpdate.decodeHttpLightClientObject( @@ -688,7 +688,7 @@ proc ETHLightClientStoreProcessOptimisticUpdate( optUpdateBytes.toOpenArray(0, numOptUpdateBytes - 1), Opt.none(ConsensusFork), cfg[]) else: - let consensusFork = decodeEthConsensusVersion( + let consensusFork = ConsensusFork.decodeString( $consensusVersion).valueOr: return 1 ForkedLightClientOptimisticUpdate.decodeHttpLightClientObject( diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index 991a6fd89..cd20e31a9 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -167,9 +167,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = $error) node.withStateForBlockSlotId(bslot): - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( (root: stateRoot), - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -190,7 +191,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = $error) node.withStateForBlockSlotId(bslot): - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( ( previous_version: getStateField(state, fork).previous_version, @@ -199,7 +200,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = epoch: getStateField(state, fork).epoch ), - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -220,7 +222,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = $error) node.withStateForBlockSlotId(bslot): - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( ( previous_justified: getStateField(state, previous_justified_checkpoint), @@ -229,7 +231,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = finalized: getStateField(state, finalized_checkpoint) ), - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -255,7 +258,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = InvalidValidatorIdValueError) let ires = id.get() if len(ires) > ServerMaximumValidatorIds: - return RestApiResponse.jsonError(Http400, + return RestApiResponse.jsonError(Http414, MaximumNumberOfValidatorIdsError) ires @@ -353,9 +356,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = res.add(RestValidator.init(index, balance, toString(status), validator)) res - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( response, - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -417,9 +421,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = ValidatorStatusNotFoundError, $sres.get()) toString(sres.get()) - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( RestValidator.init(vindex, balance, status, validator), - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -507,9 +512,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = let balance = getStateField(state, balances).item(index) res.add(RestValidatorBalance.init(index, balance)) res - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( response, - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -623,9 +629,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = else: forSlot(vslot.get(), vindex, res) - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( res, - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -703,10 +710,11 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = offset.inc(length) res - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( RestEpochSyncCommittee(validators: indices, validator_aggregates: aggregates), - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -762,9 +770,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = # Fall back to full state computation node.withStateForBlockSlotId(bslot): withState(state): - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( RestEpochRandao(randao: get_randao_mix(forkyState.data, qepoch)), - node.getStateOptimistic(state) + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) ) return RestApiResponse.jsonError(Http404, StateNotFoundError) @@ -796,19 +805,20 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = return withBlck(bdata): - RestApiResponse.jsonResponseWOpt( + let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot) + RestApiResponse.jsonResponseFinalized( [ ( root: forkyBlck.root, - canonical: node.dag.isCanonical( - BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot)), + canonical: node.dag.isCanonical(bid), header: ( message: forkyBlck.toBeaconBlockHeader, signature: forkyBlck.signature ) ) ], - node.getBlockOptimistic(bdata) + node.getBlockOptimistic(bdata), + node.dag.isFinalized(bid) ) # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockHeader @@ -824,17 +834,18 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = return withBlck(bdata): - RestApiResponse.jsonResponseWOpt( + let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot) + RestApiResponse.jsonResponseFinalized( ( root: forkyBlck.root, - canonical: node.dag.isCanonical( - BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot)), + canonical: node.dag.isCanonical(bid), header: ( message: forkyBlck.toBeaconBlockHeader, signature: forkyBlck.signature ) ), - node.getBlockOptimistic(bdata) + node.getBlockOptimistic(bdata), + node.dag.isFinalized(bid) ) # https://ethereum.github.io/beacon-APIs/#/Beacon/publishBlock @@ -850,8 +861,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = var restBlock = decodeBody(RestPublishedSignedBlockContents, body, version).valueOr: - return RestApiResponse.jsonError(Http400, InvalidBlockObjectError, - $error) + return RestApiResponse.jsonError(error) forked = ForkedSignedBeaconBlock.init(restBlock) if restBlock.kind != node.dag.cfg.consensusForkAtEpoch( @@ -912,8 +922,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = var restBlock = decodeBodyJsonOrSsz(RestPublishedSignedBlockContents, body, version).valueOr: - return RestApiResponse.jsonError(Http400, InvalidBlockObjectError, - $error) + return RestApiResponse.jsonError(error) forked = ForkedSignedBeaconBlock.init(restBlock) # TODO (henridf): handle broadcast_validation flag @@ -978,19 +987,38 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = version = request.headers.getString("eth-consensus-version") body = contentBody.get() - if body.contentType == OctetStreamMediaType and - currentEpochFork.toString != version: + if (body.contentType == OctetStreamMediaType) and + (currentEpochFork.toString != version): return RestApiResponse.jsonError(Http400, BlockIncorrectFork) case currentEpochFork of ConsensusFork.Deneb: - return RestApiResponse.jsonError(Http500, $denebImplementationMissing) + let + restBlock = decodeBodyJsonOrSsz(deneb_mev.SignedBlindedBeaconBlock, + body).valueOr: + return RestApiResponse.jsonError(error) + + payloadBuilderClient = node.getPayloadBuilderClient( + restBlock.message.proposer_index).valueOr: + return RestApiResponse.jsonError( + Http400, "Unable to initialize payload builder client: " & $error) + res = await node.unblindAndRouteBlockMEV( + payloadBuilderClient, restBlock) + + if res.isErr(): + return RestApiResponse.jsonError( + Http503, BeaconNodeInSyncError, $res.error()) + if res.get().isNone(): + return RestApiResponse.jsonError(Http202, BlockValidationError) + + return RestApiResponse.jsonMsgResponse(BlockValidationSuccess) of ConsensusFork.Capella: let - restBlock = decodeBodyJsonOrSsz( - capella_mev.SignedBlindedBeaconBlock, body).valueOr: - return RestApiResponse.jsonError(Http400, InvalidBlockObjectError, - $error) + restBlock = + decodeBodyJsonOrSsz(capella_mev.SignedBlindedBeaconBlock, + body).valueOr: + return RestApiResponse.jsonError(error) + payloadBuilderClient = node.getPayloadBuilderClient( restBlock.message.proposer_index).valueOr: return RestApiResponse.jsonError( @@ -1006,7 +1034,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = return RestApiResponse.jsonMsgResponse(BlockValidationSuccess) of ConsensusFork.Bellatrix: - return RestApiResponse.jsonError(Http400, "FOO") + return RestApiResponse.jsonError(Http400, + "Bellatrix builder API unsupported") of ConsensusFork.Altair, ConsensusFork.Phase0: # Pre-Bellatrix, this endpoint will accept a `SignedBeaconBlock`. # @@ -1015,8 +1044,7 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = var restBlock = decodeBody(RestPublishedSignedBeaconBlock, body, version).valueOr: - return RestApiResponse.jsonError(Http400, InvalidBlockObjectError, - $error) + return RestApiResponse.jsonError(error) forked = ForkedSignedBeaconBlock(restBlock) if forked.kind != node.dag.cfg.consensusForkAtEpoch( @@ -1076,7 +1104,8 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = RestApiResponse.jsonResponseBlock( bdata.asSigned(), - node.getBlockOptimistic(bdata) + node.getBlockOptimistic(bdata), + node.dag.isFinalized(bid) ) else: RestApiResponse.jsonError(Http500, InvalidAcceptError) @@ -1095,9 +1124,10 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = bdata = node.dag.getForkedBlock(bid).valueOr: return RestApiResponse.jsonError(Http404, BlockNotFoundError) - return RestApiResponse.jsonResponseWOpt( + return RestApiResponse.jsonResponseFinalized( (root: bid.root), - node.getBlockOptimistic(bdata) + node.getBlockOptimistic(bdata), + node.dag.isFinalized(bid) ) # https://ethereum.github.io/beacon-APIs/#/Beacon/getBlockAttestations @@ -1105,18 +1135,19 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = "/eth/v1/beacon/blocks/{block_id}/attestations") do ( block_id: BlockIdent) -> RestApiResponse: let - bid = block_id.valueOr: + blockIdent = block_id.valueOr: return RestApiResponse.jsonError(Http400, InvalidBlockIdValueError, $error) - - bdata = node.getForkedBlock(bid).valueOr: + bdata = node.getForkedBlock(blockIdent).valueOr: return RestApiResponse.jsonError(Http404, BlockNotFoundError) return withBlck(bdata): - RestApiResponse.jsonResponseWOpt( + let bid = BlockId(root: forkyBlck.root, slot: forkyBlck.message.slot) + RestApiResponse.jsonResponseFinalized( forkyBlck.message.body.attestations.asSeq(), - node.getBlockOptimistic(bdata) + node.getBlockOptimistic(bdata), + node.dag.isFinalized(bid) ) # https://ethereum.github.io/beacon-APIs/#/Beacon/getPoolAttestations diff --git a/beacon_chain/rpc/rest_key_management_api.nim b/beacon_chain/rpc/rest_key_management_api.nim index 90ec94005..50e6c6076 100644 --- a/beacon_chain/rpc/rest_key_management_api.nim +++ b/beacon_chain/rpc/rest_key_management_api.nim @@ -123,7 +123,7 @@ proc handleRemoveValidatorReq(host: KeymanagerHost, return RemoteKeystoreStatus(status: KeystoreStatus.notFound) else: return RemoteKeystoreStatus(status: KeystoreStatus.error, - message: some($res.error())) + message: Opt.some($res.error())) proc handleAddRemoteValidatorReq(host: KeymanagerHost, keystore: RemoteKeystore): RequestItemStatus = diff --git a/beacon_chain/rpc/rest_utils.nim b/beacon_chain/rpc/rest_utils.nim index c7b21fe76..b95625a7a 100644 --- a/beacon_chain/rpc/rest_utils.nim +++ b/beacon_chain/rpc/rest_utils.nim @@ -7,8 +7,8 @@ {.push raises: [].} -import std/[options, macros], - stew/byteutils, presto, +import std/macros, + results, stew/byteutils, presto, ../spec/[forks], ../spec/eth2_apis/[rest_types, eth2_rest_serialization, rest_common], ../validators/beacon_validators, @@ -17,7 +17,7 @@ import std/[options, macros], "."/[rest_constants, state_ttl_cache] export - options, eth2_rest_serialization, blockchain_dag, presto, rest_types, + results, eth2_rest_serialization, blockchain_dag, presto, rest_types, rest_constants, rest_common type @@ -256,8 +256,8 @@ func syncCommitteeParticipants*(forkedState: ForkedHashedBeaconState, func keysToIndices*(cacheTable: var Table[ValidatorPubKey, ValidatorIndex], forkedState: ForkedHashedBeaconState, keys: openArray[ValidatorPubKey] - ): seq[Option[ValidatorIndex]] = - var indices = newSeq[Option[ValidatorIndex]](len(keys)) + ): seq[Opt[ValidatorIndex]] = + var indices = newSeq[Opt[ValidatorIndex]](len(keys)) let totalValidatorsInState = getStateField(forkedState, validators).lenu64 var keyset = block: @@ -266,7 +266,7 @@ func keysToIndices*(cacheTable: var Table[ValidatorPubKey, ValidatorIndex], # Try to search in cache first. cacheTable.withValue(pubkey, vindex): if uint64(vindex[]) < totalValidatorsInState: - indices[inputIndex] = some(vindex[]) + indices[inputIndex] = Opt.some(vindex[]) do: res[pubkey] = inputIndex res @@ -276,48 +276,48 @@ func keysToIndices*(cacheTable: var Table[ValidatorPubKey, ValidatorIndex], # Store pair (pubkey, index) into cache table. cacheTable[validator.pubkey] = ValidatorIndex(validatorIndex) # Fill result sequence. - indices[listIndex[]] = some(ValidatorIndex(validatorIndex)) + indices[listIndex[]] = Opt.some(ValidatorIndex(validatorIndex)) indices -proc getBidOptimistic*(node: BeaconNode, bid: BlockId): Option[bool] = +proc getBidOptimistic*(node: BeaconNode, bid: BlockId): Opt[bool] = if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH: - some[bool](node.dag.is_optimistic(bid)) + Opt.some(node.dag.is_optimistic(bid)) else: - none[bool]() + Opt.none(bool) proc getShufflingOptimistic*(node: BeaconNode, dependentSlot: Slot, - dependentRoot: Eth2Digest): Option[bool] = + dependentRoot: Eth2Digest): Opt[bool] = if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH: # `slot` in this `BlockId` may be higher than block's actual slot, # this is alright for the purpose of calling `is_optimistic`. let bid = BlockId(slot: dependentSlot, root: dependentRoot) - some[bool](node.dag.is_optimistic(bid)) + Opt.some(node.dag.is_optimistic(bid)) else: - none[bool]() + Opt.none(bool) proc getStateOptimistic*(node: BeaconNode, - state: ForkedHashedBeaconState): Option[bool] = + state: ForkedHashedBeaconState): Opt[bool] = if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH: if state.kind >= ConsensusFork.Bellatrix: # A state is optimistic iff the block which created it is let stateBid = withState(state): forkyState.latest_block_id - some[bool](node.dag.is_optimistic(stateBid)) + Opt.some(node.dag.is_optimistic(stateBid)) else: - some[bool](false) + Opt.some(false) else: - none[bool]() + Opt.none(bool) proc getBlockOptimistic*(node: BeaconNode, blck: ForkedTrustedSignedBeaconBlock | - ForkedSignedBeaconBlock): Option[bool] = + ForkedSignedBeaconBlock): Opt[bool] = if node.currentSlot().epoch() >= node.dag.cfg.BELLATRIX_FORK_EPOCH: if blck.kind >= ConsensusFork.Bellatrix: - some[bool](node.dag.is_optimistic(blck.toBlockId())) + Opt.some(node.dag.is_optimistic(blck.toBlockId())) else: - some[bool](false) + Opt.some(false) else: - none[bool]() + Opt.none(bool) const jsonMediaType* = MediaType.init("application/json") diff --git a/beacon_chain/rpc/rest_validator_api.nim b/beacon_chain/rpc/rest_validator_api.nim index 3678a0099..139c52300 100644 --- a/beacon_chain/rpc/rest_validator_api.nim +++ b/beacon_chain/rpc/rest_validator_api.nim @@ -309,7 +309,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = return RestApiResponse.jsonResponseWOpt(res, optimistic) else: let res = emptyResponse() - return RestApiResponse.jsonResponseWOpt(res, execOpt = some(false)) + return RestApiResponse.jsonResponseWOpt(res, execOpt = Opt.some(false)) return RestApiResponse.jsonError(Http404, StateNotFoundError) diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index 9aa7ad1bd..dc24028b6 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -8,7 +8,7 @@ import std/[typetraits, strutils] import stew/[assign2, results, base10, byteutils, endians2], presto/common, libp2p/peerid, serialization, json_serialization, - json_serialization/std/[options, net, sets], + json_serialization/std/[net, sets], chronicles import ".."/[eth2_ssz_serialization, forks, keystore], ".."/../consensus_object_pools/block_pools_types, @@ -23,7 +23,7 @@ from ".."/datatypes/deneb import BeaconState export eth2_ssz_serialization, results, peerid, common, serialization, chronicles, - json_serialization, options, net, sets, rest_types, slashing_protection_common + json_serialization, net, sets, rest_types, slashing_protection_common from web3/ethtypes import BlockHash export ethtypes.BlockHash @@ -34,14 +34,6 @@ func decodeMediaType*( return err("Missing or incorrect Content-Type") ok contentType.get.mediaType -func decodeEthConsensusVersion*( - value: string): Result[ConsensusFork, string] = - let normalizedValue = value.toLowerAscii() - for consensusFork in ConsensusFork: - if normalizedValue == ($consensusFork).toLowerAscii(): - return ok consensusFork - err("Unsupported Eth-Consensus-Version: " & value) - Json.createFlavor RestJson ## The RestJson format implements JSON serialization in the way specified @@ -85,6 +77,9 @@ const TextPlainMediaType* = MediaType.init("text/plain") OctetStreamMediaType* = MediaType.init("application/octet-stream") UrlEncodedMediaType* = MediaType.init("application/x-www-form-urlencoded") + UnableDecodeVersionError = "Unable to decode version" + UnableDecodeError = "Unable to decode data" + UnexpectedDecodeError = "Unexpected decoding error" type EmptyBody* = object @@ -225,7 +220,7 @@ proc prepareJsonStringResponse*(t: typedesc[RestApiResponse], d: auto): string = proc jsonResponseWRoot*(t: typedesc[RestApiResponse], data: auto, dependent_root: Eth2Digest, - execOpt: Option[bool]): RestApiResponse = + execOpt: Opt[bool]): RestApiResponse = let res = block: var default: seq[byte] @@ -264,7 +259,8 @@ proc jsonResponse*(t: typedesc[RestApiResponse], data: auto): RestApiResponse = proc jsonResponseBlock*(t: typedesc[RestApiResponse], data: ForkedSignedBeaconBlock, - execOpt: Option[bool]): RestApiResponse = + execOpt: Opt[bool], + finalized: bool): RestApiResponse = let headers = [("eth-consensus-version", data.kind.toString())] res = @@ -277,6 +273,7 @@ proc jsonResponseBlock*(t: typedesc[RestApiResponse], writer.writeField("version", data.kind.toString()) if execOpt.isSome(): writer.writeField("execution_optimistic", execOpt.get()) + writer.writeField("finalized", finalized) withBlck(data): writer.writeField("data", forkyBlck) writer.endRecord() @@ -289,7 +286,7 @@ proc jsonResponseBlock*(t: typedesc[RestApiResponse], proc jsonResponseState*(t: typedesc[RestApiResponse], data: ForkedHashedBeaconState, - execOpt: Option[bool]): RestApiResponse = + execOpt: Opt[bool]): RestApiResponse = let headers = [("eth-consensus-version", data.kind.toString())] res = @@ -313,7 +310,7 @@ proc jsonResponseState*(t: typedesc[RestApiResponse], RestApiResponse.response(res, Http200, "application/json", headers = headers) proc jsonResponseWOpt*(t: typedesc[RestApiResponse], data: auto, - execOpt: Option[bool]): RestApiResponse = + execOpt: Opt[bool]): RestApiResponse = let res = block: var default: seq[byte] @@ -332,6 +329,28 @@ proc jsonResponseWOpt*(t: typedesc[RestApiResponse], data: auto, default RestApiResponse.response(res, Http200, "application/json") +proc jsonResponseFinalized*(t: typedesc[RestApiResponse], data: auto, + exec: Opt[bool], + finalized: bool): RestApiResponse = + let res = + block: + var default: seq[byte] + try: + var stream = memoryOutput() + var writer = JsonWriter[RestJson].init(stream) + writer.beginRecord() + if exec.isSome(): + writer.writeField("execution_optimistic", exec.get()) + writer.writeField("finalized", finalized) + writer.writeField("data", data) + writer.endRecord() + stream.getOutput(seq[byte]) + except SerializationError: + default + except IOError: + default + RestApiResponse.response(res, Http200, "application/json") + proc jsonResponseWVersion*(t: typedesc[RestApiResponse], data: auto, version: ConsensusFork): RestApiResponse = let @@ -491,6 +510,27 @@ proc jsonError*(t: typedesc[RestApiResponse], status: HttpCode = Http200, default RestApiResponse.error(status, data, "application/json") +proc jsonError*(t: typedesc[RestApiResponse], + rmsg: RestErrorMessage): RestApiResponse = + let data = + block: + var default: string + try: + var stream = memoryOutput() + var writer = JsonWriter[RestJson].init(stream) + writer.beginRecord() + writer.writeField("code", rmsg.code) + writer.writeField("message", rmsg.message) + if rmsg.stacktraces.isSome(): + writer.writeField("stacktraces", rmsg.stacktraces) + writer.endRecord() + stream.getOutput(string) + except SerializationError: + default + except IOError: + default + RestApiResponse.error(rmsg.code.toHttpCode().get(), data, "application/json") + proc jsonErrorList*(t: typedesc[RestApiResponse], status: HttpCode = Http200, msg: string = "", failures: auto): RestApiResponse = @@ -944,11 +984,10 @@ template unrecognizedFieldIgnore = discard readValue(reader, JsonString) ## ForkedBeaconBlock -template prepareForkedBlockReading( - reader: var JsonReader[RestJson], - version: var Option[ConsensusFork], - data: var Option[JsonString], - blockTypeName: cstring) = +template prepareForkedBlockReading(reader: var JsonReader[RestJson], + version: var Opt[ConsensusFork], + data: var Opt[JsonString], + blockTypeName: cstring) = for fieldName {.inject.} in readObjectFields(reader): case fieldName of "version": @@ -958,22 +997,22 @@ template prepareForkedBlockReading( let vres = reader.readValue(string).toLowerAscii() case vres of "phase0": - version = some(ConsensusFork.Phase0) + version = Opt.some(ConsensusFork.Phase0) of "altair": - version = some(ConsensusFork.Altair) + version = Opt.some(ConsensusFork.Altair) of "bellatrix": - version = some(ConsensusFork.Bellatrix) + version = Opt.some(ConsensusFork.Bellatrix) of "capella": - version = some(ConsensusFork.Capella) + version = Opt.some(ConsensusFork.Capella) of "deneb": - version = some(ConsensusFork.Deneb) + version = Opt.some(ConsensusFork.Deneb) else: reader.raiseUnexpectedValue("Incorrect version field value") of "block", "block_header", "data": if data.isSome(): - reader.raiseUnexpectedField("Multiple block or block_header fields found", - blockTypeName) - data = some(reader.readValue(JsonString)) + reader.raiseUnexpectedField( + "Multiple block or block_header fields found", blockTypeName) + data = Opt.some(reader.readValue(JsonString)) else: unrecognizedFieldWarning() @@ -986,8 +1025,8 @@ proc readValue*[BlockType: ForkedBeaconBlock]( reader: var JsonReader[RestJson], value: var BlockType) {.raises: [IOError, SerializationError].} = var - version: Option[ConsensusFork] - data: Option[JsonString] + version: Opt[ConsensusFork] + data: Opt[JsonString] prepareForkedBlockReading(reader, version, data, "ForkedBeaconBlock") @@ -995,60 +1034,60 @@ proc readValue*[BlockType: ForkedBeaconBlock]( of ConsensusFork.Phase0: let res = try: - some(RestJson.decode(string(data.get()), - phase0.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + phase0.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[phase0.BeaconBlock]() + Opt.none(phase0.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect phase0 block format") value = ForkedBeaconBlock.init(res.get()).BlockType of ConsensusFork.Altair: let res = try: - some(RestJson.decode(string(data.get()), - altair.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + altair.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[altair.BeaconBlock]() + Opt.none(altair.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect altair block format") value = ForkedBeaconBlock.init(res.get()).BlockType of ConsensusFork.Bellatrix: let res = try: - some(RestJson.decode(string(data.get()), - bellatrix.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + bellatrix.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[bellatrix.BeaconBlock]() + Opt.none(bellatrix.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect bellatrix block format") value = ForkedBeaconBlock.init(res.get()).BlockType of ConsensusFork.Capella: let res = try: - some(RestJson.decode(string(data.get()), - capella.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + capella.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[capella.BeaconBlock]() + Opt.none(capella.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect capella block format") value = ForkedBeaconBlock.init(res.get()).BlockType of ConsensusFork.Deneb: let res = try: - some(RestJson.decode(string(data.get()), - deneb.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + deneb.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[deneb.BeaconBlock]() + Opt.none(deneb.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect deneb block format") value = ForkedBeaconBlock.init(res.get()).BlockType @@ -1057,8 +1096,8 @@ proc readValue*[BlockType: ProduceBlockResponseV2]( reader: var JsonReader[RestJson], value: var BlockType) {.raises: [IOError, SerializationError].} = var - version: Option[ConsensusFork] - data: Option[JsonString] + version: Opt[ConsensusFork] + data: Opt[JsonString] prepareForkedBlockReading(reader, version, data, "ProduceBlockResponseV2") @@ -1066,12 +1105,12 @@ proc readValue*[BlockType: ProduceBlockResponseV2]( of ConsensusFork.Phase0: let res = try: - some(RestJson.decode(string(data.get()), - phase0.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + phase0.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[phase0.BeaconBlock]() + Opt.none(phase0.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect phase0 block format") value = ProduceBlockResponseV2(kind: ConsensusFork.Phase0, @@ -1079,12 +1118,12 @@ proc readValue*[BlockType: ProduceBlockResponseV2]( of ConsensusFork.Altair: let res = try: - some(RestJson.decode(string(data.get()), - altair.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + altair.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[altair.BeaconBlock]() + Opt.none(altair.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect altair block format") value = ProduceBlockResponseV2(kind: ConsensusFork.Altair, @@ -1092,12 +1131,12 @@ proc readValue*[BlockType: ProduceBlockResponseV2]( of ConsensusFork.Bellatrix: let res = try: - some(RestJson.decode(string(data.get()), - bellatrix.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + bellatrix.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[bellatrix.BeaconBlock]() + Opt.none(bellatrix.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect bellatrix block format") value = ProduceBlockResponseV2(kind: ConsensusFork.Bellatrix, @@ -1105,12 +1144,12 @@ proc readValue*[BlockType: ProduceBlockResponseV2]( of ConsensusFork.Capella: let res = try: - some(RestJson.decode(string(data.get()), - capella.BeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + capella.BeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[capella.BeaconBlock]() + Opt.none(capella.BeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect capella block format") value = ProduceBlockResponseV2(kind: ConsensusFork.Capella, @@ -1118,12 +1157,12 @@ proc readValue*[BlockType: ProduceBlockResponseV2]( of ConsensusFork.Deneb: let res = try: - some(RestJson.decode(string(data.get()), - DenebBlockContents, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + DenebBlockContents, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[DenebBlockContents]() + Opt.none(DenebBlockContents) if res.isNone(): reader.raiseUnexpectedValue("Incorrect deneb block format") value = ProduceBlockResponseV2(kind: ConsensusFork.Deneb, @@ -1134,8 +1173,8 @@ proc readValue*[BlockType: ForkedBlindedBeaconBlock]( value: var BlockType ) {.raises: [IOError, SerializationError].} = var - version: Option[ConsensusFork] - data: Option[JsonString] + version: Opt[ConsensusFork] + data: Opt[JsonString] prepareForkedBlockReading(reader, version, data, "ForkedBlindedBeaconBlock") @@ -1196,20 +1235,20 @@ proc readValue*[BlockType: Web3SignerForkedBeaconBlock]( reader: var JsonReader[RestJson], value: var BlockType) {.raises: [IOError, SerializationError].} = var - version: Option[ConsensusFork] - data: Option[JsonString] + version: Opt[ConsensusFork] + data: Opt[JsonString] prepareForkedBlockReading(reader, version, data, "Web3SignerForkedBeaconBlock") let res = try: - some(RestJson.decode(string(data.get()), - BeaconBlockHeader, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + BeaconBlockHeader, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[BeaconBlockHeader]() + Opt.none(BeaconBlockHeader) if res.isNone(): reader.raiseUnexpectedValue("Incorrect block header format") if version.get() <= ConsensusFork.Altair: @@ -1259,21 +1298,20 @@ proc readValue*(reader: var JsonReader[RestJson], value: var RestPublishedBeaconBlockBody) {. raises: [IOError, SerializationError].} = 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[RestExecutionPayload] - bls_to_execution_changes: Option[SignedBLSToExecutionChangeList] - blob_kzg_commitments: Option[KzgCommitments] + randao_reveal: Opt[ValidatorSig] + eth1_data: Opt[Eth1Data] + graffiti: Opt[GraffitiBytes] + proposer_slashings: + Opt[List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]] + attester_slashings: + Opt[List[AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS]] + attestations: Opt[List[Attestation, Limit MAX_ATTESTATIONS]] + deposits: Opt[List[Deposit, Limit MAX_DEPOSITS]] + voluntary_exits: Opt[List[SignedVoluntaryExit, Limit MAX_VOLUNTARY_EXITS]] + sync_aggregate: Opt[SyncAggregate] + execution_payload: Opt[RestExecutionPayload] + bls_to_execution_changes: Opt[SignedBLSToExecutionChangeList] + blob_kzg_commitments: Opt[KzgCommitments] for fieldName in readObjectFields(reader): case fieldName @@ -1281,68 +1319,69 @@ proc readValue*(reader: var JsonReader[RestJson], if randao_reveal.isSome(): reader.raiseUnexpectedField("Multiple `randao_reveal` fields found", "RestPublishedBeaconBlockBody") - randao_reveal = some(reader.readValue(ValidatorSig)) + randao_reveal = Opt.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)) + eth1_data = Opt.some(reader.readValue(Eth1Data)) of "graffiti": if graffiti.isSome(): reader.raiseUnexpectedField("Multiple `graffiti` fields found", "RestPublishedBeaconBlockBody") - graffiti = some(reader.readValue(GraffitiBytes)) + graffiti = Opt.some(reader.readValue(GraffitiBytes)) of "proposer_slashings": if proposer_slashings.isSome(): reader.raiseUnexpectedField( "Multiple `proposer_slashings` fields found", "RestPublishedBeaconBlockBody") - proposer_slashings = some( + proposer_slashings = Opt.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( + attester_slashings = Opt.some( reader.readValue(List[AttesterSlashing, Limit MAX_ATTESTER_SLASHINGS])) of "attestations": if attestations.isSome(): reader.raiseUnexpectedField("Multiple `attestations` fields found", "RestPublishedBeaconBlockBody") - attestations = some( + attestations = Opt.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])) + deposits = Opt.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( + voluntary_exits = Opt.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)) + sync_aggregate = Opt.some(reader.readValue(SyncAggregate)) of "execution_payload": if execution_payload.isSome(): reader.raiseUnexpectedField("Multiple `execution_payload` fields found", "RestPublishedBeaconBlockBody") - execution_payload = some(reader.readValue(RestExecutionPayload)) + execution_payload = Opt.some(reader.readValue(RestExecutionPayload)) of "bls_to_execution_changes": if bls_to_execution_changes.isSome(): reader.raiseUnexpectedField("Multiple `bls_to_execution_changes` fields found", "RestPublishedBeaconBlockBody") - bls_to_execution_changes = some(reader.readValue(SignedBLSToExecutionChangeList)) + bls_to_execution_changes = Opt.some( + reader.readValue(SignedBLSToExecutionChangeList)) of "blob_kzg_commitments_changes": if blob_kzg_commitments.isSome(): reader.raiseUnexpectedField("Multiple `blob_kzg_commitments` fields found", "RestPublishedBeaconBlockBody") - blob_kzg_commitments = some(reader.readValue(KzgCommitments)) + blob_kzg_commitments = Opt.some(reader.readValue(KzgCommitments)) else: unrecognizedFieldWarning() @@ -1496,11 +1535,11 @@ proc readValue*(reader: var JsonReader[RestJson], value: var RestPublishedBeaconBlock) {. raises: [IOError, SerializationError].} = var - slot: Option[Slot] - proposer_index: Option[uint64] - parent_root: Option[Eth2Digest] - state_root: Option[Eth2Digest] - blockBody: Option[RestPublishedBeaconBlockBody] + slot: Opt[Slot] + proposer_index: Opt[uint64] + parent_root: Opt[Eth2Digest] + state_root: Opt[Eth2Digest] + blockBody: Opt[RestPublishedBeaconBlockBody] for fieldName in readObjectFields(reader): case fieldName @@ -1508,27 +1547,27 @@ proc readValue*(reader: var JsonReader[RestJson], if slot.isSome(): reader.raiseUnexpectedField("Multiple `slot` fields found", "RestPublishedBeaconBlock") - slot = some(reader.readValue(Slot)) + slot = Opt.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)) + proposer_index = Opt.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)) + parent_root = Opt.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)) + state_root = Opt.some(reader.readValue(Eth2Digest)) of "body": if blockBody.isSome(): reader.raiseUnexpectedField("Multiple `body` fields found", "RestPublishedBeaconBlock") - blockBody = some(reader.readValue(RestPublishedBeaconBlockBody)) + blockBody = Opt.some(reader.readValue(RestPublishedBeaconBlockBody)) else: unrecognizedFieldWarning() @@ -1602,20 +1641,20 @@ proc readValue*(reader: var JsonReader[RestJson], proc readValue*(reader: var JsonReader[RestJson], value: var RestPublishedSignedBeaconBlock) {. raises: [IOError, SerializationError].} = - var signature: Option[ValidatorSig] - var message: Option[RestPublishedBeaconBlock] + var signature: Opt[ValidatorSig] + var message: Opt[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)) + message = Opt.some(reader.readValue(RestPublishedBeaconBlock)) of "signature": if signature.isSome(): reader.raiseUnexpectedField("Multiple `signature` fields found", "RestPublishedSignedBeaconBlock") - signature = some(reader.readValue(ValidatorSig)) + signature = Opt.some(reader.readValue(ValidatorSig)) else: unrecognizedFieldWarning() @@ -1667,12 +1706,12 @@ proc readValue*(reader: var JsonReader[RestJson], proc readValue*(reader: var JsonReader[RestJson], value: var RestPublishedSignedBlockContents) {. raises: [IOError, SerializationError].} = - var signature: Option[ValidatorSig] - var message: Option[RestPublishedBeaconBlock] - var signed_message: Option[RestPublishedSignedBeaconBlock] - var signed_block_data: Option[JsonString] - var signed_blob_sidecars: Option[List[SignedBlobSidecar, - Limit MAX_BLOBS_PER_BLOCK]] + var signature: Opt[ValidatorSig] + var message: Opt[RestPublishedBeaconBlock] + var signed_message: Opt[RestPublishedSignedBeaconBlock] + var signed_block_data: Opt[JsonString] + var signed_blob_sidecars: Opt[List[SignedBlobSidecar, + Limit MAX_BLOBS_PER_BLOCK]] # Pre-Deneb, there were always the same two top-level fields # ('signature' and 'message'). For Deneb, there's a different set of @@ -1684,33 +1723,33 @@ proc readValue*(reader: var JsonReader[RestJson], if message.isSome(): reader.raiseUnexpectedField("Multiple `message` fields found", "RestPublishedSignedBlockContents") - message = some(reader.readValue(RestPublishedBeaconBlock)) + message = Opt.some(reader.readValue(RestPublishedBeaconBlock)) of "signature": if signature.isSome(): reader.raiseUnexpectedField("Multiple `signature` fields found", "RestPublishedSignedBlockContents") - signature = some(reader.readValue(ValidatorSig)) + signature = Opt.some(reader.readValue(ValidatorSig)) of "signed_block": if signed_block_data.isSome(): reader.raiseUnexpectedField("Multiple `signed_block` fields found", "RestPublishedSignedBlockContents") - signed_block_data = some(reader.readValue(JsonString)) + signed_block_data = Opt.some(reader.readValue(JsonString)) if message.isSome() or signature.isSome(): reader.raiseUnexpectedField( "Found `signed_block` field alongside message or signature fields", "RestPublishedSignedBlockContents") signed_message = try: - some(RestJson.decode(string(signed_block_data.get()), - RestPublishedSignedBeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(signed_block_data.get()), + RestPublishedSignedBeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[RestPublishedSignedBeaconBlock]() + Opt.none(RestPublishedSignedBeaconBlock) if signed_message.isNone(): reader.raiseUnexpectedValue("Incorrect signed_block format") let blck = ForkedSignedBeaconBlock(signed_message.get()) - message = some(RestPublishedBeaconBlock( + message = Opt.some(RestPublishedBeaconBlock( case blck.kind of ConsensusFork.Phase0, ConsensusFork.Altair, ConsensusFork.Bellatrix, ConsensusFork.Capella: @@ -1719,7 +1758,7 @@ proc readValue*(reader: var JsonReader[RestJson], ForkedBeaconBlock.init(blck.denebData.message) )) - signature = some(forks.signature( + signature = Opt.some(forks.signature( ForkedSignedBeaconBlock(signed_message.get()))) of "signed_blob_sidecars": if signed_blob_sidecars.isSome(): @@ -1730,7 +1769,7 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedField( "Found `signed_block` field alongside message or signature fields", "RestPublishedSignedBlockContents") - signed_blob_sidecars = some(reader.readValue( + signed_blob_sidecars = Opt.some(reader.readValue( List[SignedBlobSidecar, Limit MAX_BLOBS_PER_BLOCK])) else: @@ -1792,8 +1831,8 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ForkedSignedBeaconBlock) {. raises: [IOError, SerializationError].} = var - version: Option[ConsensusFork] - data: Option[JsonString] + version: Opt[ConsensusFork] + data: Opt[JsonString] for fieldName in readObjectFields(reader): case fieldName @@ -1804,22 +1843,22 @@ proc readValue*(reader: var JsonReader[RestJson], let vres = reader.readValue(string) case vres of "phase0": - version = some(ConsensusFork.Phase0) + version = Opt.some(ConsensusFork.Phase0) of "altair": - version = some(ConsensusFork.Altair) + version = Opt.some(ConsensusFork.Altair) of "bellatrix": - version = some(ConsensusFork.Bellatrix) + version = Opt.some(ConsensusFork.Bellatrix) of "capella": - version = some(ConsensusFork.Capella) + version = Opt.some(ConsensusFork.Capella) of "deneb": - version = some(ConsensusFork.Deneb) + version = Opt.some(ConsensusFork.Deneb) else: reader.raiseUnexpectedValue("Incorrect version field value") of "data": if data.isSome(): reader.raiseUnexpectedField("Multiple data fields found", "ForkedSignedBeaconBlock") - data = some(reader.readValue(JsonString)) + data = Opt.some(reader.readValue(JsonString)) else: unrecognizedFieldWarning() @@ -1832,60 +1871,60 @@ proc readValue*(reader: var JsonReader[RestJson], of ConsensusFork.Phase0: let res = try: - some(RestJson.decode(string(data.get()), - phase0.SignedBeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + phase0.SignedBeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[phase0.SignedBeaconBlock]() + Opt.none(phase0.SignedBeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect phase0 block format") value = ForkedSignedBeaconBlock.init(res.get()) of ConsensusFork.Altair: let res = try: - some(RestJson.decode(string(data.get()), - altair.SignedBeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + altair.SignedBeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[altair.SignedBeaconBlock]() + Opt.none(altair.SignedBeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect altair block format") value = ForkedSignedBeaconBlock.init(res.get()) of ConsensusFork.Bellatrix: let res = try: - some(RestJson.decode(string(data.get()), - bellatrix.SignedBeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + bellatrix.SignedBeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[bellatrix.SignedBeaconBlock]() + Opt.none(bellatrix.SignedBeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect bellatrix block format") value = ForkedSignedBeaconBlock.init(res.get()) of ConsensusFork.Capella: let res = try: - some(RestJson.decode(string(data.get()), - capella.SignedBeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + capella.SignedBeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[capella.SignedBeaconBlock]() + Opt.none(capella.SignedBeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect capella block format") value = ForkedSignedBeaconBlock.init(res.get()) of ConsensusFork.Deneb: let res = try: - some(RestJson.decode(string(data.get()), - deneb.SignedBeaconBlock, - requireAllFields = true, - allowUnknownFields = true)) + Opt.some(RestJson.decode(string(data.get()), + deneb.SignedBeaconBlock, + requireAllFields = true, + allowUnknownFields = true)) except SerializationError: - none[deneb.SignedBeaconBlock]() + Opt.none(deneb.SignedBeaconBlock) if res.isNone(): reader.raiseUnexpectedValue("Incorrect deneb block format") value = ForkedSignedBeaconBlock.init(res.get()) @@ -1920,8 +1959,8 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ForkedHashedBeaconState) {. raises: [IOError, SerializationError].} = var - version: Option[ConsensusFork] - data: Option[JsonString] + version: Opt[ConsensusFork] + data: Opt[JsonString] for fieldName in readObjectFields(reader): case fieldName @@ -1931,17 +1970,17 @@ proc readValue*(reader: var JsonReader[RestJson], "ForkedBeaconState") let vres = reader.readValue(string) version = case vres - of "phase0": some(ConsensusFork.Phase0) - of "altair": some(ConsensusFork.Altair) - of "bellatrix": some(ConsensusFork.Bellatrix) - of "capella": some(ConsensusFork.Capella) - of "deneb": some(ConsensusFork.Deneb) + of "phase0": Opt.some(ConsensusFork.Phase0) + of "altair": Opt.some(ConsensusFork.Altair) + of "bellatrix": Opt.some(ConsensusFork.Bellatrix) + of "capella": Opt.some(ConsensusFork.Capella) + of "deneb": Opt.some(ConsensusFork.Deneb) else: reader.raiseUnexpectedValue("Incorrect version field value") of "data": if data.isSome(): reader.raiseUnexpectedField("Multiple data fields found", "ForkedBeaconState") - data = some(reader.readValue(JsonString)) + data = Opt.some(reader.readValue(JsonString)) else: unrecognizedFieldWarning() @@ -2052,7 +2091,7 @@ proc readValue*[T: SomeForkedLightClientObject]( if version.isSome: reader.raiseUnexpectedField("Multiple version fields found", T.name) let consensusFork = - decodeEthConsensusVersion(reader.readValue(string)).valueOr: + ConsensusFork.decodeString(reader.readValue(string)).valueOr: reader.raiseUnexpectedValue("Incorrect version field value") version.ok consensusFork of "data": @@ -2186,10 +2225,10 @@ proc readValue*(reader: var JsonReader[RestJson], value: var Web3SignerRequest) {. raises: [IOError, SerializationError].} = var - requestKind: Option[Web3SignerRequestKind] - forkInfo: Option[Web3SignerForkInfo] - signingRoot: Option[Eth2Digest] - data: Option[JsonString] + requestKind: Opt[Web3SignerRequestKind] + forkInfo: Opt[Web3SignerForkInfo] + signingRoot: Opt[Eth2Digest] + data: Opt[JsonString] proofs: seq[Web3SignerMerkleProof] dataName: string @@ -2200,7 +2239,7 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedField("Multiple `type` fields found", "Web3SignerRequest") let vres = reader.readValue(string) - requestKind = some( + requestKind = Opt.some( case vres of "AGGREGATION_SLOT": Web3SignerRequestKind.AggregationSlot @@ -2231,12 +2270,12 @@ proc readValue*(reader: var JsonReader[RestJson], if forkInfo.isSome(): reader.raiseUnexpectedField("Multiple `fork_info` fields found", "Web3SignerRequest") - forkInfo = some(reader.readValue(Web3SignerForkInfo)) + forkInfo = Opt.some(reader.readValue(Web3SignerForkInfo)) of "signingRoot": if signingRoot.isSome(): reader.raiseUnexpectedField("Multiple `signingRoot` fields found", "Web3SignerRequest") - signingRoot = some(reader.readValue(Eth2Digest)) + signingRoot = Opt.some(reader.readValue(Eth2Digest)) of "proofs": let newProofs = reader.readValue(seq[Web3SignerMerkleProof]) proofs.add(newProofs) @@ -2248,7 +2287,7 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedField("Multiple data fields found", "Web3SignerRequest") dataName = fieldName - data = some(reader.readValue(JsonString)) + data = Opt.some(reader.readValue(JsonString)) else: unrecognizedFieldWarning() @@ -2463,8 +2502,8 @@ proc writeValue*( proc readValue*(reader: var JsonReader[RestJson], value: var RemoteKeystoreStatus) {. raises: [IOError, SerializationError].} = - var message: Option[string] - var status: Option[KeystoreStatus] + var message: Opt[string] + var status: Opt[KeystoreStatus] for fieldName in readObjectFields(reader): case fieldName @@ -2472,13 +2511,13 @@ proc readValue*(reader: var JsonReader[RestJson], if message.isSome(): reader.raiseUnexpectedField("Multiple `message` fields found", "RemoteKeystoreStatus") - message = some(reader.readValue(string)) + message = Opt.some(reader.readValue(string)) of "status": if status.isSome(): reader.raiseUnexpectedField("Multiple `status` fields found", "RemoteKeystoreStatus") let res = reader.readValue(string) - status = some( + status = Opt.some( case res of "error": KeystoreStatus.error @@ -2525,10 +2564,10 @@ proc writeValue*( proc readValue*(reader: var JsonReader[RestJson], value: var Pbkdf2Params) {. raises: [SerializationError, IOError].} = var - dklen: Option[uint64] - c: Option[uint64] - prf: Option[PrfKind] - salt: Option[Pbkdf2Salt] + dklen: Opt[uint64] + c: Opt[uint64] + prf: Opt[PrfKind] + salt: Opt[Pbkdf2Salt] for fieldName in readObjectFields(reader): case fieldName @@ -2536,22 +2575,22 @@ proc readValue*(reader: var JsonReader[RestJson], value: var Pbkdf2Params) {. if dklen.isSome(): reader.raiseUnexpectedField("Multiple `dklen` fields found", "Pbkdf2Params") - dklen = some(reader.readValue(uint64)) + dklen = Opt.some(reader.readValue(uint64)) of "c": if c.isSome(): reader.raiseUnexpectedField("Multiple `c` fields found", "Pbkdf2Params") - c = some(reader.readValue(uint64)) + c = Opt.some(reader.readValue(uint64)) of "prf": if prf.isSome(): reader.raiseUnexpectedField("Multiple `prf` fields found", "Pbkdf2Params") - prf = some(reader.readValue(PrfKind)) + prf = Opt.some(reader.readValue(PrfKind)) of "salt": if salt.isSome(): reader.raiseUnexpectedField("Multiple `salt` fields found", "Pbkdf2Params") - salt = some(reader.readValue(Pbkdf2Salt)) + salt = Opt.some(reader.readValue(Pbkdf2Salt)) else: unrecognizedFieldWarning() @@ -2586,9 +2625,9 @@ proc writeValue*( proc readValue*(reader: var JsonReader[RestJson], value: var ScryptParams) {. raises: [SerializationError, IOError].} = var - dklen: Option[uint64] - n, p, r: Option[int] - salt: Option[ScryptSalt] + dklen: Opt[uint64] + n, p, r: Opt[int] + salt: Opt[ScryptSalt] for fieldName in readObjectFields(reader): case fieldName @@ -2596,7 +2635,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ScryptParams) {. if dklen.isSome(): reader.raiseUnexpectedField("Multiple `dklen` fields found", "ScryptParams") - dklen = some(reader.readValue(uint64)) + dklen = Opt.some(reader.readValue(uint64)) of "n": if n.isSome(): reader.raiseUnexpectedField("Multiple `n` fields found", @@ -2604,7 +2643,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ScryptParams) {. let res = reader.readValue(int) if res < 0: reader.raiseUnexpectedValue("Unexpected negative `n` value") - n = some(res) + n = Opt.some(res) of "p": if p.isSome(): reader.raiseUnexpectedField("Multiple `p` fields found", @@ -2612,7 +2651,7 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ScryptParams) {. let res = reader.readValue(int) if res < 0: reader.raiseUnexpectedValue("Unexpected negative `p` value") - p = some(res) + p = Opt.some(res) of "r": if r.isSome(): reader.raiseUnexpectedField("Multiple `r` fields found", @@ -2620,12 +2659,12 @@ proc readValue*(reader: var JsonReader[RestJson], value: var ScryptParams) {. let res = reader.readValue(int) if res < 0: reader.raiseUnexpectedValue("Unexpected negative `r` value") - r = some(res) + r = Opt.some(res) of "salt": if salt.isSome(): reader.raiseUnexpectedField("Multiple `salt` fields found", "ScryptParams") - salt = some(reader.readValue(ScryptSalt)) + salt = Opt.some(reader.readValue(ScryptSalt)) else: unrecognizedFieldWarning() @@ -2681,7 +2720,7 @@ proc readValue*(reader: var JsonReader[RestJson], var strKeystores: seq[string] passwords: seq[string] - strSlashing: Option[string] + strSlashing: Opt[string] for fieldName in readObjectFields(reader): case fieldName @@ -2694,7 +2733,7 @@ proc readValue*(reader: var JsonReader[RestJson], reader.raiseUnexpectedField( "Multiple `slashing_protection` fields found", "KeystoresAndSlashingProtection") - strSlashing = some(reader.readValue(string)) + strSlashing = Opt.some(reader.readValue(string)) else: unrecognizedFieldWarning() @@ -2727,9 +2766,9 @@ proc readValue*(reader: var JsonReader[RestJson], allowUnknownFields = true) except SerializationError: reader.raiseUnexpectedValue("Invalid slashing protection format") - some(db) + Opt.some(db) else: - none[SPDIR]() + Opt.none(SPDIR) value = KeystoresAndSlashingProtection( keystores: keystores, passwords: passwords, slashing_protection: slashing @@ -2748,9 +2787,9 @@ proc writeValue*( proc readValue*(reader: var JsonReader[RestJson], value: var RestActivityItem) {. raises: [SerializationError, IOError].} = - var index: Option[ValidatorIndex] - var epoch: Option[Epoch] - var active: Option[bool] + var index: Opt[ValidatorIndex] + var epoch: Opt[Epoch] + var active: Opt[bool] for fieldName in readObjectFields(reader): case fieldName @@ -2758,17 +2797,17 @@ proc readValue*(reader: var JsonReader[RestJson], if index.isSome(): reader.raiseUnexpectedField( "Multiple `index` fields found", "RestActivityItem") - index = some(reader.readValue(ValidatorIndex)) + index = Opt.some(reader.readValue(ValidatorIndex)) of "epoch": if epoch.isSome(): reader.raiseUnexpectedField( "Multiple `epoch` fields found", "RestActivityItem") - epoch = some(reader.readValue(Epoch)) + epoch = Opt.some(reader.readValue(Epoch)) of "active": if active.isSome(): reader.raiseUnexpectedField( "Multiple `active` fields found", "RestActivityItem") - active = some(reader.readValue(bool)) + active = Opt.some(reader.readValue(bool)) else: unrecognizedFieldIgnore() @@ -2794,8 +2833,8 @@ proc writeValue*( proc readValue*(reader: var JsonReader[RestJson], value: var RestLivenessItem) {. raises: [SerializationError, IOError].} = - var index: Option[ValidatorIndex] - var isLive: Option[bool] + var index: Opt[ValidatorIndex] + var isLive: Opt[bool] for fieldName in readObjectFields(reader): case fieldName @@ -2803,12 +2842,12 @@ proc readValue*(reader: var JsonReader[RestJson], if index.isSome(): reader.raiseUnexpectedField( "Multiple `index` fields found", "RestLivenessItem") - index = some(reader.readValue(ValidatorIndex)) + index = Opt.some(reader.readValue(ValidatorIndex)) of "is_live": if isLive.isSome(): reader.raiseUnexpectedField( "Multiple `is_live` fields found", "RestLivenessItem") - isLive = some(reader.readValue(bool)) + isLive = Opt.some(reader.readValue(bool)) else: unrecognizedFieldIgnore() @@ -2901,7 +2940,7 @@ proc readValue*(reader: var JsonReader[RestJson], var code: Opt[int] message: Opt[string] - stacktraces: Option[seq[string]] + stacktraces: Opt[seq[string]] for fieldName in readObjectFields(reader): case fieldName @@ -2936,7 +2975,7 @@ proc readValue*(reader: var JsonReader[RestJson], if stacktraces.isSome(): reader.raiseUnexpectedField("Multiple `stacktraces` fields found", "RestErrorMessage") - stacktraces = some(reader.readValue(seq[string])) + stacktraces = Opt.some(reader.readValue(seq[string])) else: unrecognizedFieldIgnore() @@ -2950,6 +2989,15 @@ proc readValue*(reader: var JsonReader[RestJson], stacktraces: stacktraces ) +proc writeValue*(writer: var JsonWriter[RestJson], value: RestErrorMessage) {. + raises: [IOError].} = + writer.beginRecord() + writer.writeField("code", value.code) + writer.writeField("message", value.message) + if value.stacktraces.isSome(): + writer.writeField("stacktraces", value.stacktraces.get()) + writer.endRecord() + ## VCRuntimeConfig proc readValue*(reader: var JsonReader[RestJson], value: var VCRuntimeConfig) {. @@ -2970,7 +3018,7 @@ proc decodeBody*( t: typedesc[RestPublishedSignedBeaconBlock], body: ContentBody, version: string - ): Result[RestPublishedSignedBeaconBlock, cstring] = + ): Result[RestPublishedSignedBeaconBlock, RestErrorMessage] = if body.contentType == ApplicationJsonMediaType: let data = try: @@ -2978,69 +3026,84 @@ proc decodeBody*( requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(data) elif body.contentType == OctetStreamMediaType: - let consensusFork = ? ConsensusFork.decodeString(version) + let consensusFork = ConsensusFork.decodeString(version).valueOr: + return err(RestErrorMessage.init(Http400, UnableDecodeVersionError, + [version, $error])) case consensusFork of ConsensusFork.Phase0: let blck = try: SSZ.decode(body.data, phase0.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck))) of ConsensusFork.Altair: let blck = try: SSZ.decode(body.data, altair.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck))) of ConsensusFork.Bellatrix: let blck = try: SSZ.decode(body.data, bellatrix.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck))) of ConsensusFork.Capella: let blck = try: SSZ.decode(body.data, capella.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck))) of ConsensusFork.Deneb: let blck = try: SSZ.decode(body.data, deneb.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBeaconBlock(ForkedSignedBeaconBlock.init(blck))) else: - return err("Unsupported or invalid content media type") + err(RestErrorMessage.init(Http415, "Invalid content type", + [version, $body.contentType])) proc decodeBody*( t: typedesc[RestPublishedSignedBlockContents], body: ContentBody, version: string - ): Result[RestPublishedSignedBlockContents, string] = + ): Result[RestPublishedSignedBlockContents, RestErrorMessage] = if body.contentType == ApplicationJsonMediaType: let data = try: @@ -3048,70 +3111,83 @@ proc decodeBody*( requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(data) elif body.contentType == OctetStreamMediaType: - let consensusFork = - decodeEthConsensusVersion(version).valueOr: - return err("Invalid or Unsupported consensus version") + let consensusFork = ConsensusFork.decodeString(version).valueOr: + return err(RestErrorMessage.init(Http400, UnableDecodeVersionError, + [version, $error])) case consensusFork of ConsensusFork.Phase0: let blck = try: SSZ.decode(body.data, phase0.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Phase0, phase0Data: blck)) of ConsensusFork.Altair: let blck = try: SSZ.decode(body.data, altair.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Altair, altairData: blck)) of ConsensusFork.Bellatrix: let blck = try: SSZ.decode(body.data, bellatrix.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Bellatrix, bellatrixData: blck)) of ConsensusFork.Capella: let blck = try: SSZ.decode(body.data, capella.SignedBeaconBlock) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Capella, capellaData: blck)) of ConsensusFork.Deneb: let blckContents = try: SSZ.decode(body.data, DenebSignedBlockContents) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err(RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err(RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Deneb, denebData: blckContents)) else: - return err("Unsupported or invalid content media type") + err(RestErrorMessage.init(Http415, "Invalid content type", + [version, $body.contentType])) proc decodeBody*[T](t: typedesc[T], body: ContentBody): Result[T, cstring] = @@ -3135,13 +3211,13 @@ proc decodeBodyJsonOrSsz*( t: typedesc[RestPublishedSignedBlockContents], body: ContentBody, version: string - ): Result[RestPublishedSignedBlockContents, string] = + ): Result[RestPublishedSignedBlockContents, RestErrorMessage] = if body.contentType == OctetStreamMediaType: decodeBody(RestPublishedSignedBlockContents, body, version) elif body.contentType == ApplicationJsonMediaType: - let consensusFork = - decodeEthConsensusVersion(version).valueOr: - return err("Invalid or Unsupported consensus version") + let consensusFork = ConsensusFork.decodeString(version).valueOr: + return err(RestErrorMessage.init(Http400, UnableDecodeVersionError, + [version, $error])) case consensusFork of ConsensusFork.Phase0: let blck = @@ -3150,13 +3226,16 @@ proc decodeBodyJsonOrSsz*( requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize JSON for fork " & - version & ": " & exc.formatMsg("")) + return err( + RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) except CatchableError as exc: - return err("Unexpected JSON deserialization error: " & exc.msg) + return err( + RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Phase0, phase0Data: blck)) of ConsensusFork.Altair: @@ -3166,12 +3245,16 @@ proc decodeBodyJsonOrSsz*( requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + return err( + RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err( + RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Altair, altairData: blck)) of ConsensusFork.Bellatrix: @@ -3181,12 +3264,16 @@ proc decodeBodyJsonOrSsz*( requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + return err( + RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err( + RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Bellatrix, bellatrixData: blck)) of ConsensusFork.Capella: @@ -3196,12 +3283,16 @@ proc decodeBodyJsonOrSsz*( requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + return err( + RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err( + RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Capella, capellaData: blck)) of ConsensusFork.Deneb: @@ -3211,20 +3302,24 @@ proc decodeBodyJsonOrSsz*( requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + return err( + RestErrorMessage.init(Http400, UnableDecodeError, + [version, exc.formatMsg("")])) + except CatchableError as exc: + return err( + RestErrorMessage.init(Http400, UnexpectedDecodeError, + [version, $exc.msg])) ok(RestPublishedSignedBlockContents( kind: ConsensusFork.Deneb, denebData: blckContents)) else: - return err("Unsupported or invalid content media type") - + err(RestErrorMessage.init(Http415, "Invalid content type", + [version, $body.contentType])) proc decodeBodyJsonOrSsz*[T](t: typedesc[T], - body: ContentBody): Result[T, cstring] = + body: ContentBody): Result[T, RestErrorMessage] = if body.contentType == ApplicationJsonMediaType: let data = try: @@ -3232,24 +3327,31 @@ proc decodeBodyJsonOrSsz*[T](t: typedesc[T], requireAllFields = true, allowUnknownFields = true) except SerializationError as exc: - debug "Failed to deserialize REST JSON data", + debug "Failed to decode JSON data", err = exc.formatMsg(""), data = string.fromBytes(body.data) - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + return err( + RestErrorMessage.init(Http400, UnableDecodeError, + [exc.formatMsg("")])) + except CatchableError as exc: + return err( + RestErrorMessage.init(Http400, UnexpectedDecodeError, [$exc.msg])) ok(data) elif body.contentType == OctetStreamMediaType: let blck = try: SSZ.decode(body.data, T) - except SerializationError: - return err("Unable to deserialize data") - except CatchableError: - return err("Unexpected deserialization error") + except SerializationError as exc: + return err( + RestErrorMessage.init(Http400, UnableDecodeError, + [exc.formatMsg("")])) + except CatchableError as exc: + return err( + RestErrorMessage.init(Http400, UnexpectedDecodeError, [$exc.msg])) ok(blck) else: - return err("Unsupported content type") + err(RestErrorMessage.init(Http415, "Invalid content type", + [$body.contentType])) proc encodeBytes*[T: EncodeTypes](value: T, contentType: string): RestResult[seq[byte]] = @@ -3352,7 +3454,7 @@ proc decodeBytes*[T: DecodeConsensysTypes]( return err("Serialization error") elif mediaType == OctetStreamMediaType: when t is ProduceBlockResponseV2: - let fork = decodeEthConsensusVersion(consensusVersion).valueOr: + let fork = ConsensusFork.decodeString(consensusVersion).valueOr: return err("Invalid or Unsupported consensus version") case fork of ConsensusFork.Deneb: @@ -3376,7 +3478,7 @@ proc decodeBytes*[T: DecodeConsensysTypes]( ok(ProduceBlockResponseV2(kind: ConsensusFork.Phase0, phase0Data: blck)) elif t is ProduceBlindedBlockResponse: - let fork = decodeEthConsensusVersion(consensusVersion).valueOr: + let fork = ConsensusFork.decodeString(consensusVersion).valueOr: return err("Invalid or Unsupported consensus version") case fork of ConsensusFork.Deneb: diff --git a/beacon_chain/spec/eth2_apis/rest_keymanager_types.nim b/beacon_chain/spec/eth2_apis/rest_keymanager_types.nim index ee61912a6..7907b6849 100644 --- a/beacon_chain/spec/eth2_apis/rest_keymanager_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_keymanager_types.nim @@ -1,5 +1,5 @@ # beacon_chain -# Copyright (c) 2021-2022 Status Research & Development GmbH +# Copyright (c) 2021-2023 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). @@ -31,7 +31,7 @@ type KeystoresAndSlashingProtection* = object keystores*: seq[Keystore] passwords*: seq[string] - slashing_protection*: Option[SPDIR] + slashing_protection*: Opt[SPDIR] DeleteKeystoresBody* = object pubkeys*: seq[ValidatorPubKey] @@ -64,7 +64,7 @@ type RemoteKeystoreStatus* = object status*: KeystoreStatus - message*: Option[string] + message*: Opt[string] DeleteRemoteKeystoresResponse* = object data*: seq[RemoteKeystoreStatus] diff --git a/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim b/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim index 3fb504d6f..51dc5b2b7 100644 --- a/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim +++ b/beacon_chain/spec/eth2_apis/rest_light_client_calls.nim @@ -209,7 +209,7 @@ proc getLightClientBootstrap*( return case resp.status of 200: - let consensusForkRes = decodeEthConsensusVersion( + let consensusForkRes = ConsensusFork.decodeString( resp.headers.getString("eth-consensus-version")) if consensusForkRes.isErr: raiseRestDecodingBytesError(cstring(consensusForkRes.error)) @@ -291,7 +291,7 @@ proc getLightClientFinalityUpdate*( return case resp.status of 200: - let consensusForkRes = decodeEthConsensusVersion( + let consensusForkRes = ConsensusFork.decodeString( resp.headers.getString("eth-consensus-version")) if consensusForkRes.isErr: raiseRestDecodingBytesError(cstring(consensusForkRes.error)) @@ -333,7 +333,7 @@ proc getLightClientOptimisticUpdate*( return case resp.status of 200: - let consensusForkRes = decodeEthConsensusVersion( + let consensusForkRes = ConsensusFork.decodeString( resp.headers.getString("eth-consensus-version")) if consensusForkRes.isErr: raiseRestDecodingBytesError(cstring(consensusForkRes.error)) diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index c5354b2e4..9b0773cd8 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -15,7 +15,7 @@ import std/[json, tables], - stew/base10, web3/ethtypes, + stew/base10, web3/ethtypes, httputils, ".."/forks, ".."/datatypes/[phase0, altair, bellatrix, deneb], ".."/mev/[capella_mev, deneb_mev] @@ -23,7 +23,7 @@ import from ".."/datatypes/capella import BeaconBlockBody export forks, phase0, altair, bellatrix, capella, capella_mev, deneb_mev, - tables + tables, httputils const # https://github.com/ethereum/eth2.0-APIs/blob/master/apis/beacon/states/validator_balances.yaml#L17 @@ -189,7 +189,7 @@ type ## https://github.com/ethereum/beacon-APIs/blob/v2.4.0/types/http.yaml#L130 code*: int message*: string - stacktraces*: Option[seq[string]] + stacktraces*: Opt[seq[string]] RestIndexedErrorMessage* = object ## https://github.com/ethereum/beacon-APIs/blob/v2.4.0/types/http.yaml#L145 @@ -447,8 +447,8 @@ type ValidatorRegistration Web3SignerRequest* = object - signingRoot*: Option[Eth2Digest] - forkInfo* {.serializedFieldName: "fork_info".}: Option[Web3SignerForkInfo] + signingRoot*: Opt[Eth2Digest] + forkInfo* {.serializedFieldName: "fork_info".}: Opt[Web3SignerForkInfo] case kind* {.dontSerialize.}: Web3SignerRequestKind of Web3SignerRequestKind.AggregationSlot: aggregationSlot* {. @@ -641,11 +641,11 @@ func init*(t: typedesc[RestValidatorBalance], index: ValidatorIndex, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: Slot, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.AggregationSlot, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -654,11 +654,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: AggregateAndProof, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.AggregateAndProof, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -667,11 +667,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: AttestationData, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.Attestation, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -681,11 +681,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: Web3SignerForkedBeaconBlock, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.BlockV2, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -696,11 +696,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: Web3SignerForkedBeaconBlock, proofs: openArray[Web3SignerMerkleProof], - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.BlockV2, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -710,7 +710,7 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], genesisForkVersion: Version, data: DepositMessage, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.Deposit, @@ -725,11 +725,11 @@ func init*(t: typedesc[Web3SignerRequest], genesisForkVersion: Version, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: Epoch, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.RandaoReveal, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -738,11 +738,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: VoluntaryExit, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.VoluntaryExit, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -751,11 +751,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, blockRoot: Eth2Digest, - slot: Slot, signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + slot: Slot, signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.SyncCommitteeMessage, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -767,11 +767,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: SyncAggregatorSelectionData, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.SyncCommitteeSelectionProof, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -781,11 +781,11 @@ func init*(t: typedesc[Web3SignerRequest], fork: Fork, func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: ContributionAndProof, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.SyncCommitteeContributionAndProof, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -797,11 +797,11 @@ from stew/byteutils import to0xHex func init*(t: typedesc[Web3SignerRequest], fork: Fork, genesis_validators_root: Eth2Digest, data: ValidatorRegistrationV1, - signingRoot: Option[Eth2Digest] = none[Eth2Digest]() + signingRoot: Opt[Eth2Digest] = Opt.none(Eth2Digest) ): Web3SignerRequest = Web3SignerRequest( kind: Web3SignerRequestKind.ValidatorRegistration, - forkInfo: some(Web3SignerForkInfo( + forkInfo: Opt.some(Web3SignerForkInfo( fork: fork, genesis_validators_root: genesis_validators_root )), signingRoot: signingRoot, @@ -864,3 +864,31 @@ func init*(t: typedesc[RestSignedContributionAndProof], signature: signature) func len*(p: RestWithdrawalPrefix): int = sizeof(p) + +func init*(t: typedesc[RestErrorMessage], code: int, + message: string): RestErrorMessage = + RestErrorMessage(code: code, message: message) + +func init*(t: typedesc[RestErrorMessage], code: int, + message: string, stacktrace: string): RestErrorMessage = + RestErrorMessage(code: code, message: message, + stacktraces: Opt.some(@[stacktrace])) + +func init*(t: typedesc[RestErrorMessage], code: int, + message: string, stacktrace: openArray[string]): RestErrorMessage = + RestErrorMessage(code: code, message: message, + stacktraces: Opt.some(@stacktrace)) + +func init*(t: typedesc[RestErrorMessage], code: HttpCode, + message: string): RestErrorMessage = + RestErrorMessage(code: code.toInt(), message: message) + +func init*(t: typedesc[RestErrorMessage], code: HttpCode, + message: string, stacktrace: string): RestErrorMessage = + RestErrorMessage(code: code.toInt(), message: message, + stacktraces: Opt.some(@[stacktrace])) + +func init*(t: typedesc[RestErrorMessage], code: HttpCode, + message: string, stacktrace: openArray[string]): RestErrorMessage = + RestErrorMessage(code: code.toInt(), message: message, + stacktraces: Opt.some(@stacktrace)) diff --git a/tests/test_serialization.nim b/tests/test_serialization.nim index ce6a1b1c0..95293230e 100644 --- a/tests/test_serialization.nim +++ b/tests/test_serialization.nim @@ -15,20 +15,6 @@ import suite "Serialization/deserialization test suite": test "RestErrorMessage parser tests": - proc init(t: typedesc[RestErrorMessage], status: int, - message: string): RestErrorMessage = - RestErrorMessage( - code: status, message: message, - stacktraces: none[seq[string]]() - ) - proc init(t: typedesc[RestErrorMessage], status: int, - message: string, - stacktraces: openArray[string]): RestErrorMessage = - RestErrorMessage( - code: status, message: message, - stacktraces: some(@stacktraces) - ) - const GoodTestVectors = [ ( """{"code": 500, "message": "block not found"}""", @@ -138,6 +124,7 @@ suite "Serialization/deserialization test suite": Opt.some(contentType)) checkpoint test check res.isErr() + test "RestErrorMessage writer tests": proc `==`(a: RestApiResponse, b: string): bool = case a.kind diff --git a/vendor/nim-http-utils b/vendor/nim-http-utils index a85bd52ae..87b7cbf03 160000 --- a/vendor/nim-http-utils +++ b/vendor/nim-http-utils @@ -1 +1 @@ -Subproject commit a85bd52ae0a956983ca6b3267c72961d2ec0245f +Subproject commit 87b7cbf032c90b9e6b446081f4a647e950362cec