2023-07-10 20:44:58 +00:00
|
|
|
import { createBeaconConfig } from "@lodestar/config";
|
|
|
|
import { BEACON_SYNC_SUPER_MAJORITY, mainnetConfig } from "./constants.js";
|
|
|
|
import { networksChainConfig } from "@lodestar/config/networks";
|
|
|
|
import { fromHexString } from "@chainsafe/ssz";
|
|
|
|
import { capella, OptimisticUpdate, phase0, VerifyWithReason } from "#types.js";
|
|
|
|
import { assertValidSignedHeader } from "@lodestar/light-client/validation";
|
|
|
|
import bls from "@chainsafe/bls/switchable";
|
|
|
|
import axiosRetry from "axios-retry";
|
|
|
|
import axios from "axios";
|
|
|
|
import { digest } from "@chainsafe/as-sha256";
|
|
|
|
import { concatBytes } from "@noble/hashes/utils";
|
|
|
|
|
|
|
|
export function getDefaultClientConfig() {
|
|
|
|
const chainConfig = createBeaconConfig(
|
|
|
|
networksChainConfig.mainnet,
|
|
|
|
fromHexString(mainnetConfig.genesis_validator_root),
|
|
|
|
);
|
|
|
|
return {
|
|
|
|
genesis: {
|
|
|
|
committee: mainnetConfig.committee_pk,
|
|
|
|
slot: parseInt(mainnetConfig.slot),
|
|
|
|
time: parseInt(mainnetConfig.genesis_time),
|
|
|
|
},
|
|
|
|
chainConfig,
|
|
|
|
n: 1,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function optimisticUpdateFromJSON(update: any): OptimisticUpdate {
|
|
|
|
return capella.ssz.LightClientOptimisticUpdate.fromJson(update);
|
|
|
|
}
|
|
|
|
export async function optimisticUpdateVerify(
|
|
|
|
committee: Uint8Array[],
|
|
|
|
update: OptimisticUpdate,
|
|
|
|
): Promise<VerifyWithReason> {
|
|
|
|
try {
|
|
|
|
const { attestedHeader: header, syncAggregate } = update;
|
|
|
|
const headerBlockRoot = phase0.ssz.BeaconBlockHeader.hashTreeRoot(
|
|
|
|
header.beacon,
|
|
|
|
);
|
|
|
|
const committeeFast = this.deserializeSyncCommittee(committee);
|
|
|
|
try {
|
|
|
|
assertValidSignedHeader(
|
|
|
|
this.config.chainConfig,
|
|
|
|
committeeFast,
|
|
|
|
syncAggregate,
|
|
|
|
headerBlockRoot,
|
|
|
|
header.beacon.slot,
|
|
|
|
);
|
|
|
|
} catch (e) {
|
|
|
|
return { correct: false, reason: "invalid signatures" };
|
|
|
|
}
|
|
|
|
|
|
|
|
const participation =
|
|
|
|
syncAggregate.syncCommitteeBits.getTrueBitIndexes().length;
|
|
|
|
if (participation < BEACON_SYNC_SUPER_MAJORITY) {
|
|
|
|
return { correct: false, reason: "insufficient signatures" };
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.isValidLightClientHeader(this.config.chainConfig, header)) {
|
|
|
|
return { correct: false, reason: "invalid header" };
|
|
|
|
}
|
|
|
|
|
|
|
|
return { correct: true };
|
|
|
|
} catch (e) {
|
|
|
|
console.error(e);
|
|
|
|
return { correct: false, reason: (e as Error).message };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
export function deserializePubkeys(pubkeys) {
|
|
|
|
return pubkeys.map((pk) => bls.PublicKey.fromBytes(pk));
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getCommitteeHash(committee: Uint8Array[]): Uint8Array {
|
|
|
|
return digest(concatBytes(...committee));
|
|
|
|
}
|
|
|
|
|
|
|
|
export const consensusClient = axios.create();
|
|
|
|
axiosRetry(consensusClient, { retries: 3 });
|
2023-07-11 05:50:42 +00:00
|
|
|
|
|
|
|
export async function getConsensusOptimisticUpdate() {
|
|
|
|
const resp = await consensusClient.get(
|
|
|
|
`/eth/v1/beacon/light_client/optimistic_update`,
|
|
|
|
);
|
|
|
|
|
2023-07-11 05:54:01 +00:00
|
|
|
const update = resp.data;
|
2023-07-11 05:50:42 +00:00
|
|
|
|
2023-07-11 05:54:01 +00:00
|
|
|
if (!update) {
|
2023-07-11 05:50:42 +00:00
|
|
|
throw Error(`fetching optimistic update failed`);
|
|
|
|
}
|
2023-07-11 05:54:01 +00:00
|
|
|
|
|
|
|
return update;
|
2023-07-11 05:50:42 +00:00
|
|
|
}
|