@lodestar/state-transition 1.45.0-dev.0cff4e6916 → 1.45.0-dev.17a48050e6
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/block/processOperations.d.ts.map +1 -1
- package/lib/block/processOperations.js +17 -1
- package/lib/block/processOperations.js.map +1 -1
- package/lib/block/processParentExecutionPayload.d.ts.map +1 -1
- package/lib/block/processParentExecutionPayload.js +15 -1
- package/lib/block/processParentExecutionPayload.js.map +1 -1
- package/lib/block/processWithdrawals.js +1 -1
- package/lib/block/processWithdrawals.js.map +1 -1
- package/lib/cache/stateCache.d.ts.map +1 -1
- package/lib/cache/stateCache.js +2 -4
- package/lib/cache/stateCache.js.map +1 -1
- package/lib/epoch/processParticipationFlagUpdates.d.ts +2 -2
- package/lib/epoch/processParticipationFlagUpdates.d.ts.map +1 -1
- package/lib/epoch/processParticipationFlagUpdates.js +13 -0
- package/lib/epoch/processParticipationFlagUpdates.js.map +1 -1
- package/lib/epoch/processRewardsAndPenalties.d.ts.map +1 -1
- package/lib/epoch/processRewardsAndPenalties.js +1 -2
- package/lib/epoch/processRewardsAndPenalties.js.map +1 -1
- package/lib/lightClient/proofs.d.ts.map +1 -1
- package/lib/lightClient/proofs.js +28 -2
- package/lib/lightClient/proofs.js.map +1 -1
- package/lib/lightClient/spec/index.d.ts.map +1 -1
- package/lib/lightClient/spec/index.js +7 -4
- package/lib/lightClient/spec/index.js.map +1 -1
- package/lib/lightClient/spec/utils.d.ts +7 -1
- package/lib/lightClient/spec/utils.d.ts.map +1 -1
- package/lib/lightClient/spec/utils.js +123 -14
- package/lib/lightClient/spec/utils.js.map +1 -1
- package/lib/lightClient/spec/validateLightClientBootstrap.d.ts.map +1 -1
- package/lib/lightClient/spec/validateLightClientBootstrap.js +2 -7
- package/lib/lightClient/spec/validateLightClientBootstrap.js.map +1 -1
- package/lib/lightClient/spec/validateLightClientUpdate.d.ts.map +1 -1
- package/lib/lightClient/spec/validateLightClientUpdate.js +6 -5
- package/lib/lightClient/spec/validateLightClientUpdate.js.map +1 -1
- package/lib/lightClient/types.d.ts +3 -1
- package/lib/lightClient/types.d.ts.map +1 -1
- package/lib/metrics.js +2 -4
- package/lib/metrics.js.map +1 -1
- package/lib/slot/upgradeStateToGloas.d.ts.map +1 -1
- package/lib/slot/upgradeStateToGloas.js +48 -9
- package/lib/slot/upgradeStateToGloas.js.map +1 -1
- package/lib/stateView/beaconStateView.d.ts +1 -1
- package/lib/stateView/beaconStateView.d.ts.map +1 -1
- package/lib/stateView/beaconStateView.js +6 -8
- package/lib/stateView/beaconStateView.js.map +1 -1
- package/lib/stateView/interface.d.ts +1 -1
- package/lib/stateView/interface.d.ts.map +1 -1
- package/lib/stateView/nativeBeaconStateView.d.ts +2 -2
- package/lib/stateView/nativeBeaconStateView.d.ts.map +1 -1
- package/lib/stateView/nativeBeaconStateView.js +7 -9
- package/lib/stateView/nativeBeaconStateView.js.map +1 -1
- package/lib/util/execution.d.ts.map +1 -1
- package/lib/util/execution.js +7 -2
- package/lib/util/execution.js.map +1 -1
- package/lib/util/index.d.ts +1 -0
- package/lib/util/index.d.ts.map +1 -1
- package/lib/util/index.js +1 -0
- package/lib/util/index.js.map +1 -1
- package/lib/util/ssz.d.ts +9 -0
- package/lib/util/ssz.d.ts.map +1 -0
- package/lib/util/ssz.js +39 -0
- package/lib/util/ssz.js.map +1 -0
- package/package.json +10 -10
- package/src/block/processOperations.ts +28 -1
- package/src/block/processParentExecutionPayload.ts +26 -1
- package/src/block/processWithdrawals.ts +1 -1
- package/src/cache/stateCache.ts +2 -4
- package/src/epoch/processParticipationFlagUpdates.ts +20 -2
- package/src/epoch/processRewardsAndPenalties.ts +1 -2
- package/src/lightClient/proofs.ts +36 -1
- package/src/lightClient/spec/index.ts +8 -4
- package/src/lightClient/spec/utils.ts +193 -25
- package/src/lightClient/spec/validateLightClientBootstrap.ts +3 -10
- package/src/lightClient/spec/validateLightClientUpdate.ts +11 -21
- package/src/lightClient/types.ts +3 -1
- package/src/metrics.ts +2 -4
- package/src/slot/upgradeStateToGloas.ts +84 -9
- package/src/stateView/beaconStateView.ts +8 -8
- package/src/stateView/interface.ts +1 -1
- package/src/stateView/nativeBeaconStateView.ts +8 -10
- package/src/util/execution.ts +9 -4
- package/src/util/index.ts +1 -0
- package/src/util/ssz.ts +42 -0
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ForkSeq,
|
|
3
|
+
MAX_ATTESTATIONS_ELECTRA,
|
|
4
|
+
MAX_ATTESTER_SLASHINGS_ELECTRA,
|
|
5
|
+
MAX_BLS_TO_EXECUTION_CHANGES,
|
|
6
|
+
MAX_PAYLOAD_ATTESTATIONS,
|
|
7
|
+
MAX_PROPOSER_SLASHINGS,
|
|
8
|
+
MAX_VOLUNTARY_EXITS,
|
|
9
|
+
} from "@lodestar/params";
|
|
2
10
|
import {BeaconBlockBody, capella, electra, gloas} from "@lodestar/types";
|
|
3
11
|
import {BeaconStateTransitionMetrics} from "../metrics.js";
|
|
4
12
|
import {
|
|
@@ -39,6 +47,10 @@ export function processOperations(
|
|
|
39
47
|
opts: ProcessBlockOpts = {verifySignatures: true},
|
|
40
48
|
metrics?: BeaconStateTransitionMetrics | null
|
|
41
49
|
): void {
|
|
50
|
+
if (fork >= ForkSeq.gloas) {
|
|
51
|
+
assertGloasOperationLimits(body as gloas.BeaconBlockBody);
|
|
52
|
+
}
|
|
53
|
+
|
|
42
54
|
// verify that outstanding deposits are processed up to the maximum number of deposits.
|
|
43
55
|
// From Fulu the eth1 bridge deposit mechanism was removed, so blocks must not contain any deposits.
|
|
44
56
|
const maxDeposits = fork >= ForkSeq.fulu ? 0 : getEth1DepositCount(state);
|
|
@@ -94,3 +106,18 @@ export function processOperations(
|
|
|
94
106
|
}
|
|
95
107
|
}
|
|
96
108
|
}
|
|
109
|
+
|
|
110
|
+
function assertGloasOperationLimits(body: gloas.BeaconBlockBody): void {
|
|
111
|
+
assertMaxLength("proposerSlashings", body.proposerSlashings.length, MAX_PROPOSER_SLASHINGS);
|
|
112
|
+
assertMaxLength("attesterSlashings", body.attesterSlashings.length, MAX_ATTESTER_SLASHINGS_ELECTRA);
|
|
113
|
+
assertMaxLength("attestations", body.attestations.length, MAX_ATTESTATIONS_ELECTRA);
|
|
114
|
+
assertMaxLength("voluntaryExits", body.voluntaryExits.length, MAX_VOLUNTARY_EXITS);
|
|
115
|
+
assertMaxLength("blsToExecutionChanges", body.blsToExecutionChanges.length, MAX_BLS_TO_EXECUTION_CHANGES);
|
|
116
|
+
assertMaxLength("payloadAttestations", body.payloadAttestations.length, MAX_PAYLOAD_ATTESTATIONS);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function assertMaxLength(name: string, length: number, limit: number): void {
|
|
120
|
+
if (length > limit) {
|
|
121
|
+
throw new Error(`Block contains too many ${name}: count=${length} limit=${limit}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ForkPostGloas,
|
|
3
|
+
MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD,
|
|
4
|
+
MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD,
|
|
5
|
+
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD,
|
|
6
|
+
MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD,
|
|
7
|
+
SLOTS_PER_EPOCH,
|
|
8
|
+
SLOTS_PER_HISTORICAL_ROOT,
|
|
9
|
+
} from "@lodestar/params";
|
|
2
10
|
import {BeaconBlock, gloas, ssz} from "@lodestar/types";
|
|
3
11
|
import {byteArrayEquals, toRootHex} from "@lodestar/utils";
|
|
4
12
|
import {CachedBeaconStateGloas} from "../types.js";
|
|
@@ -47,6 +55,8 @@ export function processParentExecutionPayload(state: CachedBeaconStateGloas, blo
|
|
|
47
55
|
* Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/beacon-chain.md#new-apply_parent_execution_payload
|
|
48
56
|
*/
|
|
49
57
|
export function applyParentExecutionPayload(state: CachedBeaconStateGloas, requests: gloas.ExecutionRequests): void {
|
|
58
|
+
assertExecutionRequestsWithinLimits(requests);
|
|
59
|
+
|
|
50
60
|
const fork = state.config.getForkSeq(state.slot);
|
|
51
61
|
const parentBid = state.latestExecutionPayloadBid;
|
|
52
62
|
const parentSlot = parentBid.slot;
|
|
@@ -116,6 +126,21 @@ function settleBuilderPayment(state: CachedBeaconStateGloas, paymentIndex: numbe
|
|
|
116
126
|
state.builderPendingPayments.set(paymentIndex, ssz.gloas.BuilderPendingPayment.defaultViewDU());
|
|
117
127
|
}
|
|
118
128
|
|
|
129
|
+
function assertExecutionRequestsWithinLimits(requests: gloas.ExecutionRequests): void {
|
|
130
|
+
assertMaxLength("withdrawals", requests.withdrawals.length, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD);
|
|
131
|
+
assertMaxLength("consolidations", requests.consolidations.length, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD);
|
|
132
|
+
// New in GLOAS:EIP8282
|
|
133
|
+
assertMaxLength("builderDeposits", requests.builderDeposits.length, MAX_BUILDER_DEPOSIT_REQUESTS_PER_PAYLOAD);
|
|
134
|
+
// New in GLOAS:EIP8282
|
|
135
|
+
assertMaxLength("builderExits", requests.builderExits.length, MAX_BUILDER_EXIT_REQUESTS_PER_PAYLOAD);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function assertMaxLength(name: string, length: number, limit: number): void {
|
|
139
|
+
if (length > limit) {
|
|
140
|
+
throw new Error(`Too many parent execution request ${name} count=${length} limit=${limit}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
119
144
|
function assertEmptyExecutionRequests(requests: gloas.ExecutionRequests): void {
|
|
120
145
|
if (
|
|
121
146
|
requests.deposits.length !== 0 ||
|
|
@@ -99,7 +99,7 @@ export function processWithdrawals(
|
|
|
99
99
|
const stateGloas = state as CachedBeaconStateGloas;
|
|
100
100
|
|
|
101
101
|
// Store expected withdrawals for verification
|
|
102
|
-
stateGloas.payloadExpectedWithdrawals = ssz.
|
|
102
|
+
stateGloas.payloadExpectedWithdrawals = ssz.gloas.Withdrawals.toViewDU(expectedWithdrawals);
|
|
103
103
|
|
|
104
104
|
// Update builder pending withdrawals queue
|
|
105
105
|
stateGloas.builderPendingWithdrawals = stateGloas.builderPendingWithdrawals.sliceFrom(
|
package/src/cache/stateCache.ts
CHANGED
|
@@ -254,11 +254,9 @@ export function isCachedBeaconState<T extends BeaconStateAllForks>(
|
|
|
254
254
|
// This cache is populated during epoch transition, and should be preserved for performance.
|
|
255
255
|
// If the cache is missing too often, means that our clone strategy is not working well.
|
|
256
256
|
export function isStateValidatorsNodesPopulated(state: CachedBeaconStateAllForks): boolean {
|
|
257
|
-
|
|
258
|
-
return state.validators["nodesPopulated"] === true;
|
|
257
|
+
return (state.validators as unknown as {nodesPopulated?: boolean}).nodesPopulated === true;
|
|
259
258
|
}
|
|
260
259
|
|
|
261
260
|
export function isStateBalancesNodesPopulated(state: CachedBeaconStateAllForks): boolean {
|
|
262
|
-
|
|
263
|
-
return state.balances["nodesPopulated"] === true;
|
|
261
|
+
return (state.balances as unknown as {nodesPopulated?: boolean}).nodesPopulated === true;
|
|
264
262
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {zeroNode} from "@chainsafe/persistent-merkle-tree";
|
|
2
2
|
import {ssz} from "@lodestar/types";
|
|
3
|
-
import {
|
|
3
|
+
import type {BeaconStateAltair, BeaconStateGloas} from "../types.js";
|
|
4
|
+
import {isGloasStateType} from "../util/execution.js";
|
|
5
|
+
import {zeroProgressiveListBasicRootNode} from "../util/ssz.js";
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Updates `state.previousEpochParticipation` with precalculated epoch participation. Creates a new empty tree for
|
|
@@ -9,7 +11,12 @@ import {CachedBeaconStateAltair} from "../types.js";
|
|
|
9
11
|
* PERF: Cost = 'proportional' $VALIDATOR_COUNT. Since it updates all of them at once, it will always recreate both
|
|
10
12
|
* trees completely.
|
|
11
13
|
*/
|
|
12
|
-
export function processParticipationFlagUpdates(state:
|
|
14
|
+
export function processParticipationFlagUpdates(state: BeaconStateAltair | BeaconStateGloas): void {
|
|
15
|
+
if (isGloasStateType(state)) {
|
|
16
|
+
processParticipationFlagUpdatesGloas(state);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
13
20
|
// Set view and tree from currentEpochParticipation to previousEpochParticipation
|
|
14
21
|
state.previousEpochParticipation = state.currentEpochParticipation;
|
|
15
22
|
|
|
@@ -25,3 +32,14 @@ export function processParticipationFlagUpdates(state: CachedBeaconStateAltair):
|
|
|
25
32
|
|
|
26
33
|
state.currentEpochParticipation = ssz.altair.EpochParticipation.getViewDU(currentEpochParticipationNode);
|
|
27
34
|
}
|
|
35
|
+
|
|
36
|
+
function processParticipationFlagUpdatesGloas(state: BeaconStateGloas): void {
|
|
37
|
+
state.previousEpochParticipation = state.currentEpochParticipation;
|
|
38
|
+
|
|
39
|
+
// Same trick as the altair path above, adapted to the progressive-list tree shape: all chunks
|
|
40
|
+
// are zero so the chunks tree is a chain of pre-computed zeroNodes, built in O(log n) instead
|
|
41
|
+
// of re-merkleizing a validator-count-sized array every epoch.
|
|
42
|
+
state.currentEpochParticipation = ssz.gloas.EpochParticipation.getViewDU(
|
|
43
|
+
zeroProgressiveListBasicRootNode(ssz.gloas.EpochParticipation.itemsPerChunk, state.currentEpochParticipation.length)
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {ForkSeq, GENESIS_EPOCH} from "@lodestar/params";
|
|
2
|
-
import {ssz} from "@lodestar/types";
|
|
3
2
|
import {
|
|
4
3
|
CachedBeaconStateAllForks,
|
|
5
4
|
CachedBeaconStateAltair,
|
|
@@ -39,7 +38,7 @@ export function processRewardsAndPenalties(
|
|
|
39
38
|
|
|
40
39
|
// important: do not change state one balance at a time. Set them all at once, constructing the tree in one go
|
|
41
40
|
// cache the balances array, too
|
|
42
|
-
state.balances =
|
|
41
|
+
state.balances = state.type.fields.balances.toViewDU(balances);
|
|
43
42
|
|
|
44
43
|
// For processEffectiveBalanceUpdates() to prevent having to re-compute the balances array.
|
|
45
44
|
// For validator metrics
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {Tree} from "@chainsafe/persistent-merkle-tree";
|
|
2
2
|
import {
|
|
3
3
|
BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX,
|
|
4
|
+
CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS,
|
|
4
5
|
FINALIZED_ROOT_GINDEX,
|
|
5
6
|
FINALIZED_ROOT_GINDEX_ELECTRA,
|
|
7
|
+
FINALIZED_ROOT_GINDEX_GLOAS,
|
|
6
8
|
ForkName,
|
|
7
9
|
ForkPostBellatrix,
|
|
10
|
+
NEXT_SYNC_COMMITTEE_GINDEX_GLOAS,
|
|
8
11
|
isForkPostElectra,
|
|
12
|
+
isForkPostGloas,
|
|
9
13
|
} from "@lodestar/params";
|
|
10
14
|
import {BeaconBlockBody, SSZTypesFor, ssz} from "@lodestar/types";
|
|
11
15
|
import {BeaconStateAllForks, CachedBeaconStateAllForks} from "../types.js";
|
|
@@ -17,6 +21,24 @@ export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllFo
|
|
|
17
21
|
let currentSyncCommitteeRoot: Uint8Array;
|
|
18
22
|
let nextSyncCommitteeRoot: Uint8Array;
|
|
19
23
|
|
|
24
|
+
if (isForkPostGloas(fork)) {
|
|
25
|
+
const tree = new Tree(state.node);
|
|
26
|
+
const currentSyncCommitteeGindex = BigInt(CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS);
|
|
27
|
+
const nextSyncCommitteeGindex = BigInt(NEXT_SYNC_COMMITTEE_GINDEX_GLOAS);
|
|
28
|
+
|
|
29
|
+
currentSyncCommitteeRoot = tree.getRoot(currentSyncCommitteeGindex);
|
|
30
|
+
nextSyncCommitteeRoot = tree.getRoot(nextSyncCommitteeGindex);
|
|
31
|
+
witness = [];
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
witness,
|
|
35
|
+
currentSyncCommitteeRoot,
|
|
36
|
+
nextSyncCommitteeRoot,
|
|
37
|
+
currentSyncCommitteeBranch: tree.getSingleProof(currentSyncCommitteeGindex),
|
|
38
|
+
nextSyncCommitteeBranch: tree.getSingleProof(nextSyncCommitteeGindex),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
20
42
|
if (isForkPostElectra(fork)) {
|
|
21
43
|
const n2 = n1.left;
|
|
22
44
|
const n5 = n2.right;
|
|
@@ -60,17 +82,30 @@ export function getSyncCommitteesWitness(fork: ForkName, state: BeaconStateAllFo
|
|
|
60
82
|
}
|
|
61
83
|
|
|
62
84
|
export function getNextSyncCommitteeBranch(syncCommitteesWitness: SyncCommitteeWitness): Uint8Array[] {
|
|
85
|
+
if (syncCommitteesWitness.nextSyncCommitteeBranch) {
|
|
86
|
+
return syncCommitteesWitness.nextSyncCommitteeBranch;
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
// Witness branch is sorted by descending gindex
|
|
64
90
|
return [syncCommitteesWitness.currentSyncCommitteeRoot, ...syncCommitteesWitness.witness];
|
|
65
91
|
}
|
|
66
92
|
|
|
67
93
|
export function getCurrentSyncCommitteeBranch(syncCommitteesWitness: SyncCommitteeWitness): Uint8Array[] {
|
|
94
|
+
if (syncCommitteesWitness.currentSyncCommitteeBranch) {
|
|
95
|
+
return syncCommitteesWitness.currentSyncCommitteeBranch;
|
|
96
|
+
}
|
|
97
|
+
|
|
68
98
|
// Witness branch is sorted by descending gindex
|
|
69
99
|
return [syncCommitteesWitness.nextSyncCommitteeRoot, ...syncCommitteesWitness.witness];
|
|
70
100
|
}
|
|
71
101
|
|
|
72
102
|
export function getFinalizedRootProof(state: CachedBeaconStateAllForks): Uint8Array[] {
|
|
73
|
-
const
|
|
103
|
+
const fork = state.config.getForkName(state.slot);
|
|
104
|
+
const finalizedRootGindex = isForkPostGloas(fork)
|
|
105
|
+
? FINALIZED_ROOT_GINDEX_GLOAS
|
|
106
|
+
: state.epochCtx.isPostElectra()
|
|
107
|
+
? FINALIZED_ROOT_GINDEX_ELECTRA
|
|
108
|
+
: FINALIZED_ROOT_GINDEX;
|
|
74
109
|
return new Tree(state.node).getSingleProof(BigInt(finalizedRootGindex));
|
|
75
110
|
}
|
|
76
111
|
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
processLightClientUpdate,
|
|
15
15
|
} from "./processLightClientUpdate.js";
|
|
16
16
|
import {type ILightClientStore, LightClientStore, type LightClientStoreEvents} from "./store.js";
|
|
17
|
-
import {
|
|
17
|
+
import {ZERO_SYNC_COMMITTEE, getZeroFinalityBranch, getZeroSyncCommitteeBranch} from "./utils.js";
|
|
18
18
|
|
|
19
19
|
export type {LightClientUpdateSummary} from "./isBetterUpdate.js";
|
|
20
20
|
export {isBetterUpdate, toLightClientUpdateSummary} from "./isBetterUpdate.js";
|
|
@@ -76,12 +76,16 @@ export class LightclientSpec {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
onOptimisticUpdate(currentSlot: Slot, optimisticUpdate: LightClientOptimisticUpdate): void {
|
|
79
|
+
const fork = this.config.getForkName(optimisticUpdate.signatureSlot);
|
|
80
|
+
|
|
79
81
|
this.onUpdate(currentSlot, {
|
|
80
82
|
attestedHeader: optimisticUpdate.attestedHeader,
|
|
81
83
|
nextSyncCommittee: ZERO_SYNC_COMMITTEE,
|
|
82
|
-
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(
|
|
83
|
-
finalizedHeader:
|
|
84
|
-
|
|
84
|
+
nextSyncCommitteeBranch: getZeroSyncCommitteeBranch(fork),
|
|
85
|
+
finalizedHeader: this.config
|
|
86
|
+
.getPostAltairForkTypes(optimisticUpdate.signatureSlot)
|
|
87
|
+
.LightClientHeader.defaultValue(),
|
|
88
|
+
finalityBranch: getZeroFinalityBranch(fork),
|
|
85
89
|
syncAggregate: optimisticUpdate.syncAggregate,
|
|
86
90
|
signatureSlot: optimisticUpdate.signatureSlot,
|
|
87
91
|
});
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {Tree} from "@chainsafe/persistent-merkle-tree";
|
|
2
3
|
import {BitArray} from "@chainsafe/ssz";
|
|
3
4
|
import {ChainForkConfig} from "@lodestar/config";
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX,
|
|
7
|
+
CURRENT_SYNC_COMMITTEE_GINDEX,
|
|
8
|
+
CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA,
|
|
9
|
+
CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS,
|
|
10
|
+
EXECUTION_BLOCK_HASH_GINDEX,
|
|
11
|
+
EXECUTION_BLOCK_HASH_GINDEX_DENEB,
|
|
12
|
+
EXECUTION_BLOCK_HASH_GINDEX_GLOAS,
|
|
13
|
+
FINALIZED_ROOT_GINDEX,
|
|
14
|
+
FINALIZED_ROOT_GINDEX_ELECTRA,
|
|
15
|
+
FINALIZED_ROOT_GINDEX_GLOAS,
|
|
9
16
|
ForkName,
|
|
10
17
|
ForkSeq,
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
NEXT_SYNC_COMMITTEE_GINDEX,
|
|
19
|
+
NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA,
|
|
20
|
+
NEXT_SYNC_COMMITTEE_GINDEX_GLOAS,
|
|
13
21
|
isForkPostElectra,
|
|
22
|
+
isForkPostGloas,
|
|
14
23
|
} from "@lodestar/params";
|
|
15
24
|
import {
|
|
16
25
|
BeaconBlockHeader,
|
|
@@ -74,17 +83,11 @@ export function getSafetyThreshold(maxActiveParticipants: number): number {
|
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
export function getZeroSyncCommitteeBranch(fork: ForkName): Uint8Array[] {
|
|
77
|
-
|
|
78
|
-
? NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA
|
|
79
|
-
: NEXT_SYNC_COMMITTEE_DEPTH;
|
|
80
|
-
|
|
81
|
-
return Array.from({length: nextSyncCommitteeDepth}, () => ZERO_HASH);
|
|
86
|
+
return Array.from({length: getGindexDepth(nextSyncCommitteeGindexAtFork(fork))}, () => ZERO_HASH);
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
export function getZeroFinalityBranch(fork: ForkName): Uint8Array[] {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return Array.from({length: finalizedRootDepth}, () => ZERO_HASH);
|
|
90
|
+
return Array.from({length: getGindexDepth(finalizedRootGindexAtFork(fork))}, () => ZERO_HASH);
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
export function isSyncCommitteeUpdate(update: LightClientUpdate): boolean {
|
|
@@ -129,12 +132,73 @@ export function isValidMerkleBranch(
|
|
|
129
132
|
return verifyMerkleBranch(leaf, branch, depth, index, root);
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
export function
|
|
135
|
+
export function isValidNormalizedMerkleBranch(
|
|
136
|
+
leaf: Uint8Array,
|
|
137
|
+
branch: Uint8Array[],
|
|
138
|
+
gindex: number,
|
|
139
|
+
root: Uint8Array
|
|
140
|
+
): boolean {
|
|
141
|
+
const depth = getGindexDepth(gindex);
|
|
142
|
+
const index = getGindexIndex(gindex);
|
|
143
|
+
const numExtraDepth = branch.length - depth;
|
|
144
|
+
if (numExtraDepth < 0) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (let i = 0; i < numExtraDepth; i++) {
|
|
149
|
+
if (!byteArrayEquals(branch[i], ZERO_HASH)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return isValidMerkleBranch(leaf, branch.slice(numExtraDepth), depth, index, root);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function normalizeMerkleBranch(branch: Uint8Array[], gindex: number): Uint8Array[] {
|
|
158
|
+
const depth = getGindexDepth(gindex);
|
|
133
159
|
const numExtraDepth = depth - branch.length;
|
|
134
160
|
|
|
135
161
|
return [...Array.from({length: numExtraDepth}, () => ZERO_HASH), ...branch];
|
|
136
162
|
}
|
|
137
163
|
|
|
164
|
+
export function currentSyncCommitteeGindexAtFork(fork: ForkName): number {
|
|
165
|
+
if (isForkPostGloas(fork)) {
|
|
166
|
+
return CURRENT_SYNC_COMMITTEE_GINDEX_GLOAS;
|
|
167
|
+
}
|
|
168
|
+
if (isForkPostElectra(fork)) {
|
|
169
|
+
return CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA;
|
|
170
|
+
}
|
|
171
|
+
return CURRENT_SYNC_COMMITTEE_GINDEX;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function finalizedRootGindexAtFork(fork: ForkName): number {
|
|
175
|
+
if (isForkPostGloas(fork)) {
|
|
176
|
+
return FINALIZED_ROOT_GINDEX_GLOAS;
|
|
177
|
+
}
|
|
178
|
+
if (isForkPostElectra(fork)) {
|
|
179
|
+
return FINALIZED_ROOT_GINDEX_ELECTRA;
|
|
180
|
+
}
|
|
181
|
+
return FINALIZED_ROOT_GINDEX;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function nextSyncCommitteeGindexAtFork(fork: ForkName): number {
|
|
185
|
+
if (isForkPostGloas(fork)) {
|
|
186
|
+
return NEXT_SYNC_COMMITTEE_GINDEX_GLOAS;
|
|
187
|
+
}
|
|
188
|
+
if (isForkPostElectra(fork)) {
|
|
189
|
+
return NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA;
|
|
190
|
+
}
|
|
191
|
+
return NEXT_SYNC_COMMITTEE_GINDEX;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function getGindexDepth(gindex: number): number {
|
|
195
|
+
return Math.floor(Math.log2(gindex));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function getGindexIndex(gindex: number): number {
|
|
199
|
+
return gindex - 2 ** getGindexDepth(gindex);
|
|
200
|
+
}
|
|
201
|
+
|
|
138
202
|
export function upgradeLightClientHeader(
|
|
139
203
|
config: ChainForkConfig,
|
|
140
204
|
targetFork: ForkName,
|
|
@@ -147,7 +211,7 @@ export function upgradeLightClientHeader(
|
|
|
147
211
|
|
|
148
212
|
// We are modifying the same header object, may be we could create a copy, but its
|
|
149
213
|
// not required as of now
|
|
150
|
-
|
|
214
|
+
let upgradedHeader = header;
|
|
151
215
|
const startUpgradeFromFork = Object.values(ForkName)[ForkSeq[headerFork] + 1];
|
|
152
216
|
|
|
153
217
|
switch (startUpgradeFromFork) {
|
|
@@ -198,7 +262,11 @@ export function upgradeLightClientHeader(
|
|
|
198
262
|
if (ForkSeq[targetFork] <= ForkSeq.fulu) break;
|
|
199
263
|
|
|
200
264
|
case ForkName.gloas:
|
|
201
|
-
|
|
265
|
+
if (isGloasLightClientHeader(upgradedHeader)) {
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
upgradedHeader = upgradeLightClientHeaderToGloas(config, upgradedHeader as LightClientHeader<ForkName.electra>);
|
|
202
270
|
|
|
203
271
|
// Break if no further upgrades is required else fall through
|
|
204
272
|
if (ForkSeq[targetFork] <= ForkSeq.gloas) break;
|
|
@@ -209,6 +277,40 @@ export function upgradeLightClientHeader(
|
|
|
209
277
|
export function isValidLightClientHeader(config: ChainForkConfig, header: LightClientHeader): boolean {
|
|
210
278
|
const epoch = computeEpochAtSlot(header.beacon.slot);
|
|
211
279
|
|
|
280
|
+
if (isGloasLightClientHeader(header)) {
|
|
281
|
+
if (epoch >= config.GLOAS_FORK_EPOCH) {
|
|
282
|
+
return isValidNormalizedMerkleBranch(
|
|
283
|
+
header.executionBlockHash,
|
|
284
|
+
header.executionBranch,
|
|
285
|
+
EXECUTION_BLOCK_HASH_GINDEX_GLOAS,
|
|
286
|
+
header.beacon.bodyRoot
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (epoch >= config.DENEB_FORK_EPOCH) {
|
|
291
|
+
return isValidNormalizedMerkleBranch(
|
|
292
|
+
header.executionBlockHash,
|
|
293
|
+
header.executionBranch,
|
|
294
|
+
EXECUTION_BLOCK_HASH_GINDEX_DENEB,
|
|
295
|
+
header.beacon.bodyRoot
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (epoch >= config.CAPELLA_FORK_EPOCH) {
|
|
300
|
+
return isValidNormalizedMerkleBranch(
|
|
301
|
+
header.executionBlockHash,
|
|
302
|
+
header.executionBranch,
|
|
303
|
+
EXECUTION_BLOCK_HASH_GINDEX,
|
|
304
|
+
header.beacon.bodyRoot
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
byteArrayEquals(header.executionBlockHash, ZERO_HASH) &&
|
|
310
|
+
header.executionBranch.every((node) => byteArrayEquals(node, ZERO_HASH))
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
212
314
|
if (epoch < config.CAPELLA_FORK_EPOCH) {
|
|
213
315
|
return (
|
|
214
316
|
((header as LightClientHeader<ForkName.capella>).execution === undefined ||
|
|
@@ -239,8 +341,8 @@ export function isValidLightClientHeader(config: ChainForkConfig, header: LightC
|
|
|
239
341
|
.getPostBellatrixForkTypes(header.beacon.slot)
|
|
240
342
|
.ExecutionPayloadHeader.hashTreeRoot((header as LightClientHeader<ForkName.capella>).execution),
|
|
241
343
|
(header as LightClientHeader<ForkName.capella>).executionBranch,
|
|
242
|
-
|
|
243
|
-
|
|
344
|
+
getGindexDepth(BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX),
|
|
345
|
+
getGindexIndex(BLOCK_BODY_EXECUTION_PAYLOAD_GINDEX),
|
|
244
346
|
header.beacon.bodyRoot
|
|
245
347
|
);
|
|
246
348
|
}
|
|
@@ -254,12 +356,9 @@ export function upgradeLightClientUpdate(
|
|
|
254
356
|
update.finalizedHeader = upgradeLightClientHeader(config, targetFork, update.finalizedHeader);
|
|
255
357
|
update.nextSyncCommitteeBranch = normalizeMerkleBranch(
|
|
256
358
|
update.nextSyncCommitteeBranch,
|
|
257
|
-
|
|
258
|
-
);
|
|
259
|
-
update.finalityBranch = normalizeMerkleBranch(
|
|
260
|
-
update.finalityBranch,
|
|
261
|
-
isForkPostElectra(targetFork) ? FINALIZED_ROOT_DEPTH_ELECTRA : FINALIZED_ROOT_DEPTH
|
|
359
|
+
nextSyncCommitteeGindexAtFork(targetFork)
|
|
262
360
|
);
|
|
361
|
+
update.finalityBranch = normalizeMerkleBranch(update.finalityBranch, finalizedRootGindexAtFork(targetFork));
|
|
263
362
|
|
|
264
363
|
return update;
|
|
265
364
|
}
|
|
@@ -273,7 +372,7 @@ export function upgradeLightClientFinalityUpdate(
|
|
|
273
372
|
finalityUpdate.finalizedHeader = upgradeLightClientHeader(config, targetFork, finalityUpdate.finalizedHeader);
|
|
274
373
|
finalityUpdate.finalityBranch = normalizeMerkleBranch(
|
|
275
374
|
finalityUpdate.finalityBranch,
|
|
276
|
-
|
|
375
|
+
finalizedRootGindexAtFork(targetFork)
|
|
277
376
|
);
|
|
278
377
|
|
|
279
378
|
return finalityUpdate;
|
|
@@ -315,3 +414,72 @@ export function upgradeLightClientStore(
|
|
|
315
414
|
|
|
316
415
|
return store;
|
|
317
416
|
}
|
|
417
|
+
|
|
418
|
+
function isGloasLightClientHeader(header: LightClientHeader): header is LightClientHeader<ForkName.gloas> {
|
|
419
|
+
return (header as LightClientHeader<ForkName.gloas>).executionBlockHash !== undefined;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function upgradeLightClientHeaderToGloas(
|
|
423
|
+
config: ChainForkConfig,
|
|
424
|
+
pre: LightClientHeader<ForkName.electra>
|
|
425
|
+
): LightClientHeader<ForkName.gloas> {
|
|
426
|
+
if (ssz.electra.LightClientHeader.equals(pre, ssz.electra.LightClientHeader.defaultValue())) {
|
|
427
|
+
return ssz.gloas.LightClientHeader.defaultValue();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const epoch = computeEpochAtSlot(pre.beacon.slot);
|
|
431
|
+
|
|
432
|
+
if (epoch >= config.DENEB_FORK_EPOCH) {
|
|
433
|
+
const blockHashGindex = ssz.deneb.ExecutionPayloadHeader.getPathInfo(["blockHash"]).gindex;
|
|
434
|
+
const executionBranch = new Tree(ssz.deneb.ExecutionPayloadHeader.toView(pre.execution).node).getSingleProof(
|
|
435
|
+
blockHashGindex
|
|
436
|
+
);
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
beacon: pre.beacon,
|
|
440
|
+
executionBlockHash: pre.execution.blockHash,
|
|
441
|
+
executionBranch: normalizeMerkleBranch(
|
|
442
|
+
[...executionBranch, ...pre.executionBranch],
|
|
443
|
+
EXECUTION_BLOCK_HASH_GINDEX_GLOAS
|
|
444
|
+
),
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (epoch >= config.CAPELLA_FORK_EPOCH) {
|
|
449
|
+
const executionHeader = {
|
|
450
|
+
parentHash: pre.execution.parentHash,
|
|
451
|
+
feeRecipient: pre.execution.feeRecipient,
|
|
452
|
+
stateRoot: pre.execution.stateRoot,
|
|
453
|
+
receiptsRoot: pre.execution.receiptsRoot,
|
|
454
|
+
logsBloom: pre.execution.logsBloom,
|
|
455
|
+
prevRandao: pre.execution.prevRandao,
|
|
456
|
+
blockNumber: pre.execution.blockNumber,
|
|
457
|
+
gasLimit: pre.execution.gasLimit,
|
|
458
|
+
gasUsed: pre.execution.gasUsed,
|
|
459
|
+
timestamp: pre.execution.timestamp,
|
|
460
|
+
extraData: pre.execution.extraData,
|
|
461
|
+
baseFeePerGas: pre.execution.baseFeePerGas,
|
|
462
|
+
blockHash: pre.execution.blockHash,
|
|
463
|
+
transactionsRoot: pre.execution.transactionsRoot,
|
|
464
|
+
withdrawalsRoot: pre.execution.withdrawalsRoot,
|
|
465
|
+
};
|
|
466
|
+
const blockHashGindex = ssz.capella.ExecutionPayloadHeader.getPathInfo(["blockHash"]).gindex;
|
|
467
|
+
const executionBranch = new Tree(ssz.capella.ExecutionPayloadHeader.toView(executionHeader).node).getSingleProof(
|
|
468
|
+
blockHashGindex
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
return {
|
|
472
|
+
beacon: pre.beacon,
|
|
473
|
+
executionBlockHash: executionHeader.blockHash,
|
|
474
|
+
executionBranch: normalizeMerkleBranch(
|
|
475
|
+
[...executionBranch, ...pre.executionBranch],
|
|
476
|
+
EXECUTION_BLOCK_HASH_GINDEX_GLOAS
|
|
477
|
+
),
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
...ssz.gloas.LightClientHeader.defaultValue(),
|
|
483
|
+
beacon: pre.beacon,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {isForkPostElectra} from "@lodestar/params";
|
|
3
2
|
import {LightClientBootstrap, Root, ssz} from "@lodestar/types";
|
|
4
3
|
import {byteArrayEquals, toHex} from "@lodestar/utils";
|
|
5
|
-
import {isValidLightClientHeader,
|
|
6
|
-
|
|
7
|
-
const CURRENT_SYNC_COMMITTEE_INDEX = 22;
|
|
8
|
-
const CURRENT_SYNC_COMMITTEE_DEPTH = 5;
|
|
9
|
-
const CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA = 22;
|
|
10
|
-
const CURRENT_SYNC_COMMITTEE_DEPTH_ELECTRA = 6;
|
|
4
|
+
import {currentSyncCommitteeGindexAtFork, isValidLightClientHeader, isValidNormalizedMerkleBranch} from "./utils.js";
|
|
11
5
|
|
|
12
6
|
export function validateLightClientBootstrap(
|
|
13
7
|
config: ChainForkConfig,
|
|
@@ -26,11 +20,10 @@ export function validateLightClientBootstrap(
|
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
if (
|
|
29
|
-
!
|
|
23
|
+
!isValidNormalizedMerkleBranch(
|
|
30
24
|
ssz.altair.SyncCommittee.hashTreeRoot(bootstrap.currentSyncCommittee),
|
|
31
25
|
bootstrap.currentSyncCommitteeBranch,
|
|
32
|
-
|
|
33
|
-
isForkPostElectra(fork) ? CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA : CURRENT_SYNC_COMMITTEE_INDEX,
|
|
26
|
+
currentSyncCommitteeGindexAtFork(fork),
|
|
34
27
|
bootstrap.header.beacon.stateRoot
|
|
35
28
|
)
|
|
36
29
|
) {
|
|
@@ -1,29 +1,19 @@
|
|
|
1
1
|
import {PublicKey, Signature, fastAggregateVerify} from "@chainsafe/blst";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
FINALIZED_ROOT_DEPTH,
|
|
6
|
-
FINALIZED_ROOT_DEPTH_ELECTRA,
|
|
7
|
-
FINALIZED_ROOT_INDEX,
|
|
8
|
-
FINALIZED_ROOT_INDEX_ELECTRA,
|
|
9
|
-
GENESIS_SLOT,
|
|
10
|
-
MIN_SYNC_COMMITTEE_PARTICIPANTS,
|
|
11
|
-
NEXT_SYNC_COMMITTEE_DEPTH,
|
|
12
|
-
NEXT_SYNC_COMMITTEE_DEPTH_ELECTRA,
|
|
13
|
-
NEXT_SYNC_COMMITTEE_INDEX,
|
|
14
|
-
NEXT_SYNC_COMMITTEE_INDEX_ELECTRA,
|
|
15
|
-
} from "@lodestar/params";
|
|
16
|
-
import {LightClientUpdate, Root, isElectraLightClientUpdate, ssz} from "@lodestar/types";
|
|
3
|
+
import {DOMAIN_SYNC_COMMITTEE, GENESIS_SLOT, MIN_SYNC_COMMITTEE_PARTICIPANTS} from "@lodestar/params";
|
|
4
|
+
import {LightClientUpdate, Root, ssz} from "@lodestar/types";
|
|
17
5
|
import type {ILightClientStore, SyncCommitteeFast} from "./store.js";
|
|
18
6
|
import {
|
|
19
7
|
ZERO_HASH,
|
|
8
|
+
finalizedRootGindexAtFork,
|
|
20
9
|
getParticipantPubkeys,
|
|
21
10
|
isFinalityUpdate,
|
|
22
11
|
isSyncCommitteeUpdate,
|
|
23
12
|
isValidLightClientHeader,
|
|
24
|
-
|
|
13
|
+
isValidNormalizedMerkleBranch,
|
|
25
14
|
isZeroedHeader,
|
|
26
15
|
isZeroedSyncCommittee,
|
|
16
|
+
nextSyncCommitteeGindexAtFork,
|
|
27
17
|
sumBits,
|
|
28
18
|
} from "./utils.js";
|
|
29
19
|
|
|
@@ -33,6 +23,8 @@ export function validateLightClientUpdate(
|
|
|
33
23
|
update: LightClientUpdate,
|
|
34
24
|
syncCommittee: SyncCommitteeFast
|
|
35
25
|
): void {
|
|
26
|
+
const attestedFork = config.getForkName(update.attestedHeader.beacon.slot);
|
|
27
|
+
|
|
36
28
|
// Verify sync committee has sufficient participants
|
|
37
29
|
if (sumBits(update.syncAggregate.syncCommitteeBits) < MIN_SYNC_COMMITTEE_PARTICIPANTS) {
|
|
38
30
|
throw Error("Sync committee has not sufficient participants");
|
|
@@ -78,11 +70,10 @@ export function validateLightClientUpdate(
|
|
|
78
70
|
}
|
|
79
71
|
|
|
80
72
|
if (
|
|
81
|
-
!
|
|
73
|
+
!isValidNormalizedMerkleBranch(
|
|
82
74
|
finalizedRoot,
|
|
83
75
|
update.finalityBranch,
|
|
84
|
-
|
|
85
|
-
isElectraLightClientUpdate(update) ? FINALIZED_ROOT_INDEX_ELECTRA : FINALIZED_ROOT_INDEX,
|
|
76
|
+
finalizedRootGindexAtFork(attestedFork),
|
|
86
77
|
update.attestedHeader.beacon.stateRoot
|
|
87
78
|
)
|
|
88
79
|
) {
|
|
@@ -98,11 +89,10 @@ export function validateLightClientUpdate(
|
|
|
98
89
|
}
|
|
99
90
|
} else {
|
|
100
91
|
if (
|
|
101
|
-
!
|
|
92
|
+
!isValidNormalizedMerkleBranch(
|
|
102
93
|
ssz.altair.SyncCommittee.hashTreeRoot(update.nextSyncCommittee),
|
|
103
94
|
update.nextSyncCommitteeBranch,
|
|
104
|
-
|
|
105
|
-
isElectraLightClientUpdate(update) ? NEXT_SYNC_COMMITTEE_INDEX_ELECTRA : NEXT_SYNC_COMMITTEE_INDEX,
|
|
95
|
+
nextSyncCommitteeGindexAtFork(attestedFork),
|
|
106
96
|
update.attestedHeader.beacon.stateRoot
|
|
107
97
|
)
|
|
108
98
|
) {
|
package/src/lightClient/types.ts
CHANGED
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
* ```
|
|
27
27
|
*/
|
|
28
28
|
export type SyncCommitteeWitness = {
|
|
29
|
-
/**
|
|
29
|
+
/** Shared witness for pre-Gloas forks where current and next sync committees are siblings. */
|
|
30
30
|
witness: Uint8Array[];
|
|
31
31
|
currentSyncCommitteeRoot: Uint8Array;
|
|
32
32
|
nextSyncCommitteeRoot: Uint8Array;
|
|
33
|
+
currentSyncCommitteeBranch?: Uint8Array[];
|
|
34
|
+
nextSyncCommitteeBranch?: Uint8Array[];
|
|
33
35
|
};
|