Skip to content

Commit

Permalink
Add LightclientUpdater to BeaconChain
Browse files Browse the repository at this point in the history
  • Loading branch information
dapplion committed May 28, 2021
1 parent efea313 commit 7ad8709
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 23 deletions.
19 changes: 12 additions & 7 deletions packages/light-client/src/server/LightClientUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import {
getForkVersion,
} from "@chainsafe/lodestar-beacon-state-transition";
import {FINALIZED_ROOT_INDEX, NEXT_SYNC_COMMITTEE_INDEX} from "@chainsafe/lodestar-params";
import {Checkpoint, Epoch, LightClientUpdate, Slot} from "@chainsafe/lodestar-types/lib/altair";
import {Checkpoint, Epoch, LightClientUpdate, Slot, SyncPeriod} from "@chainsafe/lodestar-types/lib/altair";
import {isZeroHash, sumBits, toBlockHeader} from "../utils/utils";

type CommitteePeriod = number;
type DbRepo<K, T> = {put(key: K, data: T): void; get(key: K): T | null};
type DbItem<T> = {put(data: T): void; get(): T | null};

Expand Down Expand Up @@ -42,7 +41,7 @@ export type LightClientUpdaterDb = {
*
* Must persist the best update for each committee period between the longest possible weak subjectivity epoch and now.
*/
bestUpdatePerCommitteePeriod: DbRepo<CommitteePeriod, LightClientUpdate>;
bestUpdatePerCommitteePeriod: DbRepo<SyncPeriod, LightClientUpdate>;
latestFinalizedUpdate: DbItem<LightClientUpdate>;
latestNonFinalizedUpdate: DbItem<LightClientUpdate>;
};
Expand All @@ -52,12 +51,18 @@ export type LightClientUpdaterDb = {
*/
const PREV_DATA_MAX_SIZE = 64;

export interface ILightClientUpdater {
getBestUpdates(periods: SyncPeriod[]): Promise<altair.LightClientUpdate[]>;
getLatestUpdateFinalized(): Promise<altair.LightClientUpdate | null>;
getLatestUpdateNonFinalized(): Promise<altair.LightClientUpdate | null>;
}

/**
* Compute and cache LightClientUpdate objects as the chain advances
*
* Spec v1.0.1
*/
export class LightClientUpdater {
export class LightClientUpdater implements ILightClientUpdater {
private readonly prevHeadData = new Map<string, SyncAttestedData>();
private readonly zero: Pick<
LightClientUpdate,
Expand All @@ -81,7 +86,7 @@ export class LightClientUpdater {
/**
* To be called in API route GET /eth/v1/lightclient/best_update/:periods
*/
async getBestUpdates(periods: CommitteePeriod[]): Promise<LightClientUpdate[]> {
async getBestUpdates(periods: SyncPeriod[]): Promise<LightClientUpdate[]> {
const updates: LightClientUpdate[] = [];
for (const period of periods) {
const update = this.db.bestUpdatePerCommitteePeriod.get(period);
Expand Down Expand Up @@ -182,7 +187,7 @@ export class LightClientUpdater {
private persistBestFinalizedUpdate(
syncAttestedData: SyncAttestedData,
signatureData: CommitteeSignatureData
): CommitteePeriod | null {
): SyncPeriod | null {
// Retrieve finality branch for attested finalized checkpoint
const finalizedEpoch = syncAttestedData.finalizedCheckpoint.epoch;
const finalizedData = this.db.lightclientFinalizedCheckpoint.get(finalizedEpoch);
Expand Down Expand Up @@ -234,7 +239,7 @@ export class LightClientUpdater {
private persistBestNonFinalizedUpdate(
syncAttestedData: SyncAttestedData,
signatureData: CommitteeSignatureData,
committeePeriodWithFinalized: CommitteePeriod | null
committeePeriodWithFinalized: SyncPeriod | null
): void {
const committeePeriod = computeSyncPeriodAtSlot(this.config, syncAttestedData.header.slot);
const signaturePeriod = computeSyncPeriodAtSlot(this.config, signatureData.slot);
Expand Down
1 change: 1 addition & 0 deletions packages/lodestar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@chainsafe/lodestar-config": "^0.22.0",
"@chainsafe/lodestar-db": "^0.22.0",
"@chainsafe/lodestar-fork-choice": "^0.22.0",
"@chainsafe/lodestar-light-client": "^0.22.0",
"@chainsafe/lodestar-params": "^0.22.0",
"@chainsafe/lodestar-types": "^0.22.0",
"@chainsafe/lodestar-utils": "^0.22.0",
Expand Down
18 changes: 6 additions & 12 deletions packages/lodestar/src/api/impl/lightclient/index.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
import {altair, SyncPeriod} from "@chainsafe/lodestar-types";
import {altair} from "@chainsafe/lodestar-types";
import {ApiModules} from "../types";
import {resolveStateId} from "../beacon/state/utils";
import {routes} from "@chainsafe/lodestar-api";
import {ApiError} from "../errors";
import {linspace} from "../../../util/numpy";

// TODO: Import from lightclient/server package
interface ILightClientUpdater {
getBestUpdates(from: SyncPeriod, to: SyncPeriod): Promise<altair.LightClientUpdate[]>;
getLatestUpdateFinalized(): Promise<altair.LightClientUpdate | null>;
getLatestUpdateNonFinalized(): Promise<altair.LightClientUpdate | null>;
}

export function getLightclientApi({
chain,
config,
db,
}: Pick<ApiModules, "chain" | "config" | "db">): routes.lightclient.Api {
// TODO:
const lightClientUpdater = {} as ILightClientUpdater;

return {
// Proofs API

Expand All @@ -31,17 +24,18 @@ export function getLightclientApi({
// Sync API

async getBestUpdates(from, to) {
return {data: await lightClientUpdater.getBestUpdates(from, to)};
const periods = linspace(from, to);
return {data: await chain.lightclientUpdater.getBestUpdates(periods)};
},

async getLatestUpdateFinalized() {
const update = await lightClientUpdater.getLatestUpdateFinalized();
const update = await chain.lightclientUpdater.getLatestUpdateFinalized();
if (!update) throw new ApiError(404, "No update available");
return {data: update};
},

async getLatestUpdateNonFinalized() {
const update = await lightClientUpdater.getLatestUpdateNonFinalized();
const update = await chain.lightclientUpdater.getLatestUpdateNonFinalized();
if (!update) throw new ApiError(404, "No update available");
return {data: update};
},
Expand Down
4 changes: 4 additions & 0 deletions packages/lodestar/src/chain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {IForkChoice} from "@chainsafe/lodestar-fork-choice";
import {allForks, ForkDigest, Number64, Root, Slot} from "@chainsafe/lodestar-types";
import {ILogger} from "@chainsafe/lodestar-utils";
import {TreeBacked} from "@chainsafe/ssz";
import {LightClientUpdater} from "@chainsafe/lodestar-light-client/lib/server/LightClientUpdater";
import {AbortController} from "abort-controller";
import {GENESIS_EPOCH, ZERO_HASH} from "../constants";
import {IBeaconDb} from "../db";
Expand Down Expand Up @@ -53,6 +54,7 @@ export class BeaconChain implements IBeaconChain {
pendingAttestations: AttestationPool;
pendingBlocks: BlockPool;
forkDigestContext: IForkDigestContext;
lightclientUpdater: LightClientUpdater;

protected attestationProcessor: AttestationProcessor;
protected blockProcessor: BlockProcessor;
Expand Down Expand Up @@ -120,6 +122,8 @@ export class BeaconChain implements IBeaconChain {
this.checkpointStateCache = checkpointStateCache;
this.stateCache = stateCache;

this.lightclientUpdater = new LightClientUpdater(config, db);

handleChainEvents.bind(this)(this.abortController.signal);
}

Expand Down
32 changes: 28 additions & 4 deletions packages/lodestar/src/chain/eventHandlers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {AbortSignal} from "abort-controller";
import {readonlyValues, toHexString} from "@chainsafe/ssz";
import {allForks, phase0, Slot, Version} from "@chainsafe/lodestar-types";
import {readonlyValues, toHexString, TreeBacked} from "@chainsafe/ssz";
import {allForks, altair, phase0, Slot, Version} from "@chainsafe/lodestar-types";
import {ILogger} from "@chainsafe/lodestar-utils";
import {IBlockSummary} from "@chainsafe/lodestar-fork-choice";
import {CachedBeaconState} from "@chainsafe/lodestar-beacon-state-transition";
import {CachedBeaconState, computeEpochAtSlot} from "@chainsafe/lodestar-beacon-state-transition";

import {AttestationError, AttestationErrorCode, BlockError, BlockErrorCode} from "./errors";
import {IBlockJob} from "./interface";
Expand Down Expand Up @@ -172,9 +172,28 @@ export function onJustified(
this.metrics?.currentJustifiedEpoch.set(cp.epoch);
}

export function onFinalized(this: BeaconChain, cp: phase0.Checkpoint): void {
export async function onFinalized(this: BeaconChain, cp: phase0.Checkpoint): Promise<void> {
this.logger.verbose("Checkpoint finalized", this.config.types.phase0.Checkpoint.toJson(cp));
this.metrics?.finalizedEpoch.set(cp.epoch);

// Only after altair
if (cp.epoch >= this.config.params.ALTAIR_FORK_EPOCH) {
try {
const state = await this.regen.getCheckpointState(cp);
const block = await this.getCanonicalBlockAtSlot(state.slot);
if (!block) {
throw Error(`No block found for checkpoint ${cp.epoch} : ${toHexString(cp.root)}`);
}

this.lightclientUpdater.onFinalized(
cp,
block.message as altair.BeaconBlock,
state as TreeBacked<altair.BeaconState>
);
} catch (e) {
this.logger.error("Error lightclientUpdater.onFinalized", {epoch: cp.epoch}, e);
}
}
}

export function onForkChoiceJustified(this: BeaconChain, cp: phase0.Checkpoint): void {
Expand Down Expand Up @@ -261,6 +280,11 @@ export async function onBlock(
})
);
}

// Only after altair
if (computeEpochAtSlot(this.config, block.message.slot) >= this.config.params.ALTAIR_FORK_EPOCH) {
this.lightclientUpdater.onHead(block.message as altair.BeaconBlock, postState as TreeBacked<altair.BeaconState>);
}
}

export async function onErrorAttestation(this: BeaconChain, err: AttestationError): Promise<void> {
Expand Down
2 changes: 2 additions & 0 deletions packages/lodestar/src/chain/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {allForks, Number64, Root, Slot} from "@chainsafe/lodestar-types";
import {ForkName} from "@chainsafe/lodestar-config";
import {phase0, CachedBeaconState} from "@chainsafe/lodestar-beacon-state-transition";
import {IForkChoice} from "@chainsafe/lodestar-fork-choice";
import {LightClientUpdater} from "@chainsafe/lodestar-light-client/lib/server/LightClientUpdater";

import {IBeaconClock} from "./clock/interface";
import {ChainEventEmitter} from "./emitter";
Expand Down Expand Up @@ -66,6 +67,7 @@ export interface IBeaconChain {
pendingBlocks: BlockPool;
pendingAttestations: AttestationPool;
forkDigestContext: IForkDigestContext;
lightclientUpdater: LightClientUpdater;

/** Stop beacon chain processing */
close(): void;
Expand Down

0 comments on commit 7ad8709

Please sign in to comment.