Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor validator client #2416

Merged
merged 24 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
9283775
Simplify validator API client
dapplion Apr 21, 2021
633a9c8
Review logic and comments
dapplion Apr 21, 2021
d2a4e5b
Use hasVotingPubkey util
dapplion Apr 21, 2021
9ad64b4
Review AttestationDutiesService
dapplion Apr 21, 2021
3e3f8d1
Review BlockDutiesService
dapplion Apr 21, 2021
8541244
Review ValidatorStore
dapplion Apr 21, 2021
289d5f2
Better logging in tests
dapplion Apr 21, 2021
ad9d19c
Update validator unit tests
dapplion Apr 21, 2021
44fab2f
Don't fetch duties for slot, epoch < 0
dapplion Apr 23, 2021
1899c46
Update sim test timeouts and params
dapplion Apr 23, 2021
c4310ed
Wait a max of SLOT_DIFF for requested slot in validator API
dapplion Apr 24, 2021
87a2191
Prioritize the most valuable aggregates
dapplion Apr 24, 2021
e202c07
Sim test multi-thread, connect nodes before starting validators
dapplion Apr 24, 2021
960b366
Don't wait for genesisTime on validator
dapplion Apr 24, 2021
5f8f6a5
Improve handling of genesis duties
dapplion Apr 24, 2021
fdc9a46
Fix BlockDuties pre-genesis logic
dapplion Apr 24, 2021
13ac7d7
Improve error messages in epochContext duties methods
dapplion Apr 24, 2021
6127dbb
Consider the node synced before or close to genesis
dapplion Apr 24, 2021
dcb0345
Improve notWhileSyncing function
dapplion Apr 24, 2021
6355e86
Log to debug each time a validator signs and attestation and aggregate
dapplion Apr 24, 2021
fa8d830
In sim test report rendering log undefined as -
dapplion Apr 24, 2021
5532e94
Use MAX_API_CLOCK_DISPARITY_MS in validator API
dapplion Apr 24, 2021
edd29d9
Update unit tests
dapplion Apr 24, 2021
de90ae1
Fix comment
dapplion Apr 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 11 additions & 6 deletions packages/beacon-state-transition/src/fast/util/epochContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {ByteVector, hash, toHexString, BitList, List, readonlyValues} from "@cha
import bls, {CoordType, PublicKey} from "@chainsafe/bls";
import {BLSSignature, CommitteeIndex, Epoch, Slot, ValidatorIndex, phase0, allForks} from "@chainsafe/lodestar-types";
import {IBeaconConfig} from "@chainsafe/lodestar-config";
import {intToBytes, assert} from "@chainsafe/lodestar-utils";
import {intToBytes} from "@chainsafe/lodestar-utils";

import {GENESIS_EPOCH} from "../../constants";
import {
Expand Down Expand Up @@ -206,7 +206,7 @@ export class EpochContext {
getBeaconCommittee(slot: Slot, index: CommitteeIndex): ValidatorIndex[] {
const slotCommittees = this._getSlotCommittees(slot);
if (index >= slotCommittees.length) {
throw new Error(`crosslink committee retrieval: out of range committee index: ${index}`);
throw new Error(`Requesting beacon committee index ${index} over slot committees len ${slotCommittees.length}`);
}
return slotCommittees[index];
}
Expand All @@ -218,7 +218,9 @@ export class EpochContext {
getBeaconProposer(slot: Slot): ValidatorIndex {
const epoch = computeEpochAtSlot(this.config, slot);
if (epoch !== this.currentShuffling.epoch) {
throw new Error("beacon proposer index out of range");
throw new Error(
`Requesting beacon proposer for different epoch current shuffling: ${epoch} != ${this.currentShuffling.epoch}`
);
}
return this.proposers[slot % this.config.params.SLOTS_PER_EPOCH];
}
Expand Down Expand Up @@ -260,8 +262,11 @@ export class EpochContext {
* Return null if no assignment..
*/
getCommitteeAssignment(epoch: Epoch, validatorIndex: ValidatorIndex): phase0.CommitteeAssignment | null {
const nextEpoch = this.currentShuffling.epoch + 1;
assert.lte(epoch, nextEpoch, "Cannot get committee assignment for epoch more than 1 ahead");
if (epoch > this.currentShuffling.epoch + 1) {
throw Error(
`Requesting committee assignment for more than 1 epoch ahead: ${epoch} > ${this.currentShuffling.epoch} + 1`
);
}

const epochStartSlot = computeStartSlotAtEpoch(this.config, epoch);
for (let slot = epochStartSlot; slot < epochStartSlot + this.config.params.SLOTS_PER_EPOCH; slot++) {
Expand Down Expand Up @@ -301,7 +306,7 @@ export class EpochContext {
} else if (epoch === this.nextShuffling.epoch) {
return this.nextShuffling.committees[epochSlot];
} else {
throw new Error(`crosslink committee retrieval: out of range epoch: ${epoch}`);
throw new Error(`Requesting slot committee out of range epoch: ${epoch} current: ${this.currentShuffling.epoch}`);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {Root} from "@chainsafe/lodestar-types";
import {SlashingProtection} from "@chainsafe/lodestar-validator";
import {ApiClientOverRest, SlashingProtection} from "@chainsafe/lodestar-validator";
import {LevelDbController} from "@chainsafe/lodestar-db";
import {ApiClientProvider} from "@chainsafe/lodestar-validator";
import {YargsError} from "../../../../../util";
import {IGlobalArgs} from "../../../../../options";
import {getValidatorPaths} from "../../../../validator/paths";
Expand Down Expand Up @@ -30,8 +29,7 @@ export async function getGenesisValidatorsRoot(args: IGlobalArgs & ISlashingProt
const server = args.server;

const config = getBeaconConfigFromArgs(args);
const logger = errorLogger();
const api = new ApiClientProvider(config, logger, server);
const api = ApiClientOverRest(config, server);
const genesis = await api.beacon.getGenesis();

if (genesis) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ BE UNTIL AT LEAST TWO YEARS AFTER THE PHASE 0 MAINNET LAUNCH.

const config = getBeaconConfigFromArgs(args);

const validatorClient = new Validator({
const validatorClient = await Validator.initializeFromBeaconNode({
slashingProtection: getSlashingProtection(args),
config,
api: args.server,
Expand Down
7 changes: 6 additions & 1 deletion packages/cli/src/cmds/dev/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from "fs";
import {promisify} from "util";
import rimraf from "rimraf";
import path from "path";
import {AbortController} from "abort-controller";
import {GENESIS_SLOT} from "@chainsafe/lodestar-params";
import {BeaconNode, BeaconDb, initStateFromAnchorState, createNodeJsLibp2p, nodeUtils} from "@chainsafe/lodestar";
import {IApiClient, SlashingProtection, Validator} from "@chainsafe/lodestar-validator";
Expand Down Expand Up @@ -109,7 +110,11 @@ export async function devHandler(args: IDevArgs & IGlobalArgs): Promise<void> {
controller: new LevelDbController({name: dbPath}, {logger}),
});

const validator = new Validator({config, slashingProtection, api, logger, secretKeys});
const controller = new AbortController();
onGracefulShutdownCbs.push(async () => controller.abort());

// Initailize genesis once for all validators
const validator = await Validator.initializeFromBeaconNode({config, slashingProtection, api, logger, secretKeys});

onGracefulShutdownCbs.push(() => validator.stop());
await validator.start();
Expand Down
32 changes: 19 additions & 13 deletions packages/cli/src/cmds/validator/handler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {AbortController} from "abort-controller";
import {ApiClientOverRest} from "@chainsafe/lodestar-validator";
import {Validator, SlashingProtection} from "@chainsafe/lodestar-validator";
import {LevelDbController} from "@chainsafe/lodestar-db";
import {getBeaconConfigFromArgs} from "../../config";
Expand Down Expand Up @@ -33,21 +35,25 @@ export async function validatorHandler(args: IValidatorCliArgs & IGlobalArgs): P
const dbPath = validatorPaths.validatorsDbDir;
mkdir(dbPath);

const validator = new Validator({
config,
slashingProtection: new SlashingProtection({
config: config,
controller: new LevelDbController({name: dbPath}, {logger}),
}),
api: args.server,
logger,
secretKeys,
graffiti,
});

const onGracefulShutdownCbs: (() => Promise<void>)[] = [];
onGracefulShutdown(async () => {
await validator.stop();
for (const cb of onGracefulShutdownCbs) await cb();
}, logger.info.bind(logger));

// This AbortController interrupts the sleep() calls when waiting for genesis
const controller = new AbortController();
onGracefulShutdownCbs.push(async () => controller.abort());

const api = ApiClientOverRest(config, args.server);
const slashingProtection = new SlashingProtection({
config: config,
controller: new LevelDbController({name: dbPath}, {logger}),
});
const validator = await Validator.initializeFromBeaconNode(
{config, slashingProtection, api, logger, secretKeys, graffiti},
controller.signal
);

onGracefulShutdownCbs.push(async () => await validator.stop());
await validator.start();
}
2 changes: 0 additions & 2 deletions packages/lodestar/src/api/impl/beacon/blocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {IApiModules} from "../../interface";
import {BlockId, IBeaconBlocksApi} from "./interface";
import {resolveBlockId, toBeaconHeaderResponse} from "./utils";
import {IBeaconSync} from "../../../../sync";
import {checkSyncStatus} from "../../utils";
import {INetwork} from "../../../../network/interface";

export * from "./interface";
Expand Down Expand Up @@ -109,7 +108,6 @@ export class BeaconBlockApi implements IBeaconBlocksApi {
}

async publishBlock(signedBlock: phase0.SignedBeaconBlock): Promise<void> {
await checkSyncStatus(this.config, this.sync);
dapplion marked this conversation as resolved.
Show resolved Hide resolved
await Promise.all([this.chain.receiveBlock(signedBlock), this.network.gossip.publishBeaconBlock(signedBlock)]);
}
}
2 changes: 0 additions & 2 deletions packages/lodestar/src/api/impl/beacon/pool/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {INetwork} from "../../../../network";
import {IBeaconSync} from "../../../../sync";
import {IApiOptions} from "../../../options";
import {IApiModules} from "../../interface";
import {checkSyncStatus} from "../../utils";
import {IAttestationFilters, IBeaconPoolApi} from "./interface";
import {phase0} from "@chainsafe/lodestar-types";
import {fast} from "@chainsafe/lodestar-beacon-state-transition";
Expand Down Expand Up @@ -43,7 +42,6 @@ export class BeaconPoolApi implements IBeaconPoolApi {
}

async submitAttestations(attestations: phase0.Attestation[]): Promise<void> {
await checkSyncStatus(this.config, this.sync);
dapplion marked this conversation as resolved.
Show resolved Hide resolved
for (const attestation of attestations) {
const attestationJob = {
attestation,
Expand Down
23 changes: 0 additions & 23 deletions packages/lodestar/src/api/impl/utils.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,7 @@
import {IBeaconConfig} from "@chainsafe/lodestar-config";
import {allForks} from "@chainsafe/lodestar-types";
import {ValidatorIndex} from "@chainsafe/lodestar-types/phase0";
import {ByteVector} from "@chainsafe/ssz";
import {IBeaconChain} from "../../chain/interface";
import {IBeaconSync} from "../../sync";
import {ApiError} from "./errors";

/**
* Check the sync status of the beacon chain.
*/
export async function checkSyncStatus(config: IBeaconConfig, sync: IBeaconSync): Promise<void> {
if (!sync.isSynced()) {
let syncStatus;
try {
syncStatus = sync.getSyncStatus();
} catch (e) {
throw new ApiError(503, "Node is stopped");
}
if (syncStatus.syncDistance > config.params.SLOTS_PER_EPOCH) {
throw new ApiError(
503,
`Node is syncing, status: ${JSON.stringify(config.types.phase0.SyncingStatus.toJson(syncStatus))}`
);
}
}
}

export function getStateValidatorIndex(
id: number | ByteVector,
Expand Down