@lodestar/fork-choice 1.46.0 → 1.47.0-dev.02d2dbea03
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/errors.d.ts +6 -1
- package/lib/forkChoice/errors.d.ts.map +1 -1
- package/lib/forkChoice/errors.js +1 -0
- package/lib/forkChoice/errors.js.map +1 -1
- package/lib/forkChoice/fastConfirmation/data.d.ts +1 -1
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.d.ts +4 -4
- package/lib/forkChoice/fastConfirmation/index.d.ts +3 -3
- package/lib/forkChoice/fastConfirmation/rules.d.ts +1 -1
- package/lib/forkChoice/fastConfirmation/types.d.ts +3 -3
- package/lib/forkChoice/fastConfirmation/types.d.ts.map +1 -1
- package/lib/forkChoice/fastConfirmation/utils.d.ts +3 -3
- package/lib/forkChoice/forkChoice.d.ts +12 -17
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +48 -31
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +8 -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 +5 -13
- package/lib/forkChoice/safeBlocks.d.ts.map +1 -1
- package/lib/forkChoice/safeBlocks.js +23 -32
- package/lib/forkChoice/safeBlocks.js.map +1 -1
- package/lib/forkChoice/store.d.ts +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/metrics.d.ts +2 -2
- 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/errors.ts +3 -1
- package/src/forkChoice/fastConfirmation/data.ts +2 -2
- package/src/forkChoice/fastConfirmation/fastConfirmationRule.ts +6 -6
- package/src/forkChoice/fastConfirmation/index.ts +3 -3
- package/src/forkChoice/fastConfirmation/rules.ts +3 -3
- package/src/forkChoice/fastConfirmation/types.ts +3 -3
- package/src/forkChoice/fastConfirmation/utils.ts +3 -3
- package/src/forkChoice/forkChoice.ts +71 -41
- package/src/forkChoice/interface.ts +6 -6
- package/src/forkChoice/safeBlocks.ts +27 -35
- package/src/forkChoice/store.ts +1 -1
- package/src/index.ts +2 -2
- package/src/metrics.ts +1 -1
- package/src/protoArray/interface.ts +4 -4
- package/src/protoArray/protoArray.ts +65 -41
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
} from "@lodestar/state-transition";
|
|
10
10
|
import {Epoch, RootHex, Slot, ValidatorIndex} from "@lodestar/types";
|
|
11
11
|
import {Logger, fromHex} from "@lodestar/utils";
|
|
12
|
-
import {ExecutionStatus, ProtoBlock} from "../../protoArray/interface.
|
|
13
|
-
import {CheckpointWithHex, computeTotalBalance, equalCheckpointWithHex} from "../store.
|
|
12
|
+
import {ExecutionStatus, ProtoBlock} from "../../protoArray/interface.js";
|
|
13
|
+
import {CheckpointWithHex, computeTotalBalance, equalCheckpointWithHex} from "../store.js";
|
|
14
14
|
import {
|
|
15
15
|
type BalanceSourceKey,
|
|
16
16
|
FastConfirmationBalanceSource,
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
FastConfirmationSnapshot,
|
|
20
20
|
IFastConfirmationStore,
|
|
21
21
|
type TotalActiveBalanceCacheKey,
|
|
22
|
-
} from "./types.
|
|
22
|
+
} from "./types.js";
|
|
23
23
|
|
|
24
24
|
// Spec: adjust_committee_weight_estimate_to_ensure_safety
|
|
25
25
|
// https://github.com/ethereum/consensus-specs/blob/master/specs/phase0/fast-confirmation.md#adjust_committee_weight_estimate_to_ensure_safety
|
|
@@ -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,
|
|
@@ -49,7 +55,8 @@ import {
|
|
|
49
55
|
FastConfirmationRule,
|
|
50
56
|
FastConfirmationSteps,
|
|
51
57
|
type IFastConfirmationRule,
|
|
52
|
-
|
|
58
|
+
type IFastConfirmationSpecStore,
|
|
59
|
+
} from "./fastConfirmation/fastConfirmationRule.js";
|
|
53
60
|
import {
|
|
54
61
|
AncestorResult,
|
|
55
62
|
AncestorStatus,
|
|
@@ -67,6 +74,8 @@ export type ForkChoiceOpts = {
|
|
|
67
74
|
fastConfirmation?: boolean;
|
|
68
75
|
};
|
|
69
76
|
|
|
77
|
+
const EFFECTIVE_BALANCE_INCREMENT_BIGINT = BigInt(EFFECTIVE_BALANCE_INCREMENT);
|
|
78
|
+
|
|
70
79
|
export enum UpdateHeadOpt {
|
|
71
80
|
GetCanonicalHead = "getCanonicalHead", // Skip getProposerHead
|
|
72
81
|
GetProposerHead = "getProposerHead", // With getProposerHead
|
|
@@ -146,7 +155,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
146
155
|
/** Boost the entire branch with this proposer root as the leaf */
|
|
147
156
|
private proposerBoostRoot: RootHex | null = null;
|
|
148
157
|
/** Score to use in proposer boost, evaluated lazily from justified balances */
|
|
149
|
-
private justifiedProposerBoostScore:
|
|
158
|
+
private justifiedProposerBoostScore: bigint | null = null;
|
|
150
159
|
/** The current effective balances */
|
|
151
160
|
private balances: EffectiveBalanceIncrements;
|
|
152
161
|
/** Optional fast confirmation rule implementation */
|
|
@@ -228,6 +237,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
228
237
|
return this.fastConfirmationRule?.getConfirmedRoot() ?? this.fcStore.justified.checkpoint.rootHex;
|
|
229
238
|
}
|
|
230
239
|
|
|
240
|
+
getFastConfirmationStore(): IFastConfirmationSpecStore {
|
|
241
|
+
return {
|
|
242
|
+
confirmedRoot: this.getConfirmedRoot(),
|
|
243
|
+
previousEpochObservedJustifiedCheckpoint: this.fcStore.previousEpochObservedJustifiedCheckpoint,
|
|
244
|
+
currentEpochObservedJustifiedCheckpoint: this.fcStore.currentEpochObservedJustifiedCheckpoint,
|
|
245
|
+
previousEpochGreatestUnrealizedCheckpoint: this.fcStore.previousEpochGreatestUnrealizedCheckpoint,
|
|
246
|
+
previousSlotHead: this.fcStore.previousSlotHead,
|
|
247
|
+
currentSlotHead: this.fcStore.currentSlotHead,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
231
251
|
getConfirmedBlock(): ProtoBlock | null {
|
|
232
252
|
return this.getBlockHexDefaultStatus(this.getConfirmedRoot());
|
|
233
253
|
}
|
|
@@ -575,7 +595,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
575
595
|
* The structure in line with deltas to propagate boost up the branch
|
|
576
596
|
* starting from the proposerIndex
|
|
577
597
|
*/
|
|
578
|
-
let proposerBoost: {root: RootHex; score:
|
|
598
|
+
let proposerBoost: {root: RootHex; score: bigint} | null = null;
|
|
579
599
|
if (this.opts?.proposerBoost && this.proposerBoostRoot) {
|
|
580
600
|
const proposerBoostScore =
|
|
581
601
|
this.justifiedProposerBoostScore ??
|
|
@@ -614,8 +634,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
614
634
|
return this.protoArray.nodes.filter((node) => node.slot > windowStart).length;
|
|
615
635
|
}
|
|
616
636
|
|
|
617
|
-
|
|
618
|
-
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);
|
|
619
639
|
}
|
|
620
640
|
|
|
621
641
|
/** Very expensive function, iterates the entire ProtoArray. Called only in debug API */
|
|
@@ -623,22 +643,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
623
643
|
return this.protoArray.nodes.filter((node) => node.bestChild === undefined);
|
|
624
644
|
}
|
|
625
645
|
|
|
626
|
-
/**
|
|
627
|
-
|
|
628
|
-
* For compliance test use only
|
|
629
|
-
*/
|
|
630
|
-
getViableHeads(): {root: RootHex; payloadStatus: PayloadStatus; weight: number}[] {
|
|
646
|
+
/** Returns exact Gwei weights for the compliance test. */
|
|
647
|
+
getViableHeads(): {root: RootHex; payloadStatus: PayloadStatus; weight: bigint}[] {
|
|
631
648
|
return this.protoArray.getViableHeads(this.fcStore.currentSlot);
|
|
632
649
|
}
|
|
633
650
|
|
|
634
|
-
/**
|
|
635
|
-
* The cached justified total active balance, in EFFECTIVE_BALANCE_INCREMENT units.
|
|
636
|
-
* For compliance test use only
|
|
637
|
-
*/
|
|
638
|
-
getJustifiedTotalActiveBalanceByIncrement(): number {
|
|
639
|
-
return this.fcStore.justified.totalBalance;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
651
|
/** This is for the debug API only */
|
|
643
652
|
getAllNodes(): ProtoNode[] {
|
|
644
653
|
return this.protoArray.nodes;
|
|
@@ -1069,11 +1078,20 @@ export class ForkChoice implements IForkChoice {
|
|
|
1069
1078
|
while (this.fcStore.currentSlot < currentSlot) {
|
|
1070
1079
|
const previousSlot = this.fcStore.currentSlot;
|
|
1071
1080
|
// Note: we are relying upon `onTick` to update `fcStore.time` to ensure we don't get stuck in a loop.
|
|
1072
|
-
this.onTick(previousSlot + 1);
|
|
1081
|
+
const didUpdateCheckpoints = this.onTick(previousSlot + 1);
|
|
1073
1082
|
this.queuedAttestationsPreviousSlot = 0;
|
|
1074
1083
|
// Process any attestations that might now be eligible before running FCR for this slot.
|
|
1075
1084
|
this.processAttestationQueue();
|
|
1076
|
-
this.runFastConfirmation();
|
|
1085
|
+
const didRecomputeHead = this.runFastConfirmation();
|
|
1086
|
+
|
|
1087
|
+
// An epoch-boundary checkpoint pull-up can move the head's dependent root and stale the cached
|
|
1088
|
+
// head before block 0 of the new epoch is imported, making isProposerBoostSameDependentRoot()
|
|
1089
|
+
// wrong for that block. Recompute the head so it reflects the new checkpoint and the queued
|
|
1090
|
+
// votes — unless fast confirmation already did, to avoid a redundant head calculation.
|
|
1091
|
+
if (didUpdateCheckpoints && !didRecomputeHead) {
|
|
1092
|
+
this.updateHead();
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1077
1095
|
this.validatedAttestationDatas = new Set();
|
|
1078
1096
|
}
|
|
1079
1097
|
}
|
|
@@ -1624,7 +1642,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
1624
1642
|
// the justified state) - fall back to the head state's effective balance
|
|
1625
1643
|
balance = state.effectiveBalanceIncrements[validatorIndex];
|
|
1626
1644
|
}
|
|
1627
|
-
headWeight += balance;
|
|
1645
|
+
headWeight += BigInt(balance) * EFFECTIVE_BALANCE_INCREMENT_BIGINT;
|
|
1628
1646
|
}
|
|
1629
1647
|
}
|
|
1630
1648
|
}
|
|
@@ -1654,7 +1672,6 @@ export class ForkChoice implements IForkChoice {
|
|
|
1654
1672
|
|
|
1655
1673
|
// pre-gloas uses get_weight() (boost-inclusive), gloas uses get_attestation_score() (boost-excluded)
|
|
1656
1674
|
const parentWeight = isForkPostGloas(this.config.getForkName(node.slot)) ? node.attestationScore : node.weight;
|
|
1657
|
-
|
|
1658
1675
|
return parentWeight > parentThreshold;
|
|
1659
1676
|
}
|
|
1660
1677
|
|
|
@@ -1720,23 +1737,31 @@ export class ForkChoice implements IForkChoice {
|
|
|
1720
1737
|
* May need the justified balances of:
|
|
1721
1738
|
* - unrealizedJustified: Already available in `CheckpointWithBalance`
|
|
1722
1739
|
* Since this balances are already available the getter is just `() => balances`, without cache interaction
|
|
1740
|
+
*
|
|
1741
|
+
* @returns Whether either checkpoint was updated.
|
|
1723
1742
|
*/
|
|
1724
1743
|
private updateCheckpoints(
|
|
1725
1744
|
justifiedCheckpoint: CheckpointWithHex,
|
|
1726
1745
|
finalizedCheckpoint: CheckpointWithHex,
|
|
1727
1746
|
getJustifiedBalances: () => JustifiedBalances
|
|
1728
|
-
):
|
|
1747
|
+
): boolean {
|
|
1748
|
+
let updated = false;
|
|
1749
|
+
|
|
1729
1750
|
// Update justified checkpoint.
|
|
1730
1751
|
if (justifiedCheckpoint.epoch > this.fcStore.justified.checkpoint.epoch) {
|
|
1731
1752
|
this.fcStore.justified = {checkpoint: justifiedCheckpoint, balances: getJustifiedBalances()};
|
|
1732
1753
|
this.justifiedProposerBoostScore = null;
|
|
1754
|
+
updated = true;
|
|
1733
1755
|
}
|
|
1734
1756
|
|
|
1735
1757
|
// Update finalized checkpoint.
|
|
1736
1758
|
if (finalizedCheckpoint.epoch > this.fcStore.finalizedCheckpoint.epoch) {
|
|
1737
1759
|
this.fcStore.finalizedCheckpoint = finalizedCheckpoint;
|
|
1738
1760
|
this.justifiedProposerBoostScore = null;
|
|
1761
|
+
updated = true;
|
|
1739
1762
|
}
|
|
1763
|
+
|
|
1764
|
+
return updated;
|
|
1740
1765
|
}
|
|
1741
1766
|
|
|
1742
1767
|
/**
|
|
@@ -2022,8 +2047,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
2022
2047
|
* Equivalent to:
|
|
2023
2048
|
*
|
|
2024
2049
|
* https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#on_tick
|
|
2050
|
+
*
|
|
2051
|
+
* @returns Whether an epoch-boundary checkpoint was updated.
|
|
2025
2052
|
*/
|
|
2026
|
-
private onTick(time: Slot):
|
|
2053
|
+
private onTick(time: Slot): boolean {
|
|
2027
2054
|
const previousSlot = this.fcStore.currentSlot;
|
|
2028
2055
|
|
|
2029
2056
|
if (time > previousSlot + 1) {
|
|
@@ -2044,11 +2071,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
2044
2071
|
|
|
2045
2072
|
// Not a new epoch, return.
|
|
2046
2073
|
if (computeSlotsSinceEpochStart(time) !== 0) {
|
|
2047
|
-
return;
|
|
2074
|
+
return false;
|
|
2048
2075
|
}
|
|
2049
2076
|
|
|
2050
|
-
// If a new epoch, pull-up justification and finalization from previous epoch
|
|
2051
|
-
this.updateCheckpoints(
|
|
2077
|
+
// If a new epoch, pull-up justification and finalization from previous epoch.
|
|
2078
|
+
return this.updateCheckpoints(
|
|
2052
2079
|
this.fcStore.unrealizedJustified.checkpoint,
|
|
2053
2080
|
this.fcStore.unrealizedFinalizedCheckpoint,
|
|
2054
2081
|
() => this.fcStore.unrealizedJustified.balances
|
|
@@ -2074,11 +2101,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
2074
2101
|
return {prelimProposerHead, prelimNotReorgedReason: NotReorgedReason.HeadBlockIsTimely};
|
|
2075
2102
|
}
|
|
2076
2103
|
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
if (isAtEpochBoundary) {
|
|
2081
|
-
return {prelimProposerHead, prelimNotReorgedReason: NotReorgedReason.AtEpochBoundary};
|
|
2104
|
+
const isShufflingStable = isForkPostFulu(this.config.getForkName(slot)) || slot % SLOTS_PER_EPOCH !== 0;
|
|
2105
|
+
if (!isShufflingStable) {
|
|
2106
|
+
return {prelimProposerHead, prelimNotReorgedReason: NotReorgedReason.NotShufflingStable};
|
|
2082
2107
|
}
|
|
2083
2108
|
|
|
2084
2109
|
// No reorg if headBlock and parentBlock are not ffg competitive
|
|
@@ -2109,10 +2134,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
2109
2134
|
return {prelimProposerHead};
|
|
2110
2135
|
}
|
|
2111
2136
|
|
|
2112
|
-
|
|
2137
|
+
/** Returns whether it recomputed the head, so the caller can avoid a redundant `updateHead()`. */
|
|
2138
|
+
private runFastConfirmation(): boolean {
|
|
2113
2139
|
const fastConfirmationRule = this.fastConfirmationRule;
|
|
2114
2140
|
const fastConfirmationContext = this.fastConfirmationContext;
|
|
2115
|
-
if (!fastConfirmationRule || !fastConfirmationContext) return;
|
|
2141
|
+
if (!fastConfirmationRule || !fastConfirmationContext) return false;
|
|
2116
2142
|
|
|
2117
2143
|
if (this.fastConfirmationPaused) {
|
|
2118
2144
|
// Keep consumers on a safe, available root while the rule is paused
|
|
@@ -2123,7 +2149,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
2123
2149
|
// Runs outside the timed try/catch below; a throw would escape to the clock listener
|
|
2124
2150
|
this.logger?.debug("Fast confirmation notify failed", {slot: this.fcStore.currentSlot}, err as Error);
|
|
2125
2151
|
}
|
|
2126
|
-
return;
|
|
2152
|
+
return false;
|
|
2127
2153
|
}
|
|
2128
2154
|
|
|
2129
2155
|
withObservedDuration(this.metrics?.fastConfirmation.totalDuration.startTimer(), () => {
|
|
@@ -2146,6 +2172,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
2146
2172
|
);
|
|
2147
2173
|
}
|
|
2148
2174
|
});
|
|
2175
|
+
|
|
2176
|
+
return true;
|
|
2149
2177
|
}
|
|
2150
2178
|
|
|
2151
2179
|
private createFastConfirmationContext(): FastConfirmationContext {
|
|
@@ -2197,12 +2225,14 @@ export class ForkChoice implements IForkChoice {
|
|
|
2197
2225
|
}
|
|
2198
2226
|
}
|
|
2199
2227
|
|
|
2200
|
-
//
|
|
2228
|
+
// https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/phase0/fork-choice.md#calculate_committee_fraction
|
|
2201
2229
|
// Calculates proposer boost score when committeePercent = config.PROPOSER_SCORE_BOOST
|
|
2202
|
-
|
|
2230
|
+
function getCommitteeFraction(
|
|
2203
2231
|
justifiedTotalActiveBalanceByIncrement: number,
|
|
2204
2232
|
config: {slotsPerEpoch: number; committeePercent: number}
|
|
2205
|
-
):
|
|
2206
|
-
const
|
|
2207
|
-
|
|
2233
|
+
): bigint {
|
|
2234
|
+
const committeeWeightGwei =
|
|
2235
|
+
(BigInt(justifiedTotalActiveBalanceByIncrement) * EFFECTIVE_BALANCE_INCREMENT_BIGINT) /
|
|
2236
|
+
BigInt(config.slotsPerEpoch);
|
|
2237
|
+
return (committeeWeightGwei * BigInt(config.committeePercent)) / 100n;
|
|
2208
2238
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ProtoBlock,
|
|
9
9
|
ProtoNode,
|
|
10
10
|
} from "../protoArray/interface.js";
|
|
11
|
+
import {IFastConfirmationSpecStore} from "./fastConfirmation/types.js";
|
|
11
12
|
import {UpdateAndGetHeadOpt} from "./forkChoice.js";
|
|
12
13
|
import {CheckpointWithHex} from "./store.js";
|
|
13
14
|
|
|
@@ -53,7 +54,7 @@ export enum NotReorgedReason {
|
|
|
53
54
|
HeadBlockIsTimely = "headBlockIsTimely",
|
|
54
55
|
ParentBlockNotAvailable = "parentBlockNotAvailable",
|
|
55
56
|
ProposerBoostReorgDisabled = "proposerBoostReorgDisabled",
|
|
56
|
-
|
|
57
|
+
NotShufflingStable = "notShufflingStable",
|
|
57
58
|
NotFFGCompetitive = "notFFGCompetitive",
|
|
58
59
|
ChainLongUnfinality = "chainLongUnfinality",
|
|
59
60
|
ParentBlockDistanceMoreThanOneSlot = "parentBlockDistanceMoreThanOneSlot",
|
|
@@ -100,6 +101,8 @@ export interface IForkChoice {
|
|
|
100
101
|
getHead(): ProtoBlock;
|
|
101
102
|
getConfirmedRoot(): RootHex;
|
|
102
103
|
getConfirmedBlock(): ProtoBlock | null;
|
|
104
|
+
/** Snapshot of the spec `FastConfirmationStore` fields, mirroring `get_fast_confirmation_store` */
|
|
105
|
+
getFastConfirmationStore(): IFastConfirmationSpecStore;
|
|
103
106
|
/** Resume the fast confirmation rule; restarts from the finalized root on the next slot tick */
|
|
104
107
|
resumeFastConfirmation(): void;
|
|
105
108
|
/** Pause the fast confirmation rule (e.g. while syncing); pins the confirmed root to the finalized root */
|
|
@@ -246,11 +249,8 @@ export interface IForkChoice {
|
|
|
246
249
|
hasPayloadUnsafe(blockRoot: Root): boolean;
|
|
247
250
|
hasPayloadHexUnsafe(blockRoot: RootHex): boolean;
|
|
248
251
|
getSlotsPresent(windowStart: number): number;
|
|
249
|
-
/**
|
|
250
|
-
|
|
251
|
-
* payload (FULL variant exists). Used by the builder circuit breaker.
|
|
252
|
-
*/
|
|
253
|
-
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};
|
|
254
254
|
getPTCVotes(blockRootHex: RootHex): (boolean | null)[] | null;
|
|
255
255
|
/** Raw PTC vote tallies for the debug fork choice endpoint; `null` for pre-Gloas roots. */
|
|
256
256
|
getPTCVoteCounts(blockRootHex: RootHex): {
|
|
@@ -1,24 +1,10 @@
|
|
|
1
|
-
import {GENESIS_SLOT
|
|
2
|
-
import {
|
|
3
|
-
import {LogLevel, Logger
|
|
4
|
-
import {HEX_ZERO_HASH, ProtoBlock, isGloasBlock} from "../protoArray/interface.js";
|
|
1
|
+
import {GENESIS_SLOT} from "@lodestar/params";
|
|
2
|
+
import {RootHex} from "@lodestar/types";
|
|
3
|
+
import {LogLevel, Logger} from "@lodestar/utils";
|
|
4
|
+
import {ExecutionStatus, HEX_ZERO_HASH, ProtoBlock, isGloasBlock} from "../protoArray/interface.js";
|
|
5
|
+
import {ForkChoiceError, ForkChoiceErrorCode} from "./errors.js";
|
|
5
6
|
import {IForkChoice} from "./interface.js";
|
|
6
7
|
|
|
7
|
-
/**
|
|
8
|
-
* Under honest majority and certain network synchronicity assumptions there exists a block
|
|
9
|
-
* that is safe from re-orgs. Normally this block is pretty close to the head of canonical
|
|
10
|
-
* chain which makes it valuable to expose a safe block to users.
|
|
11
|
-
*
|
|
12
|
-
* @deprecated The merged fast-confirmation spec only defines `get_safe_execution_block_hash`.
|
|
13
|
-
*/
|
|
14
|
-
export function getSafeBeaconBlockRoot(fc: IForkChoice): Root {
|
|
15
|
-
const confirmedRoot = fc.getConfirmedRoot();
|
|
16
|
-
if (confirmedRoot && fc.hasBlockHex(confirmedRoot)) {
|
|
17
|
-
return fromHex(confirmedRoot);
|
|
18
|
-
}
|
|
19
|
-
return fc.getJustifiedCheckpoint().root;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
8
|
/**
|
|
23
9
|
* Get execution payload hash to report as `safeBlockHash` in `engine_forkchoiceUpdated`.
|
|
24
10
|
*
|
|
@@ -27,31 +13,36 @@ export function getSafeBeaconBlockRoot(fc: IForkChoice): Root {
|
|
|
27
13
|
* payload may not yet be confirmed canonical, so we report the parent EL block which has
|
|
28
14
|
* been (the bid commits to extending it).
|
|
29
15
|
*
|
|
30
|
-
* 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
|
|
31
17
|
*/
|
|
32
|
-
export function getSafeExecutionBlockHash(forkChoice: IForkChoice, logger?: Pick<Logger, LogLevel.
|
|
18
|
+
export function getSafeExecutionBlockHash(forkChoice: IForkChoice, logger?: Pick<Logger, LogLevel.debug>): RootHex {
|
|
19
|
+
const confirmedRoot = forkChoice.getConfirmedRoot();
|
|
33
20
|
const confirmedBlock = forkChoice.getConfirmedBlock();
|
|
34
21
|
if (confirmedBlock === null) {
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
throw new ForkChoiceError({code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK, root: confirmedRoot});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (confirmedBlock.blockRoot === forkChoice.getFinalizedBlock().blockRoot) {
|
|
26
|
+
logger?.debug("Confirmed block is the finalized block", {
|
|
27
|
+
blockRoot: confirmedBlock.blockRoot,
|
|
28
|
+
slot: confirmedBlock.slot,
|
|
37
29
|
});
|
|
38
|
-
return ZERO_HASH_HEX;
|
|
39
30
|
}
|
|
40
31
|
|
|
41
|
-
return getExecutionBlockHash(confirmedBlock
|
|
32
|
+
return getExecutionBlockHash(confirmedBlock);
|
|
42
33
|
}
|
|
43
34
|
|
|
44
35
|
/**
|
|
45
36
|
* Get execution payload hash to report as `finalizedBlockHash` in `engine_forkchoiceUpdated`.
|
|
46
37
|
* Mirrors `getSafeExecutionBlockHash`: post-Gloas returns the bid `parent_block_hash`.
|
|
47
38
|
*
|
|
48
|
-
* 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
|
|
49
40
|
*/
|
|
50
|
-
export function getFinalizedExecutionBlockHash(forkChoice: IForkChoice
|
|
51
|
-
return getExecutionBlockHash(forkChoice.getFinalizedBlock()
|
|
41
|
+
export function getFinalizedExecutionBlockHash(forkChoice: IForkChoice): RootHex {
|
|
42
|
+
return getExecutionBlockHash(forkChoice.getFinalizedBlock());
|
|
52
43
|
}
|
|
53
44
|
|
|
54
|
-
function getExecutionBlockHash(block: ProtoBlock
|
|
45
|
+
function getExecutionBlockHash(block: ProtoBlock): RootHex {
|
|
55
46
|
if (isGloasBlock(block)) {
|
|
56
47
|
return block.parentBlockHash;
|
|
57
48
|
}
|
|
@@ -63,13 +54,14 @@ function getExecutionBlockHash(block: ProtoBlock, logger?: Pick<Logger, LogLevel
|
|
|
63
54
|
return HEX_ZERO_HASH;
|
|
64
55
|
}
|
|
65
56
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
const {blockRoot, slot} = block;
|
|
58
|
+
if (block.executionPayloadBlockHash === null && block.executionStatus !== ExecutionStatus.PreMerge) {
|
|
59
|
+
throw new ForkChoiceError({
|
|
60
|
+
code: ForkChoiceErrorCode.MISSING_EXECUTION_PAYLOAD_BLOCK_HASH,
|
|
61
|
+
root: blockRoot,
|
|
62
|
+
slot,
|
|
70
63
|
});
|
|
71
|
-
return HEX_ZERO_HASH;
|
|
72
64
|
}
|
|
73
65
|
|
|
74
|
-
return block.executionPayloadBlockHash;
|
|
66
|
+
return block.executionPayloadBlockHash ?? HEX_ZERO_HASH;
|
|
75
67
|
}
|
package/src/forkChoice/store.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {EffectiveBalanceIncrements, IBeaconStateView} from "@lodestar/state-transition";
|
|
2
2
|
import {RootHex, Slot, ValidatorIndex, phase0} from "@lodestar/types";
|
|
3
3
|
import {toRootHex} from "@lodestar/utils";
|
|
4
|
-
import {ForkChoiceStateGetter, IFastConfirmationStore} from "./fastConfirmation/types.
|
|
4
|
+
import {ForkChoiceStateGetter, IFastConfirmationStore} from "./fastConfirmation/types.js";
|
|
5
5
|
import {CheckpointWithBalance, CheckpointWithTotalBalance} from "./interface.js";
|
|
6
6
|
|
|
7
7
|
/**
|
package/src/index.ts
CHANGED
|
@@ -16,8 +16,8 @@ export {
|
|
|
16
16
|
type IFastConfirmationRule,
|
|
17
17
|
type IFastConfirmationStore,
|
|
18
18
|
getFastConfirmationMetrics,
|
|
19
|
-
} from "./forkChoice/fastConfirmation/fastConfirmationRule.
|
|
20
|
-
export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt
|
|
19
|
+
} from "./forkChoice/fastConfirmation/fastConfirmationRule.js";
|
|
20
|
+
export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt} from "./forkChoice/forkChoice.js";
|
|
21
21
|
export {
|
|
22
22
|
type AncestorResult,
|
|
23
23
|
AncestorStatus,
|
package/src/metrics.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {MetricsRegisterExtra} from "@lodestar/utils";
|
|
2
|
-
import {FastConfirmationMetrics, getFastConfirmationMetrics} from "./forkChoice/fastConfirmation/metrics.
|
|
2
|
+
import {FastConfirmationMetrics, getFastConfirmationMetrics} from "./forkChoice/fastConfirmation/metrics.js";
|
|
3
3
|
import {UpdateHeadOpt} from "./forkChoice/forkChoice.js";
|
|
4
4
|
import {NotReorgedReason} from "./forkChoice/interface.js";
|
|
5
5
|
|
|
@@ -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
|
};
|