Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions packages/cli/src/cmds/dev/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export async function devHandler(args: IDevArgs & IGlobalArgs): Promise<void> {
const onGracefulShutdownCbs: (() => Promise<void>)[] = [];
onGracefulShutdown(async () => {
for (const cb of onGracefulShutdownCbs) await cb();
await Promise.all([Promise.all(validators.map((v) => v.stop())), node.close()]);
await Promise.all([Promise.all(validators.map((v) => v.close())), node.close()]);
if (args.reset) {
logger.info("Cleaning db directories");
await promisify(rimraf)(beaconDbDir);
Expand Down Expand Up @@ -138,7 +138,6 @@ export async function devHandler(args: IDevArgs & IGlobalArgs): Promise<void> {
})),
});

onGracefulShutdownCbs.push(() => validator.stop());
await validator.start();
onGracefulShutdownCbs.push(() => validator.close());
}
}
3 changes: 1 addition & 2 deletions packages/cli/src/cmds/validator/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ export async function validatorHandler(args: IValidatorCliArgs & IGlobalArgs): P
metrics
);

onGracefulShutdownCbs.push(() => validator.stop());
await validator.start();
onGracefulShutdownCbs.push(() => validator.close());

// Start keymanager API backend
// Only if keymanagerEnabled flag is set to true
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/cmds/validator/voluntaryExit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ BE UNTIL AT LEAST TWO YEARS AFTER THE PHASE 0 MAINNET LAUNCH.
signers: [{type: SignerType.Local, secretKey}],
logger: errorLogger(),
graffiti: args.graffiti,
closed: true,
});

await validatorClient.voluntaryExit(publicKey, args.exitEpoch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ describe("lodestar / api / impl / state", function () {
useRestApi: false,
testLoggerOpts,
});
afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.stop())));

await Promise.all(validators.map((validator) => validator.start()));
afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.close())));

const client = getClient({baseUrl: `http://127.0.0.1:${restPort}`}, {config}).beacon;

Expand Down Expand Up @@ -92,9 +90,7 @@ describe("lodestar / api / impl / state", function () {
useRestApi: false,
testLoggerOpts,
});
afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.stop())));

await Promise.all(validators.map((validator) => validator.start()));
afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.close())));

const client = getClient({baseUrl: `http://127.0.0.1:${restPort}`}, {config}).beacon;

Expand Down
7 changes: 1 addition & 6 deletions packages/lodestar/test/e2e/chain/lightclient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,7 @@ describe("chain / lightclient", function () {
});

afterEachCallbacks.push(async () => {
await Promise.all(validators.map((v) => v.stop()));
});

await Promise.all(validators.map((validator) => validator.start()));
afterEachCallbacks.push(async () => {
await Promise.all(validators.map((v) => v.stop()));
await Promise.all(validators.map((v) => v.close()));
});

// This promise chain does:
Expand Down
8 changes: 4 additions & 4 deletions packages/lodestar/test/e2e/keymanager/keymanager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe("keymanager delete and import test", async function () {
testLoggerOpts,
});

afterEachCallbacks.push(() => vc1Info.validator.stop());
afterEachCallbacks.push(() => vc1Info.validator.close());

const vc2Info = await getAndInitValidatorsWithKeystore({
node: bn,
Expand All @@ -76,7 +76,7 @@ describe("keymanager delete and import test", async function () {
testLoggerOpts,
});

afterEachCallbacks.push(() => vc2Info.validator.stop());
afterEachCallbacks.push(() => vc2Info.validator.close());

const portKM1 = 10000;
const portKM2 = 10001;
Expand Down Expand Up @@ -105,7 +105,7 @@ describe("keymanager delete and import test", async function () {
// Register clean up
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
afterEachCallbacks.push(async () => {
await Promise.all([vc1Info.validator.stop(), vc2Info.validator.stop()]);
await Promise.all([vc1Info.validator.close(), vc2Info.validator.close()]);
vc1Info.tempDirs.keystoreDir.removeCallback();
vc1Info.tempDirs.passwordFile.removeCallback();
vc2Info.tempDirs.keystoreDir.removeCallback();
Expand Down Expand Up @@ -337,7 +337,7 @@ describe("keymanager delete and import test", async function () {
externalSignerUrl: opts?.useRemoteSigner ? "http://localhost:38000" : undefined,
});

afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.stop())));
afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.close())));

const keystoresDir = tmp.dirSync({unsafeCleanup: true, prefix: "keystores"});
const tokenDir = tmp.dirSync({unsafeCleanup: true, prefix: "token"});
Expand Down
4 changes: 1 addition & 3 deletions packages/lodestar/test/e2e/sync/finalizedSync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,8 @@ describe("sync / finalized sync", function () {
testLoggerOpts,
});

afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.stop())));
afterEachCallbacks.push(() => Promise.all(validators.map((validator) => validator.close())));

await Promise.all(validators.map((validator) => validator.start()));
afterEachCallbacks.push(() => Promise.all(validators.map((v) => v.stop())));
// stop beacon node after validators
afterEachCallbacks.push(() => bn.close());

Expand Down
4 changes: 1 addition & 3 deletions packages/lodestar/test/e2e/sync/unknownBlockSync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,8 @@ describe("sync / unknown block sync", function () {
testLoggerOpts,
});

afterEachCallbacks.push(() => Promise.all(validators.map((v) => v.stop())));
afterEachCallbacks.push(() => Promise.all(validators.map((v) => v.close())));

await Promise.all(validators.map((validator) => validator.start()));
afterEachCallbacks.push(() => Promise.all(validators.map((v) => v.stop())));
// stop bn after validators
afterEachCallbacks.push(() => bn.close());

Expand Down
3 changes: 1 addition & 2 deletions packages/lodestar/test/e2e/sync/wss.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ describe("Start from WSS", function () {
testLoggerOpts,
});

afterEachCallbacks.push(() => Promise.all(validators.map((v) => v.stop())));
await Promise.all(validators.map((v) => v.start()));
afterEachCallbacks.push(() => Promise.all(validators.map((v) => v.close())));

try {
await finalizedEventistener;
Expand Down
6 changes: 2 additions & 4 deletions packages/lodestar/test/sim/merge-interop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,11 +353,9 @@ describe("executionEngine / ExecutionEngineHttp", function () {
});

afterEachCallbacks.push(async function () {
await Promise.all(validators.map((v) => v.stop()));
await Promise.all(validators.map((v) => v.close()));
});

await Promise.all(validators.map((v) => v.start()));

if (TX_SCENARIOS.includes("simple")) {
// If bellatrixEpoch > 0, this is the case of pre-merge transaction submission on EL pow
await sendTransaction(jsonRpcUrl, {
Expand Down Expand Up @@ -418,7 +416,7 @@ describe("executionEngine / ExecutionEngineHttp", function () {

// Stop chain and un-subscribe events so the execution engine won't update it's head
// Allow some time to broadcast finalized events and complete the importBlock routine
await Promise.all(validators.map((v) => v.stop()));
await Promise.all(validators.map((v) => v.close()));
await bn.close();
await sleep(500);

Expand Down
7 changes: 2 additions & 5 deletions packages/lodestar/test/sim/multiNodeSingleThread.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ describe("Run multi node single thread interop validators (no eth1) until checkp
startIndex: i * validatorsPerNode,
testLoggerOpts,
});
afterEachCallbacks.push(async () => await Promise.all(validators.map((validator) => validator.stop())));
afterEachCallbacks.push(async () => await Promise.all(validators.map((validator) => validator.close())));

afterEachCallbacks.push(async () => {
await Promise.all(validators.map((validator) => validator.stop()));
await Promise.all(validators.map((validator) => validator.close()));
console.log("--- Stopped all validators ---");
// wait for 1 slot
await sleep(1 * testParams.SECONDS_PER_SLOT * 1000);
Expand Down Expand Up @@ -124,9 +124,6 @@ describe("Run multi node single thread interop validators (no eth1) until checkp
}
}

// Start all validators at once.
await Promise.all(validators.map((validator) => validator.start()));

// Wait for justified checkpoint on all nodes
await Promise.all(nodes.map((node) => waitForEvent<phase0.Checkpoint>(node.chain.emitter, event, 240000)));
console.log("--- All nodes reached justified checkpoint ---");
Expand Down
3 changes: 1 addition & 2 deletions packages/lodestar/test/sim/singleNodeSingleThread.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ describe("Run single node single thread interop validators (no eth1) until check
}

// TODO: Previous code waited for 1 slot between stopping the validators and stopInfoTracker()
afterEachCallbacks[1] = () => Promise.all(validators.map((v) => v.stop()));
await Promise.all(validators.map((v) => v.start()));
afterEachCallbacks[1] = () => Promise.all(validators.map((v) => v.close()));

// Wait for test to complete
await waitForEvent<phase0.Checkpoint>(bn.chain.emitter, event, timeout);
Expand Down
3 changes: 1 addition & 2 deletions packages/lodestar/test/sim/threaded/noEth1SimWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ async function runWorker(): Promise<void> {
);

node.chain.emitter.on(checkpointEvent, async (checkpoint) => {
await Promise.all(validators.map((validator) => validator.stop()));
await Promise.all(validators.map((validator) => validator.close()));
if (stopInfoTracker) stopInfoTracker();
await node.close();
parent.postMessage({
Expand All @@ -88,7 +88,6 @@ async function runWorker(): Promise<void> {
startIndex,
testLoggerOpts,
});
await Promise.all(validators.map((validator) => validator.start()));
}

runWorker().catch((e: Error) => {
Expand Down
47 changes: 21 additions & 26 deletions packages/validator/src/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type ValidatorOptions = {
graffiti?: string;
defaultFeeRecipient?: string;
strictFeeRecipientCheck?: boolean;
closed?: boolean;
};

// TODO: Extend the timeout, and let it be customizable
Expand All @@ -42,11 +43,9 @@ export type ValidatorOptions = {

enum Status {
running,
stopped,
closed,
}

type State = {status: Status.running; controller: AbortController} | {status: Status.stopped};

/**
* Main class for the Validator client.
*/
Expand All @@ -63,20 +62,23 @@ export class Validator {
private readonly emitter: ValidatorEventEmitter;
private readonly chainHeaderTracker: ChainHeaderTracker;
private readonly logger: ILogger;
private state: State = {status: Status.stopped};
private state: Status;
private readonly controller: AbortController;

constructor(opts: ValidatorOptions, readonly genesis: Genesis, metrics: Metrics | null = null) {
const {dbOps, logger, slashingProtection, signers, graffiti, defaultFeeRecipient, strictFeeRecipientCheck} = opts;
const config = createIBeaconConfig(dbOps.config, genesis.genesisValidatorsRoot);

this.controller = new AbortController();

const api =
typeof opts.api === "string"
? getClient(
{
baseUrl: opts.api,
// Validator would need the beacon to respond within the slot
timeoutMs: config.SECONDS_PER_SLOT * 1000,
getAbortSignal: this.getAbortSignal,
getAbortSignal: () => this.controller.signal,
},
{config, logger, metrics: metrics?.restApiClient}
)
Expand Down Expand Up @@ -140,6 +142,16 @@ export class Validator {
if (metrics) {
opts.dbOps.controller.setMetrics(metrics.db);
}

if (opts.closed) {
this.state = Status.closed;
} else {
// "start" the validator
// Instantiates block and attestation services and runs them once the chain has been started.
this.state = Status.running;
this.clock.start(this.controller.signal);
this.chainHeaderTracker.start(this.controller.signal);
}
}

/** Waits for genesis and genesis time */
Expand Down Expand Up @@ -177,25 +189,13 @@ export class Validator {
this.syncCommitteeService.removeDutiesForKey(pubkey);
}

/**
* Instantiates block and attestation services and runs them once the chain has been started.
*/
async start(): Promise<void> {
if (this.state.status === Status.running) return;
const controller = new AbortController();
this.state = {status: Status.running, controller};
const {signal} = controller;
this.clock.start(signal);
this.chainHeaderTracker.start(signal);
}

/**
* Stops all validator functions.
*/
async stop(): Promise<void> {
if (this.state.status === Status.stopped) return;
this.state.controller.abort();
this.state = {status: Status.stopped};
async close(): Promise<void> {
if (this.state === Status.closed) return;
this.controller.abort();
this.state = Status.closed;
}

/**
Expand All @@ -218,11 +218,6 @@ export class Validator {

this.logger.info(`Submitted voluntary exit for ${publicKey} to the network`);
}

/** Provide the current AbortSignal to the api instance */
private getAbortSignal = (): AbortSignal | undefined => {
return this.state.status === Status.running ? this.state.controller.signal : undefined;
};
}

/** Assert the same genesisValidatorRoot and genesisTime */
Expand Down