diff --git a/beacon_chain/rpc/rest_beacon_api.nim b/beacon_chain/rpc/rest_beacon_api.nim index bbed3dea6..16cb88ed1 100644 --- a/beacon_chain/rpc/rest_beacon_api.nim +++ b/beacon_chain/rpc/rest_beacon_api.nim @@ -236,6 +236,136 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = ) return RestApiResponse.jsonError(Http404, StateNotFoundError) + proc getIndices( + node: BeaconNode, + validatorIds: openArray[ValidatorIdent], + state: ForkedHashedBeaconState + ): Result[seq[ValidatorIndex], RestErrorMessage] = + var + keyset: HashSet[ValidatorPubKey] + indexset: HashSet[ValidatorIndex] + + let validatorsCount = lenu64(getStateField(state, validators)) + + for item in validatorIds: + case item.kind + of ValidatorQueryKind.Key: + # Test for uniqueness of value. + if keyset.containsOrIncl(item.key): + return err(RestErrorMessage.init( + Http400, NonUniqueValidatorIdError, $item.key)) + of ValidatorQueryKind.Index: + let vindex = item.index.toValidatorIndex().valueOr: + case error + of ValidatorIndexError.TooHighValue: + return err(RestErrorMessage.init( + Http400, TooHighValidatorIndexValueError)) + of ValidatorIndexError.UnsupportedValue: + return err(RestErrorMessage.init( + Http500, UnsupportedValidatorIndexValueError)) + if uint64(vindex) < validatorsCount: + # We're only adding validator indices which are present in + # validators list at this moment. + if indexset.containsOrIncl(vindex): + return err(RestErrorMessage.init( + Http400, NonUniqueValidatorIdError, + Base10.toString(uint64(vindex)))) + + if len(keyset) > 0: + let optIndices = keysToIndices(node.restKeysCache, state, keyset.toSeq()) + # Remove all the duplicates. + for item in optIndices: + # We ignore missing keys. + if item.isSome(): + indexset.incl(item.get()) + ok(indexset.toSeq()) + + proc getValidators( + node: BeaconNode, + bslot: BlockSlotId, + validatorsMask: ValidatorFilter, + validatorIds: openArray[ValidatorIdent] + ): RestApiResponse = + node.withStateForBlockSlotId(bslot): + let + stateEpoch = getStateField(state, slot).epoch() + validatorsCount = lenu64(getStateField(state, validators)) + indices = node.getIndices(validatorIds, state).valueOr: + return RestApiResponse.jsonError(error) + response = + block: + var res: seq[RestValidator] + if len(indices) == 0: + # Case when `len(indices) == 0 and len(validatorIds) != 0` means + # that we can't find validator identifiers in state, so we should + # return empty response. + if len(validatorIds) == 0: + # There are no indices, so we're going to filter all the + # validators. + for index, validator in getStateField(state, validators): + let + balance = getStateField(state, balances).item(index) + status = validator.getStatus(stateEpoch).valueOr: + return RestApiResponse.jsonError( + Http400, ValidatorStatusNotFoundError, $error) + if status in validatorsMask: + res.add(RestValidator.init(ValidatorIndex(index), balance, + toString(status), validator)) + else: + for index in indices: + let + validator = getStateField(state, validators).item(index) + balance = getStateField(state, balances).item(index) + status = validator.getStatus(stateEpoch).valueOr: + return RestApiResponse.jsonError( + Http400, ValidatorStatusNotFoundError, $error) + if status in validatorsMask: + res.add(RestValidator.init(index, balance, toString(status), + validator)) + res + return RestApiResponse.jsonResponseFinalized( + response, + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) + ) + RestApiResponse.jsonError(Http404, StateNotFoundError) + + proc getBalances( + node: BeaconNode, + bslot: BlockSlotId, + validatorIds: openArray[ValidatorIdent] + ): RestApiResponse = + node.withStateForBlockSlotId(bslot): + let + validatorsCount = lenu64(getStateField(state, validators)) + indices = node.getIndices(validatorIds, state).valueOr: + return RestApiResponse.jsonError(error) + response = + block: + var res: seq[RestValidatorBalance] + if len(indices) == 0: + # Case when `len(indices) == 0 and len(validatorIds) != 0` means + # that we can't find validator identifiers in state, so we should + # return empty response. + if len(validatorIds) == 0: + # There are no indices, so we're going to return balances of all + # known validators. + for index, balance in getStateField(state, balances): + res.add(RestValidatorBalance.init(ValidatorIndex(index), + balance)) + else: + for index in indices: + let balance = getStateField(state, balances).item(index) + res.add(RestValidatorBalance.init(index, balance)) + res + + return RestApiResponse.jsonResponseFinalized( + response, + node.getStateOptimistic(state), + node.dag.isFinalized(bslot.bid) + ) + RestApiResponse.jsonError(Http404, StateNotFoundError) + # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidators router.api(MethodGet, "/eth/v1/beacon/states/{state_id}/validators") do ( state_id: StateIdent, id: seq[ValidatorIdent], @@ -249,119 +379,54 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = # TODO (cheatfate): Its impossible to retrieve state by `state_root` # in current version of database. return RestApiResponse.jsonError(Http500, NoImplementationError) - return RestApiResponse.jsonError(Http404, StateNotFoundError, - $error) - let validatorIds = - block: - if id.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidValidatorIdValueError) - let ires = id.get() - if len(ires) > ServerMaximumValidatorIds: - return RestApiResponse.jsonError(Http414, - MaximumNumberOfValidatorIdsError) - ires - - let validatorsMask = - block: - if status.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidValidatorStatusValueError) - let res = validateFilter(status.get()) - if res.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidValidatorStatusValueError, - $res.error()) - res.get() - - node.withStateForBlockSlotId(bslot): - let - current_epoch = getStateField(state, slot).epoch() - validatorsCount = lenu64(getStateField(state, validators)) - - let indices = + return RestApiResponse.jsonError( + Http404, StateNotFoundError, $error) + validatorIds = block: - var keyset: HashSet[ValidatorPubKey] - var indexset: HashSet[ValidatorIndex] - for item in validatorIds: - case item.kind - of ValidatorQueryKind.Key: - keyset.incl(item.key) - of ValidatorQueryKind.Index: - let vindex = - block: - let vres = item.index.toValidatorIndex() - if vres.isErr(): - case vres.error() - of ValidatorIndexError.TooHighValue: - return RestApiResponse.jsonError(Http400, - TooHighValidatorIndexValueError) - of ValidatorIndexError.UnsupportedValue: - return RestApiResponse.jsonError(Http500, - UnsupportedValidatorIndexValueError) - let index = vres.get() - index - if uint64(vindex) < validatorsCount: - # We only adding validator indices which are present in - # validators list at this moment. - indexset.incl(vindex) - - if len(keyset) > 0: - let optIndices = keysToIndices(node.restKeysCache, state, - keyset.toSeq()) - # Remove all the duplicates. - for item in optIndices: - # We ignore missing keys. - if item.isSome(): - indexset.incl(item.get()) - indexset.toSeq() - - let response = + if id.isErr(): + return RestApiResponse.jsonError( + Http400, InvalidValidatorIdValueError) + let ires = id.get() + if len(ires) > ServerMaximumValidatorIds: + return RestApiResponse.jsonError( + Http414, MaximumNumberOfValidatorIdsError) + ires + validatorsMask = block: - var res: seq[RestValidator] - if len(indices) == 0: - # Case when `len(indices) == 0 and len(validatorIds) != 0` means - # that we can't find validator identifiers in state, so we should - # return empty response. - if len(validatorIds) == 0: - # There is no indices, so we going to filter all the validators. - for index, validator in getStateField(state, validators): - let - balance = getStateField(state, balances).item(index) - status = - block: - let sres = validator.getStatus(current_epoch) - if sres.isErr(): - return RestApiResponse.jsonError(Http400, - ValidatorStatusNotFoundError, - $sres.get()) - sres.get() - if status in validatorsMask: - res.add(RestValidator.init(ValidatorIndex(index), balance, - toString(status), validator)) - else: - for index in indices: - let - validator = getStateField(state, validators).item(index) - balance = getStateField(state, balances).item(index) - status = - block: - let sres = validator.getStatus(current_epoch) - if sres.isErr(): - return RestApiResponse.jsonError(Http400, - ValidatorStatusNotFoundError, - $sres.get()) - sres.get() - if status in validatorsMask: - res.add(RestValidator.init(index, balance, toString(status), - validator)) - res - return RestApiResponse.jsonResponseFinalized( - response, - node.getStateOptimistic(state), - node.dag.isFinalized(bslot.bid) - ) - return RestApiResponse.jsonError(Http404, StateNotFoundError) + if status.isErr(): + return RestApiResponse.jsonError(Http400, + InvalidValidatorStatusValueError) + validateFilter(status.get()).valueOr: + return RestApiResponse.jsonError( + Http400, InvalidValidatorStatusValueError, $error) + getValidators(node, bslot, validatorsMask, validatorIds) + + # https://ethereum.github.io/beacon-APIs/#/Beacon/postStateValidators + router.api(MethodPost, "/eth/v1/beacon/states/{state_id}/validators") do ( + state_id: StateIdent, contentBody: Option[ContentBody]) -> RestApiResponse: + let + (validatorIds, validatorsMask) = + block: + if contentBody.isNone(): + return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) + let request = + decodeBody(RestValidatorRequest, contentBody.get()).valueOr: + return RestApiResponse.jsonError( + Http400, InvalidRequestBodyError, $error) + let + ids = request.ids.valueOr: @[] + filter = request.status.valueOr: {} + (ids, filter) + sid = state_id.valueOr: + return RestApiResponse.jsonError(Http400, InvalidStateIdValueError, + $error) + bslot = node.getBlockSlotId(sid).valueOr: + if sid.kind == StateQueryKind.Root: + # TODO (cheatfate): Its impossible to retrieve state by `state_root` + # in current version of database. + return RestApiResponse.jsonError(Http500, NoImplementationError) + return RestApiResponse.jsonError(Http404, StateNotFoundError, $error) + getValidators(node, bslot, validatorsMask, validatorIds) # https://ethereum.github.io/beacon-APIs/#/Beacon/getStateValidator router.api(MethodGet, @@ -441,84 +506,42 @@ proc installBeaconApiHandlers*(router: var RestRouter, node: BeaconNode) = # TODO (cheatfate): Its impossible to retrieve state by `state_root` # in current version of database. return RestApiResponse.jsonError(Http500, NoImplementationError) - return RestApiResponse.jsonError(Http404, StateNotFoundError, - $error) - - let validatorIds = - block: - if id.isErr(): - return RestApiResponse.jsonError(Http400, - InvalidValidatorIdValueError) - let ires = id.get() - if len(ires) > ServerMaximumValidatorIds: - return RestApiResponse.jsonError(Http400, - MaximumNumberOfValidatorIdsError) - ires - - node.withStateForBlockSlotId(bslot): - let validatorsCount = lenu64(getStateField(state, validators)) - - let indices = + return RestApiResponse.jsonError(Http404, StateNotFoundError, $error) + validatorIds = block: - var keyset: HashSet[ValidatorPubKey] - var indexset: HashSet[ValidatorIndex] - for item in validatorIds: - case item.kind - of ValidatorQueryKind.Key: - keyset.incl(item.key) - of ValidatorQueryKind.Index: - let vindex = - block: - let vres = item.index.toValidatorIndex() - if vres.isErr(): - case vres.error() - of ValidatorIndexError.TooHighValue: - return RestApiResponse.jsonError(Http400, - TooHighValidatorIndexValueError) - of ValidatorIndexError.UnsupportedValue: - return RestApiResponse.jsonError(Http500, - UnsupportedValidatorIndexValueError) - vres.get() - # We only adding validator indices which are present in - # validators list at this moment. - if uint64(vindex) < validatorsCount: - indexset.incl(vindex) + if id.isErr(): + return RestApiResponse.jsonError( + Http400, InvalidValidatorIdValueError) + let ires = id.get() + if len(ires) > ServerMaximumValidatorIds: + return RestApiResponse.jsonError( + Http400, MaximumNumberOfValidatorIdsError) + ires + getBalances(node, bslot, validatorIds) - if len(keyset) > 0: - let optIndices = keysToIndices(node.restKeysCache, state, - keyset.toSeq()) - # Remove all the duplicates. - for item in optIndices: - # We ignore missing keys. - if item.isSome(): - indexset.incl(item.get()) - indexset.toSeq() - - let response = + # https://ethereum.github.io/beacon-APIs/#/Beacon/postStateValidatorBalances + router.api(MethodPost, + "/eth/v1/beacon/states/{state_id}/validator_balances") do ( + state_id: StateIdent, contentBody: Option[ContentBody]) -> RestApiResponse: + let + validatorIds = block: - var res: seq[RestValidatorBalance] - if len(indices) == 0: - # Case when `len(indices) == 0 and len(validatorIds) != 0` means - # that we can't find validator identifiers in state, so we should - # return empty response. - if len(validatorIds) == 0: - # There is no indices, so we going to return balances of all - # known validators. - for index, balance in getStateField(state, balances): - res.add(RestValidatorBalance.init(ValidatorIndex(index), - balance)) - else: - for index in indices: - let balance = getStateField(state, balances).item(index) - res.add(RestValidatorBalance.init(index, balance)) - res - return RestApiResponse.jsonResponseFinalized( - response, - node.getStateOptimistic(state), - node.dag.isFinalized(bslot.bid) - ) - - return RestApiResponse.jsonError(Http404, StateNotFoundError) + if contentBody.isNone(): + return RestApiResponse.jsonError(Http400, EmptyRequestBodyError) + let body = contentBody.get() + decodeBody(seq[ValidatorIdent], body).valueOr: + return RestApiResponse.jsonError( + Http400, InvalidValidatorIdValueError, $error) + sid = state_id.valueOr: + return RestApiResponse.jsonError(Http400, InvalidStateIdValueError, + $error) + bslot = node.getBlockSlotId(sid).valueOr: + if sid.kind == StateQueryKind.Root: + # TODO (cheatfate): Its impossible to retrieve state by `state_root` + # in current version of database. + return RestApiResponse.jsonError(Http500, NoImplementationError) + return RestApiResponse.jsonError(Http404, StateNotFoundError, $error) + getBalances(node, bslot, validatorIds) # https://ethereum.github.io/beacon-APIs/#/Beacon/getEpochCommittees router.api(MethodGet, diff --git a/beacon_chain/rpc/rest_constants.nim b/beacon_chain/rpc/rest_constants.nim index 49819d723..5df072a33 100644 --- a/beacon_chain/rpc/rest_constants.nim +++ b/beacon_chain/rpc/rest_constants.nim @@ -25,7 +25,9 @@ const BlockNotFoundError* = "Block header/data has not been found" EmptyRequestBodyError* = - "Empty request's body" + "Empty request body" + InvalidRequestBodyError* = + "Invalid request body" InvalidBlockObjectError* = "Unable to decode block object(s)" InvalidAttestationObjectError* = @@ -100,7 +102,9 @@ const InvalidBlockIdValueError* = "Invalid block identifier value" InvalidValidatorIdValueError* = - "Invalid validator's identifier value(s)" + "Invalid validator identifier value(s)" + NonUniqueValidatorIdError* = + "Non-unique validator identifier value(s)" MaximumNumberOfValidatorIdsError* = "Maximum number of validator identifier values exceeded" InvalidValidatorStatusValueError* = diff --git a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim index 2474cf39d..f6b63e696 100644 --- a/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim +++ b/beacon_chain/spec/eth2_apis/eth2_rest_serialization.nim @@ -3991,6 +3991,45 @@ proc encodeString*(value: set[EventTopic]): Result[string, cstring] = res.setLen(len(res) - 1) ok(res) +proc toList*(value: set[ValidatorFilterKind]): seq[string] = + const + pendingSet = {ValidatorFilterKind.PendingInitialized, + ValidatorFilterKind.PendingQueued} + activeSet = {ValidatorFilterKind.ActiveOngoing, + ValidatorFilterKind.ActiveExiting, + ValidatorFilterKind.ActiveSlashed} + exitedSet = {ValidatorFilterKind.ExitedUnslashed, + ValidatorFilterKind.ExitedSlashed} + withdrawSet = {ValidatorFilterKind.WithdrawalPossible, + ValidatorFilterKind.WithdrawalDone} + var + res: seq[string] + v = value + + template processSet(argSet, argName: untyped): untyped = + if argSet * v == argSet: + res.add(argName) + v.excl(argSet) + + template processSingle(argSingle, argName): untyped = + if argSingle in v: + res.add(argName) + + processSet(pendingSet, "pending") + processSet(activeSet, "active") + processSet(exitedSet, "exited") + processSet(withdrawSet, "withdrawal") + processSingle(ValidatorFilterKind.PendingInitialized, "pending_initialized") + processSingle(ValidatorFilterKind.PendingQueued, "pending_queued") + processSingle(ValidatorFilterKind.ActiveOngoing, "active_ongoing") + processSingle(ValidatorFilterKind.ActiveExiting, "active_exiting") + processSingle(ValidatorFilterKind.ActiveSlashed, "active_slashed") + processSingle(ValidatorFilterKind.ExitedUnslashed, "exited_unslashed") + processSingle(ValidatorFilterKind.ExitedSlashed, "exited_slashed") + processSingle(ValidatorFilterKind.WithdrawalPossible, "withdrawal_possible") + processSingle(ValidatorFilterKind.WithdrawalDone, "withdrawal_done") + res + proc decodeString*(t: typedesc[ValidatorSig], value: string): Result[ValidatorSig, cstring] = if len(value) != ValidatorSigSize + 2: @@ -4215,3 +4254,82 @@ proc decodeString*(t: typedesc[EventBeaconBlockObject], allowUnknownFields = true)) except SerializationError as exc: err(exc.formatMsg("")) + +## ValidatorIdent +proc writeValue*(w: var JsonWriter[RestJson], + value: ValidatorIdent) {.raises: [IOError].} = + writeValue(w, value.encodeString().get()) + +proc readValue*(reader: var JsonReader[RestJson], + value: var ValidatorIdent) {. + raises: [IOError, SerializationError].} = + value = decodeString(ValidatorIdent, reader.readValue(string)).valueOr: + raise newException(SerializationError, $error) + +## RestValidatorRequest +proc readValue*(reader: var JsonReader[RestJson], + value: var RestValidatorRequest) {. + raises: [IOError, SerializationError].} = + var + statuses: Opt[seq[string]] + ids: Opt[seq[string]] + + for fieldName in readObjectFields(reader): + case fieldName + of "ids": + if ids.isSome(): + reader.raiseUnexpectedField("Multiple `ids` fields found", + "RestValidatorRequest") + ids = Opt.some(reader.readValue(seq[string])) + of "statuses": + if statuses.isSome(): + reader.raiseUnexpectedField("Multiple `statuses` fields found", + "RestValidatorRequest") + statuses = Opt.some(reader.readValue(seq[string])) + else: + unrecognizedFieldWarning() + + let + validatorIds = + block: + # Test for uniqueness of value will be happened on higher layer. + if ids.isSome(): + var res: seq[ValidatorIdent] + for item in ids.get(): + let value = decodeString(ValidatorIdent, item).valueOr: + reader.raiseUnexpectedValue($error) + res.add(value) + Opt.some(res) + else: + Opt.none(seq[ValidatorIdent]) + filter = + block: + if statuses.isSome(): + var res: ValidatorFilter + for item in statuses.get(): + let value = decodeString(ValidatorFilter, item).valueOr: + reader.raiseUnexpectedValue($error) + # Test for uniqueness of value. + if value * res != {}: + reader.raiseUnexpectedValue( + "The `statuses` array should consist of only unique values") + res.incl(value) + Opt.some(res) + else: + Opt.none(ValidatorFilter) + + value = RestValidatorRequest(ids: validatorIds, status: filter) + +proc writeValue*(writer: var JsonWriter[RestJson], + value: RestValidatorRequest) {.raises: [IOError].} = + writer.beginRecord() + if value.ids.isSome(): + var res: seq[string] + for item in value.ids.get(): + res.add(item.encodeString().get()) + writer.writeField("ids", res) + if value.status.isSome(): + let res = value.status.get().toList() + if len(res) > 0: + writer.writeField("statuses", res) + writer.endRecord() diff --git a/beacon_chain/spec/eth2_apis/rest_types.nim b/beacon_chain/spec/eth2_apis/rest_types.nim index 6b6c9361e..838959369 100644 --- a/beacon_chain/spec/eth2_apis/rest_types.nim +++ b/beacon_chain/spec/eth2_apis/rest_types.nim @@ -121,6 +121,10 @@ type RestNumeric* = distinct int + RestValidatorRequest* = object + ids*: Opt[seq[ValidatorIdent]] + status*: Opt[ValidatorFilter] + RestAttesterDuty* = object pubkey*: ValidatorPubKey validator_index*: ValidatorIndex diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json index ec19ceb58..772068f10 100644 --- a/ncli/resttest-rules.json +++ b/ncli/resttest-rules.json @@ -919,9 +919,9 @@ "headers": {"Accept": "application/json"} }, "response": { - "status": {"operator": "equals", "value": "200"}, + "status": {"operator": "equals", "value": "400"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] } }, { @@ -932,9 +932,9 @@ "headers": {"Accept": "application/json"} }, "response": { - "status": {"operator": "equals", "value": "200"}, + "status": {"operator": "equals", "value": "400"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] } }, { @@ -1015,6 +1015,724 @@ "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] } }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Empty request", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators"], + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"1\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators"], + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\",\"11\",\"12\",\"13\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\"20\",\"21\",\"22\",\"23\",\"24\",\"25\",\"26\",\"27\",\"28\",\"29\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "testnet", "post"], + "comment": "Index which not exists in state", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"2147483647\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": []}] + } + }, + { + "topics": ["beacon", "states_validators", "testnet", "post"], + "comment": "Key which is not exist in state", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": []}] + } + }, + { + "topics": ["beacon", "states_validators", "testnet", "post"], + "comment": "Index and key which not exists in state", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"2147483647\",\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": []}] + } + }, + { + "topics": ["beacon", "states_validators", "testnet", "post"], + "comment": "3 identifiers where only one exists", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"2147483647\",\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\",\"1\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "mainnet", "post"], + "comment": "Index value equal to high(uint32) + 1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"2147483648\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 500, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "mainnet", "post"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT - 1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"1099511627775\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 500, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "mainnet", "post"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"1099511627776\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Index value which is bigger max(ValidatorIndex)", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"18446744073709551615\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Index value which is bigger uint64", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"18446744073709551616\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Incorrect hexadecimal values #1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0x\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Incorrect hexadecimal values #2", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0x0\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Incorrect hexadecimal values #3", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0x00\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Incorrect hexadecimal values #4", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0xZZ\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct hexadecimal values #1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct hexadecimal values #2", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct values of different types", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004\",\"5\",\"6\",\"7\",\"8\",\"9\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #2", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #3", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\",\"active_ongoing\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #4", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\",\"active_ongoing\",\"active_exiting\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #5", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\",\"active_ongoing\",\"active_exiting\",\"active_slashed\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #6", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\",\"active_ongoing\",\"active_exiting\",\"active_slashed\",\"exited_unslashed\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #7", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\",\"active_ongoing\",\"active_exiting\",\"active_slashed\",\"exited_unslashed\",\"exited_slashed\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #8", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\",\"active_ongoing\",\"active_exiting\",\"active_slashed\",\"exited_unslashed\",\"exited_slashed\",\"withdrawal_possible\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #9", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"pending_initialized\",\"pending_queued\",\"active_ongoing\",\"active_exiting\",\"active_slashed\",\"exited_unslashed\",\"exited_slashed\",\"withdrawal_possible\",\"withdrawal_done\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #10", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"active\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #11", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"active\",\"pending\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #12", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"active\",\"pending\",\"exited\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Correct status value #13", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\"],\"statuses\":[\"active\",\"pending\",\"exited\",\"withdrawal\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique id values #1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0\",\"0\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique id values #2", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"ids\":[\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique status values #1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"statuses\": [\"pending\",\"pending_initialized\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique status values #2", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"statuses\": [\"active\",\"active_ongoing\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique status values #3", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"statuses\": [\"exited\",\"exited_unslashed\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique status values #4", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"statuses\": [\"withdrawal\",\"withdrawal_done\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique status values #5", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"statuses\": [\"active\",\"active\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "post"], + "comment": "Non-unique status values #6", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "{\"statuses\": [\"pending_initialized\",\"pending_initialized\"]}" + }, + "url": "/eth/v1/beacon/states/head/validators", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, { "topics": ["beacon", "states_validatorid"], "comment": "Correct ValidatorIndex value", @@ -1394,39 +2112,323 @@ "topics": ["beacon", "states_validator_balances"], "comment": "Correct hexadecimal values #1", "request": { - "url": "/eth/v1/beacon/states/head/validators?id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "url": "/eth/v1/beacon/states/head/validator_balances?id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "headers": {"Accept": "application/json"} }, "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] } }, { "topics": ["beacon", "states_validator_balances"], "comment": "Correct hexadecimal values #2", "request": { - "url": "/eth/v1/beacon/states/head/validators?id=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "url": "/eth/v1/beacon/states/head/validator_balances?id=0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "headers": {"Accept": "application/json"} }, "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] } }, { "topics": ["beacon", "states_validator_balances"], "comment": "Correct values of different types", "request": { - "url": "/eth/v1/beacon/states/head/validators?id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004&id=5&id=6&id=7&id=8&id=9", + "url": "/eth/v1/beacon/states/head/validator_balances?id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004&id=5&id=6&id=7&id=8&id=9", "headers": {"Accept": "application/json"} }, "response": { "status": {"operator": "equals", "value": "200"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": "", "status": "", "validator": {"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}}]}] + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] + } + }, + { + "topics": ["beacon", "states_validator_balances_slow", "slow", "post"], + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Maximum number of id[] is 30", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0\",\"1\",\"2\",\"3\",\"4\",\"5\",\"6\",\"7\",\"8\",\"9\",\"10\",\"11\",\"12\",\"13\",\"14\",\"15\",\"16\",\"17\",\"18\",\"19\",\"20\",\"21\",\"22\",\"23\",\"24\",\"25\",\"26\",\"27\",\"28\",\"29\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet", "post"], + "comment": "Index value equal to high(int32)", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"2147483647\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": ["400", "200"]} + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet", "post"], + "comment": "Index value equal to high(int32) + 1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"2147483648\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 500, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet", "post"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT - 1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"1099511627775\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 500, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet", "post"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"1099511627776\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Index value which is bigger max(ValidatorIndex)", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"18446744073709551615\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Index value which is bigger uint64", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"18446744073709551616\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, +{ + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Incorrect hexadecimal values #1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0x\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Incorrect hexadecimal values #2", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0x0\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Incorrect hexadecimal values #3", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0x00\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Incorrect hexadecimal values #4", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0xJJ\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Correct hexadecimal values #1", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Correct hexadecimal values #2", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "post"], + "comment": "Correct values of different types", + "request": { + "method": "POST", + "body": { + "content-type": "application/json", + "data": "[\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003\",\"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004\",\"5\",\"6\",\"7\",\"8\",\"9\"]" + }, + "url": "/eth/v1/beacon/states/head/validator_balances", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "start": ["data"], "value": [{"index": "", "balance": ""}]}] } }, { @@ -3626,7 +4628,7 @@ "response": { "status": {"operator": "equals", "value": "400"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": "Empty request's body"}}] + "body": [{"operator": "jstructcmpns", "value": {"code": 400, "message": "Empty request body"}}] } }, { @@ -3643,7 +4645,7 @@ "response": { "status": {"operator": "equals", "value": "400"}, "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], - "body": [{"operator": "jstructcmpnsav", "value": {"code": 400, "message": "Empty request's body"}}] + "body": [{"operator": "jstructcmpnsav", "value": {"code": 400, "message": "Empty request body"}}] } }, {