From 0c3302b8267b9b9e73d6f659f9d686816f43e73b Mon Sep 17 00:00:00 2001 From: Eugene Kabanov Date: Tue, 27 Apr 2021 23:46:24 +0300 Subject: [PATCH] REST API test framework and tests. (#2492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * REST API test framework and tests. * Fix ValidatorIndex tests to properly handle int32, but not uint32 values. * Fix tests to follow latest REST fixes. * refactor restapi.sh and add it to the test suite * Fix issues. Add delay timeout which is required. * Fix restapi.sh script for Windows. Co-authored-by: Ștefan Talpalaru --- Makefile | 2 + beacon_chain/rpc/rest_utils.nim | 2 +- beacon_chain/rpc/validator_rest_api.nim | 18 +- env.sh | 2 +- ncli/resttest-rules.json | 3149 +++++++++++++++++++++++ ncli/resttest.nim | 1128 ++++++++ tests/simulation/restapi.sh | 142 + 7 files changed, 4426 insertions(+), 17 deletions(-) create mode 100644 ncli/resttest-rules.json create mode 100644 ncli/resttest.nim create mode 100755 tests/simulation/restapi.sh diff --git a/Makefile b/Makefile index 41cccb6c2..5d4764143 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,7 @@ endif TOOLS := \ nimbus_beacon_node \ deposit_contract \ + resttest \ inspector \ logtrace \ nbench \ @@ -268,6 +269,7 @@ test: | $(TEST_BINARIES) ifeq ($(DISABLE_TEST_FIXTURES_SCRIPT), 0) V=$(V) scripts/setup_official_tests.sh endif + tests/simulation/restapi.sh for TEST_BINARY in $(TEST_BINARIES); do \ PARAMS=""; \ if [[ "$${TEST_BINARY}" == "state_sim" ]]; then PARAMS="--validators=6000 --slots=128"; \ diff --git a/beacon_chain/rpc/rest_utils.nim b/beacon_chain/rpc/rest_utils.nim index d91ccc2d1..9b70aaa3c 100644 --- a/beacon_chain/rpc/rest_utils.nim +++ b/beacon_chain/rpc/rest_utils.nim @@ -478,7 +478,7 @@ proc getCurrentHead*(node: BeaconNode, proc getCurrentHead*(node: BeaconNode, epoch: Epoch): Result[BlockRef, cstring] = - if epoch >= MaxEpoch: + if epoch > MaxEpoch: return err("Requesting epoch for which slot would overflow") node.getCurrentHead(compute_start_slot_at_epoch(epoch)) diff --git a/beacon_chain/rpc/validator_rest_api.nim b/beacon_chain/rpc/validator_rest_api.nim index b2f795bd6..f724b39ae 100644 --- a/beacon_chain/rpc/validator_rest_api.nim +++ b/beacon_chain/rpc/validator_rest_api.nim @@ -83,11 +83,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = block: let res = node.getCurrentHead(qepoch) if res.isErr(): - if not(node.isSynced(node.chainDag.head)): - return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) - else: - return RestApiResponse.jsonError(Http400, NoHeadForSlotError, - $res.error()) + return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) res.get() let droot = if qepoch >= Epoch(2): @@ -148,11 +144,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = block: let res = node.getCurrentHead(qepoch) if res.isErr(): - if not(node.isSynced(node.chainDag.head)): - return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) - else: - return RestApiResponse.jsonError(Http400, NoHeadForSlotError, - $res.error()) + return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) res.get() let droot = if qepoch >= Epoch(2): @@ -266,11 +258,7 @@ proc installValidatorApiHandlers*(router: var RestRouter, node: BeaconNode) = block: let res = node.getCurrentHead(qslot) if res.isErr(): - if not(node.isSynced(node.chainDag.head)): - return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) - else: - return RestApiResponse.jsonError(Http400, NoHeadForSlotError, - $res.error()) + return RestApiResponse.jsonError(Http503, BeaconNodeInSyncError) res.get() let epochRef = node.chainDag.getEpochRef(qhead, qslot.epoch) makeAttestationData(epochRef, qhead.atSlot(qslot), qindex) diff --git a/env.sh b/env.sh index 6448a0694..43a591de4 100755 --- a/env.sh +++ b/env.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # We use ${BASH_SOURCE[0]} instead of $0 to allow sourcing this file # and we fall back to a Zsh-specific special var to also support Zsh. diff --git a/ncli/resttest-rules.json b/ncli/resttest-rules.json new file mode 100644 index 000000000..d88bf6a0e --- /dev/null +++ b/ncli/resttest-rules.json @@ -0,0 +1,3149 @@ +[ + { + "topics": ["beacon", "genesis"], + "request": { + "url": "/eth/v1/beacon/genesis", + "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": {"genesis_time": "", "genesis_validators_root": "", "genesis_fork_version": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/head/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/genesis/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/finalized/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/justified/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/heat/root"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/genezis/root"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/finalised/root"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/justilied/root"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/0/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/18446744073709551615/root", + "headers": {"Accept": "application/json"} + }, + "comment": "Maximum value for uint64", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/18446744073709551616/root"}, + "comment": "Overflow uint64 value test", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/0x/root"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/0x0/root"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/0x00/root"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/0x1/root"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": {"url": "/eth/v1/beacon/states/0x11/root"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/0x0000000000000000000000000000000000000000000000000000000000000000/root", + "headers": {"Accept": "application/json"} + }, + "comment": "Hexadecimal state root tests", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_root"], + "request": { + "url": "/eth/v1/beacon/states/0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF/root", + "headers": {"Accept": "application/json"} + }, + "comment": "Hexadecimal state root tests", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/head/fork", + "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": {"previous_version": "", "current_version": "", "epoch": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/genesis/fork", + "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": {"previous_version": "", "current_version": "", "epoch": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/finalized/fork", + "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": {"previous_version": "", "current_version": "", "epoch": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/justified/fork", + "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": {"previous_version": "", "current_version": "", "epoch": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/heat/fork"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/genezis/fork"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/finalised/fork"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/justilied/fork"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/0/fork", + "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": {"previous_version": "", "current_version": "", "epoch": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/18446744073709551615/fork", + "headers": {"Accept": "application/json"} + }, + "comment": "Maximum value for uint64", + "response": {"status": {"operator": "equals", "value": "404"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/18446744073709551616/fork", + "headers": {"Accept": "application/json"} + }, + "comment": "Overflow uint64 value test", + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/0x/fork"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/0x0/fork"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/0x00/fork"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/0x1/fork"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": {"url": "/eth/v1/beacon/states/0x11/fork"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/0x0000000000000000000000000000000000000000000000000000000000000000/fork", + "headers": {"Accept": "application/json"} + }, + "comment": "Hexadecimal state root tests", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_fork"], + "request": { + "url": "/eth/v1/beacon/states/0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF/fork", + "headers": {"Accept": "application/json"} + }, + "comment": "Hexadecimal state root tests", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/head/finality_checkpoints", + "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": {"previous_justified": {"epoch": "", "root": ""}, "current_justified": {"epoch": "", "root": ""}, "finalized": {"epoch": "", "root": ""}}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/genesis/finality_checkpoints", + "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": {"previous_justified": {"epoch": "", "root": ""}, "current_justified": {"epoch": "", "root": ""}, "finalized": {"epoch": "", "root": ""}}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/finalized/finality_checkpoints", + "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": {"previous_justified": {"epoch": "", "root": ""}, "current_justified": {"epoch": "", "root": ""}, "finalized": {"epoch": "", "root": ""}}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/justified/finality_checkpoints", + "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": {"previous_justified": {"epoch": "", "root": ""}, "current_justified": {"epoch": "", "root": ""}, "finalized": {"epoch": "", "root": ""}}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/heat/finality_checkpoints"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/genezis/finality_checkpoints"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/finalised/finality_checkpoints"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/justilied/finality_checkpoints"}, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/0/finality_checkpoints", + "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": {"previous_justified": {"epoch": "", "root": ""}, "current_justified": {"epoch": "", "root": ""}, "finalized": {"epoch": "", "root": ""}}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/18446744073709551615/finality_checkpoints", + "headers": {"Accept": "application/json"} + }, + "comment": "Maximum value for uint64", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/18446744073709551616/finality_checkpoints", + "headers": {"Accept": "application/json"} + }, + "comment": "Overflow uint64 value test", + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/0x/finality_checkpoints"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/0x0/finality_checkpoints"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/0x00/finality_checkpoints"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/0x1/finality_checkpoints"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": {"url": "/eth/v1/beacon/states/0x11/finality_checkpoints"}, + "comment": "Hexadecimal state root tests", + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/0x0000000000000000000000000000000000000000000000000000000000000000/finality_checkpoints", + "headers": {"Accept": "application/json"} + }, + "comment": "Hexadecimal state root tests", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_finality_checkpoints"], + "request": { + "url": "/eth/v1/beacon/states/0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF/finality_checkpoints", + "headers": {"Accept": "application/json"} + }, + "comment": "Hexadecimal state root tests", + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators_slow", "slow"], + "request": { + "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": { + "url": "/eth/v1/beacon/states/head/validators?id=1", + "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"], + "comment": "Maximum number of id[] is 30", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&id=1&id=2&id=3&id=4&id=5&id=6&id=7&id=8&id=9&id=10&id=11&id=12&id=13&id=14&id=15&id=16&id=17&id=18&id=19&id=20&id=21&id=22&id=23&id=24&id=25&id=26&id=27&id=28&id=29", + "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"], + "comment": "Number of id[] is bigger than 30", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&id=1&id=2&id=3&id=4&id=5&id=6&id=7&id=8&id=9&id=10&id=11&id=12&id=13&id=14&id=15&id=16&id=17&id=18&id=19&id=20&id=21&id=22&id=23&id=24&id=25&id=26&id=27&id=28&id=29&id=30", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "mainnet"], + "comment": "Index value equal to high(int32)", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=2147483647", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": ["404", "200"]}, + } + }, + { + "topics": ["beacon", "states_validators", "mainnet"], + "comment": "Index value equal to high(uint32) + 1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=2147483648", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "mainnet"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT - 1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=1099511627775", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators", "mainnet"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=1099511627776", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Index value which is bigger max(ValidatorIndex)", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Index value which is bigger uint64", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Incorrect hexadecimal values #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0x", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Incorrect hexadecimal values #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0x0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Incorrect hexadecimal values #3", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0x00", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Incorrect hexadecimal values #4", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0xZZ", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Correct hexadecimal values #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?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": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Correct hexadecimal values #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators?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": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validators"], + "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", + "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"], + "comment": "Correct status value #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized", + "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"], + "comment": "Correct status value #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued", + "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"], + "comment": "Correct status value #3", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued&status=active_ongoing", + "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"], + "comment": "Correct status value #4", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued&status=active_ongoing&status=active_exiting", + "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"], + "comment": "Correct status value #5", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued&status=active_ongoing&status=active_exiting&status=active_slashed", + "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"], + "comment": "Correct status value #6", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued&status=active_ongoing&status=active_exiting&status=active_slashed&status=exited_unslashed", + "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"], + "comment": "Correct status value #7", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued&status=active_ongoing&status=active_exiting&status=active_slashed&status=exited_unslashed&status=exited_slashed", + "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"], + "comment": "Correct status value #8", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued&status=active_ongoing&status=active_exiting&status=active_slashed&status=exited_unslashed&status=exited_slashed&status=withdrawal_possible", + "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"], + "comment": "Correct status value #9", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_queued&status=active_ongoing&status=active_exiting&status=active_slashed&status=exited_unslashed&status=exited_slashed&status=withdrawal_possible&status=withdrawal_done", + "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"], + "comment": "Correct status value #10", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=active", + "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"], + "comment": "Correct status value #11", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=active&status=pending", + "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"], + "comment": "Correct status value #12", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=active&status=pending&status=exited", + "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"], + "comment": "Correct status value #13", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=active&status=pending&status=exited&status=withdrawal", + "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"], + "comment": "Non-unique id values #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&id=0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Non-unique id values #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000&id=0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Non-unique status values #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending&status=pending_initialized", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Non-unique status values #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=active&status=active_ongoing", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Non-unique status values #3", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=exited&status=exited_unslashed", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Non-unique status values #4", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=withdrawal&status=withdrawal_done", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Non-unique status values #5", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=active&status=active", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validators"], + "comment": "Non-unique status values #6", + "request": { + "url": "/eth/v1/beacon/states/head/validators?id=0&status=pending_initialized&status=pending_initialized", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Correct ValidatorIndex value", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0", + "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_validatorid", "mainnet"], + "comment": "Validator index high(int32) value", + "request": { + "url": "/eth/v1/beacon/states/head/validators/2147483647", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": ["404", "200"]}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + } + }, + { + "topics": ["beacon", "states_validatorid", "mainnet"], + "comment": "Validator index high(int32) + 1 value (unsupported)", + "request": { + "url": "/eth/v1/beacon/states/head/validators/2147483648", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid", "mainnet"], + "comment": "Validator index VALIDATOR_REGISTRY_LIMIT - 1", + "request": { + "url": "/eth/v1/beacon/states/head/validators/1099511627775", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid", "mainnet"], + "comment": "Validator index VALIDATOR_REGISTRY_LIMIT", + "request": { + "url": "/eth/v1/beacon/states/head/validators/1099511627776", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid", "mainnet"], + "comment": "Validator index high(uint64)", + "request": { + "url": "/eth/v1/beacon/states/head/validators/18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid", "mainnet"], + "comment": "Validator index high(uint64) + 1", + "request": { + "url": "/eth/v1/beacon/states/head/validators/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Incorrect uint64 integer as validator index", + "request": { + "url": "/eth/v1/beacon/states/head/validators/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Incorrect uint64 integer as validator index", + "request": { + "url": "/eth/v1/beacon/states/head/validators/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Incorrect hexadecimal value as validator key #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0x", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Incorrect hexadecimal value as validator key #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0x0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Incorrect hexadecimal value as validator key #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0x00", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Incorrect hexadecimal value as validator key #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0xHH", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Incorrect hexadecimal value as validator key #3", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Correct hexadecimal value as validator key #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validatorid"], + "comment": "Correct hexadecimal value as validator key #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances_slow", "slow"], + "request": { + "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"], + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=0", + "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"], + "comment": "Maximum number of id[] is 30", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=0&id=1&id=2&id=3&id=4&id=5&id=6&id=7&id=8&id=9&id=10&id=11&id=12&id=13&id=14&id=15&id=16&id=17&id=18&id=19&id=20&id=21&id=22&id=23&id=24&id=25&id=26&id=27&id=28&id=29", + "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"], + "comment": "Number of id[] is bigger than 30", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=0&id=1&id=2&id=3&id=4&id=5&id=6&id=7&id=8&id=9&id=10&id=11&id=12&id=13&id=14&id=15&id=16&id=17&id=18&id=19&id=20&id=21&id=22&id=23&id=24&id=25&id=26&id=27&id=28&id=29&id=30", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet"], + "comment": "Index value equal to high(int32)", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=2147483647", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": ["404", "200"]}, + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet"], + "comment": "Index value equal to high(int32) + 1", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=2147483648", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT - 1", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=1099511627775", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances", "mainnet"], + "comment": "Index value equal to VALIDATOR_REGISTRY_LIMIT", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=1099511627776", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances"], + "comment": "Index value which is bigger max(ValidatorIndex)", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances"], + "comment": "Index value which is bigger uint64", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, +{ + "topics": ["beacon", "states_validator_balances"], + "comment": "Incorrect hexadecimal values #1", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=0x", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances"], + "comment": "Incorrect hexadecimal values #2", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=0x0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances"], + "comment": "Incorrect hexadecimal values #3", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=0x00", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances"], + "comment": "Incorrect hexadecimal values #4", + "request": { + "url": "/eth/v1/beacon/states/head/validator_balances?id=0xJJ", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_validator_balances"], + "comment": "Correct hexadecimal values #1", + "request": { + "url": "/eth/v1/beacon/states/head/validators?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": ""}}]}] + } + }, + { + "topics": ["beacon", "states_validator_balances"], + "comment": "Correct hexadecimal values #2", + "request": { + "url": "/eth/v1/beacon/states/head/validators?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": ""}}]}] + } + }, + { + "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", + "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_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?index=0", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?index=18446744073709551615", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?index=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?slot=0", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?slot=18446744073709551615", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?slot=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?epoch=0", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?epoch=576460752303423487", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?epoch=576460752303423488", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?epoch=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?epoch=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "states_committees"], + "request": { + "url": "/eth/v1/beacon/states/head/committees?slot=0&index=0&epoch=0", + "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": "", "slot": "", "validators": [""]}]}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers", + "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": {"root": "", "canonical": true, "header": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers?slot=0", + "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": {"root": "", "canonical": true, "header": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers?slot=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers?slot=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers?parent_root=0x", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers?parent_root=0x0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers?parent_root=0xPP", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers"], + "request": { + "url": "/eth/v1/beacon/headers?parent_root=0x0000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/head", + "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": {"root": "", "canonical": true, "header": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/genesis", + "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": {"root": "", "canonical": true, "header": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/finalized", + "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": {"root": "", "canonical": true, "header": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/heat", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/geneziz", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/finalised", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/0", + "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": {"root": "", "canonical": true, "header": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/0x0000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/0x", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/0x0", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/0x00", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/0xII", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_headers_blockid"], + "request": { + "url": "/eth/v1/beacon/headers/foobar", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/head", + "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": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/genesis", + "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": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/finalized", + "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": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0", + "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": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body": {"randao_reveal": "", "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "graffiti": "", "proposer_slashings": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""},"signature": ""}}], "attester_slashings": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}], "attestations": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}], "deposits": [{"proof": [""], "data": {"pubkey": "", "withdrawal_credentials": "", "amount": "", "signature": ""}}], "voluntary_exits": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}}, "signature": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x0000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/heat", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/geneziz", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/finalised", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/foobar", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x0", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x00", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_blocks_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x000000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/head/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/genesis/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/finalized/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0/root", + "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": {"root": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551615/root", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551616/root", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x0000000000000000000000000000000000000000000000000000000000000000/root", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551616/root", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/heat/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/geneziz/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/finalised/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/foobar/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x0/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x00/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_root_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x000000000000000000000000000000000000000000000000000000000000000000/root", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/head/attestations", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/genesis/attestations", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/finalized/attestations", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0/attestations", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551615/attestations", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/18446744073709551616/attestations", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x0000000000000000000000000000000000000000000000000000000000000000/attestations", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "404"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/heat/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/geneziz/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/finalised/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/foobar/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x0/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x00/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "beacon_block_attestations_blockid"], + "request": { + "url": "/eth/v1/beacon/blocks/0x000000000000000000000000000000000000000000000000000000000000000000/attestations", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "equals", "value": "400"}} + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=0", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=18446744073709551615", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=word", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?committee_index=0", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?committee_index=18446744073709551615", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?committee_index=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?committee_index=word", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=0&committee_index=0", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=word&committee_index=word", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=18446744073709551615&committee_index=18446744073709551615", + "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": [{"aggregation_bits": "", "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}]}] + } + }, + { + "topics": ["beacon", "pool_attestations"], + "request": { + "url": "/eth/v1/beacon/pool/attestations?slot=18446744073709551616&committee_index=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["beacon", "pool_attester_slashings"], + "request": { + "url": "/eth/v1/beacon/pool/attester_slashings", + "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": [{"attestation_1": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}, "attestation_2": {"attesting_indices": [""], "signature": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}}]}] + } + }, + { + "topics": ["beacon", "pool_proposer_slashings"], + "request": { + "url": "/eth/v1/beacon/pool/proposer_slashings", + "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": [{"signed_header_1": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}, "signed_header_2": {"message": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "signature": ""}}]}] + } + }, + { + "topics": ["beacon", "pool_voluntary_exits"], + "request": { + "url": "/eth/v1/beacon/pool/voluntary_exits", + "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": [{"message": {"epoch": "", "validator_index": ""}, "signature": ""}]}] + } + }, + { + "topics": ["config"], + "request": { + "url": "/eth/v1/config/fork_schedule", + "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": [{"previous_version": "", "current_version": "", "epoch": ""}]}] + } + }, + { + "topics": ["config"], + "request": { + "url": "/eth/v1/config/spec", + "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": {"MAX_COMMITTEES_PER_SLOT": "", "TARGET_COMMITTEE_SIZE": "", "MAX_VALIDATORS_PER_COMMITTEE": "", "MIN_PER_EPOCH_CHURN_LIMIT": "", "CHURN_LIMIT_QUOTIENT": "", "SHUFFLE_ROUND_COUNT": "", "MIN_GENESIS_ACTIVE_VALIDATOR_COUNT": "", "MIN_GENESIS_TIME": "", "HYSTERESIS_QUOTIENT": "", "HYSTERESIS_DOWNWARD_MULTIPLIER": "", "HYSTERESIS_UPWARD_MULTIPLIER": "", "SAFE_SLOTS_TO_UPDATE_JUSTIFIED": "", "ETH1_FOLLOW_DISTANCE": "", "TARGET_AGGREGATORS_PER_COMMITTEE": "", "RANDOM_SUBNETS_PER_VALIDATOR": "", "EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION": "", "SECONDS_PER_ETH1_BLOCK": "", "DEPOSIT_CHAIN_ID": "", "DEPOSIT_NETWORK_ID": "", "DEPOSIT_CONTRACT_ADDRESS": "", "MIN_DEPOSIT_AMOUNT": "", "MAX_EFFECTIVE_BALANCE": "", "EJECTION_BALANCE": "", "EFFECTIVE_BALANCE_INCREMENT": "", "GENESIS_FORK_VERSION": "", "BLS_WITHDRAWAL_PREFIX": "", "GENESIS_DELAY": "", "SECONDS_PER_SLOT": "", "MIN_ATTESTATION_INCLUSION_DELAY": "", "SLOTS_PER_EPOCH": "", "MIN_SEED_LOOKAHEAD": "", "MAX_SEED_LOOKAHEAD": "", "EPOCHS_PER_ETH1_VOTING_PERIOD": "", "SLOTS_PER_HISTORICAL_ROOT": "", "MIN_VALIDATOR_WITHDRAWABILITY_DELAY": "", "SHARD_COMMITTEE_PERIOD": "", "MIN_EPOCHS_TO_INACTIVITY_PENALTY": "", "EPOCHS_PER_HISTORICAL_VECTOR": "", "EPOCHS_PER_SLASHINGS_VECTOR": "", "HISTORICAL_ROOTS_LIMIT": "", "VALIDATOR_REGISTRY_LIMIT": "", "BASE_REWARD_FACTOR": "", "WHISTLEBLOWER_REWARD_QUOTIENT": "", "PROPOSER_REWARD_QUOTIENT": "", "INACTIVITY_PENALTY_QUOTIENT": "", "MIN_SLASHING_PENALTY_QUOTIENT" :"", "PROPORTIONAL_SLASHING_MULTIPLIER" :"", "MAX_PROPOSER_SLASHINGS" :"", "MAX_ATTESTER_SLASHINGS" :"", "MAX_ATTESTATIONS" :"", "MAX_DEPOSITS" :"", "MAX_VOLUNTARY_EXITS" :"", "DOMAIN_BEACON_PROPOSER" :"", "DOMAIN_BEACON_ATTESTER" :"", "DOMAIN_RANDAO" :"", "DOMAIN_DEPOSIT" :"", "DOMAIN_VOLUNTARY_EXIT" :"", "DOMAIN_SELECTION_PROOF" :"", "DOMAIN_AGGREGATE_AND_PROOF" :""}}] + } + }, + { + "topics": ["config"], + "request": { + "url": "/eth/v1/config/deposit_contract", + "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": {"chain_id": "", "address": ""}}] + } + }, + { + "topics": ["debug"], + "request": { + "url": "/eth/v1/debug/beacon/heads", + "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": [{"root": "", "slot": ""}]}] + } + }, + { + "topics": ["debug", "beacon_states_head_slow", "slow"], + "request": { + "url": "/eth/v1/debug/beacon/states/head", + "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": {"genesis_time": "", "genesis_validators_root": "", "slot": "", "fork": {"previous_version": "", "current_version": "", "epoch": ""}, "latest_block_header": {"slot": "", "proposer_index": "", "parent_root": "", "state_root": "", "body_root": ""}, "block_roots": [""], "state_roots": [""], "historical_roots": [""], "eth1_data": {"deposit_root": "", "deposit_count": "", "block_hash": ""}, "eth1_data_votes": [{"deposit_root": "", "deposit_count": "", "block_hash": ""}], "eth1_deposit_index": "", "validators": [{"pubkey": "", "withdrawal_credentials": "", "effective_balance": "", "slashed": false, "activation_eligibility_epoch": "", "activation_epoch": "", "exit_epoch": "", "withdrawable_epoch": ""}], "balances": [""], "randao_mixes": [""], "slashings": [""], "previous_epoch_attestations": [{"aggregation_bits": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}, "inclusion_delay": "", "proposer_index": ""}], "current_epoch_attestations": [{"aggregation_bits": "", "data": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}, "inclusion_delay": "", "proposer_index": ""}], "justification_bits": "", "previous_justified_checkpoint": {"epoch": "", "root": ""}, "current_justified_checkpoint": {"epoch": "", "root": ""}, "finalized_checkpoint": {"epoch": "", "root": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/identity", + "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": {"peer_id": "", "enr": "", "p2p_addresses": [""], "discovery_addresses": [""], "metadata": {"seq_number": "", "attnets": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?state=disconnected", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?state=connecting", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?state=connected", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?state=disconnecting", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?state=disconnected&state=connecting&state=connected&state=disconnecting", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?state=disconnected&state=connecting&state=connected&state=disconnecting&direction=inbound&direction=outbound", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?direction=inbound", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?direction=outbound", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"data": [{"peer_id": "", "enr": "", "last_seen_p2p_address": "", "state": "", "direction": ""}], "meta": {"count": ""}}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?state=disconnected&state=disconnected", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers?direction=inbound&direction=inbound", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": ["200", "404"]} + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"} + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peers/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5l", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"} + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/peer_count", + "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": {"disconnected": "", "connecting": "", "connected": "", "disconnecting": ""}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/version", + "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": {"version": ""}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/syncing", + "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": {"head_slot": "", "sync_distance": "", "is_syncing": false}}] + } + }, + { + "topics": ["node"], + "request": { + "url": "/eth/v1/node/health", + "headers": {"Accept": "application/json"} + }, + "response": {"status": {"operator": "oneof", "value": ["200", "206", "503"]}} + }, + { + "topics": ["validator", "proposer_duties"], + "request": { + "url": "/eth/v1/validator/duties/proposer/0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "data": [{"pubkey": "", "validator_index": "", "slot": ""}]}}] + } + }, + { + "topics": ["validator", "proposer_duties", "mainnet"], + "comment": "Maximum epoch for mainnet parameters", + "request": { + "url": "/eth/v1/validator/duties/proposer/576460752303423487", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "503"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "proposer_duties", "mainnet"], + "comment": "Maximum epoch + 1 for mainnet parameters", + "request": { + "url": "/eth/v1/validator/duties/proposer/576460752303423488", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "proposer_duties"], + "request": { + "url": "/eth/v1/validator/duties/proposer/18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "proposer_duties"], + "request": { + "url": "/eth/v1/validator/duties/proposer/18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "proposer_duties"], + "request": { + "url": "/eth/v1/validator/duties/proposer/foobar", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"} + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data?slot=0&committee_index=0", + "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": {"slot": "", "index": "", "beacon_block_root": "", "source": {"epoch": "", "root": ""}, "target": {"epoch": "", "root": ""}}}] + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data?slot=0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data?committee_index=0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data?slot=18446744073709551615&committee_index=18446744073709551615", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "503"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data?slot=18446744073709551616&committee_index=18446744073709551616", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data?slot=foobar&committee_index=foobar", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attestation_data"], + "request": { + "url": "/eth/v1/validator/attestation_data?slot=&committee_index=", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "aggregate_attestation"], + "request": { + "url": "/eth/v1/validator/aggregate_attestation", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "aggregate_attestation"], + "request": { + "url": "/eth/v1/validator/aggregate_attestation?slot=0", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "aggregate_attestation"], + "request": { + "url": "/eth/v1/validator/aggregate_attestation?slot=&attestation_data_root=", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "aggregate_attestation"], + "request": { + "url": "/eth/v1/validator/aggregate_attestation?slot=0&attestation_data_root=0x0000000000000000000000000000000000000000000000000000000000000000", + "headers": {"Accept": "application/json"} + }, + "response": { + "status": {"operator": "oneof", "value": ["400", "200"]}, + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"0\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "data":[{"pubkey": "", "validator_index": "", "committee_index": "", "committee_length": "", "committees_at_slot": "", "validator_committee_index": "", "slot": ""}]}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "method": "POST", + "url": "/eth/v1/validator/duties/attester/0", + "headers": {"Accept": "application/json"}, + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/576460752303423487", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"0\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "503"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/576460752303423488", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"0\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/18446744073709551615", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"0\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/18446744073709551616", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"0\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/foobar", + "method": "POST", + "headers": {"Accept": "application/json"}, + }, + "response": { + "status": {"operator": "equals", "value": "400"} + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"2147483647\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "200"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmps", "value": {"dependent_root": "", "data":[{"pubkey": "", "validator_index": "", "committee_index": "", "committee_length": "", "committees_at_slot": "", "validator_committee_index": "", "slot": ""}]}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"2147483648\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"1099511627775\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "500"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"1099511627776\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"18446744073709551615\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, + { + "topics": ["validator", "attester_duties"], + "request": { + "url": "/eth/v1/validator/duties/attester/0", + "method": "POST", + "headers": {"Accept": "application/json"}, + "body": {"content-type": "application/json", "data": "[\"18446744073709551616\"]"} + }, + "response": { + "status": {"operator": "equals", "value": "400"}, + "headers": [{"key": "Content-Type", "value": "application/json", "operator": "equals"}], + "body": [{"operator": "jstructcmpns", "value": {"code": "", "message": ""}}] + } + }, +] diff --git a/ncli/resttest.nim b/ncli/resttest.nim new file mode 100644 index 000000000..c6d269279 --- /dev/null +++ b/ncli/resttest.nim @@ -0,0 +1,1128 @@ +import std/[strutils, os, options, uri, json, tables] +import stew/[results, io2, base10] +import confutils, chronicles, httputils, + chronos, chronos/streams/[asyncstream, tlsstream] + +const + RestTesterName* = "Ethereum2 REST API Tester" + RestTesterMajor*: int = 0 + RestTesterMinor*: int = 0 + RestTesterPatch*: int = 1 + RestTesterVersion* = $RestTesterMajor & "." & $RestTesterMinor & "." & + $RestTesterPatch + RestTesterIdent* = "RestTester/$1 ($2/$3)" % [RestTesterVersion, + hostCPU, hostOS] + RestTesterCopyright* = "Copyright(C) 2021" & + " Status Research & Development GmbH" + RestTesterHeader* = RestTesterName & ", Version " & RestTesterVersion & + " [" & hostOS & ": " & hostCPU & "]\r\n" & + RestTesterCopyright & "\r\n" + HeadersMark = @[0x0D'u8, 0x0A'u8, 0x0D'u8, 0x0A'u8] + +type + StatusOperatorKind* {.pure.} = enum + Equals, OneOf, Inside, InsideOrEq + + HeaderOperatorKind {.pure.} = enum + Exists, Equals, OneOf, Substr + + BodyOperatorKind {.pure.} = enum + Exists, JsonStructCmpS, JsonStructCmpNS + + StatusExpect = object + kind: StatusOperatorKind + value: seq[int] + + HeaderExpect = object + kind: HeaderOperatorKind + key: string + value: seq[string] + + HeadersExpect = object + headers: seq[HeaderExpect] + + BodyItemExpect = object + kind: BodyOperatorKind + startPath: seq[string] + value: JsonNode + + BodyExpect = object + items: seq[BodyItemExpect] + + TestResultKind* {.pure.} = enum + RuleError, + NoSupportError, + WriteRequestError, + ReadResponseHeadersError, + ReadResponseBodyError, + RequestError, + ResponseError, + ValidationError, + ValidationSuccess + + TestResultFlag* {.pure.} = enum + ResetConnection, StatusValidationFailure, + HeadersValidationFailure, BodyValidationFailure + + TestResult* = object + kind: TestResultKind + message: string + flags: set[TestResultFlag] + times: array[4, Duration] + + TestCase* = object + index: int + rule: JsonNode + + TestCaseResult* = object + index: int + data: TestResult + + HttpConnectionType* {.pure.} = enum + Nonsecure, Secure + + HttpConnectionRef* = ref object + case kind*: HttpConnectionType + of HttpConnectionType.Nonsecure: + discard + of HttpConnectionType.Secure: + stream: TLSAsyncStream + treader: AsyncStreamReader + twriter: AsyncStreamWriter + transp: StreamTransport + reader*: AsyncStreamReader + writer*: AsyncStreamWriter + + RestTestError* = object of CatchableError + ConnectionError* = object of RestTestError + + RestTesterConf* = object + delayTime* {. + defaultValue: 0 + desc: "Time (in seconds) to wait before initial connection could be " & + "established" + abbr: "d" + name: "delay" .}: int + + attemptTimeout* {. + defaultValue: 60 + desc: "Time (in seconds) during which to continue trying to establish " & + "connection with remote server" + name: "timeout" .}: int + + noVerifyCert* {. + defaultValue: false + desc: "Skip remote server SSL/TLS certificate validation" + name: "no-verify-host" .}: bool + + noVerifyName* {. + defaultValue: false + desc: "Skep remote server name verification" + name: "no-verify-name" .}: bool + + rulesFilename* {. + defaultValue: "resttest-rules.json", + desc: "JSON formatted tests file" + name: "rules-file" .}: string + + topicsFilter* {. + desc: "Topics which should be included in testing" + name: "topic", + abbr: "t" .}: seq[string] + + skipTopicsFilter* {. + desc: "Topics which should be skipped in testing" + name: "skip-topic", + abbr: "s" .}: seq[string] + + connectionsCount* {. + defaultValue: 1 + desc: "Number of concurrent connections to remote server" + name: "connections" + abbr: "c" .}: int + + url* {. + argument, + desc: "Address of remote REST server to test" + .}: string + +proc getUri(conf: RestTesterConf): Result[Uri, cstring] = + var res = parseUri(conf.url) + if res.scheme notin ["http", "https"]: + return err("URL scheme should be http or https") + if len(res.hostname) == 0: + return err("URL missing hostname") + if len(res.query) != 0: + return err("URL should not contain query parameters") + if len(res.anchor) != 0: + return err("URL should not contain anchor parameter") + # TODO: Disable this check when at least BASIC AUTH will be implemented. + if len(res.username) != 0 or len(res.password) != 0: + return err("URL should not contain username:password") + if len(res.port) == 0: + if res.scheme == "http": + res.port = "80" + else: + res.port = "443" + ok(res) + +proc getAddress(uri: Uri): Result[TransportAddress, cstring] = + let txtaddr = uri.hostname & ":" & uri.port + let numeric = + try: + initTAddress(txtaddr) + except TransportAddressError: + # We ignore errors here because `hostname` address could be non-numeric. + TransportAddress() + if numeric.family in {AddressFamily.IPv4, AddressFamily.IPv6}: + ok(numeric) + else: + var default: seq[TransportAddress] + let v4addresses = + try: + resolveTAddress(txtaddr, AddressFamily.IPv4) + except TransportAddressError: + # We ignore errors here because `hostname` could be resolved to IPv6. + default + if len(v4addresses) > 0: + ok(v4addresses[0]) + else: + let v6addresses = + try: + resolveTAddress(txtaddr, AddressFamily.IPv6) + except TransportAddressError: + return err("Unable to resolve hostname") + if len(v6addresses) == 0: + return err("Unable to resolve hostname") + ok(v6addresses[0]) + +proc getTLSFlags(conf: RestTesterConf): set[TLSFlags] = + var res: set[TLSFlags] + if conf.noVerifyName: + res.incl(TLSFlags.NoVerifyServerName) + if conf.noVerifyCert: + res.incl(TLSFlags.NoVerifyHost) + res + +proc checkTopic(conf: RestTesterConf, rule: JsonNode): bool = + var default: seq[string] + if (len(conf.topicsFilter) == 0) and (len(conf.skipTopicsFilter) == 0): + true + else: + let topics = + block: + let jtopics = rule.getOrDefault("topics") + if isNil(jtopics): + default + else: + case jtopics.kind + of JString: + @[jtopics.str] + of JArray: + if len(jtopics.elems) == 0: + default + else: + var res: seq[string] + for jitem in jtopics.elems: + case jitem.kind: + of JString: + res.add(jitem.str) + else: + continue + res + else: + default + if len(conf.topicsFilter) == 0: + if len(topics) == 0: + true + else: + for item in topics: + if item in conf.skipTopicsFilter: + return false + true + else: + for item in topics: + if item in conf.skipTopicsFilter: + return false + for item in topics: + if item in conf.topicsFilter: + return true + false + +proc getTestRules(conf: RestTesterConf): Result[seq[JsonNode], cstring] = + let data = + block: + let res = io2.readAllChars(conf.rulesFilename) + if res.isErr(): + fatal "Could not read rules file", error_msg = ioErrorMsg(res.error()), + error_os_code = $res.error(), filename = conf.rulesFilename + return err("Unable to read rules file") + res.get() + let node = + try: + parseJson(data) + except CatchableError as exc: + fatal "JSON processing error while reading rules file", + error_msg = exc.msg, filename = conf.rulesFilename + return err("Unable to parse json") + + let elems = node.getElems() + if len(elems) == 0: + fatal "There empty array of rules found in file", + filename = conf.rulesFilename + return err("Incorrect json") + + var res: seq[JsonNode] + for item in elems: + if conf.checkTopic(item): + res.add(item) + + notice "Rules file loaded", total_rules_count = len(elems), + rules_count = len(res) + ok(res) + +proc openConnection*(address: TransportAddress, uri: Uri, + flags: set[TLSFlags]): Future[HttpConnectionRef] {.async.} = + let transp = + try: + await connect(address) + except TransportOsError as exc: + raise newException(ConnectionError, "Unable to establish connection") + + let treader = newAsyncStreamReader(transp) + let twriter = newAsyncStreamWriter(transp) + if uri.scheme == "http": + return HttpConnectionRef( + kind: HttpConnectionType.Nonsecure, + transp: transp, reader: treader, writer: twriter + ) + else: + let tlsstream = newTLSClientAsyncStream(treader, twriter, uri.hostname, + flags = flags) + return HttpConnectionRef( + kind: HttpConnectionType.Secure, + transp: transp, reader: tlsstream.reader, writer: tlsstream.writer, + treader: treader, twriter: twriter + ) + +proc closeWait*(conn: HttpConnectionRef): Future[void] {.async.} = + case conn.kind + of HttpConnectionType.Nonsecure: + await allFutures(conn.reader.closeWait(), conn.writer.closeWait()) + await conn.transp.closeWait() + of HttpConnectionType.Secure: + await allFutures(conn.reader.closeWait(), conn.writer.closeWait()) + await allFutures(conn.treader.closeWait(), conn.twriter.closeWait()) + await conn.transp.closeWait() + +proc checkConnection*(conf: RestTesterConf, uri: Uri): Future[void] {.async.} = + let timeFut = sleepAsync(conf.attemptTimeout.seconds) + var sleepTime = 1000.milliseconds + let hostname = uri.hostname & ":" & uri.port + while true: + if timeFut.finished(): + fatal "Connection with remote host could not be established in time", + uri = uri.hostname, time = $conf.attemptTimeout.seconds + raise newException(ConnectionError, "Unable to establish connection") + + let address = + block: + let res = uri.getAddress() + if res.isErr(): + fatal "Unable to resolve remote host address", host = hostname + raise newException(ConnectionError, "Unable to resolve address") + else: + res.get() + + let conn = + try: + await openConnection(address, uri, conf.getTLSFlags()) + except ConnectionError: + notice "Unable to establish connection with remote host", + host = hostname, + sleep_until_next_attempt = $(((sleepTime * 3) div 2).seconds) + nil + + if not(isNil(conn)): + notice "Connection with remote host established", host = hostname + await closeWait(conn) + return + + if timeFut.finished(): + fatal "Connection with remote host could not be established in time", + uri = hostname, time = $conf.attemptTimeout.seconds + raise newException(ConnectionError, "Unable to establish connection") + + await sleepAsync(sleepTime) + # Increasing sleep time by 50%. + sleepTime = (sleepTime * 3) div 2 + +proc compact(v: string, size: int): string = + let delim = "..." + doAssert(size >= (len(delim) + 2)) + if len(v) <= size: + v + else: + var length1 = (size - len(delim)) div 2 + var length2 = size - length1 - len(delim) + if length1 < length2: + swap(length1, length2) + v[0 .. (length1 - 1)] & delim & v[len(v) - length2 .. ^1] + +proc getTestName(rule: JsonNode): string = + let request = rule.getOrDefault("request") + if isNil(request): + "[incorrect]" + else: + let juri = request.getOrDefault("url") + if isNil(juri): + "[incorrect]" + else: + compact(juri.str, 40) + +proc prepareRequest(uri: Uri, + rule: JsonNode): Result[tuple[url: string, request: string], + cstring] = + let request = rule.getOrDefault("request") + if isNil(request): + return err("Missing `request` field") + + let meth = + block: + let jmethod = request.getOrDefault("method") + if isNil(jmethod): + "GET" + else: + if jmethod.kind != JString: + return err("Field `method` should be string") + jmethod.str + + let requestUri = + block: + let juri = request.getOrDefault("url") + if isNil(juri): + return err("Missing requests' `url`") + else: + if juri.kind != JString: + return err("Field `url` should be string") + juri.str + + let requestHeaders = + block: + var default: seq[tuple[key: string, value: string]] + let jheaders = request.getOrDefault("headers") + if isNil(jheaders): + default + else: + var res: seq[tuple[key: string, value: string]] + if jheaders.kind != JObject: + return err("Field `headers` should be an object") + for key, value in jheaders.fields.pairs(): + if value.kind != JString: + return err("Field `headers` element should be only strings") + res.add((key, value.str)) + res + + let (requestBodyType, requestBodyData) = + block: + let jbody = request.getOrDefault("body") + if isNil(jbody): + ("", "") + else: + if jbody.kind != JObject: + return err("Field `body` should be object") + let btype = jbody.getOrDefault("content-type") + if isNil(btype): + return err("Field `body.content-type` must be present") + if btype.kind != JString: + return err("Field `body.content-type` should be string") + let bdata = jbody.getOrDefault("data") + if isNil(bdata): + return err("Field `body.data` must be present") + if bdata.kind != JString: + return err("Field `body.data` should be string") + (btype.str, bdata.str) + + var res = meth & " " & uri.path & requestUri & " HTTP/1.1\r\n" + res.add("Content-Length: " & Base10.toString(uint64(len(requestBodyData))) & + "\r\n") + + if len(requestBodyType) > 0: + res.add("Content-Type: " & requestBodyType & "\r\n") + + for item in requestHeaders: + res.add(item.key & ": " & item.value & "\r\n") + + let (hostPresent, datePresent) = + block: + var flag1 = false + var flag2 = false + for item in requestHeaders: + if cmpIgnoreCase(item.key, "host") == 0: + flag1 = true + elif cmpIgnoreCase(item.key, "date") == 0: + flag2 = true + (flag1, flag2) + + if not(hostPresent): + res.add("Host: " & $uri.hostname & "\r\n") + if not(datePresent): + res.add("Date: " & httpDate() & "\r\n") + res.add("\r\n") + if len(requestBodyData) > 0: + res.add(requestBodyData) + ok((uri.path & requestUri, res)) + +proc getResponseStatusExpect(rule: JsonNode): Result[StatusExpect, cstring] = + let response = rule.getOrDefault("response") + if isNil(response): + return err("Missing `response` field") + let jstatus = response.getOrDefault("status") + if isNil(jstatus): + return err("Missing `response.status` field") + + let value = + block: + var res: seq[int] + let jvalue = jstatus.getOrDefault("value") + if isNil(jvalue): + return err("Field `status.value` should be present") + case jvalue.kind + of JString: + let nres = Base10.decode(uint16, jvalue.str) + if nres.isErr(): + return err("Field `status.value` has incorrect value") + res.add(int(nres.get())) + of JInt: + if jvalue.num < 0 or jvalue.num >= 1000: + return err("Field `status.value` has incorrect value") + res.add(int(jvalue.num)) + of JArray: + if len(jvalue.elems) == 0: + return err("Field `status.value` has an empty array") + for jitem in jvalue.elems: + let iitem = + case jitem.kind + of JString: + let nres = Base10.decode(uint16, jitem.str) + if nres.isErr(): + return err("Field `status.value` element has incorrect value") + int(nres.get()) + of JInt: + if jitem.num < 0 or jitem.num >= 1000: + return err("Field `status.value` element has incorrect value") + int(jitem.num) + else: + return err("Field `status.value` has incorrect elements") + res.add(iitem) + else: + return err("Field `status.value` should be an array, string or integer") + res + + let kind = + block: + let joperator = jstatus.getOrDefault("operator") + if isNil(joperator): + if len(value) > 1: + StatusOperatorKind.OneOf + else: + StatusOperatorKind.Equals + else: + if joperator.kind != JString: + return err("Field `status.operator` should be string") + case toLowerAscii(joperator.str) + of "equals": + StatusOperatorKind.Equals + of "oneof": + StatusOperatorKind.OneOf + of "insideoreq": + StatusOperatorKind.InsideOrEq + of "inside": + StatusOperatorKind.Inside + else: + return err("Field `status.operator` has unknown or empty value") + + ok(StatusExpect(kind: kind, value: value)) + +proc getResponseHeadersExpect(rule: JsonNode): Result[HeadersExpect, cstring] = + let response = rule.getOrDefault("response") + if isNil(response): + return err("Missing `response` field") + let jheaders = response.getOrDefault("headers") + if isNil(jheaders): + return ok(HeadersExpect()) + if jheaders.kind != JArray: + return err("`response.headers` should be array") + if len(jheaders.elems) == 0: + return ok(HeadersExpect()) + var res: seq[HeaderExpect] + for jitem in jheaders.elems: + if jitem.kind != JObject: + return err("`response.headers` elements should be objects") + let jkey = jitem.getOrDefault("key") + if isNil(jkey) or jkey.kind != JString: + continue + let key = jkey.str + let operator = + block: + let jop = jitem.getOrDefault("operator") + if isNil(jop) or jop.kind != JString: + HeaderOperatorKind.Exists + else: + case toLowerAscii(jop.str) + of "exists": + HeaderOperatorKind.Exists + of "equals": + HeaderOperatorKind.Equals + of "oneof": + HeaderOperatorKind.OneOf + of "substr": + HeaderOperatorKind.Substr + else: + return err("`response.header` element has incorrect operator") + let value = + block: + var vres: seq[string] + let jvalue = jitem.getOrDefault("value") + case jvalue.kind + of JArray: + if len(jvalue.elems) == 0: + return err("`response.header` element has an empty array value") + for jelem in jvalue.elems: + case jelem.kind + of JString: + vres.add(jvalue.str) + of JInt: + vres.add(Base10.toString(uint64(jvalue.num))) + else: + return err("`response.header` element has incorrect value") + of JString: + vres.add(jvalue.str) + of JInt: + vres.add(Base10.toString(uint64(jvalue.num))) + else: + return err("`response.header` element has incorrect value") + vres + res.add(HeaderExpect(key: key, value: value, kind: operator)) + ok(HeadersExpect(headers: res)) + +proc getResponseBodyExpect(rule: JsonNode): Result[BodyExpect, cstring] = + let response = rule.getOrDefault("response") + if isNil(response): + return err("Missing `response` field") + let jbody = response.getOrDefault("body") + if isNil(jbody): + return ok(BodyExpect()) + if jbody.kind != JArray: + return err("`response.body` should be array") + if len(jbody.elems) == 0: + return ok(BodyExpect()) + + var res: seq[BodyItemExpect] + + for jitem in jbody.elems: + if jitem.kind != JObject: + return err("`response.body` elements should be objects") + + let operator = + block: + let jop = jitem.getOrDefault("operator") + if isNil(jop) or jop.kind != JString: + BodyOperatorKind.Exists + else: + case toLowerAscii(jop.str) + of "exists": + BodyOperatorKind.Exists + of "jstructcmps": + BodyOperatorKind.JsonStructCmpS + of "jstructcmpns": + BodyOperatorKind.JsonStructCmpNS + else: + return err("`response.body` element has incorrect operator") + + case operator + of BodyOperatorKind.Exists: + res.add(BodyItemExpect(kind: operator)) + of BodyOperatorKind.JsonStructCmpS, BodyOperatorKind.JsonStructCmpNS: + let start = + block: + var default: seq[string] + var rstart: seq[string] + let jstart = jitem.getOrDefault("start") + if isNil(jstart): + default + else: + case jstart.kind + of JString: + rstart.add(jstart.str) + of JArray: + if len(jstart.elems) != 0: + for elem in jstart.elems: + case elem.kind + of JString: + rstart.add(elem.str) + else: + return err("`response.body` element has incorrect `start`" & + " option") + else: + return err("`response.body` element has incorrect `start` option") + rstart + let body = + block: + let jvalue = jitem.getOrDefault("value") + if jvalue.isNil(): + return err("`response.body` element has incorrect `value` option") + jvalue + res.add(BodyItemExpect(kind: operator, startPath: start, value: body)) + ok(BodyExpect(items: res)) + +proc validateStatus(status: int, expect: StatusExpect): bool = + case expect.kind + of StatusOperatorKind.Equals: + expect.value[0] == status + of StatusOperatorKind.OneOf: + status in expect.value + of StatusOperatorKind.InsideOrEq: + if len(expect.value) < 2: + status >= expect.value[0] + else: + status >= expect.value[0] and status <= expect.value[1] + of StatusOperatorKind.Inside: + if len(expect.value) < 2: + status > expect.value[0] + else: + status > expect.value[0] and status < expect.value[1] + +proc validateHeaders(resp: HttpResponseHeader, expect: HeadersExpect): bool = + if len(expect.headers) == 0: + true + else: + for item in expect.headers: + case item.kind + of HeaderOperatorKind.Exists: + if item.key notin resp: + return false + of HeaderOperatorKind.Equals: + if item.key notin resp: + return false + let v = resp[item.key] + if cmpIgnoreCase(v, item.value[0]) != 0: + return false + of HeaderOperatorKind.OneOf: + if item.key notin resp: + return false + let v = resp[item.key] + var r = false + for citem in item.value: + if cmpIgnoreCase(citem, v) == 0: + r = true + break + if not(r): + return false + of HeaderOperatorKind.Substr: + if item.key notin resp: + return false + let v = resp[item.key] + if strutils.find(v, item.value[0]) < 0: + return false + true + +proc jsonBody(body: openarray[byte]): Result[JsonNode, cstring] = + var sbody = cast[string](@body) + let res = + try: + parseJson(sbody) + except CatchableError as exc: + return err("Unable to parse json") + ok(res) + +proc getPath(jobj: JsonNode, path: seq[string]): Result[JsonNode, cstring] = + var jnode = jobj + for item in path: + let jitem = jnode.getOrDefault(item) + if isNil(jitem): + return err("Path not found") + jnode = jitem + ok(jnode) + +proc structCmp(j1, j2: JsonNode, strict: bool): bool = + if j1.kind != j2.kind: + return false + case j1.kind + of JArray: + # In case of array we checking first element of `expect` with all the + # elements in `result`. + if len(j1.elems) == 0: + true + else: + if len(j2.elems) == 0: + false + else: + for item in j1.elems: + if not(structCmp(item, j2.elems[0], strict)): + return false + true + of JObject: + if strict: + if len(j1.fields) != len(j2.fields): + return false + for key, value in j1.fields.pairs(): + let j2node = j2.getOrDefault(key) + if isNil(j2node): + return false + if not(structCmp(value, j2node, strict)): + return false + true + else: + for key, value in j2.fields.pairs(): + let j1node = j1.getOrDefault(key) + if isNil(j1node): + return false + if not(structCmp(j1node, value, strict)): + return false + true + else: + true + +proc validateBody(body: openarray[byte], expect: BodyExpect): bool = + if len(expect.items) == 0: + true + else: + for item in expect.items: + case item.kind + of BodyOperatorKind.Exists: + if len(body) == 0: + return false + of BodyOperatorKind.JsonStructCmpS, BodyOperatorKind.JsonStructCmpNS: + let jbody = + block: + let jres = jsonBody(body) + if jres.isErr(): + return false + let jnode = jres.get() + let jpathres = jnode.getPath(item.startPath) + if jpathres.isErr(): + return false + jpathres.get() + let strict = + if item.kind == BodyOperatorKind.JsonStructCmpS: + true + else: + false + if not(structCmp(jbody, item.value, strict)): + return false + true + +proc failure(t: typedesc[TestResult], code: TestResultKind, + message: string = "", + flags: set[TestResultFlag] = {}, + times: array[4, Duration]): TestResult = + TestResult(kind: code, message: message, flags: flags, times: times) + +proc success(t: typedesc[TestResult], times: array[4, Duration]): TestResult = + TestResult(kind: TestResultKind.ValidationSuccess, times: times) + +proc runTest(conn: HttpConnectionRef, uri: Uri, + rule: JsonNode, workerIndex: int, + testIndex: int): Future[TestResult] {.async.} = + var times: array[4, Duration] + let testName = rule.getTestName() + let testPath = uri.path & rule.getTestName() + + debug "Running test", name = testName, test_index = testIndex, + worker_index = workerIndex + + let (requestUri, request) = + block: + let res = prepareRequest(uri, rule) + if res.isErr(): + return TestResult.failure(TestResultKind.RuleError, + "Could not read request data: " & + $res.error(), times = times) + res.get() + + let statusExpect = + block: + let res = getResponseStatusExpect(rule) + if res.isErr(): + return TestResult.failure(TestResultKind.RuleError, + "Could not read response status data: " & + $res.error(), times = times) + res.get() + + let headersExpect = + block: + let res = getResponseHeadersExpect(rule) + if res.isErr(): + return TestResult.failure(TestResultKind.RuleError, + "Could not read response headers data: " & + $res.error(), times = times) + res.get() + + let bodyExpect = + block: + let res = getResponseBodyExpect(rule) + if res.isErr(): + return TestResult.failure(TestResultKind.RuleError, + "Could not read response body data: " & + $res.error(), times = times) + res.get() + + let testSm = Moment.now() + var headersBuf = newSeq[byte](8192) + var dataBuf = newSeq[byte](8192) + + try: + let sm = Moment.now() + await conn.writer.write(request) + times[0] = Moment.now() - sm + debug "Request sent", name = testName, + elapsed = $times[0], test_index = testIndex, + worker_index = workerIndex + except AsyncStreamError: + return TestResult.failure(TestResultKind.WriteRequestError, + "Unable to send request", {ResetConnection}, + times) + let rlen = + try: + let sm = Moment.now() + let res = await conn.reader.readUntil(addr headersBuf[0], + len(headersBuf), HeadersMark) + times[1] = Moment.now() - sm + debug "Response headers received", name = testName, + length = res, elapsed = $times[1], + test_index = testIndex, + worker_index = workerIndex + res + except AsyncStreamError: + return TestResult.failure(TestResultKind.ReadResponseHeadersError, + "Unable to read response headers", + {ResetConnection}, times) + + headersBuf.setLen(rlen) + + let resp = parseResponse(headersBuf, true) + if not(resp.success()): + return TestResult.failure(TestResultKind.ResponseError, + "Response headers could not be parsed", + {ResetConnection}, times) + + if "Content-Length" notin resp: + return TestResult.failure(TestResultKind.ResponseError, + "Content-Length header must be present", + {ResetConnection}, times) + let contentLength = resp.contentLength() + if contentLength < 0: + return TestResult.failure(TestResultKind.ResponseError, + "Content-Length value is incorrect", + {ResetConnection}, times) + else: + # TODO: We are not checking Content-Length size here + if contentLength > 0: + dataBuf.setLen(contentLength) + try: + let sm = Moment.now() + await conn.reader.readExactly(addr dataBuf[0], len(dataBuf)) + times[2] = Moment.now() - sm + debug "Response body received", length = len(dataBuf), + name = testName, + elapsed = $times[2], test_index = testIndex, + worker_index = workerIndex + except AsyncStreamError: + return TestResult.failure(TestResultKind.ReadResponseBodyError, + "Unable to read response body", + {ResetConnection}, times) + else: + debug "Response body is missing", name = testName, path = testPath + dataBuf.setLen(0) + + let res1 = validateStatus(resp.code, statusExpect) + let res2 = validateHeaders(resp, headersExpect) + let res3 = validateBody(dataBuf, bodyExpect) + times[3] = Moment.now() - testSm + + if res1 and res2 and res3: + return TestResult.success(times) + else: + let flags = + block: + var res: set[TestResultFlag] + if not(res1): + res.incl(StatusValidationFailure) + if not(res2): + res.incl(HeadersValidationFailure) + if not(res3): + res.incl(BodyValidationFailure) + res + return TestResult.failure(TestResultKind.ValidationError, times = times, + flags = flags) + +proc workerLoop(address: TransportAddress, uri: Uri, worker: int, + conf: RestTesterConf, + inputQueue: AsyncQueue[TestCase], + outputQueue: AsyncQueue[TestCaseResult]) {.async.} = + let hostname = uri.hostname & ":" & uri.port + var conn: HttpConnectionRef = nil + var index: int + debug "Test worker has been started", worker = worker + while true: + try: + let test = await inputQueue.popFirst() + index = test.index + if isNil(conn): + conn = await openConnection(address, uri, conf.getTLSFlags()) + debug "Opened new connection with remote host", address = $address, + worker = worker + let testRes = await runTest(conn, uri, test.rule, worker, test.index) + let caseRes = TestCaseResult(index: test.index, data: testRes) + await outputQueue.addLast(caseRes) + if ResetConnection in testRes.flags: + await conn.closeWait() + conn = nil + index = 0 + except CancelledError: + if not(isNil(conn)): + await conn.closeWait() + conn = nil + notice "Got signal, exiting", worker = worker + return + except ConnectionError: + warn "Unable to establish connection with remote host", host = hostname, + worker = worker + return + except CatchableError as exc: + warn "Unexpected exception while running test test run", host = hostname, + error_name = exc.name, error_msg = exc.msg, index = index, + worker = worker + return + +proc startTests(conf: RestTesterConf, uri: Uri, + rules: seq[JsonNode]): Future[int] {.async.} = + var workers = newSeq[Future[void]](conf.connectionsCount) + var inputQueue = newAsyncQueue[TestCase](len(rules)) + var outputQueue = newAsyncQueue[TestCaseResult](conf.connectionsCount) + var results = newSeq[TestResult](len(rules)) + var restarts = 0 + + let address = + block: + let res = uri.getAddress() + if res.isErr(): + fatal "Unable to resolve remote host address, exiting", + uri = $uri + return 1 + res.get() + + for index, item in rules.pairs(): + inputQueue.addLastNoWait(TestCase(index: index, rule: item)) + + for i in 0 ..< len(workers): + workers[i] = workerLoop(address, uri, i, conf, inputQueue, outputQueue) + + block: + var pending = newSeq[FutureBase](len(workers) + 1) + for i in 0 ..< len(workers): + pending[i] = workers[i] + + var fut: Future[TestCaseResult] + for i in 0 ..< len(rules): + fut = outputQueue.popFirst() + pending[^1] = fut + discard await race(pending) + for i in 0 ..< len(pending): + if pending[i].finished(): + if i < len(workers): + warn "Test worker quits unexpectedly", index = i, restarts = restarts + return 1 + else: + if pending[i].failed() or pending[i].cancelled(): + warn "Unexpected result from queue" + return 1 + + let tcaseRes = fut.read() + results[tcaseRes.index] = tcaseRes.data + notice "Got test result", index = tcaseRes.index, + value = tcaseRes.data.kind + pending[i] = nil + + debug "Stopping workers" + # Stopping workers + block: + var pending = newSeq[Future[void]]() + for worker in workers: + if not(worker.finished()): + pending.add(worker.cancelAndWait()) + await allFutures(pending) + + var errCode = 0 + let headerLine = "\r\n" & + '-'.repeat(45 + 20 + 7 + 20 + 20) & "\r\n" & + alignLeft("TEST", 45) & alignLeft("STATUS", 20) & + alignLeft("ERROR", 7) & alignLeft("ELAPSED", 20) & + alignLeft("MESSAGE", 20) & "\r\n" & + '-'.repeat(45 + 20 + 7 + 20 + 20) + echo headerLine + for index, item in rules.pairs(): + let errorFlag = + block: + var tmp = "---" + if StatusValidationFailure in results[index].flags: + tmp[0] = 'S' + if HeadersValidationFailure in results[index].flags: + tmp[1] = 'H' + if BodyValidationFailure in results[index].flags: + tmp[2] = 'R' + tmp + let line = + alignLeft(item.getTestName() & "#" & $index, 45) & + alignLeft($results[index].kind, 20) & + alignLeft(errorFlag, 7) & + alignLeft($results[index].times[3], 20) & + alignLeft($results[index].message, 20) + echo line + if results[index].kind != ValidationSuccess: + errCode = 1 + return errCode + +proc run(conf: RestTesterConf): int = + let uri = + block: + let res = conf.getUri() + if res.isErr(): + fatal "Incomplete/incorrect URL", url = conf.url, + error_msg = $res.error() + return 1 + res.get() + + let jnodes = + block: + let res = conf.getTestRules() + if res.isErr(): + fatal "Incomplete/incorrect rules file", file = conf.rulesFilename + return 1 + res.get() + + notice "Waiting for initial connection attempt", time = conf.delayTime + try: + waitFor(sleepAsync(conf.delayTime.seconds)) + except CatchableError as exc: + fatal "Unexpected test failure", error_name = exc.name, error_msg = exc.msg + return 1 + + notice "Exploring remote server hostname", + hostname = uri.hostname & ":" & uri.port + try: + waitFor(checkConnection(conf, uri)) + except ConnectionError as exc: + return 1 + + try: + return waitFor(startTests(conf, uri, jnodes)) + except CatchableError as exc: + fatal "Unexpected test failure", error_name = exc.name, error_msg = exc.msg + return 1 + +when isMainModule: + echo RestTesterHeader + var conf = RestTesterConf.load(version = RestTesterVersion) + quit run(conf) diff --git a/tests/simulation/restapi.sh b/tests/simulation/restapi.sh new file mode 100755 index 000000000..7dd30cbf7 --- /dev/null +++ b/tests/simulation/restapi.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash + +NUM_VALIDATORS=${VALIDATORS:-32} +TOTAL_NODES=${NODES:-1} +GIT_ROOT="$(git rev-parse --show-toplevel)" +TEST_DIR="${GIT_ROOT}/build/resttest_sim" +LOG_FILE="${TEST_DIR}/resttest_node.log" +VALIDATORS_DIR="${TEST_DIR}/validators" +SECRETS_DIR="${TEST_DIR}/secrets" +SNAPSHOT_FILE="${TEST_DIR}/state_snapshot.ssz" +NETWORK_BOOTSTRAP_FILE="${TEST_DIR}/bootstrap_hidden_nodes.txt" +RESTTEST_RULES="${GIT_ROOT}/ncli/resttest-rules.json" +DEPOSIT_CONTRACT_BIN="${GIT_ROOT}/build/deposit_contract" +BOOTSTRAP_ENR_FILE="${TEST_DIR}/beacon_node.enr" +NETWORK_METADATA_FILE="${TEST_DIR}/network.json" +DEPOSITS_FILE="${TEST_DIR}/deposits.json" +REST_ADDRESS="127.0.0.1" +REST_PORT="5052" +MKDIR_SCRIPT="${GIT_ROOT}/scripts/makedir.sh" + +$MKDIR_SCRIPT "${TEST_DIR}" +cd "${TEST_DIR}" + +# Windows detection +if uname | grep -qiE "mingw|msys"; then + MAKE="mingw32-make" +else + MAKE="make" +fi + +# number of CPU cores +if uname | grep -qi darwin; then + NPROC="$(sysctl -n hw.logicalcpu)" +else + NPROC="$(nproc)" +fi + +build_if_missing () { + if [[ ! -e "${GIT_ROOT}/build/${1}" ]]; then + ${MAKE} -C "${GIT_ROOT}" -j ${NPROC} ${1} + fi +} + +EXISTING_VALIDATORS=0 +if [[ -f "${DEPOSITS_FILE}" ]]; then + # We count the number of deposits by counting the number of + # occurrences of the 'deposit_data_root' field: + EXISTING_VALIDATORS=$(grep -o -i deposit_data_root "${DEPOSITS_FILE}" | wc -l) +fi + +if [[ ${EXISTING_VALIDATORS} -ne ${NUM_VALIDATORS} ]]; then + build_if_missing deposit_contract + rm -rf "${VALIDATORS_DIR}" "${SECRETS_DIR}" + ../deposit_contract generateSimulationDeposits \ + --count="${NUM_VALIDATORS}" \ + --out-validators-dir="${VALIDATORS_DIR}" \ + --out-secrets-dir="${SECRETS_DIR}" \ + --out-deposits-file="${DEPOSITS_FILE}" + echo "All deposits prepared" +fi + +build_if_missing nimbus_beacon_node +build_if_missing resttest + +if [[ ! -f "${SNAPSHOT_FILE}" ]]; then + echo "Creating testnet genesis..." + ../nimbus_beacon_node \ + --data-dir="${TEST_DIR}" \ + createTestnet \ + --deposits-file="${DEPOSITS_FILE}" \ + --total-validators="${NUM_VALIDATORS}" \ + --output-genesis="${SNAPSHOT_FILE}" \ + --output-bootstrap-file="${NETWORK_BOOTSTRAP_FILE}" \ + --netkey-file=network_key.json \ + --insecure-netkey-password=true \ + --genesis-offset=0 # Delay in seconds +fi + +DEPOSIT_CONTRACT_ADDRESS="0x0000000000000000000000000000000000000000" +DEPOSIT_CONTRACT_BLOCK="0x0000000000000000000000000000000000000000000000000000000000000000" + +echo "Writing ${NETWORK_METADATA_FILE}:" +tee "${NETWORK_METADATA_FILE}" < ${LOG_FILE} 2>&1 & +BEACON_NODE_STATUS=$? + +if [[ ${BEACON_NODE_STATUS} -eq 0 ]]; then + echo "nimbus_beacon_node has been successfully started" + + BEACON_NODE_PID="$(jobs -p)" + + ../resttest \ + --delay=30 \ + --timeout=60 \ + --skip-topic=slow \ + --connections=4 \ + --rules-file="${RESTTEST_RULES}" \ + http://${REST_ADDRESS}:${REST_PORT}/api + RESTTEST_STATUS=$? + + kill -SIGINT ${BEACON_NODE_PID} + + if [[ ${RESTTEST_STATUS} -eq 0 ]]; then + echo "All tests were completed successfully!" + else + echo "Some of the tests failed!" + tail -n 100 ${LOG_FILE} + exit 1 + fi +else + echo "nimbus_beacon_node failed to start" +fi