@lodestar/state-transition 1.43.0-dev.8ad5d8be4c → 1.43.0-dev.8e18007572
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/lightClient/spec/index.d.ts +22 -0
- package/lib/lightClient/spec/index.d.ts.map +1 -0
- package/lib/lightClient/spec/index.js +58 -0
- package/lib/lightClient/spec/index.js.map +1 -0
- package/lib/lightClient/spec/isBetterUpdate.d.ts +23 -0
- package/lib/lightClient/spec/isBetterUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/isBetterUpdate.js +66 -0
- package/lib/lightClient/spec/isBetterUpdate.js.map +1 -0
- package/lib/lightClient/spec/processLightClientUpdate.d.ts +12 -0
- package/lib/lightClient/spec/processLightClientUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/processLightClientUpdate.js +80 -0
- package/lib/lightClient/spec/processLightClientUpdate.js.map +1 -0
- package/lib/lightClient/spec/store.d.ts +45 -0
- package/lib/lightClient/spec/store.d.ts.map +1 -0
- package/lib/lightClient/spec/store.js +56 -0
- package/lib/lightClient/spec/store.js.map +1 -0
- package/lib/lightClient/spec/utils.d.ts +47 -0
- package/lib/lightClient/spec/utils.d.ts.map +1 -0
- package/lib/lightClient/spec/utils.js +197 -0
- package/lib/lightClient/spec/utils.js.map +1 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.d.ts +4 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.d.ts.map +1 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.js +22 -0
- package/lib/lightClient/spec/validateLightClientBootstrap.js.map +1 -0
- package/lib/lightClient/spec/validateLightClientUpdate.d.ts +5 -0
- package/lib/lightClient/spec/validateLightClientUpdate.d.ts.map +1 -0
- package/lib/lightClient/spec/validateLightClientUpdate.js +88 -0
- package/lib/lightClient/spec/validateLightClientUpdate.js.map +1 -0
- package/package.json +12 -7
- package/src/lightClient/spec/index.ts +101 -0
- package/src/lightClient/spec/isBetterUpdate.ts +94 -0
- package/src/lightClient/spec/processLightClientUpdate.ts +119 -0
- package/src/lightClient/spec/store.ts +106 -0
- package/src/lightClient/spec/utils.ts +317 -0
- package/src/lightClient/spec/validateLightClientBootstrap.ts +39 -0
- package/src/lightClient/spec/validateLightClientUpdate.ts +145 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { PublicKey } from "@chainsafe/blst";
|
|
2
|
+
import { BLOCK_BODY_EXECUTION_PAYLOAD_DEPTH as EXECUTION_PAYLOAD_DEPTH, BLOCK_BODY_EXECUTION_PAYLOAD_INDEX as EXECUTION_PAYLOAD_INDEX, FINALIZED_ROOT_DEPTH, FINALIZED_ROOT_DEPTH_ELECTRA, ForkName, ForkSeq, NEXT_SYNC_COMMITTEE_DEPTH, NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA, isForkPostElectra, } from "@lodestar/params";
|
|
3
|
+
import { isElectraLightClientUpdate, ssz, } from "@lodestar/types";
|
|
4
|
+
import { byteArrayEquals, verifyMerkleBranch } from "@lodestar/utils";
|
|
5
|
+
import { computeEpochAtSlot, computeSyncPeriodAtSlot } from "../../util/epoch.js";
|
|
6
|
+
export const GENESIS_SLOT = 0;
|
|
7
|
+
export const ZERO_HASH = new Uint8Array(32);
|
|
8
|
+
export const ZERO_PUBKEY = new Uint8Array(48);
|
|
9
|
+
export const ZERO_SYNC_COMMITTEE = ssz.altair.SyncCommittee.defaultValue();
|
|
10
|
+
export const ZERO_HEADER = ssz.phase0.BeaconBlockHeader.defaultValue();
|
|
11
|
+
/** From https://notes.ethereum.org/@vbuterin/extended_light_client_protocol#Optimistic-head-determining-function */
|
|
12
|
+
const SAFETY_THRESHOLD_FACTOR = 2;
|
|
13
|
+
export function sumBits(bits) {
|
|
14
|
+
return bits.getTrueBitIndexes().length;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Util to guarantee that all bits have a corresponding pubkey.
|
|
18
|
+
*/
|
|
19
|
+
export function getParticipantPubkeys(pubkeys, bits) {
|
|
20
|
+
// BitArray.intersectValues() checks the length is correct.
|
|
21
|
+
return bits.intersectValues(pubkeys);
|
|
22
|
+
}
|
|
23
|
+
function deserializePubkeys(pubkeys) {
|
|
24
|
+
return pubkeys.map((pk) => PublicKey.fromBytes(pk, true));
|
|
25
|
+
}
|
|
26
|
+
function serializePubkeys(pubkeys) {
|
|
27
|
+
return pubkeys.map((pk) => pk.toBytes());
|
|
28
|
+
}
|
|
29
|
+
export function deserializeSyncCommittee(syncCommittee) {
|
|
30
|
+
return {
|
|
31
|
+
pubkeys: deserializePubkeys(syncCommittee.pubkeys),
|
|
32
|
+
aggregatePubkey: PublicKey.fromBytes(syncCommittee.aggregatePubkey, true),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export function serializeSyncCommittee(syncCommittee) {
|
|
36
|
+
return {
|
|
37
|
+
pubkeys: serializePubkeys(syncCommittee.pubkeys),
|
|
38
|
+
aggregatePubkey: syncCommittee.aggregatePubkey.toBytes(),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function getSafetyThreshold(maxActiveParticipants) {
|
|
42
|
+
return Math.floor(maxActiveParticipants / SAFETY_THRESHOLD_FACTOR);
|
|
43
|
+
}
|
|
44
|
+
export function getZeroSyncCommitteeBranch(fork) {
|
|
45
|
+
const nextSyncCommitteeDepth = isForkPostElectra(fork)
|
|
46
|
+
? NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA
|
|
47
|
+
: NEXT_SYNC_COMMITTEE_DEPTH;
|
|
48
|
+
return Array.from({ length: nextSyncCommitteeDepth }, () => ZERO_HASH);
|
|
49
|
+
}
|
|
50
|
+
export function getZeroFinalityBranch(fork) {
|
|
51
|
+
const finalizedRootDepth = isForkPostElectra(fork) ? FINALIZED_ROOT_DEPTH_ELECTRA : FINALIZED_ROOT_DEPTH;
|
|
52
|
+
return Array.from({ length: finalizedRootDepth }, () => ZERO_HASH);
|
|
53
|
+
}
|
|
54
|
+
export function isSyncCommitteeUpdate(update) {
|
|
55
|
+
return (
|
|
56
|
+
// Fast return for when constructing full LightClientUpdate from partial updates
|
|
57
|
+
update.nextSyncCommitteeBranch !==
|
|
58
|
+
getZeroSyncCommitteeBranch(isElectraLightClientUpdate(update) ? ForkName.electra : ForkName.altair) &&
|
|
59
|
+
update.nextSyncCommitteeBranch.some((branch) => !byteArrayEquals(branch, ZERO_HASH)));
|
|
60
|
+
}
|
|
61
|
+
export function isFinalityUpdate(update) {
|
|
62
|
+
return (
|
|
63
|
+
// Fast return for when constructing full LightClientUpdate from partial updates
|
|
64
|
+
update.finalityBranch !==
|
|
65
|
+
getZeroFinalityBranch(isElectraLightClientUpdate(update) ? ForkName.electra : ForkName.altair) &&
|
|
66
|
+
update.finalityBranch.some((branch) => !byteArrayEquals(branch, ZERO_HASH)));
|
|
67
|
+
}
|
|
68
|
+
export function isZeroedHeader(header) {
|
|
69
|
+
// Fast return for when constructing full LightClientUpdate from partial updates
|
|
70
|
+
return header === ZERO_HEADER || byteArrayEquals(header.bodyRoot, ZERO_HASH);
|
|
71
|
+
}
|
|
72
|
+
export function isZeroedSyncCommittee(syncCommittee) {
|
|
73
|
+
// Fast return for when constructing full LightClientUpdate from partial updates
|
|
74
|
+
return syncCommittee === ZERO_SYNC_COMMITTEE || byteArrayEquals(syncCommittee.pubkeys[0], ZERO_PUBKEY);
|
|
75
|
+
}
|
|
76
|
+
export function isValidMerkleBranch(leaf, branch, depth, index, root) {
|
|
77
|
+
if (branch.length !== depth) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
return verifyMerkleBranch(leaf, branch, depth, index, root);
|
|
81
|
+
}
|
|
82
|
+
export function normalizeMerkleBranch(branch, depth) {
|
|
83
|
+
const numExtraDepth = depth - branch.length;
|
|
84
|
+
return [...Array.from({ length: numExtraDepth }, () => ZERO_HASH), ...branch];
|
|
85
|
+
}
|
|
86
|
+
export function upgradeLightClientHeader(config, targetFork, header) {
|
|
87
|
+
const headerFork = config.getForkName(header.beacon.slot);
|
|
88
|
+
if (ForkSeq[headerFork] >= ForkSeq[targetFork]) {
|
|
89
|
+
throw Error(`Invalid upgrade request from headerFork=${headerFork} to targetFork=${targetFork}`);
|
|
90
|
+
}
|
|
91
|
+
// We are modifying the same header object, may be we could create a copy, but its
|
|
92
|
+
// not required as of now
|
|
93
|
+
const upgradedHeader = header;
|
|
94
|
+
const startUpgradeFromFork = Object.values(ForkName)[ForkSeq[headerFork] + 1];
|
|
95
|
+
switch (startUpgradeFromFork) {
|
|
96
|
+
// biome-ignore lint/suspicious/useDefaultSwitchClauseLast: We want default to evaluate at first to throw error early
|
|
97
|
+
default:
|
|
98
|
+
throw Error(`Invalid startUpgradeFromFork=${startUpgradeFromFork} for headerFork=${headerFork} in upgradeLightClientHeader to targetFork=${targetFork}`);
|
|
99
|
+
case ForkName.altair:
|
|
100
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
101
|
+
case ForkName.bellatrix:
|
|
102
|
+
// Break if no further upgradation is required else fall through
|
|
103
|
+
if (ForkSeq[targetFork] <= ForkSeq.bellatrix)
|
|
104
|
+
break;
|
|
105
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
106
|
+
case ForkName.capella:
|
|
107
|
+
upgradedHeader.execution =
|
|
108
|
+
ssz.capella.LightClientHeader.fields.execution.defaultValue();
|
|
109
|
+
upgradedHeader.executionBranch =
|
|
110
|
+
ssz.capella.LightClientHeader.fields.executionBranch.defaultValue();
|
|
111
|
+
// Break if no further upgradation is required else fall through
|
|
112
|
+
if (ForkSeq[targetFork] <= ForkSeq.capella)
|
|
113
|
+
break;
|
|
114
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
115
|
+
case ForkName.deneb:
|
|
116
|
+
upgradedHeader.execution.blobGasUsed =
|
|
117
|
+
ssz.deneb.LightClientHeader.fields.execution.fields.blobGasUsed.defaultValue();
|
|
118
|
+
upgradedHeader.execution.excessBlobGas =
|
|
119
|
+
ssz.deneb.LightClientHeader.fields.execution.fields.excessBlobGas.defaultValue();
|
|
120
|
+
// Break if no further upgradation is required else fall through
|
|
121
|
+
if (ForkSeq[targetFork] <= ForkSeq.deneb)
|
|
122
|
+
break;
|
|
123
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
124
|
+
case ForkName.electra:
|
|
125
|
+
// No changes to LightClientHeader in Electra
|
|
126
|
+
// Break if no further upgrades is required else fall through
|
|
127
|
+
if (ForkSeq[targetFork] <= ForkSeq.electra)
|
|
128
|
+
break;
|
|
129
|
+
// biome-ignore lint/suspicious/noFallthroughSwitchClause: We need fall-through behavior here
|
|
130
|
+
case ForkName.fulu:
|
|
131
|
+
// No changes to LightClientHeader in Fulu
|
|
132
|
+
// Break if no further upgrades is required else fall through
|
|
133
|
+
if (ForkSeq[targetFork] <= ForkSeq.fulu)
|
|
134
|
+
break;
|
|
135
|
+
case ForkName.gloas:
|
|
136
|
+
// No changes to LightClientHeader in Gloas
|
|
137
|
+
// Break if no further upgrades is required else fall through
|
|
138
|
+
if (ForkSeq[targetFork] <= ForkSeq.gloas)
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
return upgradedHeader;
|
|
142
|
+
}
|
|
143
|
+
export function isValidLightClientHeader(config, header) {
|
|
144
|
+
const epoch = computeEpochAtSlot(header.beacon.slot);
|
|
145
|
+
if (epoch < config.CAPELLA_FORK_EPOCH) {
|
|
146
|
+
return ((header.execution === undefined ||
|
|
147
|
+
ssz.capella.ExecutionPayloadHeader.equals(header.execution, ssz.capella.LightClientHeader.fields.execution.defaultValue())) &&
|
|
148
|
+
(header.executionBranch === undefined ||
|
|
149
|
+
ssz.capella.LightClientHeader.fields.executionBranch.equals(ssz.capella.LightClientHeader.fields.executionBranch.defaultValue(), header.executionBranch)));
|
|
150
|
+
}
|
|
151
|
+
if (epoch < config.DENEB_FORK_EPOCH &&
|
|
152
|
+
((header.execution.blobGasUsed &&
|
|
153
|
+
header.execution.blobGasUsed !== BigInt(0)) ||
|
|
154
|
+
(header.execution.excessBlobGas &&
|
|
155
|
+
header.execution.excessBlobGas !== BigInt(0)))) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
return isValidMerkleBranch(config
|
|
159
|
+
.getPostBellatrixForkTypes(header.beacon.slot)
|
|
160
|
+
.ExecutionPayloadHeader.hashTreeRoot(header.execution), header.executionBranch, EXECUTION_PAYLOAD_DEPTH, EXECUTION_PAYLOAD_INDEX, header.beacon.bodyRoot);
|
|
161
|
+
}
|
|
162
|
+
export function upgradeLightClientUpdate(config, targetFork, update) {
|
|
163
|
+
update.attestedHeader = upgradeLightClientHeader(config, targetFork, update.attestedHeader);
|
|
164
|
+
update.finalizedHeader = upgradeLightClientHeader(config, targetFork, update.finalizedHeader);
|
|
165
|
+
update.nextSyncCommitteeBranch = normalizeMerkleBranch(update.nextSyncCommitteeBranch, isForkPostElectra(targetFork) ? NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA : NEXT_SYNC_COMMITTEE_DEPTH);
|
|
166
|
+
update.finalityBranch = normalizeMerkleBranch(update.finalityBranch, isForkPostElectra(targetFork) ? FINALIZED_ROOT_DEPTH_ELECTRA : FINALIZED_ROOT_DEPTH);
|
|
167
|
+
return update;
|
|
168
|
+
}
|
|
169
|
+
export function upgradeLightClientFinalityUpdate(config, targetFork, finalityUpdate) {
|
|
170
|
+
finalityUpdate.attestedHeader = upgradeLightClientHeader(config, targetFork, finalityUpdate.attestedHeader);
|
|
171
|
+
finalityUpdate.finalizedHeader = upgradeLightClientHeader(config, targetFork, finalityUpdate.finalizedHeader);
|
|
172
|
+
finalityUpdate.finalityBranch = normalizeMerkleBranch(finalityUpdate.finalityBranch, isForkPostElectra(targetFork) ? FINALIZED_ROOT_DEPTH_ELECTRA : FINALIZED_ROOT_DEPTH);
|
|
173
|
+
return finalityUpdate;
|
|
174
|
+
}
|
|
175
|
+
export function upgradeLightClientOptimisticUpdate(config, targetFork, optimisticUpdate) {
|
|
176
|
+
optimisticUpdate.attestedHeader = upgradeLightClientHeader(config, targetFork, optimisticUpdate.attestedHeader);
|
|
177
|
+
return optimisticUpdate;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Currently this upgradation is not required because all processing is done based on the
|
|
181
|
+
* summary that the store generates and maintains. In case store needs to be saved to disk,
|
|
182
|
+
* this could be required depending on the format the store is saved to the disk
|
|
183
|
+
*/
|
|
184
|
+
export function upgradeLightClientStore(config, targetFork, store, signatureSlot) {
|
|
185
|
+
const updateSignaturePeriod = computeSyncPeriodAtSlot(signatureSlot);
|
|
186
|
+
const bestValidUpdate = store.bestValidUpdates.get(updateSignaturePeriod);
|
|
187
|
+
if (bestValidUpdate) {
|
|
188
|
+
store.bestValidUpdates.set(updateSignaturePeriod, {
|
|
189
|
+
update: upgradeLightClientUpdate(config, targetFork, bestValidUpdate.update),
|
|
190
|
+
summary: bestValidUpdate.summary,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
store.finalizedHeader = upgradeLightClientHeader(config, targetFork, store.finalizedHeader);
|
|
194
|
+
store.optimisticHeader = upgradeLightClientHeader(config, targetFork, store.optimisticHeader);
|
|
195
|
+
return store;
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/lightClient/spec/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ChainForkConfig } from "@lodestar/config";
|
|
2
|
+
import { LightClientBootstrap, Root } from "@lodestar/types";
|
|
3
|
+
export declare function validateLightClientBootstrap(config: ChainForkConfig, trustedBlockRoot: Root, bootstrap: LightClientBootstrap): void;
|
|
4
|
+
//# sourceMappingURL=validateLightClientBootstrap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateLightClientBootstrap.d.ts","sourceRoot":"","sources":["../../../src/lightClient/spec/validateLightClientBootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAC,oBAAoB,EAAE,IAAI,EAAM,MAAM,iBAAiB,CAAC;AAShE,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,eAAe,EACvB,gBAAgB,EAAE,IAAI,EACtB,SAAS,EAAE,oBAAoB,GAC9B,IAAI,CAuBN"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isForkPostElectra } from "@lodestar/params";
|
|
2
|
+
import { ssz } from "@lodestar/types";
|
|
3
|
+
import { byteArrayEquals, toHex } from "@lodestar/utils";
|
|
4
|
+
import { isValidLightClientHeader, isValidMerkleBranch } from "./utils.js";
|
|
5
|
+
const CURRENT_SYNC_COMMITTEE_INDEX = 22;
|
|
6
|
+
const CURRENT_SYNC_COMMITTEE_DEPTH = 5;
|
|
7
|
+
const CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA = 22;
|
|
8
|
+
const CURRENT_SYNC_COMMITTEE_DEPTH_ELECTRA = 6;
|
|
9
|
+
export function validateLightClientBootstrap(config, trustedBlockRoot, bootstrap) {
|
|
10
|
+
const headerRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(bootstrap.header.beacon);
|
|
11
|
+
const fork = config.getForkName(bootstrap.header.beacon.slot);
|
|
12
|
+
if (!isValidLightClientHeader(config, bootstrap.header)) {
|
|
13
|
+
throw Error("Bootstrap Header is not Valid Light Client Header");
|
|
14
|
+
}
|
|
15
|
+
if (!byteArrayEquals(headerRoot, trustedBlockRoot)) {
|
|
16
|
+
throw Error(`bootstrap header root ${toHex(headerRoot)} != trusted root ${toHex(trustedBlockRoot)}`);
|
|
17
|
+
}
|
|
18
|
+
if (!isValidMerkleBranch(ssz.altair.SyncCommittee.hashTreeRoot(bootstrap.currentSyncCommittee), bootstrap.currentSyncCommitteeBranch, isForkPostElectra(fork) ? CURRENT_SYNC_COMMITTEE_DEPTH_ELECTRA : CURRENT_SYNC_COMMITTEE_DEPTH, isForkPostElectra(fork) ? CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA : CURRENT_SYNC_COMMITTEE_INDEX, bootstrap.header.beacon.stateRoot)) {
|
|
19
|
+
throw Error("Invalid currentSyncCommittee merkle branch");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=validateLightClientBootstrap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateLightClientBootstrap.js","sourceRoot":"","sources":["../../../src/lightClient/spec/validateLightClientBootstrap.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAA6B,GAAG,EAAC,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAC,eAAe,EAAE,KAAK,EAAC,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAC,wBAAwB,EAAE,mBAAmB,EAAC,MAAM,YAAY,CAAC;AAEzE,MAAM,4BAA4B,GAAG,EAAE,CAAC;AACxC,MAAM,4BAA4B,GAAG,CAAC,CAAC;AACvC,MAAM,oCAAoC,GAAG,EAAE,CAAC;AAChD,MAAM,oCAAoC,GAAG,CAAC,CAAC;AAE/C,MAAM,UAAU,4BAA4B,CAC1C,MAAuB,EACvB,gBAAsB,EACtB,SAA+B,EACzB;IACN,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACtF,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAE9D,IAAI,CAAC,wBAAwB,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,MAAM,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,gBAAgB,CAAC,EAAE,CAAC;QACnD,MAAM,KAAK,CAAC,yBAAyB,KAAK,CAAC,UAAU,CAAC,oBAAoB,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;IAED,IACE,CAAC,mBAAmB,CAClB,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,YAAY,CAAC,SAAS,CAAC,oBAAoB,CAAC,EACrE,SAAS,CAAC,0BAA0B,EACpC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,4BAA4B,EAC7F,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,4BAA4B,EAC7F,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAClC,EACD,CAAC;QACD,MAAM,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC5D,CAAC;AAAA,CACF"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ChainForkConfig } from "@lodestar/config";
|
|
2
|
+
import { LightClientUpdate } from "@lodestar/types";
|
|
3
|
+
import type { ILightClientStore, SyncCommitteeFast } from "./store.js";
|
|
4
|
+
export declare function validateLightClientUpdate(config: ChainForkConfig, store: ILightClientStore, update: LightClientUpdate, syncCommittee: SyncCommitteeFast): void;
|
|
5
|
+
//# sourceMappingURL=validateLightClientUpdate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateLightClientUpdate.d.ts","sourceRoot":"","sources":["../../../src/lightClient/spec/validateLightClientUpdate.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAcjD,OAAO,EAAC,iBAAiB,EAAwC,MAAM,iBAAiB,CAAC;AACzF,OAAO,KAAK,EAAC,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAarE,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,eAAe,EACvB,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,iBAAiB,GAC/B,IAAI,CA0FN"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Signature, fastAggregateVerify } from "@chainsafe/blst";
|
|
2
|
+
import { DOMAIN_SYNC_COMMITTEE, FINALIZED_ROOT_DEPTH, FINALIZED_ROOT_DEPTH_ELECTRA, FINALIZED_ROOT_INDEX, FINALIZED_ROOT_INDEX_ELECTRA, GENESIS_SLOT, MIN_SYNC_COMMITTEE_PARTICIPANTS, NEXT_SYNC_COMMITTEE_DEPTH, NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA, NEXT_SYNC_COMMITTEE_INDEX, NEXT_SYNC_COMMITTEE_INDEX_ELECTRA, } from "@lodestar/params";
|
|
3
|
+
import { isElectraLightClientUpdate, ssz } from "@lodestar/types";
|
|
4
|
+
import { ZERO_HASH, getParticipantPubkeys, isFinalityUpdate, isSyncCommitteeUpdate, isValidLightClientHeader, isValidMerkleBranch, isZeroedHeader, isZeroedSyncCommittee, sumBits, } from "./utils.js";
|
|
5
|
+
export function validateLightClientUpdate(config, store, update, syncCommittee) {
|
|
6
|
+
// Verify sync committee has sufficient participants
|
|
7
|
+
if (sumBits(update.syncAggregate.syncCommitteeBits) < MIN_SYNC_COMMITTEE_PARTICIPANTS) {
|
|
8
|
+
throw Error("Sync committee has not sufficient participants");
|
|
9
|
+
}
|
|
10
|
+
if (!isValidLightClientHeader(config, update.attestedHeader)) {
|
|
11
|
+
throw Error("Attested Header is not Valid Light Client Header");
|
|
12
|
+
}
|
|
13
|
+
// Sanity check that slots are in correct order
|
|
14
|
+
if (update.signatureSlot <= update.attestedHeader.beacon.slot) {
|
|
15
|
+
throw Error(`signature slot ${update.signatureSlot} must be after attested header slot ${update.attestedHeader.beacon.slot}`);
|
|
16
|
+
}
|
|
17
|
+
if (update.attestedHeader.beacon.slot < update.finalizedHeader.beacon.slot) {
|
|
18
|
+
throw Error(`attested header slot ${update.signatureSlot} must be after finalized header slot ${update.finalizedHeader.beacon.slot}`);
|
|
19
|
+
}
|
|
20
|
+
// Verify that the `finality_branch`, if present, confirms `finalized_header`
|
|
21
|
+
// to match the finalized checkpoint root saved in the state of `attested_header`.
|
|
22
|
+
// Note that the genesis finalized checkpoint root is represented as a zero hash.
|
|
23
|
+
if (!isFinalityUpdate(update)) {
|
|
24
|
+
if (!isZeroedHeader(update.finalizedHeader.beacon)) {
|
|
25
|
+
throw Error("finalizedHeader must be zero for non-finality update");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
let finalizedRoot;
|
|
30
|
+
if (update.finalizedHeader.beacon.slot === GENESIS_SLOT) {
|
|
31
|
+
if (!isZeroedHeader(update.finalizedHeader.beacon)) {
|
|
32
|
+
throw Error("finalizedHeader must be zero for not finality update");
|
|
33
|
+
}
|
|
34
|
+
finalizedRoot = ZERO_HASH;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
if (!isValidLightClientHeader(config, update.finalizedHeader)) {
|
|
38
|
+
throw Error("Finalized Header is not valid Light Client Header");
|
|
39
|
+
}
|
|
40
|
+
finalizedRoot = ssz.phase0.BeaconBlockHeader.hashTreeRoot(update.finalizedHeader.beacon);
|
|
41
|
+
}
|
|
42
|
+
if (!isValidMerkleBranch(finalizedRoot, update.finalityBranch, isElectraLightClientUpdate(update) ? FINALIZED_ROOT_DEPTH_ELECTRA : FINALIZED_ROOT_DEPTH, isElectraLightClientUpdate(update) ? FINALIZED_ROOT_INDEX_ELECTRA : FINALIZED_ROOT_INDEX, update.attestedHeader.beacon.stateRoot)) {
|
|
43
|
+
throw Error("Invalid finality header merkle branch");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Verify that the `next_sync_committee`, if present, actually is the next sync committee saved in the
|
|
47
|
+
// state of the `attested_header`
|
|
48
|
+
if (!isSyncCommitteeUpdate(update)) {
|
|
49
|
+
if (!isZeroedSyncCommittee(update.nextSyncCommittee)) {
|
|
50
|
+
throw Error("nextSyncCommittee must be zero for non sync committee update");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
if (!isValidMerkleBranch(ssz.altair.SyncCommittee.hashTreeRoot(update.nextSyncCommittee), update.nextSyncCommitteeBranch, isElectraLightClientUpdate(update) ? NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA : NEXT_SYNC_COMMITTEE_DEPTH, isElectraLightClientUpdate(update) ? NEXT_SYNC_COMMITTEE_INDEX_ELECTRA : NEXT_SYNC_COMMITTEE_INDEX, update.attestedHeader.beacon.stateRoot)) {
|
|
55
|
+
throw Error("Invalid next sync committee merkle branch");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Verify sync committee aggregate signature
|
|
59
|
+
const participantPubkeys = getParticipantPubkeys(syncCommittee.pubkeys, update.syncAggregate.syncCommitteeBits);
|
|
60
|
+
const signingRoot = ssz.phase0.SigningData.hashTreeRoot({
|
|
61
|
+
objectRoot: ssz.phase0.BeaconBlockHeader.hashTreeRoot(update.attestedHeader.beacon),
|
|
62
|
+
domain: store.config.getDomain(update.signatureSlot - 1, DOMAIN_SYNC_COMMITTEE),
|
|
63
|
+
});
|
|
64
|
+
if (!isValidBlsAggregate(participantPubkeys, signingRoot, update.syncAggregate.syncCommitteeSignature)) {
|
|
65
|
+
throw Error("Invalid aggregate signature");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Same as BLS.verifyAggregate but with detailed error messages
|
|
70
|
+
*/
|
|
71
|
+
function isValidBlsAggregate(publicKeys, message, signature) {
|
|
72
|
+
let sig;
|
|
73
|
+
try {
|
|
74
|
+
sig = Signature.fromBytes(signature, true);
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
e.message = `Error deserializing signature: ${e.message}`;
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return fastAggregateVerify(message, publicKeys, sig);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
e.message = `Error verifying signature: ${e.message}`;
|
|
85
|
+
throw e;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=validateLightClientUpdate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateLightClientUpdate.js","sourceRoot":"","sources":["../../../src/lightClient/spec/validateLightClientUpdate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/ChainSafe/lodestar/issues"
|
|
13
13
|
},
|
|
14
|
-
"version": "1.43.0-dev.
|
|
14
|
+
"version": "1.43.0-dev.8e18007572",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
@@ -34,6 +34,11 @@
|
|
|
34
34
|
"types": "./lib/slot/index.d.ts",
|
|
35
35
|
"import": "./lib/slot/index.js"
|
|
36
36
|
},
|
|
37
|
+
"./light-client": {
|
|
38
|
+
"bun": "./src/lightClient/spec/index.ts",
|
|
39
|
+
"types": "./lib/lightClient/spec/index.d.ts",
|
|
40
|
+
"import": "./lib/lightClient/spec/index.js"
|
|
41
|
+
},
|
|
37
42
|
"./test-utils": {
|
|
38
43
|
"bun": "./src/testUtils/index.ts",
|
|
39
44
|
"types": "./lib/testUtils/index.d.ts",
|
|
@@ -67,14 +72,14 @@
|
|
|
67
72
|
"@chainsafe/pubkey-index-map": "^3.0.0",
|
|
68
73
|
"@chainsafe/ssz": "^1.4.0",
|
|
69
74
|
"@chainsafe/swap-or-not-shuffle": "^1.2.1",
|
|
70
|
-
"@lodestar/config": "^1.43.0-dev.
|
|
71
|
-
"@lodestar/params": "^1.43.0-dev.
|
|
72
|
-
"@lodestar/types": "^1.43.0-dev.
|
|
73
|
-
"@lodestar/utils": "^1.43.0-dev.
|
|
75
|
+
"@lodestar/config": "^1.43.0-dev.8e18007572",
|
|
76
|
+
"@lodestar/params": "^1.43.0-dev.8e18007572",
|
|
77
|
+
"@lodestar/types": "^1.43.0-dev.8e18007572",
|
|
78
|
+
"@lodestar/utils": "^1.43.0-dev.8e18007572",
|
|
74
79
|
"@vekexasia/bigint-buffer2": "^1.1.1"
|
|
75
80
|
},
|
|
76
81
|
"devDependencies": {
|
|
77
|
-
"@lodestar/api": "^1.43.0-dev.
|
|
82
|
+
"@lodestar/api": "^1.43.0-dev.8e18007572"
|
|
78
83
|
},
|
|
79
84
|
"keywords": [
|
|
80
85
|
"ethereum",
|
|
@@ -82,5 +87,5 @@
|
|
|
82
87
|
"beacon",
|
|
83
88
|
"blockchain"
|
|
84
89
|
],
|
|
85
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "800bdf2d7346096735d753d5e74087966e96af00"
|
|
86
91
|
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
2
|
+
import {UPDATE_TIMEOUT} from "@lodestar/params";
|
|
3
|
+
import {
|
|
4
|
+
LightClientBootstrap,
|
|
5
|
+
LightClientFinalityUpdate,
|
|
6
|
+
LightClientOptimisticUpdate,
|
|
7
|
+
LightClientUpdate,
|
|
8
|
+
Slot,
|
|
9
|
+
} from "@lodestar/types";
|
|
10
|
+
import {computeSyncPeriodAtSlot} from "../../util/epoch.js";
|
|
11
|
+
import {
|
|
12
|
+
type ProcessUpdateOpts,
|
|
13
|
+
getSyncCommitteeAtPeriod,
|
|
14
|
+
processLightClientUpdate,
|
|
15
|
+
} from "./processLightClientUpdate.js";
|
|
16
|
+
import {type ILightClientStore, LightClientStore, type LightClientStoreEvents} from "./store.js";
|
|
17
|
+
import {ZERO_HEADER, ZERO_SYNC_COMMITTEE, getZeroFinalityBranch, getZeroSyncCommitteeBranch} from "./utils.js";
|
|
18
|
+
|
|
19
|
+
export type {LightClientUpdateSummary} from "./isBetterUpdate.js";
|
|
20
|
+
export {isBetterUpdate, toLightClientUpdateSummary} from "./isBetterUpdate.js";
|
|
21
|
+
export {
|
|
22
|
+
type ProcessUpdateOpts,
|
|
23
|
+
getSyncCommitteeAtPeriod,
|
|
24
|
+
isSafeLightClientUpdate,
|
|
25
|
+
processLightClientUpdate,
|
|
26
|
+
} from "./processLightClientUpdate.js";
|
|
27
|
+
export {
|
|
28
|
+
type ILightClientStore,
|
|
29
|
+
LightClientStore,
|
|
30
|
+
type LightClientStoreEvents,
|
|
31
|
+
type LightClientUpdateWithSummary,
|
|
32
|
+
type SyncCommitteeFast,
|
|
33
|
+
} from "./store.js";
|
|
34
|
+
export {
|
|
35
|
+
getSafetyThreshold,
|
|
36
|
+
isFinalityUpdate,
|
|
37
|
+
isSyncCommitteeUpdate,
|
|
38
|
+
isValidLightClientHeader,
|
|
39
|
+
normalizeMerkleBranch,
|
|
40
|
+
upgradeLightClientFinalityUpdate,
|
|
41
|
+
upgradeLightClientHeader,
|
|
42
|
+
upgradeLightClientOptimisticUpdate,
|
|
43
|
+
upgradeLightClientStore,
|
|
44
|
+
upgradeLightClientUpdate,
|
|
45
|
+
} from "./utils.js";
|
|
46
|
+
export {validateLightClientBootstrap} from "./validateLightClientBootstrap.js";
|
|
47
|
+
export {validateLightClientUpdate} from "./validateLightClientUpdate.js";
|
|
48
|
+
|
|
49
|
+
export class LightclientSpec {
|
|
50
|
+
readonly store: ILightClientStore;
|
|
51
|
+
readonly config: BeaconConfig;
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
config: BeaconConfig,
|
|
55
|
+
private readonly opts: ProcessUpdateOpts & LightClientStoreEvents,
|
|
56
|
+
bootstrap: LightClientBootstrap
|
|
57
|
+
) {
|
|
58
|
+
this.store = new LightClientStore(config, bootstrap, opts);
|
|
59
|
+
this.config = config;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onUpdate(currentSlot: Slot, update: LightClientUpdate): void {
|
|
63
|
+
processLightClientUpdate(this.config, this.store, currentSlot, this.opts, update);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
onFinalityUpdate(currentSlot: Slot, finalityUpdate: LightClientFinalityUpdate): void {
|
|
67
|
+
this.onUpdate(currentSlot, {
|
|
68
|
+
attestedHeader: finalityUpdate.attestedHeader,
|
|
69
|
+
nextSyncCommittee: ZERO_SYNC_COMMITTEE,
|
|
70
|
+
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(this.config.getForkName(finalityUpdate.signatureSlot)),
|
|
71
|
+
finalizedHeader: finalityUpdate.finalizedHeader,
|
|
72
|
+
finalityBranch: finalityUpdate.finalityBranch,
|
|
73
|
+
syncAggregate: finalityUpdate.syncAggregate,
|
|
74
|
+
signatureSlot: finalityUpdate.signatureSlot,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
onOptimisticUpdate(currentSlot: Slot, optimisticUpdate: LightClientOptimisticUpdate): void {
|
|
79
|
+
this.onUpdate(currentSlot, {
|
|
80
|
+
attestedHeader: optimisticUpdate.attestedHeader,
|
|
81
|
+
nextSyncCommittee: ZERO_SYNC_COMMITTEE,
|
|
82
|
+
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(this.config.getForkName(optimisticUpdate.signatureSlot)),
|
|
83
|
+
finalizedHeader: {beacon: ZERO_HEADER},
|
|
84
|
+
finalityBranch: getZeroFinalityBranch(this.config.getForkName(optimisticUpdate.signatureSlot)),
|
|
85
|
+
syncAggregate: optimisticUpdate.syncAggregate,
|
|
86
|
+
signatureSlot: optimisticUpdate.signatureSlot,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
forceUpdate(currentSlot: Slot): void {
|
|
91
|
+
for (const bestValidUpdate of this.store.bestValidUpdates.values()) {
|
|
92
|
+
if (currentSlot > bestValidUpdate.update.finalizedHeader.beacon.slot + UPDATE_TIMEOUT) {
|
|
93
|
+
const updatePeriod = computeSyncPeriodAtSlot(bestValidUpdate.update.signatureSlot);
|
|
94
|
+
// Simulate process_light_client_store_force_update() by forcing to apply a bestValidUpdate
|
|
95
|
+
// https://github.com/ethereum/consensus-specs/blob/a57e15636013eeba3610ff3ade41781dba1bb0cd/specs/altair/light-client/sync-protocol.md?plain=1#L394
|
|
96
|
+
// Call for `updatePeriod + 1` to force the update at `update.signatureSlot` to be applied
|
|
97
|
+
getSyncCommitteeAtPeriod(this.store, updatePeriod + 1, this.opts);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {SYNC_COMMITTEE_SIZE} from "@lodestar/params";
|
|
2
|
+
import {LightClientUpdate, Slot} from "@lodestar/types";
|
|
3
|
+
import {computeSyncPeriodAtSlot} from "../../util/epoch.js";
|
|
4
|
+
import {isFinalityUpdate, isSyncCommitteeUpdate, sumBits} from "./utils.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wrapper type for `isBetterUpdate()` so we can apply its logic without requiring the full LightClientUpdate type.
|
|
8
|
+
*/
|
|
9
|
+
export type LightClientUpdateSummary = {
|
|
10
|
+
activeParticipants: number;
|
|
11
|
+
attestedHeaderSlot: Slot;
|
|
12
|
+
signatureSlot: Slot;
|
|
13
|
+
finalizedHeaderSlot: Slot;
|
|
14
|
+
/** `if update.next_sync_committee_branch != [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]` */
|
|
15
|
+
isSyncCommitteeUpdate: boolean;
|
|
16
|
+
/** `if update.finality_branch != [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]` */
|
|
17
|
+
isFinalityUpdate: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns the update with more bits. On ties, prevUpdate is the better
|
|
22
|
+
*
|
|
23
|
+
* https://github.com/ethereum/consensus-specs/blob/be3c774069e16e89145660be511c1b183056017e/specs/altair/light-client/sync-protocol.md#is_better_update
|
|
24
|
+
*/
|
|
25
|
+
export function isBetterUpdate(newUpdate: LightClientUpdateSummary, oldUpdate: LightClientUpdateSummary): boolean {
|
|
26
|
+
// Compare supermajority (> 2/3) sync committee participation
|
|
27
|
+
const newNumActiveParticipants = newUpdate.activeParticipants;
|
|
28
|
+
const oldNumActiveParticipants = oldUpdate.activeParticipants;
|
|
29
|
+
const newHasSupermajority = newNumActiveParticipants * 3 >= SYNC_COMMITTEE_SIZE * 2;
|
|
30
|
+
const oldHasSupermajority = oldNumActiveParticipants * 3 >= SYNC_COMMITTEE_SIZE * 2;
|
|
31
|
+
if (newHasSupermajority !== oldHasSupermajority) {
|
|
32
|
+
return newHasSupermajority;
|
|
33
|
+
}
|
|
34
|
+
if (!newHasSupermajority && newNumActiveParticipants !== oldNumActiveParticipants) {
|
|
35
|
+
return newNumActiveParticipants > oldNumActiveParticipants;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Compare presence of relevant sync committee
|
|
39
|
+
const newHasRelevantSyncCommittee =
|
|
40
|
+
newUpdate.isSyncCommitteeUpdate &&
|
|
41
|
+
computeSyncPeriodAtSlot(newUpdate.attestedHeaderSlot) === computeSyncPeriodAtSlot(newUpdate.signatureSlot);
|
|
42
|
+
const oldHasRelevantSyncCommittee =
|
|
43
|
+
oldUpdate.isSyncCommitteeUpdate &&
|
|
44
|
+
computeSyncPeriodAtSlot(oldUpdate.attestedHeaderSlot) === computeSyncPeriodAtSlot(oldUpdate.signatureSlot);
|
|
45
|
+
if (newHasRelevantSyncCommittee !== oldHasRelevantSyncCommittee) {
|
|
46
|
+
return newHasRelevantSyncCommittee;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Compare indication of any finality
|
|
50
|
+
const newHasFinality = newUpdate.isFinalityUpdate;
|
|
51
|
+
const oldHasFinality = oldUpdate.isFinalityUpdate;
|
|
52
|
+
if (newHasFinality !== oldHasFinality) {
|
|
53
|
+
return newHasFinality;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Compare sync committee finality
|
|
57
|
+
if (newHasFinality) {
|
|
58
|
+
const newHasSyncCommitteeFinality =
|
|
59
|
+
computeSyncPeriodAtSlot(newUpdate.finalizedHeaderSlot) === computeSyncPeriodAtSlot(newUpdate.attestedHeaderSlot);
|
|
60
|
+
const oldHasSyncCommitteeFinality =
|
|
61
|
+
computeSyncPeriodAtSlot(oldUpdate.finalizedHeaderSlot) === computeSyncPeriodAtSlot(oldUpdate.attestedHeaderSlot);
|
|
62
|
+
if (newHasSyncCommitteeFinality !== oldHasSyncCommitteeFinality) {
|
|
63
|
+
return newHasSyncCommitteeFinality;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Tiebreaker 1: Sync committee participation beyond supermajority
|
|
68
|
+
if (newNumActiveParticipants !== oldNumActiveParticipants) {
|
|
69
|
+
return newNumActiveParticipants > oldNumActiveParticipants;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Tiebreaker 2: Prefer older data (fewer changes to best)
|
|
73
|
+
if (newUpdate.attestedHeaderSlot !== oldUpdate.attestedHeaderSlot) {
|
|
74
|
+
return newUpdate.attestedHeaderSlot < oldUpdate.attestedHeaderSlot;
|
|
75
|
+
}
|
|
76
|
+
return newUpdate.signatureSlot < oldUpdate.signatureSlot;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function isSafeLightClientUpdate(update: LightClientUpdateSummary): boolean {
|
|
80
|
+
return (
|
|
81
|
+
update.activeParticipants * 3 >= SYNC_COMMITTEE_SIZE * 2 && update.isFinalityUpdate && update.isSyncCommitteeUpdate
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function toLightClientUpdateSummary(update: LightClientUpdate): LightClientUpdateSummary {
|
|
86
|
+
return {
|
|
87
|
+
activeParticipants: sumBits(update.syncAggregate.syncCommitteeBits),
|
|
88
|
+
attestedHeaderSlot: update.attestedHeader.beacon.slot,
|
|
89
|
+
signatureSlot: update.signatureSlot,
|
|
90
|
+
finalizedHeaderSlot: update.finalizedHeader.beacon.slot,
|
|
91
|
+
isSyncCommitteeUpdate: isSyncCommitteeUpdate(update),
|
|
92
|
+
isFinalityUpdate: isFinalityUpdate(update),
|
|
93
|
+
};
|
|
94
|
+
}
|