@lodestar/state-transition 1.45.0 → 1.46.0-dev.4191d8e353
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/processAttestationsAltair.d.ts +4 -1
- package/lib/block/processAttestationsAltair.d.ts.map +1 -1
- package/lib/block/processAttestationsAltair.js +7 -5
- package/lib/block/processAttestationsAltair.js.map +1 -1
- package/lib/block/processBuilderDepositRequest.d.ts.map +1 -1
- package/lib/block/processBuilderDepositRequest.js +13 -7
- package/lib/block/processBuilderDepositRequest.js.map +1 -1
- 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/epochCache.d.ts.map +1 -1
- package/lib/cache/epochCache.js +18 -19
- package/lib/cache/epochCache.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/processLightClientUpdate.d.ts.map +1 -1
- package/lib/lightClient/spec/processLightClientUpdate.js +8 -2
- package/lib/lightClient/spec/processLightClientUpdate.js.map +1 -1
- package/lib/lightClient/spec/utils.d.ts +7 -2
- package/lib/lightClient/spec/utils.d.ts.map +1 -1
- package/lib/lightClient/spec/utils.js +130 -19
- 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/upgradeStateToAltair.js +1 -1
- package/lib/slot/upgradeStateToAltair.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/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/gloas.d.ts +3 -1
- package/lib/util/gloas.d.ts.map +1 -1
- package/lib/util/gloas.js +8 -11
- package/lib/util/gloas.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 +14 -14
- package/src/block/processAttestationsAltair.ts +9 -6
- package/src/block/processBuilderDepositRequest.ts +21 -8
- package/src/block/processOperations.ts +28 -1
- package/src/block/processParentExecutionPayload.ts +26 -1
- package/src/block/processWithdrawals.ts +1 -1
- package/src/cache/epochCache.ts +19 -22
- 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/processLightClientUpdate.ts +13 -1
- package/src/lightClient/spec/utils.ts +200 -30
- 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/upgradeStateToAltair.ts +1 -1
- package/src/slot/upgradeStateToGloas.ts +84 -9
- package/src/util/execution.ts +9 -4
- package/src/util/gloas.ts +9 -14
- package/src/util/index.ts +1 -0
- package/src/util/ssz.ts +42 -0
package/lib/util/ssz.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BranchNode, LeafNode, zeroNode } from "@chainsafe/persistent-merkle-tree";
|
|
2
|
+
import { progressiveSubtreeFillToContents } from "@chainsafe/ssz";
|
|
3
|
+
// TODO: move these utils to @chainsafe/ssz (progressive.ts, next to progressiveSubtreeFillToContents)
|
|
4
|
+
/** Root node (chunks + length mix-in) of a zero-filled ProgressiveListBasicType of `length` items */
|
|
5
|
+
export function zeroProgressiveListBasicRootNode(itemsPerChunk, length) {
|
|
6
|
+
const chunkCount = Math.ceil(length / itemsPerChunk);
|
|
7
|
+
// mirrors ssz's progressiveSubtreeCount (subtree capacities 1, 4, 16, ...)
|
|
8
|
+
let numSubtrees = 0;
|
|
9
|
+
for (let remaining = chunkCount, subtreeLength = 1; remaining > 0; subtreeLength *= 4) {
|
|
10
|
+
remaining -= Math.min(remaining, subtreeLength);
|
|
11
|
+
numSubtrees++;
|
|
12
|
+
}
|
|
13
|
+
return new BranchNode(zeroProgressiveNode(numSubtrees), LeafNode.fromUint32(length));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Root node of a progressive list from its chunk/element nodes + length mix-in.
|
|
17
|
+
* `nodes` are packed 32-byte chunk leaves for basic lists, or element root nodes for composite lists.
|
|
18
|
+
*/
|
|
19
|
+
export function progressiveListRootNode(nodes, length) {
|
|
20
|
+
return new BranchNode(progressiveSubtreeFillToContents(nodes), LeafNode.fromUint32(length));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Return the chunks node of a zero-filled progressive merkle list spanning `numSubtrees`
|
|
24
|
+
* balanced subtrees (chunk capacities 1, 4, 16, ... = 4^i; depths 0, 2, 4, ... = 2i).
|
|
25
|
+
*
|
|
26
|
+
* Not memoized: the zero data is already fully shared via the cached zeroNode(2i) subtrees;
|
|
27
|
+
* only the O(numSubtrees) spine BranchNodes (~10 at 2M validators) are allocated per call.
|
|
28
|
+
*/
|
|
29
|
+
function zeroProgressiveNode(numSubtrees) {
|
|
30
|
+
// chain(k) = B(zeroNode(0), B(zeroNode(2), ... B(zeroNode(2(k-1)), zeroNode(0))))
|
|
31
|
+
let node = zeroNode(0); // terminator
|
|
32
|
+
for (let i = numSubtrees - 1; i >= 0; i--) {
|
|
33
|
+
// BranchNode(left: balanced subtree i, right: rest of chain) — chain grows to the right,
|
|
34
|
+
// same as ssz progressiveSubtreeFillToContents: `root = new BranchNode(subtreeRoots[i], root)`
|
|
35
|
+
node = new BranchNode(zeroNode(2 * i), node);
|
|
36
|
+
}
|
|
37
|
+
return node;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=ssz.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssz.js","sourceRoot":"","sources":["../../src/util/ssz.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,QAAQ,EAAQ,QAAQ,EAAC,MAAM,mCAAmC,CAAC;AACvF,OAAO,EAAC,gCAAgC,EAAC,MAAM,gBAAgB,CAAC;AAEhE,sGAAsG;AAEtG,qGAAqG;AACrG,MAAM,UAAU,gCAAgC,CAAC,aAAqB,EAAE,MAAc,EAAQ;IAC5F,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,CAAC;IACrD,2EAA2E;IAC3E,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,IAAI,SAAS,GAAG,UAAU,EAAE,aAAa,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,aAAa,IAAI,CAAC,EAAE,CAAC;QACtF,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QAChD,WAAW,EAAE,CAAC;IAChB,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,mBAAmB,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,CACtF;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,MAAc,EAAQ;IAC3E,OAAO,IAAI,UAAU,CAAC,gCAAgC,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,CAC7F;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,WAAmB,EAAQ;IACtD,kFAAkF;IAClF,IAAI,IAAI,GAAS,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa;IAC3C,KAAK,IAAI,CAAC,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,yFAAyF;QACzF,+FAA+F;QAC/F,IAAI,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACb"}
|
package/package.json
CHANGED
|
@@ -11,36 +11,36 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/ChainSafe/lodestar/issues"
|
|
13
13
|
},
|
|
14
|
-
"version": "1.
|
|
14
|
+
"version": "1.46.0-dev.4191d8e353",
|
|
15
15
|
"type": "module",
|
|
16
16
|
"exports": {
|
|
17
17
|
".": {
|
|
18
|
-
"
|
|
18
|
+
"typescript": "./src/index.ts",
|
|
19
19
|
"types": "./lib/index.d.ts",
|
|
20
20
|
"import": "./lib/index.js"
|
|
21
21
|
},
|
|
22
22
|
"./block": {
|
|
23
|
-
"
|
|
23
|
+
"typescript": "./src/block/index.ts",
|
|
24
24
|
"types": "./lib/block/index.d.ts",
|
|
25
25
|
"import": "./lib/block/index.js"
|
|
26
26
|
},
|
|
27
27
|
"./epoch": {
|
|
28
|
-
"
|
|
28
|
+
"typescript": "./src/epoch/index.ts",
|
|
29
29
|
"types": "./lib/epoch/index.d.ts",
|
|
30
30
|
"import": "./lib/epoch/index.js"
|
|
31
31
|
},
|
|
32
32
|
"./slot": {
|
|
33
|
-
"
|
|
33
|
+
"typescript": "./src/slot/index.ts",
|
|
34
34
|
"types": "./lib/slot/index.d.ts",
|
|
35
35
|
"import": "./lib/slot/index.js"
|
|
36
36
|
},
|
|
37
37
|
"./light-client": {
|
|
38
|
-
"
|
|
38
|
+
"typescript": "./src/lightClient/spec/index.ts",
|
|
39
39
|
"types": "./lib/lightClient/spec/index.d.ts",
|
|
40
40
|
"import": "./lib/lightClient/spec/index.js"
|
|
41
41
|
},
|
|
42
42
|
"./test-utils": {
|
|
43
|
-
"
|
|
43
|
+
"typescript": "./src/testUtils/index.ts",
|
|
44
44
|
"types": "./lib/testUtils/index.d.ts",
|
|
45
45
|
"import": "./lib/testUtils/index.js"
|
|
46
46
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"lint:fix": "pnpm run lint --write",
|
|
62
62
|
"test": "pnpm test:unit",
|
|
63
63
|
"test:unit": "vitest run --project unit --project unit-minimal",
|
|
64
|
-
"check-readme": "pnpm exec
|
|
64
|
+
"check-readme": "pnpm exec tsx ../../scripts/check_readme.ts"
|
|
65
65
|
},
|
|
66
66
|
"types": "lib/index.d.ts",
|
|
67
67
|
"dependencies": {
|
|
@@ -71,14 +71,14 @@
|
|
|
71
71
|
"@chainsafe/pubkey-index-map": "^3.0.0",
|
|
72
72
|
"@chainsafe/ssz": "^1.6.2",
|
|
73
73
|
"@chainsafe/swap-or-not-shuffle": "^1.2.1",
|
|
74
|
-
"@lodestar/config": "^1.
|
|
75
|
-
"@lodestar/params": "^1.
|
|
76
|
-
"@lodestar/types": "^1.
|
|
77
|
-
"@lodestar/utils": "^1.
|
|
74
|
+
"@lodestar/config": "^1.46.0-dev.4191d8e353",
|
|
75
|
+
"@lodestar/params": "^1.46.0-dev.4191d8e353",
|
|
76
|
+
"@lodestar/types": "^1.46.0-dev.4191d8e353",
|
|
77
|
+
"@lodestar/utils": "^1.46.0-dev.4191d8e353",
|
|
78
78
|
"@vekexasia/bigint-buffer2": "^1.1.1"
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
|
-
"@lodestar/api": "^1.
|
|
81
|
+
"@lodestar/api": "^1.46.0-dev.4191d8e353"
|
|
82
82
|
},
|
|
83
83
|
"keywords": [
|
|
84
84
|
"ethereum",
|
|
@@ -86,5 +86,5 @@
|
|
|
86
86
|
"beacon",
|
|
87
87
|
"blockchain"
|
|
88
88
|
],
|
|
89
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "b89fa86b332f17a1669788af878fbd0d4a1cdbfe"
|
|
90
90
|
}
|
|
@@ -20,7 +20,7 @@ import {BeaconStateTransitionMetrics} from "../metrics.js";
|
|
|
20
20
|
import {getAttestationWithIndicesSignatureSet} from "../signatureSets/indexedAttestation.js";
|
|
21
21
|
import {BeaconStateView} from "../stateView/beaconStateView.js";
|
|
22
22
|
import {CachedBeaconStateAltair, CachedBeaconStateGloas} from "../types.js";
|
|
23
|
-
import {
|
|
23
|
+
import {isAttestationSameSlotRootCache} from "../util/gloas.js";
|
|
24
24
|
import {increaseBalance, verifySignatureSet} from "../util/index.js";
|
|
25
25
|
import {RootCache} from "../util/rootCache.js";
|
|
26
26
|
import {checkpointToStr, isTimelyTarget, validateAttestation} from "./processAttestationPhase0.js";
|
|
@@ -76,7 +76,7 @@ export function processAttestationsAltair(
|
|
|
76
76
|
// Count how much additional weight added to current or previous epoch's builder pending payment (in ETH increment)
|
|
77
77
|
let paymentWeightToAdd = 0;
|
|
78
78
|
|
|
79
|
-
const flagsAttestation = getAttestationParticipationStatus(
|
|
79
|
+
const {flags: flagsAttestation, isSameSlotAttestation} = getAttestationParticipationStatus(
|
|
80
80
|
fork,
|
|
81
81
|
data,
|
|
82
82
|
stateSlot - data.slot,
|
|
@@ -132,7 +132,7 @@ export function processAttestationsAltair(
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
if (
|
|
135
|
+
if (isSameSlotAttestation && flagsNewSet !== 0) {
|
|
136
136
|
paymentWeightToAdd += effectiveBalanceIncrements[validatorIndex];
|
|
137
137
|
}
|
|
138
138
|
}
|
|
@@ -180,7 +180,7 @@ export function getAttestationParticipationStatus(
|
|
|
180
180
|
currentEpoch: Epoch,
|
|
181
181
|
rootCache: RootCache,
|
|
182
182
|
executionPayloadAvailability: BitArray | null
|
|
183
|
-
): number {
|
|
183
|
+
): {flags: number; isSameSlotAttestation: boolean} {
|
|
184
184
|
const justifiedCheckpoint =
|
|
185
185
|
data.target.epoch === currentEpoch ? rootCache.currentJustifiedCheckpoint : rootCache.previousJustifiedCheckpoint;
|
|
186
186
|
|
|
@@ -206,10 +206,13 @@ export function getAttestationParticipationStatus(
|
|
|
206
206
|
let isMatchingHead =
|
|
207
207
|
isMatchingTarget && byteArrayEquals(data.beaconBlockRoot, rootCache.getBlockRootAtSlot(data.slot));
|
|
208
208
|
|
|
209
|
+
let isSameSlotAttestation = false;
|
|
210
|
+
|
|
209
211
|
if (fork >= ForkSeq.gloas) {
|
|
210
212
|
let isMatchingPayload = false;
|
|
213
|
+
isSameSlotAttestation = isAttestationSameSlotRootCache(rootCache, data);
|
|
211
214
|
|
|
212
|
-
if (
|
|
215
|
+
if (isSameSlotAttestation) {
|
|
213
216
|
if (data.index !== 0) {
|
|
214
217
|
throw new Error("Attesting same slot must indicate empty payload");
|
|
215
218
|
}
|
|
@@ -235,7 +238,7 @@ export function getAttestationParticipationStatus(
|
|
|
235
238
|
if (isMatchingTarget && isTimelyTarget(fork, inclusionDelay)) flags |= TIMELY_TARGET;
|
|
236
239
|
if (isMatchingHead && inclusionDelay === MIN_ATTESTATION_INCLUSION_DELAY) flags |= TIMELY_HEAD;
|
|
237
240
|
|
|
238
|
-
return flags;
|
|
241
|
+
return {flags, isSameSlotAttestation};
|
|
239
242
|
}
|
|
240
243
|
|
|
241
244
|
export function checkpointValueEquals(cp1: phase0.Checkpoint, cp2: phase0.Checkpoint): boolean {
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {FAR_FUTURE_EPOCH} from "@lodestar/params";
|
|
1
|
+
import {FAR_FUTURE_EPOCH, PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
|
|
2
2
|
import {gloas} from "@lodestar/types";
|
|
3
3
|
import {CachedBeaconStateGloas} from "../types.js";
|
|
4
4
|
import {computeEpochAtSlot} from "../util/epoch.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
addBuilderToRegistry,
|
|
7
|
+
findBuilderIndexByPubkey,
|
|
8
|
+
isBuilderWithdrawalCredential,
|
|
9
|
+
isValidBuilderDepositSignature,
|
|
10
|
+
} from "../util/gloas.js";
|
|
6
11
|
|
|
7
12
|
/**
|
|
8
13
|
* Process a builder deposit request from the execution layer: register a new builder
|
|
@@ -15,6 +20,12 @@ export function processBuilderDepositRequest(
|
|
|
15
20
|
request: gloas.BuilderDepositRequest
|
|
16
21
|
): void {
|
|
17
22
|
const {pubkey, withdrawalCredentials, amount, signature} = request;
|
|
23
|
+
|
|
24
|
+
// Ignore deposits with unexpected withdrawal credential prefixes.
|
|
25
|
+
if (!isBuilderWithdrawalCredential(withdrawalCredentials)) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
const builderIndex = findBuilderIndexByPubkey(state, pubkey);
|
|
19
30
|
|
|
20
31
|
if (builderIndex === null) {
|
|
@@ -22,7 +33,7 @@ export function processBuilderDepositRequest(
|
|
|
22
33
|
addBuilderToRegistry(
|
|
23
34
|
state,
|
|
24
35
|
pubkey,
|
|
25
|
-
|
|
36
|
+
PAYLOAD_BUILDER_VERSION,
|
|
26
37
|
withdrawalCredentials.subarray(12),
|
|
27
38
|
amount,
|
|
28
39
|
state.slot
|
|
@@ -33,11 +44,13 @@ export function processBuilderDepositRequest(
|
|
|
33
44
|
|
|
34
45
|
const builder = state.builders.get(builderIndex);
|
|
35
46
|
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (builder.withdrawableEpoch !== FAR_FUTURE_EPOCH) {
|
|
47
|
+
// If the builder has exited and been fully swept (balance drained to 0), reset the
|
|
48
|
+
// withdrawable epoch so this top-up becomes withdrawable again. Must run before the
|
|
49
|
+
// balance increase, since the reset is gated on the current balance being 0.
|
|
50
|
+
if (builder.withdrawableEpoch !== FAR_FUTURE_EPOCH && builder.balance === 0) {
|
|
41
51
|
builder.withdrawableEpoch = computeEpochAtSlot(state.slot) + state.config.MIN_BUILDER_WITHDRAWABILITY_DELAY;
|
|
42
52
|
}
|
|
53
|
+
|
|
54
|
+
// Increase balance by deposit amount
|
|
55
|
+
builder.balance += amount;
|
|
43
56
|
}
|
|
@@ -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/epochCache.ts
CHANGED
|
@@ -408,12 +408,17 @@ export class EpochCache {
|
|
|
408
408
|
|
|
409
409
|
const nextShuffling = cachedNextShuffling ?? computeEpochShuffling(state, nextActiveIndices, nextEpoch);
|
|
410
410
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
let proposers: number[];
|
|
411
|
+
let proposers: ValidatorIndex[];
|
|
412
|
+
let proposersNextEpoch: ProposersDeferred;
|
|
414
413
|
if (currentEpoch >= config.FULU_FORK_EPOCH) {
|
|
415
|
-
//
|
|
416
|
-
|
|
414
|
+
// After fulu, use state.proposerLookahead for current and next epoch proposers.
|
|
415
|
+
// Computing from the unfiltered active shuffling would include slashed validators in gloas.
|
|
416
|
+
const proposerLookahead = (state as CachedBeaconStateFulu).proposerLookahead.getAll();
|
|
417
|
+
proposers = proposerLookahead.slice(0, SLOTS_PER_EPOCH);
|
|
418
|
+
proposersNextEpoch = {
|
|
419
|
+
computed: true,
|
|
420
|
+
indexes: proposerLookahead.slice(SLOTS_PER_EPOCH, SLOTS_PER_EPOCH * 2),
|
|
421
|
+
};
|
|
417
422
|
} else {
|
|
418
423
|
// We need to calculate Pre-fulu
|
|
419
424
|
// Allow to create CachedBeaconState for empty states, or no active validators
|
|
@@ -421,18 +426,17 @@ export class EpochCache {
|
|
|
421
426
|
currentShuffling.activeIndices.length > 0
|
|
422
427
|
? computeProposers(
|
|
423
428
|
config.getForkSeqAtEpoch(currentEpoch),
|
|
424
|
-
|
|
429
|
+
getSeed(state, currentEpoch, DOMAIN_BEACON_PROPOSER),
|
|
425
430
|
currentShuffling,
|
|
426
431
|
effectiveBalanceIncrements
|
|
427
432
|
)
|
|
428
433
|
: [];
|
|
434
|
+
proposersNextEpoch = {
|
|
435
|
+
computed: false,
|
|
436
|
+
seed: getSeed(state, nextEpoch, DOMAIN_BEACON_PROPOSER),
|
|
437
|
+
};
|
|
429
438
|
}
|
|
430
439
|
|
|
431
|
-
const proposersNextEpoch: ProposersDeferred = {
|
|
432
|
-
computed: false,
|
|
433
|
-
seed: getSeed(state, nextEpoch, DOMAIN_BEACON_PROPOSER),
|
|
434
|
-
};
|
|
435
|
-
|
|
436
440
|
// Only after altair, compute the indices of the current sync committee
|
|
437
441
|
const afterAltairFork = currentEpoch >= config.ALTAIR_FORK_EPOCH;
|
|
438
442
|
|
|
@@ -713,17 +717,10 @@ export class EpochCache {
|
|
|
713
717
|
// Populate proposer cache with lookahead from state
|
|
714
718
|
const proposerLookahead = (state as CachedBeaconStateFulu).proposerLookahead.getAll();
|
|
715
719
|
this.proposers = proposerLookahead.slice(0, SLOTS_PER_EPOCH);
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
indexes: proposerLookahead.slice(SLOTS_PER_EPOCH, SLOTS_PER_EPOCH * 2),
|
|
721
|
-
};
|
|
722
|
-
} else {
|
|
723
|
-
// This should not happen unless MIN_SEED_LOOKAHEAD is set to 0
|
|
724
|
-
// this ensures things don't break if the proposer lookahead is not long enough
|
|
725
|
-
this.proposersNextEpoch = {computed: false, seed: getSeed(state, epochAfterUpcoming, DOMAIN_BEACON_PROPOSER)};
|
|
726
|
-
}
|
|
720
|
+
this.proposersNextEpoch = {
|
|
721
|
+
computed: true,
|
|
722
|
+
indexes: proposerLookahead.slice(SLOTS_PER_EPOCH, SLOTS_PER_EPOCH * 2),
|
|
723
|
+
};
|
|
727
724
|
} else {
|
|
728
725
|
// Need to calculate proposers pre-fulu
|
|
729
726
|
const upcomingProposerSeed = getSeed(state, upcomingEpoch, DOMAIN_BEACON_PROPOSER);
|
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
|
});
|
|
@@ -5,7 +5,13 @@ import {pruneSetToMax} from "@lodestar/utils";
|
|
|
5
5
|
import {computeSyncPeriodAtSlot} from "../../util/epoch.js";
|
|
6
6
|
import {LightClientUpdateSummary, isBetterUpdate, toLightClientUpdateSummary} from "./isBetterUpdate.js";
|
|
7
7
|
import {type ILightClientStore, MAX_SYNC_PERIODS_CACHE, type SyncCommitteeFast} from "./store.js";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
deserializeSyncCommittee,
|
|
10
|
+
getSafetyThreshold,
|
|
11
|
+
isFinalityUpdate,
|
|
12
|
+
isSyncCommitteeUpdate,
|
|
13
|
+
sumBits,
|
|
14
|
+
} from "./utils.js";
|
|
9
15
|
import {validateLightClientUpdate} from "./validateLightClientUpdate.js";
|
|
10
16
|
|
|
11
17
|
export interface ProcessUpdateOpts {
|
|
@@ -44,7 +50,13 @@ export function processLightClientUpdate(
|
|
|
44
50
|
}
|
|
45
51
|
|
|
46
52
|
// Update finalized header
|
|
53
|
+
// Defense-in-depth: require isFinalityUpdate(update) so a non-finality update can never
|
|
54
|
+
// overwrite store.finalizedHeader, regardless of any future loosening of the zeroed-header
|
|
55
|
+
// checks in validateLightClientUpdate. Spec-faithful behavior also holds without this guard
|
|
56
|
+
// once update.finalizedHeader is guaranteed fully-zero in the non-finality case, but the
|
|
57
|
+
// guard makes the invariant local to this write site.
|
|
47
58
|
if (
|
|
59
|
+
isFinalityUpdate(update) &&
|
|
48
60
|
syncCommitteeTrueBits * 3 >= SYNC_COMMITTEE_SIZE * 2 &&
|
|
49
61
|
update.finalizedHeader.beacon.slot > store.finalizedHeader.beacon.slot
|
|
50
62
|
) {
|