diff --git a/packages/beacon-node/test/e2e/chain/lightclient.test.ts b/packages/beacon-node/test/e2e/chain/lightclient.test.ts index 6857ff8824ec..dc440d5982ec 100644 --- a/packages/beacon-node/test/e2e/chain/lightclient.test.ts +++ b/packages/beacon-node/test/e2e/chain/lightclient.test.ts @@ -131,7 +131,7 @@ describe("chain / lightclient", function () { }); loggerLC.info("Initialized lightclient", {headSlot: lightclient.getHead().beacon.slot}); - lightclient.start(); + void lightclient.start(); return new Promise((resolve, reject) => { bn.chain.emitter.on(routes.events.EventType.head, async (head) => { diff --git a/packages/cli/src/cmds/lightclient/handler.ts b/packages/cli/src/cmds/lightclient/handler.ts index 02bb98cb4d1d..04c833af92d5 100644 --- a/packages/cli/src/cmds/lightclient/handler.ts +++ b/packages/cli/src/cmds/lightclient/handler.ts @@ -33,5 +33,5 @@ export async function lightclientHandler(args: ILightClientArgs & GlobalArgs): P transport: new LightClientRestTransport(api), }); - client.start(); + void client.start(); } diff --git a/packages/light-client/README.md b/packages/light-client/README.md index 759576334489..00ebcf180874 100644 --- a/packages/light-client/README.md +++ b/packages/light-client/README.md @@ -51,51 +51,23 @@ lodestar lightclient \ For this example we will assume there is a running beacon node at `https://beacon-node.your-domain.com` ```ts -import type {Api} from "@lodestar/api/beacon"; -import {ApiError} from "@lodestar/api"; -import type {Bytes32} from "@lodestar/types"; +import {getClient} from "@lodestar/api"; import {createChainForkConfig} from "@lodestar/config"; import {networksChainConfig} from "@lodestar/config/networks"; -import { - type GenesisData, - Lightclient, - LightclientEvent, - RunStatusCode -} from "@lodestar/light-client"; -import {getClient} from "@lodestar/api"; +import {Lightclient, LightclientEvent} from "@lodestar/light-client"; import {LightClientRestTransport} from "@lodestar/light-client/transport"; -import {getLcLoggerConsole} from "@lodestar/light-client/utils"; - -async function getGenesisData(api: Pick): Promise { - const res = await api.beacon.getGenesis(); - ApiError.assert(res); - - return { - genesisTime: Number(res.response.data.genesisTime), - genesisValidatorsRoot: res.response.data.genesisValidatorsRoot, - }; -} - -async function getSyncCheckpoint(api: Pick): Promise { - const res = await api.beacon.getStateFinalityCheckpoints("head"); - ApiError.assert(res); - return res.response.data.finalized.root; -} +import {getFinalizedSyncCheckpoint, getGenesisData, getLcLoggerConsole} from "@lodestar/light-client/utils"; const config = createChainForkConfig(networksChainConfig.mainnet); - const logger = getLcLoggerConsole({logDebug: Boolean(process.env.DEBUG)}); - const api = getClient({urls: ["https://beacon-node.your-domain.com"]}, {config}); -const transport = new LightClientRestTransport(api); - const lightclient = await Lightclient.initializeFromCheckpointRoot({ config, logger, - transport, + transport: new LightClientRestTransport(api), genesisData: await getGenesisData(api), - checkpointRoot: await getSyncCheckpoint(api), + checkpointRoot: await getFinalizedSyncCheckpoint(api), opts: { allowForcedUpdates: true, updateHeadersOnForcedUpdate: true, @@ -103,26 +75,16 @@ const lightclient = await Lightclient.initializeFromCheckpointRoot({ }); // Wait for the lightclient to start -await new Promise((resolve) => { - const lightclientStarted = (status: RunStatusCode): void => { - if (status === RunStatusCode.started) { - lightclient?.emitter.off(LightclientEvent.statusChange, lightclientStarted); - resolve(); - } - }; - lightclient?.emitter.on(LightclientEvent.statusChange, lightclientStarted); - logger.info("Initiating lightclient"); - lightclient?.start(); -}); +await lightclient.start(); logger.info("Lightclient synced"); lightclient.emitter.on(LightclientEvent.lightClientFinalityHeader, async (finalityUpdate) => { - console.log(finalityUpdate); + logger.info(finalityUpdate); }); lightclient.emitter.on(LightclientEvent.lightClientOptimisticHeader, async (optimisticUpdate) => { - console.log(optimisticUpdate); + logger.info(optimisticUpdate); }); ``` diff --git a/packages/light-client/src/index.ts b/packages/light-client/src/index.ts index deac9f66f4d9..4df6f80607be 100644 --- a/packages/light-client/src/index.ts +++ b/packages/light-client/src/index.ts @@ -167,10 +167,23 @@ export class Lightclient { return new Lightclient({...args, bootstrap}); } - start(): void { - this.runLoop().catch((e) => { - this.logger.error("Error on runLoop", {}, e as Error); + /** + * @returns a `Promise` that will resolve once `LightclientEvent.statusChange` with `RunStatusCode.started` value is emitted + */ + start(): Promise { + const startPromise = new Promise((resolve) => { + const lightclientStarted = (status: RunStatusCode): void => { + if (status === RunStatusCode.started) { + this.emitter.off(LightclientEvent.statusChange, lightclientStarted); + resolve(); + } + }; + this.emitter.on(LightclientEvent.statusChange, lightclientStarted); }); + + void this.runLoop(); + + return startPromise; } stop(): void { diff --git a/packages/light-client/src/utils/utils.ts b/packages/light-client/src/utils/utils.ts index 9960921eee90..b7c2c29319e3 100644 --- a/packages/light-client/src/utils/utils.ts +++ b/packages/light-client/src/utils/utils.ts @@ -1,8 +1,10 @@ import bls from "@chainsafe/bls"; import type {PublicKey} from "@chainsafe/bls/types"; import {BitArray} from "@chainsafe/ssz"; -import {altair, Root, ssz} from "@lodestar/types"; +import {Api, ApiError} from "@lodestar/api"; +import {altair, Bytes32, Root, ssz} from "@lodestar/types"; import {BeaconBlockHeader} from "@lodestar/types/phase0"; +import {GenesisData} from "../index.js"; import {SyncCommitteeFast} from "../types.js"; export function sumBits(bits: BitArray): number { @@ -78,3 +80,19 @@ export function isEmptyHeader(header: BeaconBlockHeader): boolean { // Thanks https://github.com/iliakan/detect-node/blob/master/index.esm.js export const isNode = Object.prototype.toString.call(typeof process !== "undefined" ? process : 0) === "[object process]"; + +export async function getGenesisData(api: Pick): Promise { + const res = await api.beacon.getGenesis(); + ApiError.assert(res); + + return { + genesisTime: res.response.data.genesisTime, + genesisValidatorsRoot: res.response.data.genesisValidatorsRoot, + }; +} + +export async function getFinalizedSyncCheckpoint(api: Pick): Promise { + const res = await api.beacon.getStateFinalityCheckpoints("head"); + ApiError.assert(res); + return res.response.data.finalized.root; +} diff --git a/packages/light-client/test/unit/sync.node.test.ts b/packages/light-client/test/unit/sync.node.test.ts index 75073c80070b..4d4212d626cf 100644 --- a/packages/light-client/test/unit/sync.node.test.ts +++ b/packages/light-client/test/unit/sync.node.test.ts @@ -104,7 +104,7 @@ describe("sync", () => { resolve(); } }); - lightclient.start(); + void lightclient.start(); }); // Wait for lightclient to subscribe to header updates diff --git a/packages/prover/src/proof_provider/proof_provider.ts b/packages/prover/src/proof_provider/proof_provider.ts index d888f4269840..f88063e4f02f 100644 --- a/packages/prover/src/proof_provider/proof_provider.ts +++ b/packages/prover/src/proof_provider/proof_provider.ts @@ -96,18 +96,10 @@ export class ProofProvider { }); assertLightClient(this.lightClient); + + this.logger.info("Initiating lightclient"); // Wait for the lightclient to start - await new Promise((resolve) => { - const lightClientStarted = (status: RunStatusCode): void => { - if (status === RunStatusCode.started) { - this.lightClient?.emitter.off(LightclientEvent.statusChange, lightClientStarted); - resolve(); - } - }; - this.lightClient?.emitter.on(LightclientEvent.statusChange, lightClientStarted); - this.logger.info("Initiating lightclient"); - this.lightClient?.start(); - }); + await this.lightClient?.start(); this.logger.info("Lightclient synced", this.getStatus()); this.registerEvents();