Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ensi321 committed Oct 22, 2024
1 parent 0e4ea98 commit 64cfe4d
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 27 deletions.
5 changes: 3 additions & 2 deletions packages/beacon-node/src/chain/lightClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,12 +388,13 @@ export class LightClientServer {
parentBlockSlot: Slot
): Promise<void> {
const blockSlot = block.slot;
const header = blockToLightClientHeader(this.config.getForkName(blockSlot), block);
const fork = this.config.getForkName(blockSlot);
const header = blockToLightClientHeader(fork, block);

const blockRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(header.beacon);
const blockRootHex = toRootHex(blockRoot);

const syncCommitteeWitness = getSyncCommitteesWitness(postState);
const syncCommitteeWitness = getSyncCommitteesWitness(fork, postState);

// Only store current sync committee once per run
if (!this.storedCurrentSyncCommittee) {
Expand Down
54 changes: 40 additions & 14 deletions packages/beacon-node/src/chain/lightClient/proofs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,54 @@ import {
BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX,
ForkExecution,
FINALIZED_ROOT_GINDEX_ELECTRA,
ForkName,
isForkPostElectra,
} from "@lodestar/params";
import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types";

import {SyncCommitteeWitness} from "./types.js";

export function getSyncCommitteesWitness(state: BeaconStateAllForks): SyncCommitteeWitness {
export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllForks): SyncCommitteeWitness {
state.commit();
const n1 = state.node;
const n3 = n1.right; // [1]0110
const n6 = n3.left; // 1[0]110
const n13 = n6.right; // 10[1]10
const n27 = n13.right; // 101[1]0
const currentSyncCommitteeRoot = n27.left.root; // n54 1011[0]
const nextSyncCommitteeRoot = n27.right.root; // n55 1011[1]
let witness: Uint8Array[];
let currentSyncCommitteeRoot: Uint8Array;
let nextSyncCommitteeRoot: Uint8Array;

// Witness branch is sorted by descending gindex
const witness = [
n13.left.root, // 26
n6.left.root, // 12
n3.right.root, // 7
n1.left.root, // 2
];
if (isForkPostElectra(fork)) {
const n2 = n1.left;
const n5 = n2.right;
const n10 = n5.left;
const n21 = n10.right;
const n43 = n21.right;

currentSyncCommitteeRoot = n43.left.root; // n86
nextSyncCommitteeRoot = n43.right.root; // n87

// Witness branch is sorted by descending gindex
witness = [
n21.left.root, // 42
n10.left.root, // 20
n5.right.root, // 11
n2.right.root, // 4
n1.right.root, // 3
];
} else {
const n3 = n1.right; // [1]0110
const n6 = n3.left; // 1[0]110
const n13 = n6.right; // 10[1]10
const n27 = n13.right; // 101[1]0
currentSyncCommitteeRoot = n27.left.root; // n54 1011[0]
nextSyncCommitteeRoot = n27.right.root; // n55 1011[1]

// Witness branch is sorted by descending gindex
witness = [
n13.left.root, // 26
n6.left.root, // 12
n3.right.root, // 7
n1.left.root, // 2
];
}

return {
witness,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {describe, it, expect, beforeAll} from "vitest";
import {BeaconStateAltair} from "@lodestar/state-transition";
import {SYNC_COMMITTEE_SIZE} from "@lodestar/params";
import {ForkName, SYNC_COMMITTEE_SIZE} from "@lodestar/params";
import {altair, ssz} from "@lodestar/types";
import {verifyMerkleBranch, hash} from "@lodestar/utils";
import {getNextSyncCommitteeBranch, getSyncCommitteesWitness} from "../../../../src/chain/lightClient/proofs.js";
Expand All @@ -25,7 +25,7 @@ describe("chain / lightclient / proof", () => {
});

it("SyncCommittees proof", () => {
const syncCommitteesWitness = getSyncCommitteesWitness(state);
const syncCommitteesWitness = getSyncCommitteesWitness(ForkName.altair, state);
const syncCommitteesLeaf = hash(
syncCommitteesWitness.currentSyncCommitteeRoot,
syncCommitteesWitness.nextSyncCommitteeRoot
Expand All @@ -42,7 +42,7 @@ describe("chain / lightclient / proof", () => {
});

it("currentSyncCommittee proof", () => {
const syncCommitteesWitness = getSyncCommitteesWitness(state);
const syncCommitteesWitness = getSyncCommitteesWitness(ForkName.altair, state);
const currentSyncCommitteeBranch = [syncCommitteesWitness.nextSyncCommitteeRoot, ...syncCommitteesWitness.witness];

expect(
Expand All @@ -56,7 +56,7 @@ describe("chain / lightclient / proof", () => {
});

it("nextSyncCommittee proof", () => {
const syncCommitteesWitness = getSyncCommitteesWitness(state);
const syncCommitteesWitness = getSyncCommitteesWitness(ForkName.altair, state);
const nextSyncCommitteeBranch = getNextSyncCommitteeBranch(syncCommitteesWitness);

expect(
Expand Down
6 changes: 3 additions & 3 deletions packages/light-client/src/spec/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
import {computeSyncPeriodAtSlot} from "../utils/index.js";
import {getSyncCommitteeAtPeriod, processLightClientUpdate, ProcessUpdateOpts} from "./processLightClientUpdate.js";
import {ILightClientStore, LightClientStore, LightClientStoreEvents} from "./store.js";
import {ZERO_FINALITY_BRANCH, ZERO_HEADER, ZERO_SYNC_COMMITTEE, getZeroSyncCommitteeBranch} from "./utils.js";
import {ZERO_HEADER, ZERO_SYNC_COMMITTEE, getZeroFinalityBranch, getZeroSyncCommitteeBranch} from "./utils.js";

export {isBetterUpdate, toLightClientUpdateSummary} from "./isBetterUpdate.js";
export type {LightClientUpdateSummary} from "./isBetterUpdate.js";
Expand Down Expand Up @@ -39,7 +39,7 @@ export class LightclientSpec {
nextSyncCommittee: ZERO_SYNC_COMMITTEE,
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(this.config.getForkName(finalityUpdate.signatureSlot)),
finalizedHeader: finalityUpdate.finalizedHeader,
finalityBranch: finalityUpdate.finalityBranch,
finalityBranch: getZeroFinalityBranch(this.config.getForkName(finalityUpdate.signatureSlot)),
syncAggregate: finalityUpdate.syncAggregate,
signatureSlot: finalityUpdate.signatureSlot,
});
Expand All @@ -51,7 +51,7 @@ export class LightclientSpec {
nextSyncCommittee: ZERO_SYNC_COMMITTEE,
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(this.config.getForkName(optimisticUpdate.signatureSlot)),
finalizedHeader: {beacon: ZERO_HEADER},
finalityBranch: ZERO_FINALITY_BRANCH,
finalityBranch: getZeroFinalityBranch(this.config.getForkName(optimisticUpdate.signatureSlot)),
syncAggregate: optimisticUpdate.syncAggregate,
signatureSlot: optimisticUpdate.signatureSlot,
});
Expand Down
10 changes: 8 additions & 2 deletions packages/light-client/src/spec/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export const ZERO_HASH = new Uint8Array(32);
export const ZERO_PUBKEY = new Uint8Array(48);
export const ZERO_SYNC_COMMITTEE = ssz.altair.SyncCommittee.defaultValue();
export const ZERO_HEADER = ssz.phase0.BeaconBlockHeader.defaultValue();
export const ZERO_FINALITY_BRANCH = Array.from({length: FINALIZED_ROOT_DEPTH}, () => ZERO_HASH);
/** From https://notes.ethereum.org/@vbuterin/extended_light_client_protocol#Optimistic-head-determining-function */
const SAFETY_THRESHOLD_FACTOR = 2;

Expand All @@ -53,6 +52,12 @@ export function getZeroSyncCommitteeBranch(fork: ForkName): Uint8Array[] {
return Array.from({length: nextSyncCommitteeDepth}, () => ZERO_HASH);
}

export function getZeroFinalityBranch(fork: ForkName): Uint8Array[] {
const finalizedRootDepth = isForkPostElectra(fork) ? FINALIZED_ROOT_DEPTH_ELECTRA : FINALIZED_ROOT_DEPTH;

return Array.from({length: finalizedRootDepth}, () => ZERO_HASH);
}

export function isSyncCommitteeUpdate(update: LightClientUpdate): boolean {
return (
// Fast return for when constructing full LightClientUpdate from partial updates
Expand All @@ -65,7 +70,8 @@ export function isSyncCommitteeUpdate(update: LightClientUpdate): boolean {
export function isFinalityUpdate(update: LightClientUpdate): boolean {
return (
// Fast return for when constructing full LightClientUpdate from partial updates
update.finalityBranch !== ZERO_FINALITY_BRANCH &&
update.finalityBranch !==
getZeroFinalityBranch(isElectraLightClientUpdate(update) ? ForkName.electra : ForkName.altair) &&
update.finalityBranch.some((branch) => !byteArrayEquals(branch, ZERO_HASH))
);
}
Expand Down
13 changes: 11 additions & 2 deletions packages/light-client/src/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import bls from "@chainsafe/bls";
import type {PublicKey, Signature} from "@chainsafe/bls/types";
import {
altair,
isELectraLightClientFinalityUpdate,
isElectraLightClientUpdate,
LightClientFinalityUpdate,
LightClientUpdate,
Expand All @@ -19,6 +20,7 @@ import {
NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA,
FINALIZED_ROOT_DEPTH_ELECTRA,
NEXT_SYNC_COMMITTEE_INDEX_ELECTRA,
FINALIZED_ROOT_INDEX_ELECTRA,
} from "@lodestar/params";
import {BeaconConfig} from "@lodestar/config";
import {isValidMerkleBranch} from "./utils/verifyMerkleBranch.js";
Expand Down Expand Up @@ -80,12 +82,19 @@ export function assertValidLightClientUpdate(
* Where `hashTreeRoot(state) == update.finalityHeader.stateRoot`
*/
export function assertValidFinalityProof(update: LightClientFinalityUpdate): void {
const finalizedRootDepth = isELectraLightClientFinalityUpdate(update)
? FINALIZED_ROOT_DEPTH_ELECTRA
: FINALIZED_ROOT_DEPTH;
const finalizedRootIndex = isELectraLightClientFinalityUpdate(update)
? FINALIZED_ROOT_INDEX_ELECTRA
: FINALIZED_ROOT_INDEX;

if (
!isValidMerkleBranch(
ssz.phase0.BeaconBlockHeader.hashTreeRoot(update.finalizedHeader.beacon),
update.finalityBranch,
FINALIZED_ROOT_DEPTH,
FINALIZED_ROOT_INDEX,
finalizedRootDepth,
finalizedRootIndex,
update.attestedHeader.beacon.stateRoot
)
) {
Expand Down
11 changes: 11 additions & 0 deletions packages/types/src/utils/typeguards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
BeaconBlock,
Attestation,
LightClientUpdate,
LightClientFinalityUpdate,
} from "../types.js";

export function isExecutionPayload<F extends ForkExecution>(
Expand Down Expand Up @@ -80,3 +81,13 @@ export function isElectraLightClientUpdate(update: LightClientUpdate): update is
updatePostElectra.finalityBranch.length === FINALIZED_ROOT_DEPTH_ELECTRA
);
}

export function isELectraLightClientFinalityUpdate(
update: LightClientFinalityUpdate
): update is LightClientFinalityUpdate<ForkPostElectra> {
const updatePostElectra = update as LightClientUpdate<ForkPostElectra>;
return (
updatePostElectra.finalityBranch !== undefined &&
updatePostElectra.finalityBranch.length === FINALIZED_ROOT_DEPTH_ELECTRA
);
}

0 comments on commit 64cfe4d

Please sign in to comment.