Skip to content

Commit

Permalink
feat: use POST variant to get validators from state (#6897)
Browse files Browse the repository at this point in the history
* feat: use POST variant to get validators from state

* Fix api handler used during testing

* Update more calls to use POST variant
  • Loading branch information
nflaig authored Sep 26, 2024
1 parent bb82046 commit 58dea75
Show file tree
Hide file tree
Showing 13 changed files with 27 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ describe("lightclient api", function () {
const committeeRes = await lightclient.getLightClientCommitteeRoot({startPeriod: 0, count: 1});
committeeRes.assertOk();
const client = getClient({baseUrl: `http://127.0.0.1:${restPort}`}, {config}).beacon;
const validators = (await client.getStateValidators({stateId: "head"})).value();
const validators = (await client.postStateValidators({stateId: "head"})).value();
const pubkeys = validators.map((v) => v.validator.pubkey);
expect(pubkeys.length).toBe(validatorCount);
// only 2 validators spreading to 512 committee slots
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/cmds/validator/blsToExecutionChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ like to choose for BLS To Execution Change.",
const {genesisValidatorsRoot} = (await client.beacon.getGenesis()).value();
const config = createBeaconConfig(chainForkConfig, genesisValidatorsRoot);

const validators = (await client.beacon.getStateValidators({stateId: "head", validatorIds: [publicKey]})).value();
const validators = (await client.beacon.postStateValidators({stateId: "head", validatorIds: [publicKey]})).value();
const validator = validators[0];
if (validator === undefined) {
throw new Error(`Validator pubkey ${publicKey} not found in state`);
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/cmds/validator/voluntaryExit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ function selectSignersToExit(args: VoluntaryExitArgs, signers: Signer[]): Signer
async function resolveValidatorIndexes(client: ApiClient, signersToExit: SignerPubkey[]) {
const pubkeys = signersToExit.map(({pubkey}) => pubkey);

const validators = (await client.beacon.getStateValidators({stateId: "head", validatorIds: pubkeys})).value();
const validators = (await client.beacon.postStateValidators({stateId: "head", validatorIds: pubkeys})).value();

const dataByPubkey = new Map(validators.map((item) => [toPubkeyHex(item.validator.pubkey), item]));

Expand Down
10 changes: 5 additions & 5 deletions packages/cli/test/sim/endpoints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ await env.start({runTimeoutMs: estimatedTimeoutMs});
const node = env.nodes[0].beacon;
await waitForSlot("Wait for 2 slots before checking endpoints", {env, slot: 2});

const validators = (await node.api.beacon.getStateValidators({stateId: "head"})).value();
const validators = (await node.api.beacon.postStateValidators({stateId: "head"})).value();

await env.tracker.assert("should have correct validators count called without filters", async () => {
assert.equal(validators.length, validatorCount);
Expand All @@ -55,12 +55,12 @@ await env.tracker.assert("should have correct validator index for second validat
});

await env.tracker.assert(
"should return correct number of filtered validators when getStateValidators called with filters",
"should return correct number of filtered validators when postStateValidators called with filters",
async () => {
const filterPubKey =
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c";

const res = await node.api.beacon.getStateValidators({stateId: "head", validatorIds: [filterPubKey]});
const res = await node.api.beacon.postStateValidators({stateId: "head", validatorIds: [filterPubKey]});

assert.equal(res.value().length, 1);

Expand All @@ -71,12 +71,12 @@ await env.tracker.assert(
);

await env.tracker.assert(
"should return correct filtered validators when getStateValidators called with filters",
"should return correct filtered validators when postStateValidators called with filters",
async () => {
const filterPubKey =
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c";

const res = await node.api.beacon.getStateValidators({stateId: "head", validatorIds: [filterPubKey]});
const res = await node.api.beacon.postStateValidators({stateId: "head", validatorIds: [filterPubKey]});

assert.equal(toHexString(res.value()[0].validator.pubkey), filterPubKey);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/flare/src/cmds/selfSlashAttester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function selfSlashAttesterHandler(args: SelfSlashArgs): Promise<voi

// Retrieve the status all all validators in range at once
const pksHex = sks.map((sk) => sk.toPublicKey().toHex());
const validators = (await client.beacon.getStateValidators({stateId: "head", validatorIds: pksHex})).value();
const validators = (await client.beacon.postStateValidators({stateId: "head", validatorIds: pksHex})).value();

// All validators in the batch will be part of the same AttesterSlashing
const attestingIndices = validators.map((v) => v.index);
Expand All @@ -92,7 +92,7 @@ export async function selfSlashAttesterHandler(args: SelfSlashArgs): Promise<voi
const pkHex = pksHex[i];
const validatorPkHex = toPubkeyHex(validator.pubkey);
if (validatorPkHex !== pkHex) {
throw Error(`getStateValidators did not return same validator pubkey: ${validatorPkHex} != ${pkHex}`);
throw Error(`Beacon node did not return same validator pubkey: ${validatorPkHex} != ${pkHex}`);
}

if (status === "active_slashed" || status === "exited_slashed") {
Expand Down
4 changes: 2 additions & 2 deletions packages/flare/src/cmds/selfSlashProposer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function selfSlashProposerHandler(args: SelfSlashArgs): Promise<voi

// Retrieve the status all all validators in range at once
const pksHex = sks.map((sk) => sk.toPublicKey().toHex());
const validators = (await client.beacon.getStateValidators({stateId: "head", validatorIds: pksHex})).value();
const validators = (await client.beacon.postStateValidators({stateId: "head", validatorIds: pksHex})).value();

// Submit all ProposerSlashing for range at once
await Promise.all(
Expand All @@ -88,7 +88,7 @@ export async function selfSlashProposerHandler(args: SelfSlashArgs): Promise<voi
try {
const validatorPkHex = toPubkeyHex(validator.pubkey);
if (validatorPkHex !== pkHex) {
throw Error(`getStateValidators did not return same validator pubkey: ${validatorPkHex} != ${pkHex}`);
throw Error(`Beacon node did not return same validator pubkey: ${validatorPkHex} != ${pkHex}`);
}

if (status === "active_slashed" || status === "exited_slashed") {
Expand Down
12 changes: 7 additions & 5 deletions packages/validator/src/services/indices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import {batchItems} from "../util/index.js";
import {Metrics} from "../metrics.js";

/**
* URLs have a limitation on size, adding an unbounded num of pubkeys will break the request.
* For reasoning on the specific number see: https://github.com/ethereum/beacon-APIs/pull/328
* This is to prevent the "Request body is too large" issue for http post.
* Typical servers accept up to 1MB (2 ** 20 bytes) of request body, for example fastify and nginx.
* A hex encoded public key with "0x"-prefix has a size of 98 bytes + 2 bytes to account for commas
* and other JSON padding. `Math.floor(2 ** 20 / 100) == 10485`, we can send up to ~10k keys per request.
*/
const PUBKEYS_PER_REQUEST = 64;
const PUBKEYS_PER_REQUEST = 10_000;

// To assist with readability
type PubkeyHex = string;
Expand Down Expand Up @@ -108,7 +110,7 @@ export class IndicesService {
}

// Query the remote BN to resolve a pubkey to a validator index.
// support up to 1000 pubkeys per poll
// support up to 10k pubkeys per poll
const pubkeysHexBatches = batchItems(pubkeysHexToDiscover, {batchSize: PUBKEYS_PER_REQUEST});

const newIndices: number[] = [];
Expand All @@ -123,7 +125,7 @@ export class IndicesService {
}

private async fetchValidatorIndices(pubkeysHex: string[]): Promise<ValidatorIndex[]> {
const validators = (await this.api.beacon.getStateValidators({stateId: "head", validatorIds: pubkeysHex})).value();
const validators = (await this.api.beacon.postStateValidators({stateId: "head", validatorIds: pubkeysHex})).value();

const newIndices = [];

Expand Down
4 changes: 3 additions & 1 deletion packages/validator/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,9 @@ export class Validator {
* Create a signed voluntary exit message for the given validator by its key.
*/
async signVoluntaryExit(publicKey: string, exitEpoch?: number): Promise<phase0.SignedVoluntaryExit> {
const validators = (await this.api.beacon.getStateValidators({stateId: "head", validatorIds: [publicKey]})).value();
const validators = (
await this.api.beacon.postStateValidators({stateId: "head", validatorIds: [publicKey]})
).value();

const validator = validators[0];
if (validator === undefined) {
Expand Down
2 changes: 1 addition & 1 deletion packages/validator/test/unit/services/attestation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ describe("AttestationService", function () {
];

// Return empty replies to duties service
api.beacon.getStateValidators.mockResolvedValue(
api.beacon.postStateValidators.mockResolvedValue(
mockApiResponse({data: [], meta: {executionOptimistic: false, finalized: false}})
);
api.validator.getAttesterDuties.mockResolvedValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe("AttestationDutiesService", function () {
index,
validator: {...defaultValidator.validator, pubkey: pubkeys[0]},
};
api.beacon.getStateValidators.mockResolvedValue(
api.beacon.postStateValidators.mockResolvedValue(
mockApiResponse({data: [validatorResponse], meta: {executionOptimistic: false, finalized: false}})
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe("SyncCommitteeDutiesService", function () {
index: indices[i],
validator: {...defaultValidator.validator, pubkey: pubkeys[i]},
}));
api.beacon.getStateValidators.mockResolvedValue(
api.beacon.postStateValidators.mockResolvedValue(
mockApiResponse({data: validatorResponses, meta: {executionOptimistic: false, finalized: false}})
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe("SyncCommitteeService", function () {
];

// Return empty replies to duties service
api.beacon.getStateValidators.mockResolvedValue(
api.beacon.postStateValidators.mockResolvedValue(
mockApiResponse({data: [], meta: {executionOptimistic: false, finalized: false}})
);
api.validator.getSyncCommitteeDuties.mockResolvedValue(
Expand Down
1 change: 1 addition & 0 deletions packages/validator/test/utils/apiStub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function getApiClientStub(): ApiClientStub {
return {
beacon: {
getStateValidators: vi.fn(),
postStateValidators: vi.fn(),
publishBlindedBlockV2: vi.fn(),
publishBlockV2: vi.fn(),
submitPoolSyncCommitteeSignatures: vi.fn(),
Expand Down

0 comments on commit 58dea75

Please sign in to comment.