@lodestar/fork-choice 1.47.0-dev.d9c16ea16f → 1.47.0-dev.da8dfeabda
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/forkChoice/forkChoice.d.ts +5 -17
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +14 -24
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +5 -8
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/forkChoice/interface.js +1 -1
- package/lib/forkChoice/interface.js.map +1 -1
- package/lib/forkChoice/safeBlocks.d.ts +2 -2
- package/lib/forkChoice/safeBlocks.js +2 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/protoArray/interface.d.ts +4 -4
- package/lib/protoArray/interface.d.ts.map +1 -1
- package/lib/protoArray/protoArray.d.ts +7 -10
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +51 -37
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +8 -8
- package/src/forkChoice/forkChoice.ts +26 -30
- package/src/forkChoice/interface.ts +3 -6
- package/src/forkChoice/safeBlocks.ts +2 -2
- package/src/index.ts +1 -1
- package/src/protoArray/interface.ts +4 -4
- package/src/protoArray/protoArray.ts +65 -41
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
EFFECTIVE_BALANCE_INCREMENT,
|
|
4
|
+
MIN_SEED_LOOKAHEAD,
|
|
5
|
+
SLOTS_PER_EPOCH,
|
|
6
|
+
isForkPostFulu,
|
|
7
|
+
isForkPostGloas,
|
|
8
|
+
} from "@lodestar/params";
|
|
3
9
|
import {
|
|
4
10
|
DataAvailabilityStatus,
|
|
5
11
|
EffectiveBalanceIncrements,
|
|
@@ -68,6 +74,8 @@ export type ForkChoiceOpts = {
|
|
|
68
74
|
fastConfirmation?: boolean;
|
|
69
75
|
};
|
|
70
76
|
|
|
77
|
+
const EFFECTIVE_BALANCE_INCREMENT_BIGINT = BigInt(EFFECTIVE_BALANCE_INCREMENT);
|
|
78
|
+
|
|
71
79
|
export enum UpdateHeadOpt {
|
|
72
80
|
GetCanonicalHead = "getCanonicalHead", // Skip getProposerHead
|
|
73
81
|
GetProposerHead = "getProposerHead", // With getProposerHead
|
|
@@ -147,7 +155,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
147
155
|
/** Boost the entire branch with this proposer root as the leaf */
|
|
148
156
|
private proposerBoostRoot: RootHex | null = null;
|
|
149
157
|
/** Score to use in proposer boost, evaluated lazily from justified balances */
|
|
150
|
-
private justifiedProposerBoostScore:
|
|
158
|
+
private justifiedProposerBoostScore: bigint | null = null;
|
|
151
159
|
/** The current effective balances */
|
|
152
160
|
private balances: EffectiveBalanceIncrements;
|
|
153
161
|
/** Optional fast confirmation rule implementation */
|
|
@@ -587,7 +595,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
587
595
|
* The structure in line with deltas to propagate boost up the branch
|
|
588
596
|
* starting from the proposerIndex
|
|
589
597
|
*/
|
|
590
|
-
let proposerBoost: {root: RootHex; score:
|
|
598
|
+
let proposerBoost: {root: RootHex; score: bigint} | null = null;
|
|
591
599
|
if (this.opts?.proposerBoost && this.proposerBoostRoot) {
|
|
592
600
|
const proposerBoostScore =
|
|
593
601
|
this.justifiedProposerBoostScore ??
|
|
@@ -626,8 +634,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
626
634
|
return this.protoArray.nodes.filter((node) => node.slot > windowStart).length;
|
|
627
635
|
}
|
|
628
636
|
|
|
629
|
-
|
|
630
|
-
return this.protoArray.
|
|
637
|
+
getCanonicalPayloadCounts(fromSlot: Slot, toSlot: Slot, head: ProtoBlock): {full: number; empty: number} {
|
|
638
|
+
return this.protoArray.getCanonicalPayloadCounts(fromSlot, toSlot, head.blockRoot, head.payloadStatus);
|
|
631
639
|
}
|
|
632
640
|
|
|
633
641
|
/** Very expensive function, iterates the entire ProtoArray. Called only in debug API */
|
|
@@ -635,22 +643,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
635
643
|
return this.protoArray.nodes.filter((node) => node.bestChild === undefined);
|
|
636
644
|
}
|
|
637
645
|
|
|
638
|
-
/**
|
|
639
|
-
|
|
640
|
-
* For compliance test use only
|
|
641
|
-
*/
|
|
642
|
-
getViableHeads(): {root: RootHex; payloadStatus: PayloadStatus; weight: number}[] {
|
|
646
|
+
/** Returns exact Gwei weights for the compliance test. */
|
|
647
|
+
getViableHeads(): {root: RootHex; payloadStatus: PayloadStatus; weight: bigint}[] {
|
|
643
648
|
return this.protoArray.getViableHeads(this.fcStore.currentSlot);
|
|
644
649
|
}
|
|
645
650
|
|
|
646
|
-
/**
|
|
647
|
-
* The cached justified total active balance, in EFFECTIVE_BALANCE_INCREMENT units.
|
|
648
|
-
* For compliance test use only
|
|
649
|
-
*/
|
|
650
|
-
getJustifiedTotalActiveBalanceByIncrement(): number {
|
|
651
|
-
return this.fcStore.justified.totalBalance;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
651
|
/** This is for the debug API only */
|
|
655
652
|
getAllNodes(): ProtoNode[] {
|
|
656
653
|
return this.protoArray.nodes;
|
|
@@ -1636,7 +1633,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
1636
1633
|
// the justified state) - fall back to the head state's effective balance
|
|
1637
1634
|
balance = state.effectiveBalanceIncrements[validatorIndex];
|
|
1638
1635
|
}
|
|
1639
|
-
headWeight += balance;
|
|
1636
|
+
headWeight += BigInt(balance) * EFFECTIVE_BALANCE_INCREMENT_BIGINT;
|
|
1640
1637
|
}
|
|
1641
1638
|
}
|
|
1642
1639
|
}
|
|
@@ -1666,7 +1663,6 @@ export class ForkChoice implements IForkChoice {
|
|
|
1666
1663
|
|
|
1667
1664
|
// pre-gloas uses get_weight() (boost-inclusive), gloas uses get_attestation_score() (boost-excluded)
|
|
1668
1665
|
const parentWeight = isForkPostGloas(this.config.getForkName(node.slot)) ? node.attestationScore : node.weight;
|
|
1669
|
-
|
|
1670
1666
|
return parentWeight > parentThreshold;
|
|
1671
1667
|
}
|
|
1672
1668
|
|
|
@@ -2086,11 +2082,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
2086
2082
|
return {prelimProposerHead, prelimNotReorgedReason: NotReorgedReason.HeadBlockIsTimely};
|
|
2087
2083
|
}
|
|
2088
2084
|
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
if (isAtEpochBoundary) {
|
|
2093
|
-
return {prelimProposerHead, prelimNotReorgedReason: NotReorgedReason.AtEpochBoundary};
|
|
2085
|
+
const isShufflingStable = isForkPostFulu(this.config.getForkName(slot)) || slot % SLOTS_PER_EPOCH !== 0;
|
|
2086
|
+
if (!isShufflingStable) {
|
|
2087
|
+
return {prelimProposerHead, prelimNotReorgedReason: NotReorgedReason.NotShufflingStable};
|
|
2094
2088
|
}
|
|
2095
2089
|
|
|
2096
2090
|
// No reorg if headBlock and parentBlock are not ffg competitive
|
|
@@ -2209,12 +2203,14 @@ export class ForkChoice implements IForkChoice {
|
|
|
2209
2203
|
}
|
|
2210
2204
|
}
|
|
2211
2205
|
|
|
2212
|
-
//
|
|
2206
|
+
// https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/phase0/fork-choice.md#calculate_committee_fraction
|
|
2213
2207
|
// Calculates proposer boost score when committeePercent = config.PROPOSER_SCORE_BOOST
|
|
2214
|
-
|
|
2208
|
+
function getCommitteeFraction(
|
|
2215
2209
|
justifiedTotalActiveBalanceByIncrement: number,
|
|
2216
2210
|
config: {slotsPerEpoch: number; committeePercent: number}
|
|
2217
|
-
):
|
|
2218
|
-
const
|
|
2219
|
-
|
|
2211
|
+
): bigint {
|
|
2212
|
+
const committeeWeightGwei =
|
|
2213
|
+
(BigInt(justifiedTotalActiveBalanceByIncrement) * EFFECTIVE_BALANCE_INCREMENT_BIGINT) /
|
|
2214
|
+
BigInt(config.slotsPerEpoch);
|
|
2215
|
+
return (committeeWeightGwei * BigInt(config.committeePercent)) / 100n;
|
|
2220
2216
|
}
|
|
@@ -54,7 +54,7 @@ export enum NotReorgedReason {
|
|
|
54
54
|
HeadBlockIsTimely = "headBlockIsTimely",
|
|
55
55
|
ParentBlockNotAvailable = "parentBlockNotAvailable",
|
|
56
56
|
ProposerBoostReorgDisabled = "proposerBoostReorgDisabled",
|
|
57
|
-
|
|
57
|
+
NotShufflingStable = "notShufflingStable",
|
|
58
58
|
NotFFGCompetitive = "notFFGCompetitive",
|
|
59
59
|
ChainLongUnfinality = "chainLongUnfinality",
|
|
60
60
|
ParentBlockDistanceMoreThanOneSlot = "parentBlockDistanceMoreThanOneSlot",
|
|
@@ -249,11 +249,8 @@ export interface IForkChoice {
|
|
|
249
249
|
hasPayloadUnsafe(blockRoot: Root): boolean;
|
|
250
250
|
hasPayloadHexUnsafe(blockRoot: RootHex): boolean;
|
|
251
251
|
getSlotsPresent(windowStart: number): number;
|
|
252
|
-
/**
|
|
253
|
-
|
|
254
|
-
* payload (FULL variant exists). Used by the builder circuit breaker.
|
|
255
|
-
*/
|
|
256
|
-
getPayloadRevealCounts(fromSlot: Slot, toSlot: Slot): {blocksPresent: number; payloadsRevealed: number};
|
|
252
|
+
/** Count FULL and EMPTY blocks on the supplied head chain in the inclusive slot range. */
|
|
253
|
+
getCanonicalPayloadCounts(fromSlot: Slot, toSlot: Slot, head: ProtoBlock): {full: number; empty: number};
|
|
257
254
|
getPTCVotes(blockRootHex: RootHex): (boolean | null)[] | null;
|
|
258
255
|
/** Raw PTC vote tallies for the debug fork choice endpoint; `null` for pre-Gloas roots. */
|
|
259
256
|
getPTCVoteCounts(blockRootHex: RootHex): {
|
|
@@ -13,7 +13,7 @@ import {IForkChoice} from "./interface.js";
|
|
|
13
13
|
* payload may not yet be confirmed canonical, so we report the parent EL block which has
|
|
14
14
|
* been (the bid commits to extending it).
|
|
15
15
|
*
|
|
16
|
-
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.
|
|
16
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.14/specs/bellatrix/fast-confirmation.md#new-get_safe_execution_block_hash
|
|
17
17
|
*/
|
|
18
18
|
export function getSafeExecutionBlockHash(forkChoice: IForkChoice, logger?: Pick<Logger, LogLevel.debug>): RootHex {
|
|
19
19
|
const confirmedRoot = forkChoice.getConfirmedRoot();
|
|
@@ -36,7 +36,7 @@ export function getSafeExecutionBlockHash(forkChoice: IForkChoice, logger?: Pick
|
|
|
36
36
|
* Get execution payload hash to report as `finalizedBlockHash` in `engine_forkchoiceUpdated`.
|
|
37
37
|
* Mirrors `getSafeExecutionBlockHash`: post-Gloas returns the bid `parent_block_hash`.
|
|
38
38
|
*
|
|
39
|
-
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.
|
|
39
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.14/specs/gloas/fork-choice.md#notify_forkchoice_updated
|
|
40
40
|
*/
|
|
41
41
|
export function getFinalizedExecutionBlockHash(forkChoice: IForkChoice): RootHex {
|
|
42
42
|
return getExecutionBlockHash(forkChoice.getFinalizedBlock());
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ export {
|
|
|
17
17
|
type IFastConfirmationStore,
|
|
18
18
|
getFastConfirmationMetrics,
|
|
19
19
|
} from "./forkChoice/fastConfirmation/fastConfirmationRule.js";
|
|
20
|
-
export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt
|
|
20
|
+
export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt} from "./forkChoice/forkChoice.js";
|
|
21
21
|
export {
|
|
22
22
|
type AncestorResult,
|
|
23
23
|
AncestorStatus,
|
|
@@ -160,13 +160,13 @@ export type ProtoBlock = BlockExtraMeta & {
|
|
|
160
160
|
*/
|
|
161
161
|
export type ProtoNode = ProtoBlock & {
|
|
162
162
|
parent?: number;
|
|
163
|
-
/** Total weight, ie. attestationScore plus the proposer boost credited to this node */
|
|
164
|
-
weight:
|
|
163
|
+
/** Total weight in Gwei, ie. attestationScore plus the proposer boost credited to this node */
|
|
164
|
+
weight: bigint;
|
|
165
165
|
/**
|
|
166
|
-
* Weight from attester votes only, excluding proposer boost.
|
|
166
|
+
* Weight in Gwei from attester votes only, excluding proposer boost.
|
|
167
167
|
* Spec: get_attestation_score
|
|
168
168
|
*/
|
|
169
|
-
attestationScore:
|
|
169
|
+
attestationScore: bigint;
|
|
170
170
|
bestChild?: number;
|
|
171
171
|
bestDescendant?: number;
|
|
172
172
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {BitArray} from "@chainsafe/ssz";
|
|
2
|
-
import {GENESIS_EPOCH, PTC_SIZE} from "@lodestar/params";
|
|
2
|
+
import {EFFECTIVE_BALANCE_INCREMENT, GENESIS_EPOCH, GENESIS_SLOT, PTC_SIZE} from "@lodestar/params";
|
|
3
3
|
import {DataAvailabilityStatus, computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
4
|
import {Epoch, RootHex, Slot} from "@lodestar/types";
|
|
5
5
|
import {bitCount, toRootHex} from "@lodestar/utils";
|
|
@@ -31,7 +31,8 @@ const DATA_AVAILABILITY_TIMELY_THRESHOLD = Math.floor(PTC_SIZE / 2);
|
|
|
31
31
|
* Proposer boost deltas, back-propagated to the boosted node's ancestors in applyScoreChanges().
|
|
32
32
|
* Reuse the array to avoid memory reallocation and gc, as computeDeltas does for attestation deltas.
|
|
33
33
|
*/
|
|
34
|
-
const boostDeltas = new Array<
|
|
34
|
+
const boostDeltas = new Array<bigint>();
|
|
35
|
+
const EFFECTIVE_BALANCE_INCREMENT_BIGINT = BigInt(EFFECTIVE_BALANCE_INCREMENT);
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* popcount(attended AND NOT yes) — explicit False-vote count.
|
|
@@ -52,7 +53,7 @@ export function countNoVotes(attended: BitArray, yes: BitArray): number {
|
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export const DEFAULT_PRUNE_THRESHOLD = 0;
|
|
55
|
-
type ProposerBoost = {root: RootHex; score:
|
|
56
|
+
type ProposerBoost = {root: RootHex; score: bigint};
|
|
56
57
|
|
|
57
58
|
const ZERO_HASH_HEX = toRootHex(Buffer.alloc(32, 0));
|
|
58
59
|
|
|
@@ -379,7 +380,7 @@ export class ProtoArray {
|
|
|
379
380
|
}
|
|
380
381
|
|
|
381
382
|
boostDeltas.length = this.nodes.length;
|
|
382
|
-
boostDeltas.fill(
|
|
383
|
+
boostDeltas.fill(0n);
|
|
383
384
|
|
|
384
385
|
if (
|
|
385
386
|
justifiedEpoch !== this.justifiedEpoch ||
|
|
@@ -416,17 +417,20 @@ export class ProtoArray {
|
|
|
416
417
|
// the boost neutral with respect to EMPTY vs FULL selection.
|
|
417
418
|
const isBoostVariant = isGloasBlock(node) ? node.payloadStatus === PayloadStatus.PENDING : true; // pre-Gloas has only FULL, always boost
|
|
418
419
|
const currentBoost =
|
|
419
|
-
proposerBoost && proposerBoost.root === node.blockRoot && isBoostVariant ? proposerBoost.score :
|
|
420
|
+
proposerBoost && proposerBoost.root === node.blockRoot && isBoostVariant ? proposerBoost.score : 0n;
|
|
420
421
|
const previousBoost =
|
|
421
422
|
this.previousProposerBoost && this.previousProposerBoost.root === node.blockRoot && isBoostVariant
|
|
422
423
|
? this.previousProposerBoost.score
|
|
423
|
-
:
|
|
424
|
+
: 0n;
|
|
424
425
|
|
|
425
426
|
// If this node's execution status has been marked invalid, then the weight of the node
|
|
426
427
|
// needs to be taken out of consideration after which the node weight will become 0
|
|
427
428
|
// for subsequent iterations of applyScoreChanges
|
|
428
429
|
const isInvalid = node.executionStatus === ExecutionStatus.Invalid;
|
|
429
|
-
const attestationDelta = isInvalid
|
|
430
|
+
const attestationDelta = isInvalid
|
|
431
|
+
? -Number(node.attestationScore / EFFECTIVE_BALANCE_INCREMENT_BIGINT)
|
|
432
|
+
: attestationDeltas[nodeIndex];
|
|
433
|
+
const attestationDeltaGwei = BigInt(attestationDelta) * EFFECTIVE_BALANCE_INCREMENT_BIGINT;
|
|
430
434
|
const boostDelta = isInvalid
|
|
431
435
|
? // old boost = weight - attestationScore
|
|
432
436
|
-(node.weight - node.attestationScore)
|
|
@@ -434,8 +438,8 @@ export class ProtoArray {
|
|
|
434
438
|
|
|
435
439
|
// Apply the deltas to the node. Their sum is the node's total delta, so weight is unaffected
|
|
436
440
|
// by tracking the two scores apart.
|
|
437
|
-
node.attestationScore +=
|
|
438
|
-
node.weight +=
|
|
441
|
+
node.attestationScore += attestationDeltaGwei;
|
|
442
|
+
node.weight += attestationDeltaGwei + boostDelta;
|
|
439
443
|
|
|
440
444
|
// Update the parent deltas (if any)
|
|
441
445
|
const parentIndex = node.parent;
|
|
@@ -527,8 +531,8 @@ export class ProtoArray {
|
|
|
527
531
|
...block,
|
|
528
532
|
parent: parentIndex, // Points to parent's EMPTY/FULL or FULL (for transition)
|
|
529
533
|
payloadStatus: PayloadStatus.PENDING,
|
|
530
|
-
weight:
|
|
531
|
-
attestationScore:
|
|
534
|
+
weight: 0n,
|
|
535
|
+
attestationScore: 0n,
|
|
532
536
|
bestChild: undefined,
|
|
533
537
|
bestDescendant: undefined,
|
|
534
538
|
};
|
|
@@ -541,8 +545,8 @@ export class ProtoArray {
|
|
|
541
545
|
...block,
|
|
542
546
|
parent: pendingIndex, // Points to own PENDING
|
|
543
547
|
payloadStatus: PayloadStatus.EMPTY,
|
|
544
|
-
weight:
|
|
545
|
-
attestationScore:
|
|
548
|
+
weight: 0n,
|
|
549
|
+
attestationScore: 0n,
|
|
546
550
|
bestChild: undefined,
|
|
547
551
|
bestDescendant: undefined,
|
|
548
552
|
};
|
|
@@ -577,8 +581,8 @@ export class ProtoArray {
|
|
|
577
581
|
...block,
|
|
578
582
|
parent: this.getNodeIndexByRootAndStatus(block.parentRoot, PayloadStatus.FULL),
|
|
579
583
|
payloadStatus: PayloadStatus.FULL,
|
|
580
|
-
weight:
|
|
581
|
-
attestationScore:
|
|
584
|
+
weight: 0n,
|
|
585
|
+
attestationScore: 0n,
|
|
582
586
|
bestChild: undefined,
|
|
583
587
|
bestDescendant: undefined,
|
|
584
588
|
};
|
|
@@ -663,8 +667,8 @@ export class ProtoArray {
|
|
|
663
667
|
...pendingNode,
|
|
664
668
|
parent: pendingIndex, // Points to own PENDING (same as EMPTY)
|
|
665
669
|
payloadStatus: PayloadStatus.FULL,
|
|
666
|
-
weight:
|
|
667
|
-
attestationScore:
|
|
670
|
+
weight: 0n,
|
|
671
|
+
attestationScore: 0n,
|
|
668
672
|
bestChild: undefined,
|
|
669
673
|
bestDescendant: undefined,
|
|
670
674
|
executionStatus,
|
|
@@ -690,26 +694,42 @@ export class ProtoArray {
|
|
|
690
694
|
this.maybeUpdateBestChildAndDescendant(pendingIndex, fullIndex, currentSlot, proposerBoostRoot);
|
|
691
695
|
}
|
|
692
696
|
|
|
693
|
-
/**
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
697
|
+
/** Count blocks selected as FULL or EMPTY by the supplied head chain in the inclusive slot range. */
|
|
698
|
+
getCanonicalPayloadCounts(
|
|
699
|
+
fromSlot: Slot,
|
|
700
|
+
toSlot: Slot,
|
|
701
|
+
headRoot: RootHex,
|
|
702
|
+
headPayloadStatus: PayloadStatus
|
|
703
|
+
): {full: number; empty: number} {
|
|
704
|
+
let full = 0;
|
|
705
|
+
let empty = 0;
|
|
706
|
+
|
|
707
|
+
// Walk the canonical chain newest-first from the head, following ancestors via `getParentNodeIndex`.
|
|
708
|
+
// Ancestors are strictly slot-descending, so we stop as soon as a node falls below `fromSlot`
|
|
709
|
+
// instead of materializing the whole chain back to the anchor as `getAllAncestorNodes` does.
|
|
710
|
+
// This keeps the scan O(window) rather than O(chain-to-anchor), relevant under prolonged non-finality.
|
|
711
|
+
const headIndex = this.getNodeIndexByRootAndStatus(headRoot, headPayloadStatus);
|
|
712
|
+
let node = headIndex !== undefined ? this.nodes[headIndex] : undefined;
|
|
713
|
+
|
|
714
|
+
while (node !== undefined && node.slot >= fromSlot) {
|
|
715
|
+
if (
|
|
716
|
+
node.slot !== GENESIS_SLOT &&
|
|
717
|
+
node.slot <= toSlot &&
|
|
718
|
+
isGloasBlock(node) &&
|
|
719
|
+
node.payloadStatus !== PayloadStatus.PENDING
|
|
720
|
+
) {
|
|
721
|
+
if (node.payloadStatus === PayloadStatus.FULL) {
|
|
722
|
+
full++;
|
|
723
|
+
} else {
|
|
724
|
+
empty++;
|
|
725
|
+
}
|
|
710
726
|
}
|
|
727
|
+
|
|
728
|
+
const parentIndex = this.getParentNodeIndex(node);
|
|
729
|
+
node = parentIndex === undefined ? undefined : this.nodes[parentIndex];
|
|
711
730
|
}
|
|
712
|
-
|
|
731
|
+
|
|
732
|
+
return {full, empty};
|
|
713
733
|
}
|
|
714
734
|
|
|
715
735
|
/**
|
|
@@ -1480,13 +1500,13 @@ export class ProtoArray {
|
|
|
1480
1500
|
childNode.payloadStatus === PayloadStatus.PENDING ||
|
|
1481
1501
|
childNode.slot + 1 !== currentSlot
|
|
1482
1502
|
? childNode.weight
|
|
1483
|
-
:
|
|
1503
|
+
: 0n;
|
|
1484
1504
|
const bestChildEffectiveWeight =
|
|
1485
1505
|
!isGloasBlock(bestChildNode) ||
|
|
1486
1506
|
bestChildNode.payloadStatus === PayloadStatus.PENDING ||
|
|
1487
1507
|
bestChildNode.slot + 1 !== currentSlot
|
|
1488
1508
|
? bestChildNode.weight
|
|
1489
|
-
:
|
|
1509
|
+
: 0n;
|
|
1490
1510
|
|
|
1491
1511
|
if (childEffectiveWeight !== bestChildEffectiveWeight) {
|
|
1492
1512
|
// Different effective weights, choose the winner by weight
|
|
@@ -1581,8 +1601,8 @@ export class ProtoArray {
|
|
|
1581
1601
|
return correctJustified && correctFinalized;
|
|
1582
1602
|
}
|
|
1583
1603
|
|
|
1584
|
-
/**
|
|
1585
|
-
getViableHeads(currentSlot: Slot): {root: RootHex; payloadStatus: PayloadStatus; weight:
|
|
1604
|
+
/** Returns exact Gwei weights for the compliance test boundary. */
|
|
1605
|
+
getViableHeads(currentSlot: Slot): {root: RootHex; payloadStatus: PayloadStatus; weight: bigint}[] {
|
|
1586
1606
|
// Mirror the spec's `get_filtered_block_tree`, which is rooted at the store's justified
|
|
1587
1607
|
// checkpoint: a viable head is a leaf (no viable descendant, i.e. `bestChild === undefined`)
|
|
1588
1608
|
// that descends from the justified checkpoint block AND is itself viable for head. Iterating
|
|
@@ -1591,7 +1611,7 @@ export class ProtoArray {
|
|
|
1591
1611
|
const justifiedVariant = this.getDefaultVariant(this.justifiedRoot);
|
|
1592
1612
|
// Gloas payload-status variants of one blockRoot are distinct nodes in the spec's filtered
|
|
1593
1613
|
// tree, identified by (root, payload_status, weight) — emit one entry per variant.
|
|
1594
|
-
const heads: {root: RootHex; payloadStatus: PayloadStatus; weight:
|
|
1614
|
+
const heads: {root: RootHex; payloadStatus: PayloadStatus; weight: bigint}[] = [];
|
|
1595
1615
|
for (const node of this.nodes) {
|
|
1596
1616
|
if (node.bestChild !== undefined || !this.nodeIsViableForHead(node, currentSlot)) {
|
|
1597
1617
|
continue;
|
|
@@ -1608,7 +1628,11 @@ export class ProtoArray {
|
|
|
1608
1628
|
continue;
|
|
1609
1629
|
}
|
|
1610
1630
|
}
|
|
1611
|
-
heads.push({
|
|
1631
|
+
heads.push({
|
|
1632
|
+
root: node.blockRoot,
|
|
1633
|
+
payloadStatus: node.payloadStatus,
|
|
1634
|
+
weight: node.weight,
|
|
1635
|
+
});
|
|
1612
1636
|
}
|
|
1613
1637
|
return heads;
|
|
1614
1638
|
}
|