Skip to content

Commit

Permalink
Rewrite SyncCommitteeWitnessRepository
Browse files Browse the repository at this point in the history
  • Loading branch information
ensi321 committed Oct 23, 2024
1 parent 64cfe4d commit 118e065
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
11 changes: 10 additions & 1 deletion packages/beacon-node/src/chain/lightClient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,16 @@ export class LightClientServer {
if (!syncCommitteeWitness) {
throw Error(`syncCommitteeWitness not available at ${toRootHex(attestedData.blockRoot)}`);
}

const attestedFork = this.config.getForkName(attestedHeader.beacon.slot);
const numWitness = syncCommitteeWitness.witness.length;
if (isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS_ELECTRA) {
throw Error(`Expected ${NUM_WITNESS_ELECTRA} witnesses in post-Electra numWiteness=${numWitness}`);
}
if (!isForkPostElectra(attestedFork) && numWitness !== NUM_WITNESS) {
throw Error(`Expected ${NUM_WITNESS} witnesses in pre-Electra numWiteness=${numWitness}`);
}

const nextSyncCommittee = await this.db.syncCommittee.get(syncCommitteeWitness.nextSyncCommitteeRoot);
if (!nextSyncCommittee) {
throw Error("nextSyncCommittee not available");
Expand All @@ -642,7 +652,6 @@ export class LightClientServer {
finalityBranch = attestedData.finalityBranch;
finalizedHeader = finalizedHeaderAttested;
// Fork of LightClientUpdate is based off on attested header's fork
const attestedFork = this.config.getForkName(attestedHeader.beacon.slot);
if (this.config.getForkName(finalizedHeader.beacon.slot) !== attestedFork) {
finalizedHeader = upgradeLightClientHeader(this.config, attestedFork, finalizedHeader);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/beacon-node/src/chain/lightClient/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
* ```
*/
export type SyncCommitteeWitness = {
/** Vector[Bytes32, 4] */
/** Vector[Bytes32, 4] or Vector[Bytes32, 5] depends on the fork */
witness: Uint8Array[];
currentSyncCommitteeRoot: Uint8Array;
nextSyncCommitteeRoot: Uint8Array;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ import {ssz} from "@lodestar/types";
import {SyncCommitteeWitness} from "../../chain/lightClient/types.js";
import {Bucket, getBucketNameByValue} from "../buckets.js";

// We add a 1-byte prefix where 0 means pre-electra and 1 means post-electra
enum PrefixByte {
PRE_ELECTRA = 0,
POST_ELECTRA = 1,
}

export const NUM_WITNESS = 4;
export const NUM_WITNESS_ELECTRA = 5;

/**
* Historical sync committees witness by block root
*
Expand All @@ -13,12 +22,56 @@ import {Bucket, getBucketNameByValue} from "../buckets.js";
export class SyncCommitteeWitnessRepository extends Repository<Uint8Array, SyncCommitteeWitness> {
constructor(config: ChainForkConfig, db: DatabaseController<Uint8Array, Uint8Array>) {
const bucket = Bucket.lightClient_syncCommitteeWitness;
// Pick some type but won't be used. Witness can be 4 or 5 so need to handle dynamically
const type = new ContainerType({
witness: new VectorCompositeType(ssz.Root, 4),
witness: new VectorCompositeType(ssz.Root, NUM_WITNESS),
currentSyncCommitteeRoot: ssz.Root,
nextSyncCommitteeRoot: ssz.Root,
});

super(config, db, bucket, type, getBucketNameByValue(bucket));
}

// Overrides for multi-fork
encodeValue(value: SyncCommitteeWitness): Uint8Array {
const numWitness = value.witness.length;

if (numWitness !== NUM_WITNESS && numWitness !== NUM_WITNESS_ELECTRA) {
throw Error(`Number of witness can only be 4 pre-electra or 5 post-electra numWitness=${numWitness}`);
}

const type = new ContainerType({
witness: new VectorCompositeType(ssz.Root, numWitness),
currentSyncCommitteeRoot: ssz.Root,
nextSyncCommitteeRoot: ssz.Root,
});

const valueBytes = type.serialize(value);

// We need to differentiate between post-electra and pre-electra witness
// such that we can deserialize correctly
const isPostElectra = numWitness === NUM_WITNESS_ELECTRA;
const prefixByte = new Uint8Array(1);
prefixByte[0] = isPostElectra ? PrefixByte.POST_ELECTRA : PrefixByte.PRE_ELECTRA;

const prefixedData = new Uint8Array(1 + valueBytes.length);
prefixedData.set(prefixByte, 0);
prefixedData.set(valueBytes, 1);

return prefixedData;
}

decodeValue(data: Uint8Array): SyncCommitteeWitness {
// First byte is written
const prefix = data.subarray(0, 1);
const isPostElectra = prefix[0] === PrefixByte.POST_ELECTRA;

const type = new ContainerType({
witness: new VectorCompositeType(ssz.Root, isPostElectra ? NUM_WITNESS_ELECTRA : NUM_WITNESS),
currentSyncCommitteeRoot: ssz.Root,
nextSyncCommitteeRoot: ssz.Root,
});

return type.deserialize(data.subarray(1));
}
}

0 comments on commit 118e065

Please sign in to comment.