diff --git a/packages/api/src/routes/beacon/state.ts b/packages/api/src/routes/beacon/state.ts index 6e2851fdbbc8..24e6fcfe7bee 100644 --- a/packages/api/src/routes/beacon/state.ts +++ b/packages/api/src/routes/beacon/state.ts @@ -20,7 +20,7 @@ export type ValidatorStatus = | "withdrawal_done"; export type ValidatorFilters = { - indices?: ValidatorId[]; + id?: ValidatorId[]; statuses?: ValidatorStatus[]; }; export type CommitteesFilters = { @@ -153,8 +153,8 @@ export type ReqTypes = { getStateFork: StateIdOnlyReq; getStateRoot: StateIdOnlyReq; getStateValidator: {params: {stateId: StateId; validatorId: ValidatorId}}; - getStateValidators: {params: {stateId: StateId}; query: {indices?: ValidatorId[]; statuses?: ValidatorStatus[]}}; - getStateValidatorBalances: {params: {stateId: StateId}; query: {indices?: ValidatorId[]}}; + getStateValidators: {params: {stateId: StateId}; query: {id?: ValidatorId[]; statuses?: ValidatorStatus[]}}; + getStateValidatorBalances: {params: {stateId: StateId}; query: {id?: ValidatorId[]}}; }; export function getReqSerializers(): ReqSerializers { @@ -200,16 +200,16 @@ export function getReqSerializers(): ReqSerializers { parseReq: ({params, query}) => [params.stateId, query], schema: { params: {stateId: Schema.StringRequired}, - query: {indices: Schema.UintOrStringArray, statuses: Schema.StringArray}, + query: {id: Schema.UintOrStringArray, statuses: Schema.StringArray}, }, }, getStateValidatorBalances: { - writeReq: (stateId, indices) => ({params: {stateId}, query: {indices}}), - parseReq: ({params, query}) => [params.stateId, query.indices], + writeReq: (stateId, id) => ({params: {stateId}, query: {id}}), + parseReq: ({params, query}) => [params.stateId, query.id], schema: { params: {stateId: Schema.StringRequired}, - query: {indices: Schema.UintOrStringArray}, + query: {id: Schema.UintOrStringArray}, }, }, }; diff --git a/packages/api/src/routes/node.ts b/packages/api/src/routes/node.ts index b5b4b8a2cb83..d217f02d182d 100644 --- a/packages/api/src/routes/node.ts +++ b/packages/api/src/routes/node.ts @@ -1,4 +1,4 @@ -import {allForks, Slot, ssz, StringType} from "@chainsafe/lodestar-types"; +import {allForks, ssz, StringType} from "@chainsafe/lodestar-types"; import {ContainerType} from "@chainsafe/ssz"; import { ArrayOf, @@ -52,9 +52,9 @@ export type FilterGetPeers = { export type SyncingStatus = { /** Head slot node is trying to reach */ - headSlot: Slot; + headSlot: string; /** How many slots node needs to process to reach head. 0 if synced. */ - syncDistance: Slot; + syncDistance: string; /** Set to true if the node is syncing, false if the node is synced. */ isSyncing: boolean; }; diff --git a/packages/api/test/unit/beacon.test.ts b/packages/api/test/unit/beacon.test.ts index 81f1bf4e76fc..d930c00c7573 100644 --- a/packages/api/test/unit/beacon.test.ts +++ b/packages/api/test/unit/beacon.test.ts @@ -117,7 +117,7 @@ describe("beacon", () => { }, }, getStateValidators: { - args: ["head", {indices: [pubkeyHex, "1300"], statuses: ["active_ongoing"]}], + args: ["head", {id: [pubkeyHex, "1300"], statuses: ["active_ongoing"]}], res: {data: [validatorResponse]}, }, getStateValidator: { diff --git a/packages/api/test/unit/node.test.ts b/packages/api/test/unit/node.test.ts index 1a810265b7cf..65ff2d016961 100644 --- a/packages/api/test/unit/node.test.ts +++ b/packages/api/test/unit/node.test.ts @@ -53,7 +53,7 @@ describe("node", () => { }, getSyncingStatus: { args: [], - res: {data: {headSlot: 1, syncDistance: 2, isSyncing: false}}, + res: {data: {headSlot: "1", syncDistance: "2", isSyncing: false}}, }, getHealth: { args: [], diff --git a/packages/lodestar/src/api/impl/beacon/state/index.ts b/packages/lodestar/src/api/impl/beacon/state/index.ts index 33b27e1a92e0..eb67cf748276 100644 --- a/packages/lodestar/src/api/impl/beacon/state/index.ts +++ b/packages/lodestar/src/api/impl/beacon/state/index.ts @@ -52,8 +52,8 @@ export function getBeaconStateApi({chain, config, db}: Pick = { + SECONDS_PER_SLOT: 2, + }; + + const afterEachCallbacks: (() => Promise | void)[] = []; + afterEach(async () => { + while (afterEachCallbacks.length > 0) { + const callback = afterEachCallbacks.pop(); + if (callback) await callback(); + } + }); + + before(async function () { + await initBLS(); + }); + + it("should return all validators when getStateValidators called without filters", async function () { + const validatorCount = 2; + const bn = await getDevBeaconNode({ + params: testParams, + options: { + sync: {isSingleNode: true}, + api: {rest: {enabled: true, port: restPort}}, + }, + validatorCount, + logger: loggerNodeA, + }); + afterEachCallbacks.push(() => bn.close()); + + const {validators} = await getAndInitDevValidators({ + node: bn, + validatorsPerClient: validatorCount, + validatorClientCount: 1, + startIndex: 0, + useRestApi: false, + testLoggerOpts, + }); + afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.stop()))); + + await Promise.all(validators.map((validator) => validator.start())); + + const client = getClient(config, {baseUrl: `http://127.0.0.1:${restPort}`}).beacon; + + const response = await client.getStateValidators("head"); + expect(response.data.length).to.be.equal(validatorCount); + expect(response.data[0].index).to.be.equal(0); + expect(response.data[1].index).to.be.equal(1); + }); + + it("should return filtered validators when getStateValidators called with filters", async function () { + const validatorCount = 2; + const filterPubKey = + "0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c"; + + const bn = await getDevBeaconNode({ + params: testParams, + options: { + sync: {isSingleNode: true}, + api: {rest: {enabled: true, port: restPort}}, + }, + validatorCount, + logger: loggerNodeA, + }); + afterEachCallbacks.push(() => bn.close()); + + const {validators} = await getAndInitDevValidators({ + node: bn, + validatorsPerClient: validatorCount, + validatorClientCount: 1, + startIndex: 0, + useRestApi: false, + testLoggerOpts, + }); + afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.stop()))); + + await Promise.all(validators.map((validator) => validator.start())); + + const client = getClient(config, {baseUrl: `http://127.0.0.1:${restPort}`}).beacon; + + const response = await client.getStateValidators("head", { + id: [filterPubKey], + }); + + expect(response.data.length).to.be.equal(1); + expect(toHexString(response.data[0].validator.pubkey)).to.be.equal(filterPubKey); + }); + }); +}); diff --git a/packages/lodestar/test/e2e/sync/endpoint.test.ts b/packages/lodestar/test/e2e/sync/endpoint.test.ts new file mode 100644 index 000000000000..42d3bcf01612 --- /dev/null +++ b/packages/lodestar/test/e2e/sync/endpoint.test.ts @@ -0,0 +1,63 @@ +import chaiAsPromised from "chai-as-promised"; +import chai, {expect} from "chai"; +import {initBLS} from "@chainsafe/lodestar-cli/src/util"; +import {createIBeaconConfig, IChainConfig} from "@chainsafe/lodestar-config"; +import {chainConfig as chainConfigDef} from "@chainsafe/lodestar-config/default"; +import {getClient} from "@chainsafe/lodestar-api"; +import {getDevBeaconNode} from "../../utils/node/beacon"; +import {LogLevel, testLogger, TestLoggerOpts} from "../../utils/logger"; + +chai.use(chaiAsPromised); + +/* eslint-disable @typescript-eslint/naming-convention */ +describe("lodestar / sync", function () { + const SECONDS_PER_SLOT = 2; + const ALTAIR_FORK_EPOCH = 0; + const validatorCount = 1; + const restPort = 9596; + const chainConfig: IChainConfig = {...chainConfigDef, SECONDS_PER_SLOT, ALTAIR_FORK_EPOCH}; + const genesisValidatorsRoot = Buffer.alloc(32, 0xaa); + const config = createIBeaconConfig(chainConfig, genesisValidatorsRoot); + const testLoggerOpts: TestLoggerOpts = {logLevel: LogLevel.info}; + const loggerNodeA = testLogger("Node-A", testLoggerOpts); + + describe("/eth/v1/node/syncing", function () { + const testParams: Pick = { + SECONDS_PER_SLOT: 2, + }; + + const afterEachCallbacks: (() => Promise | void)[] = []; + afterEach(async () => { + while (afterEachCallbacks.length > 0) { + const callback = afterEachCallbacks.pop(); + if (callback) await callback(); + } + }); + + before(async function () { + await initBLS(); + }); + + it("getSyncingStatus", async function () { + this.timeout("10 min"); + const bn = await getDevBeaconNode({ + params: testParams, + options: { + sync: {isSingleNode: true}, + api: {rest: {enabled: true, port: restPort}}, + }, + validatorCount, + logger: loggerNodeA, + }); + + afterEachCallbacks.push(() => bn.close()); + + const client = getClient(config, {baseUrl: "http://127.0.0.1:9596"}).node; + + // expect headSlot and syncDistance to be string + await expect(client.getSyncingStatus()).to.eventually.be.deep.equal({ + data: {headSlot: "0", syncDistance: "0", isSyncing: false}, + }); + }); + }); +}); diff --git a/packages/lodestar/test/unit/api/impl/beacon/state/stateValidators.test.ts b/packages/lodestar/test/unit/api/impl/beacon/state/stateValidators.test.ts index 894b64125de9..e8865b8c9df3 100644 --- a/packages/lodestar/test/unit/api/impl/beacon/state/stateValidators.test.ts +++ b/packages/lodestar/test/unit/api/impl/beacon/state/stateValidators.test.ts @@ -54,7 +54,7 @@ describe("beacon api impl - state - validators", function () { } as unknown) as PubkeyIndexMap, } as CachedBeaconStateAllForks); const api = getBeaconStateApi({config, db: dbStub, chain: chainStub}); - const {data: validators} = await api.getStateValidators("someState", {indices: [0, 1, 123]}); + const {data: validators} = await api.getStateValidators("someState", {id: [0, 1, 123]}); expect(validators.length).to.equal(2); }); @@ -96,7 +96,7 @@ describe("beacon api impl - state - validators", function () { } const api = getBeaconStateApi({config, db: dbStub, chain: chainStub}); const {data: stateValidators} = await api.getStateValidators("someState", { - indices: [0, 1, 2, 123], + id: [0, 1, 2, 123], statuses: ["pending_initialized"], }); expect(stateValidators.length).to.equal(3); diff --git a/packages/validator/src/services/indices.ts b/packages/validator/src/services/indices.ts index b4959745b782..6ec3681c80c6 100644 --- a/packages/validator/src/services/indices.ts +++ b/packages/validator/src/services/indices.ts @@ -76,7 +76,7 @@ export class IndicesService { } private async fetchValidatorIndices(pubkeysHex: string[]): Promise { - const validatorsState = await this.api.beacon.getStateValidators("head", {indices: pubkeysHex}); + const validatorsState = await this.api.beacon.getStateValidators("head", {id: pubkeysHex}); const newIndices = []; for (const validatorState of validatorsState.data) { const pubkeyHex = toHexString(validatorState.validator.pubkey); diff --git a/packages/validator/src/validator.ts b/packages/validator/src/validator.ts index 116ddbf005fa..e9a0406b77de 100644 --- a/packages/validator/src/validator.ts +++ b/packages/validator/src/validator.ts @@ -132,7 +132,7 @@ export class Validator { * Perform a voluntary exit for the given validator by its key. */ async voluntaryExit(publicKey: string, exitEpoch?: number): Promise { - const {data: stateValidators} = await this.api.beacon.getStateValidators("head", {indices: [publicKey]}); + const {data: stateValidators} = await this.api.beacon.getStateValidators("head", {id: [publicKey]}); const stateValidator = stateValidators[0]; if (stateValidator === undefined) { throw new Error(`Validator pubkey ${publicKey} not found in state`);