@lodestar/fork-choice 1.44.0-dev.985999b30c → 1.44.0-dev.a879adb124
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/fastConfirmation/data.d.ts +4 -0
- package/lib/forkChoice/fastConfirmation/data.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/data.js +31 -0
- package/lib/forkChoice/fastConfirmation/data.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.d.ts +17 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.js +129 -0
- package/lib/forkChoice/fastConfirmation/fastConfirmationRule.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/index.d.ts +4 -0
- package/lib/forkChoice/fastConfirmation/index.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/index.js +4 -0
- package/lib/forkChoice/fastConfirmation/index.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/metrics.d.ts +21 -0
- package/lib/forkChoice/fastConfirmation/metrics.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/metrics.js +42 -0
- package/lib/forkChoice/fastConfirmation/metrics.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/rules.d.ts +9 -0
- package/lib/forkChoice/fastConfirmation/rules.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/rules.js +91 -0
- package/lib/forkChoice/fastConfirmation/rules.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/types.d.ts +101 -0
- package/lib/forkChoice/fastConfirmation/types.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/types.js +12 -0
- package/lib/forkChoice/fastConfirmation/types.js.map +1 -0
- package/lib/forkChoice/fastConfirmation/utils.d.ts +47 -0
- package/lib/forkChoice/fastConfirmation/utils.d.ts.map +1 -0
- package/lib/forkChoice/fastConfirmation/utils.js +681 -0
- package/lib/forkChoice/fastConfirmation/utils.js.map +1 -0
- package/lib/forkChoice/forkChoice.d.ts +23 -3
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +116 -9
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +19 -7
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/forkChoice/interface.js.map +1 -1
- package/lib/forkChoice/safeBlocks.d.ts +2 -6
- package/lib/forkChoice/safeBlocks.d.ts.map +1 -1
- package/lib/forkChoice/safeBlocks.js +15 -7
- package/lib/forkChoice/safeBlocks.js.map +1 -1
- package/lib/forkChoice/store.d.ts +13 -2
- package/lib/forkChoice/store.d.ts.map +1 -1
- package/lib/forkChoice/store.js +29 -1
- package/lib/forkChoice/store.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/metrics.d.ts +12 -1
- package/lib/metrics.d.ts.map +1 -1
- package/lib/metrics.js +2 -0
- package/lib/metrics.js.map +1 -1
- package/lib/protoArray/protoArray.d.ts +67 -20
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +170 -38
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +7 -7
- package/src/forkChoice/fastConfirmation/data.ts +43 -0
- package/src/forkChoice/fastConfirmation/fastConfirmationRule.ts +159 -0
- package/src/forkChoice/fastConfirmation/index.ts +3 -0
- package/src/forkChoice/fastConfirmation/metrics.ts +44 -0
- package/src/forkChoice/fastConfirmation/rules.ts +124 -0
- package/src/forkChoice/fastConfirmation/types.ts +111 -0
- package/src/forkChoice/fastConfirmation/utils.ts +968 -0
- package/src/forkChoice/forkChoice.ts +150 -10
- package/src/forkChoice/interface.ts +36 -7
- package/src/forkChoice/safeBlocks.ts +15 -7
- package/src/forkChoice/store.ts +34 -1
- package/src/index.ts +11 -0
- package/src/metrics.ts +3 -1
- package/src/protoArray/protoArray.ts +184 -41
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
phase0,
|
|
26
26
|
ssz,
|
|
27
27
|
} from "@lodestar/types";
|
|
28
|
-
import {Logger, MapDef, fromHex, toRootHex} from "@lodestar/utils";
|
|
28
|
+
import {Logger, MapDef, fromHex, toRootHex, withObservedDuration} from "@lodestar/utils";
|
|
29
29
|
import {ForkChoiceMetrics} from "../metrics.js";
|
|
30
30
|
import {computeDeltas} from "../protoArray/computeDeltas.js";
|
|
31
31
|
import {ProtoArrayError, ProtoArrayErrorCode} from "../protoArray/errors.js";
|
|
@@ -44,6 +44,12 @@ import {
|
|
|
44
44
|
} from "../protoArray/interface.js";
|
|
45
45
|
import {ProtoArray} from "../protoArray/protoArray.js";
|
|
46
46
|
import {ForkChoiceError, ForkChoiceErrorCode, InvalidAttestationCode, InvalidBlockCode} from "./errors.js";
|
|
47
|
+
import {
|
|
48
|
+
type FastConfirmationContext,
|
|
49
|
+
FastConfirmationRule,
|
|
50
|
+
FastConfirmationSteps,
|
|
51
|
+
type IFastConfirmationRule,
|
|
52
|
+
} from "./fastConfirmation/fastConfirmationRule.ts";
|
|
47
53
|
import {
|
|
48
54
|
AncestorResult,
|
|
49
55
|
AncestorStatus,
|
|
@@ -58,6 +64,7 @@ export type ForkChoiceOpts = {
|
|
|
58
64
|
proposerBoost?: boolean;
|
|
59
65
|
proposerBoostReorg?: boolean;
|
|
60
66
|
computeUnrealized?: boolean;
|
|
67
|
+
fastConfirmation?: boolean;
|
|
61
68
|
};
|
|
62
69
|
|
|
63
70
|
export enum UpdateHeadOpt {
|
|
@@ -142,6 +149,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
142
149
|
private justifiedProposerBoostScore: number | null = null;
|
|
143
150
|
/** The current effective balances */
|
|
144
151
|
private balances: EffectiveBalanceIncrements;
|
|
152
|
+
/** Optional fast confirmation rule implementation */
|
|
153
|
+
private readonly fastConfirmationRule?: IFastConfirmationRule;
|
|
154
|
+
private readonly fastConfirmationContext?: FastConfirmationContext;
|
|
145
155
|
/**
|
|
146
156
|
* Instantiates a Fork Choice from some existing components
|
|
147
157
|
*
|
|
@@ -167,6 +177,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
167
177
|
this.head = this.updateHead();
|
|
168
178
|
this.balances = this.fcStore.justified.balances;
|
|
169
179
|
|
|
180
|
+
if (this.opts?.fastConfirmation) {
|
|
181
|
+
this.fastConfirmationRule = new FastConfirmationRule(this.fcStore, metrics, this.logger);
|
|
182
|
+
this.fastConfirmationContext = this.createFastConfirmationContext();
|
|
183
|
+
}
|
|
184
|
+
|
|
170
185
|
metrics?.forkChoice.votes.addCollect(() => {
|
|
171
186
|
metrics.forkChoice.votes.set(this.voteNextSlots.length);
|
|
172
187
|
metrics.forkChoice.queuedAttestations.set(this.queuedAttestationsPreviousSlot);
|
|
@@ -207,6 +222,14 @@ export class ForkChoice implements IForkChoice {
|
|
|
207
222
|
return this.head;
|
|
208
223
|
}
|
|
209
224
|
|
|
225
|
+
getConfirmedRoot(): RootHex {
|
|
226
|
+
return this.fastConfirmationRule?.getConfirmedRoot() ?? this.fcStore.justified.checkpoint.rootHex;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
getConfirmedBlock(): ProtoBlock | null {
|
|
230
|
+
return this.getBlockHexDefaultStatus(this.getConfirmedRoot());
|
|
231
|
+
}
|
|
232
|
+
|
|
210
233
|
/**
|
|
211
234
|
*
|
|
212
235
|
* A multiplexer to wrap around the traditional `updateHead()` according to the scenario
|
|
@@ -310,6 +333,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
310
333
|
return this.proposerBoostRoot ?? HEX_ZERO_HASH;
|
|
311
334
|
}
|
|
312
335
|
|
|
336
|
+
getPreviousProposerBoostRoot(): RootHex {
|
|
337
|
+
return this.protoArray.getPreviousProposerBoostRoot();
|
|
338
|
+
}
|
|
339
|
+
|
|
313
340
|
/**
|
|
314
341
|
* Decides whether to extend an available payload from the previous slot,
|
|
315
342
|
* corresponding to the beacon block `blockRoot`.
|
|
@@ -318,6 +345,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
318
345
|
return this.protoArray.shouldExtendPayload(blockRoot, this.proposerBoostRoot);
|
|
319
346
|
}
|
|
320
347
|
|
|
348
|
+
/** Spec: should_build_on_full(store, head) */
|
|
349
|
+
shouldBuildOnFull(head: ProtoBlock, slot: Slot): boolean {
|
|
350
|
+
return this.protoArray.shouldBuildOnFull(head, slot);
|
|
351
|
+
}
|
|
352
|
+
|
|
321
353
|
/**
|
|
322
354
|
* To predict the proposer head of the next slot. That is, to predict if proposer-boost-reorg could happen.
|
|
323
355
|
* Reason why we can't be certain is because information of the head block is not fully available yet
|
|
@@ -596,7 +628,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
596
628
|
blockDelaySec: number,
|
|
597
629
|
currentSlot: Slot,
|
|
598
630
|
executionStatus: BlockExecutionStatus,
|
|
599
|
-
dataAvailabilityStatus: DataAvailabilityStatus
|
|
631
|
+
dataAvailabilityStatus: DataAvailabilityStatus,
|
|
632
|
+
// The expected proposer index on the canonical chain we are following.
|
|
633
|
+
// Calculated by our head state. We use it as part of the proposer
|
|
634
|
+
// boost decision making. No boost will be set if this is null.
|
|
635
|
+
expectedProposerIndex: ValidatorIndex | null
|
|
600
636
|
): ProtoBlock {
|
|
601
637
|
const {parentRoot, slot} = block;
|
|
602
638
|
const parentRootHex = toRootHex(parentRoot);
|
|
@@ -669,7 +705,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
669
705
|
this.opts?.proposerBoost &&
|
|
670
706
|
isTimely &&
|
|
671
707
|
// only boost the first block we see
|
|
672
|
-
this.proposerBoostRoot === null
|
|
708
|
+
this.proposerBoostRoot === null &&
|
|
709
|
+
expectedProposerIndex !== null &&
|
|
710
|
+
block.proposerIndex === expectedProposerIndex
|
|
673
711
|
) {
|
|
674
712
|
this.proposerBoostRoot = blockRootHex;
|
|
675
713
|
}
|
|
@@ -934,8 +972,14 @@ export class ForkChoice implements IForkChoice {
|
|
|
934
972
|
* Updates the PTC votes for multiple validators attesting to a block
|
|
935
973
|
* Spec: gloas/fork-choice.md#new-on_payload_attestation_message
|
|
936
974
|
*/
|
|
937
|
-
notifyPtcMessages(
|
|
938
|
-
|
|
975
|
+
notifyPtcMessages(
|
|
976
|
+
blockRoot: RootHex,
|
|
977
|
+
slot: Slot,
|
|
978
|
+
ptcIndices: number[],
|
|
979
|
+
payloadPresent: boolean,
|
|
980
|
+
blobDataAvailable: boolean
|
|
981
|
+
): void {
|
|
982
|
+
this.protoArray.notifyPtcMessages(blockRoot, slot, ptcIndices, payloadPresent, blobDataAvailable);
|
|
939
983
|
}
|
|
940
984
|
|
|
941
985
|
/**
|
|
@@ -977,12 +1021,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
977
1021
|
const previousSlot = this.fcStore.currentSlot;
|
|
978
1022
|
// Note: we are relying upon `onTick` to update `fcStore.time` to ensure we don't get stuck in a loop.
|
|
979
1023
|
this.onTick(previousSlot + 1);
|
|
1024
|
+
this.queuedAttestationsPreviousSlot = 0;
|
|
1025
|
+
// Process any attestations that might now be eligible before running FCR for this slot.
|
|
1026
|
+
this.processAttestationQueue();
|
|
1027
|
+
this.runFastConfirmation();
|
|
1028
|
+
this.validatedAttestationDatas = new Set();
|
|
980
1029
|
}
|
|
981
|
-
|
|
982
|
-
this.queuedAttestationsPreviousSlot = 0;
|
|
983
|
-
// Process any attestations that might now be eligible.
|
|
984
|
-
this.processAttestationQueue();
|
|
985
|
-
this.validatedAttestationDatas = new Set();
|
|
986
1030
|
}
|
|
987
1031
|
|
|
988
1032
|
getTime(): Slot {
|
|
@@ -1051,6 +1095,30 @@ export class ForkChoice implements IForkChoice {
|
|
|
1051
1095
|
return votes.toBoolArray().map((v) => v ?? null);
|
|
1052
1096
|
}
|
|
1053
1097
|
|
|
1098
|
+
getPTCVoteCounts(blockRootHex: RootHex): {
|
|
1099
|
+
attesterCount: number;
|
|
1100
|
+
payloadPresentCount: number;
|
|
1101
|
+
dataAvailableCount: number;
|
|
1102
|
+
} | null {
|
|
1103
|
+
return this.protoArray.getPTCVoteCounts(blockRootHex);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
getPayloadTimelinessVotes(blockRootHex: RootHex): (boolean | null)[] | null {
|
|
1107
|
+
return this.protoArray.getPayloadTimelinessVotes(blockRootHex);
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
getPayloadDataAvailabilityVotes(blockRootHex: RootHex): (boolean | null)[] | null {
|
|
1111
|
+
return this.protoArray.getPayloadDataAvailabilityVotes(blockRootHex);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
getUnrealizedJustifiedCheckpoint(): CheckpointWithHex {
|
|
1115
|
+
return this.fcStore.unrealizedJustified.checkpoint;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
getUnrealizedFinalizedCheckpoint(): CheckpointWithHex {
|
|
1119
|
+
return this.fcStore.unrealizedFinalizedCheckpoint;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1054
1122
|
/**
|
|
1055
1123
|
* Returns a MUTABLE `ProtoBlock` if the block is known **and** a descendant of the finalized root.
|
|
1056
1124
|
*/
|
|
@@ -1876,6 +1944,78 @@ export class ForkChoice implements IForkChoice {
|
|
|
1876
1944
|
|
|
1877
1945
|
return {prelimProposerHead};
|
|
1878
1946
|
}
|
|
1947
|
+
|
|
1948
|
+
private runFastConfirmation(): void {
|
|
1949
|
+
withObservedDuration(this.metrics?.fastConfirmation.totalDuration.startTimer(), () => {
|
|
1950
|
+
if (!this.fastConfirmationRule || !this.fastConfirmationContext) return;
|
|
1951
|
+
|
|
1952
|
+
try {
|
|
1953
|
+
withObservedDuration(
|
|
1954
|
+
this.metrics?.fastConfirmation.stepsDuration.startTimer({
|
|
1955
|
+
step: FastConfirmationSteps.updateHead,
|
|
1956
|
+
}),
|
|
1957
|
+
() => this.updateHead()
|
|
1958
|
+
);
|
|
1959
|
+
|
|
1960
|
+
const result = this.fastConfirmationRule.onSlotStartAfterPastAttestationsApplied(this.fastConfirmationContext);
|
|
1961
|
+
this.fcStore.confirmedRoot = result.confirmedRoot;
|
|
1962
|
+
} catch (err) {
|
|
1963
|
+
this.logger?.debug(
|
|
1964
|
+
"Fast confirmation failed",
|
|
1965
|
+
{slot: this.fcStore.currentSlot, head: this.head.blockRoot, confirmedRoot: this.fcStore.confirmedRoot},
|
|
1966
|
+
err as Error
|
|
1967
|
+
);
|
|
1968
|
+
}
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
private createFastConfirmationContext(): FastConfirmationContext {
|
|
1973
|
+
const confirmationByzantineThreshold = this.config.CONFIRMATION_BYZANTINE_THRESHOLD;
|
|
1974
|
+
if (!confirmationByzantineThreshold) {
|
|
1975
|
+
throw new Error("CONFIRMATION_BYZANTINE_THRESHOLD must be set to use fast confirmation");
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
return {
|
|
1979
|
+
config: {
|
|
1980
|
+
CONFIRMATION_BYZANTINE_THRESHOLD: confirmationByzantineThreshold,
|
|
1981
|
+
PROPOSER_SCORE_BOOST: this.config.PROPOSER_SCORE_BOOST,
|
|
1982
|
+
},
|
|
1983
|
+
getCurrentSlot: () => this.fcStore.currentSlot,
|
|
1984
|
+
getHead: () => this.head,
|
|
1985
|
+
getBlock: (root: RootHex) => this.getBlockHexDefaultStatus(root),
|
|
1986
|
+
getAncestor: (root: RootHex, slot: Slot) => this.getAncestor(root, slot).blockRoot,
|
|
1987
|
+
isDescendant: (ancestor: RootHex, descendant: RootHex) => {
|
|
1988
|
+
const ancestorStatus = this.protoArray.getDefaultVariant(ancestor);
|
|
1989
|
+
const descendantStatus = this.protoArray.getDefaultVariant(descendant);
|
|
1990
|
+
if (ancestorStatus === undefined || descendantStatus === undefined) return false;
|
|
1991
|
+
return this.isDescendant(ancestor, ancestorStatus, descendant, descendantStatus);
|
|
1992
|
+
},
|
|
1993
|
+
getLatestMessage: (validatorIndex: ValidatorIndex) => {
|
|
1994
|
+
const nextIndex = this.voteNextIndices[validatorIndex];
|
|
1995
|
+
if (nextIndex === undefined || nextIndex === NULL_VOTE_INDEX) {
|
|
1996
|
+
return null;
|
|
1997
|
+
}
|
|
1998
|
+
const node = this.protoArray.nodes[nextIndex];
|
|
1999
|
+
if (!node) return null;
|
|
2000
|
+
return {root: node.blockRoot, epoch: computeEpochAtSlot(this.voteNextSlots[validatorIndex])};
|
|
2001
|
+
},
|
|
2002
|
+
getUnrealizedJustified: () => ({
|
|
2003
|
+
checkpoint: this.fcStore.unrealizedJustified.checkpoint,
|
|
2004
|
+
balances: this.fcStore.unrealizedJustified.balances,
|
|
2005
|
+
}),
|
|
2006
|
+
getFinalizedCheckpoint: () => this.fcStore.finalizedCheckpoint,
|
|
2007
|
+
getEquivocatingIndices: () => this.fcStore.equivocatingIndices,
|
|
2008
|
+
getTrackedVotesCount: () => {
|
|
2009
|
+
let count = 0;
|
|
2010
|
+
for (let i = 0; i < this.voteNextIndices.length; i++) {
|
|
2011
|
+
if (this.voteNextIndices[i] !== NULL_VOTE_INDEX) {
|
|
2012
|
+
count++;
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
return count;
|
|
2016
|
+
},
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
1879
2019
|
}
|
|
1880
2020
|
|
|
1881
2021
|
// Approximate https://github.com/ethereum/consensus-specs/blob/v1.6.1/specs/phase0/fork-choice.md#calculate_committee_fraction
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import {DataAvailabilityStatus, EffectiveBalanceIncrements, IBeaconStateView} from "@lodestar/state-transition";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AttesterSlashing,
|
|
4
|
+
BeaconBlock,
|
|
5
|
+
Epoch,
|
|
6
|
+
IndexedAttestation,
|
|
7
|
+
Root,
|
|
8
|
+
RootHex,
|
|
9
|
+
Slot,
|
|
10
|
+
ValidatorIndex,
|
|
11
|
+
} from "@lodestar/types";
|
|
3
12
|
import {
|
|
4
13
|
BlockExecutionStatus,
|
|
5
14
|
LVHExecResponse,
|
|
@@ -98,6 +107,8 @@ export interface IForkChoice {
|
|
|
98
107
|
*/
|
|
99
108
|
getHeadRoot(): RootHex;
|
|
100
109
|
getHead(): ProtoBlock;
|
|
110
|
+
getConfirmedRoot(): RootHex;
|
|
111
|
+
getConfirmedBlock(): ProtoBlock | null;
|
|
101
112
|
updateAndGetHead(mode: UpdateAndGetHeadOpt): {
|
|
102
113
|
head: ProtoBlock;
|
|
103
114
|
isHeadTimely?: boolean;
|
|
@@ -123,6 +134,10 @@ export interface IForkChoice {
|
|
|
123
134
|
getAllNodes(): ProtoNode[];
|
|
124
135
|
getFinalizedCheckpoint(): CheckpointWithHex;
|
|
125
136
|
getJustifiedCheckpoint(): CheckpointWithHex;
|
|
137
|
+
getUnrealizedJustifiedCheckpoint(): CheckpointWithHex;
|
|
138
|
+
getUnrealizedFinalizedCheckpoint(): CheckpointWithHex;
|
|
139
|
+
getProposerBoostRoot(): RootHex;
|
|
140
|
+
getPreviousProposerBoostRoot(): RootHex;
|
|
126
141
|
/**
|
|
127
142
|
* Add `block` to the fork choice DAG.
|
|
128
143
|
*
|
|
@@ -145,7 +160,8 @@ export interface IForkChoice {
|
|
|
145
160
|
blockDelaySec: number,
|
|
146
161
|
currentSlot: Slot,
|
|
147
162
|
executionStatus: BlockExecutionStatus,
|
|
148
|
-
dataAvailabilityStatus: DataAvailabilityStatus
|
|
163
|
+
dataAvailabilityStatus: DataAvailabilityStatus,
|
|
164
|
+
expectedProposerIndex: ValidatorIndex | null
|
|
149
165
|
): ProtoBlock;
|
|
150
166
|
/**
|
|
151
167
|
* Register `attestation` with the fork choice DAG so that it may influence future calls to `getHead`.
|
|
@@ -181,12 +197,14 @@ export interface IForkChoice {
|
|
|
181
197
|
* ## Specification
|
|
182
198
|
*
|
|
183
199
|
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.0/specs/gloas/fork-choice.md#new-notify_ptc_messages
|
|
184
|
-
*
|
|
185
|
-
* @param blockRoot - The beacon block root being attested
|
|
186
|
-
* @param ptcIndices - Array of PTC committee indices that voted
|
|
187
|
-
* @param payloadPresent - Whether validators attest the payload is present
|
|
188
200
|
*/
|
|
189
|
-
notifyPtcMessages(
|
|
201
|
+
notifyPtcMessages(
|
|
202
|
+
blockRoot: RootHex,
|
|
203
|
+
slot: Slot,
|
|
204
|
+
ptcIndices: number[],
|
|
205
|
+
payloadPresent: boolean,
|
|
206
|
+
blobDataAvailable: boolean
|
|
207
|
+
): void;
|
|
190
208
|
/**
|
|
191
209
|
* Notify fork choice that an execution payload has arrived (Gloas fork)
|
|
192
210
|
* Creates the FULL variant of a Gloas block when the payload becomes available
|
|
@@ -235,6 +253,15 @@ export interface IForkChoice {
|
|
|
235
253
|
hasPayloadHexUnsafe(blockRoot: RootHex): boolean;
|
|
236
254
|
getSlotsPresent(windowStart: number): number;
|
|
237
255
|
getPTCVotes(blockRootHex: RootHex): (boolean | null)[] | null;
|
|
256
|
+
/** Raw PTC vote tallies for the debug fork choice endpoint; `null` for pre-Gloas roots. */
|
|
257
|
+
getPTCVoteCounts(blockRootHex: RootHex): {
|
|
258
|
+
attesterCount: number;
|
|
259
|
+
payloadPresentCount: number;
|
|
260
|
+
dataAvailableCount: number;
|
|
261
|
+
} | null;
|
|
262
|
+
getPayloadTimelinessVotes(blockRootHex: RootHex): (boolean | null)[] | null;
|
|
263
|
+
getPayloadDataAvailabilityVotes(blockRootHex: RootHex): (boolean | null)[] | null;
|
|
264
|
+
|
|
238
265
|
/**
|
|
239
266
|
* Returns a `ProtoBlock` if the block is known **and** a descendant of the finalized root.
|
|
240
267
|
*/
|
|
@@ -244,6 +271,8 @@ export interface IForkChoice {
|
|
|
244
271
|
getBlockHexDefaultStatus(blockRoot: RootHex): ProtoBlock | null;
|
|
245
272
|
getBlockHexAndBlockHash(blockRoot: RootHex, blockHash: RootHex): ProtoBlock | null;
|
|
246
273
|
shouldExtendPayload(blockRoot: RootHex): boolean;
|
|
274
|
+
/** Spec: should_build_on_full(store, head) */
|
|
275
|
+
shouldBuildOnFull(head: ProtoBlock, slot: Slot): boolean;
|
|
247
276
|
getFinalizedBlock(): ProtoBlock;
|
|
248
277
|
getJustifiedBlock(): ProtoBlock;
|
|
249
278
|
getFinalizedCheckpointSlot(): Slot;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {ZERO_HASH_HEX} from "@lodestar/params";
|
|
2
2
|
import {Root, RootHex} from "@lodestar/types";
|
|
3
|
+
import {fromHex} from "@lodestar/utils";
|
|
3
4
|
import {IForkChoice} from "./interface.js";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -7,21 +8,28 @@ import {IForkChoice} from "./interface.js";
|
|
|
7
8
|
* that is safe from re-orgs. Normally this block is pretty close to the head of canonical
|
|
8
9
|
* chain which makes it valuable to expose a safe block to users.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
+
* @deprecated The merged fast-confirmation spec only defines `get_safe_execution_block_hash`.
|
|
11
12
|
*/
|
|
12
13
|
export function getSafeBeaconBlockRoot(fc: IForkChoice): Root {
|
|
14
|
+
const confirmedRoot = fc.getConfirmedRoot();
|
|
15
|
+
if (confirmedRoot && fc.hasBlockHex(confirmedRoot)) {
|
|
16
|
+
return fromHex(confirmedRoot);
|
|
17
|
+
}
|
|
13
18
|
return fc.getJustifiedCheckpoint().root;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
22
|
* Get execution payload hash for the safe block
|
|
18
|
-
* This function assumes that safe block is post Bellatrix and function should not be called otherwise.
|
|
19
23
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* https://github.com/ethereum/consensus-specs/blob/v1.6.0/fork_choice/safe-block.md#get_safe_execution_block_hash
|
|
24
|
+
* https://github.com/ethereum/consensus-specs/blob/master/fork_choice/safe-block.md#get_safe_execution_block_hash
|
|
24
25
|
*/
|
|
25
26
|
export function getSafeExecutionBlockHash(forkChoice: IForkChoice): RootHex {
|
|
26
|
-
|
|
27
|
+
const confirmedRoot = forkChoice.getConfirmedRoot();
|
|
28
|
+
if (confirmedRoot) {
|
|
29
|
+
const confirmedBlock = forkChoice.getBlockHexDefaultStatus(confirmedRoot);
|
|
30
|
+
if (confirmedBlock?.executionPayloadBlockHash) {
|
|
31
|
+
return confirmedBlock.executionPayloadBlockHash;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return ZERO_HASH_HEX;
|
|
27
35
|
}
|
package/src/forkChoice/store.ts
CHANGED
|
@@ -1,6 +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.ts";
|
|
4
5
|
import {CheckpointWithBalance, CheckpointWithTotalBalance} from "./interface.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -35,7 +36,7 @@ export type JustifiedBalancesGetter = (
|
|
|
35
36
|
* - The actual block DAG in `ProtoArray`.
|
|
36
37
|
* - `time` is represented using `Slot` instead of UNIX epoch `u64`.
|
|
37
38
|
*/
|
|
38
|
-
export interface IForkChoiceStore {
|
|
39
|
+
export interface IForkChoiceStore extends IFastConfirmationStore {
|
|
39
40
|
currentSlot: Slot;
|
|
40
41
|
get justified(): CheckpointWithTotalBalance;
|
|
41
42
|
set justified(justified: CheckpointWithBalance);
|
|
@@ -58,12 +59,27 @@ export class ForkChoiceStore implements IForkChoiceStore {
|
|
|
58
59
|
justifiedBalancesGetter: JustifiedBalancesGetter;
|
|
59
60
|
currentSlot: Slot;
|
|
60
61
|
|
|
62
|
+
// Fast Confirmation Rule spec fields
|
|
63
|
+
confirmedRoot: RootHex;
|
|
64
|
+
previousEpochObservedJustifiedCheckpoint: CheckpointWithHex;
|
|
65
|
+
currentEpochObservedJustifiedCheckpoint: CheckpointWithHex;
|
|
66
|
+
previousEpochGreatestUnrealizedCheckpoint: CheckpointWithHex;
|
|
67
|
+
previousSlotHead: RootHex;
|
|
68
|
+
currentSlotHead: RootHex;
|
|
69
|
+
|
|
70
|
+
// Fast Confirmation Rule internal fields
|
|
71
|
+
previousEpochObservedJustifiedBalances: JustifiedBalances;
|
|
72
|
+
currentEpochObservedJustifiedBalances: JustifiedBalances;
|
|
73
|
+
previousEpochGreatestUnrealizedBalances: JustifiedBalances;
|
|
74
|
+
stateGetter: ForkChoiceStateGetter;
|
|
75
|
+
|
|
61
76
|
constructor(
|
|
62
77
|
currentSlot: Slot,
|
|
63
78
|
justifiedCheckpoint: phase0.Checkpoint,
|
|
64
79
|
finalizedCheckpoint: phase0.Checkpoint,
|
|
65
80
|
justifiedBalances: EffectiveBalanceIncrements,
|
|
66
81
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
82
|
+
stateGetter: ForkChoiceStateGetter,
|
|
67
83
|
private readonly events?: {
|
|
68
84
|
onJustified: (cp: CheckpointWithHex) => void;
|
|
69
85
|
onFinalized: (cp: CheckpointWithHex) => void;
|
|
@@ -71,6 +87,7 @@ export class ForkChoiceStore implements IForkChoiceStore {
|
|
|
71
87
|
) {
|
|
72
88
|
this.justifiedBalancesGetter = justifiedBalancesGetter;
|
|
73
89
|
this.currentSlot = currentSlot;
|
|
90
|
+
this.stateGetter = stateGetter;
|
|
74
91
|
const justified = {
|
|
75
92
|
checkpoint: toCheckpointWithHex(justifiedCheckpoint),
|
|
76
93
|
balances: justifiedBalances,
|
|
@@ -80,6 +97,22 @@ export class ForkChoiceStore implements IForkChoiceStore {
|
|
|
80
97
|
this.unrealizedJustified = justified;
|
|
81
98
|
this._finalizedCheckpoint = toCheckpointWithHex(finalizedCheckpoint);
|
|
82
99
|
this.unrealizedFinalizedCheckpoint = this._finalizedCheckpoint;
|
|
100
|
+
|
|
101
|
+
// Initialize Fast Confirmation fields conservatively from finalized, matching
|
|
102
|
+
// the spec's get_fast_confirmation_store() behavior.
|
|
103
|
+
const finalizedCheckpointWithHex = toCheckpointWithHex(finalizedCheckpoint);
|
|
104
|
+
const finalizedState = stateGetter({checkpoint: finalizedCheckpointWithHex});
|
|
105
|
+
const finalizedBalances = finalizedState?.effectiveBalanceIncrements ?? justifiedBalances;
|
|
106
|
+
const anchorRoot = finalizedCheckpointWithHex.rootHex;
|
|
107
|
+
this.previousEpochObservedJustifiedCheckpoint = finalizedCheckpointWithHex;
|
|
108
|
+
this.currentEpochObservedJustifiedCheckpoint = finalizedCheckpointWithHex;
|
|
109
|
+
this.previousEpochGreatestUnrealizedCheckpoint = finalizedCheckpointWithHex;
|
|
110
|
+
this.confirmedRoot = anchorRoot;
|
|
111
|
+
this.previousEpochObservedJustifiedBalances = finalizedBalances;
|
|
112
|
+
this.currentEpochObservedJustifiedBalances = finalizedBalances;
|
|
113
|
+
this.previousEpochGreatestUnrealizedBalances = finalizedBalances;
|
|
114
|
+
this.previousSlotHead = anchorRoot;
|
|
115
|
+
this.currentSlotHead = anchorRoot;
|
|
83
116
|
}
|
|
84
117
|
|
|
85
118
|
get justified(): CheckpointWithTotalBalance {
|
package/src/index.ts
CHANGED
|
@@ -6,6 +6,17 @@ export {
|
|
|
6
6
|
type InvalidBlock,
|
|
7
7
|
InvalidBlockCode,
|
|
8
8
|
} from "./forkChoice/errors.js";
|
|
9
|
+
export {
|
|
10
|
+
type FastConfirmationBalanceSource,
|
|
11
|
+
type FastConfirmationContext,
|
|
12
|
+
type FastConfirmationMetrics,
|
|
13
|
+
type FastConfirmationResult,
|
|
14
|
+
FastConfirmationRule,
|
|
15
|
+
type ForkChoiceStateGetter,
|
|
16
|
+
type IFastConfirmationRule,
|
|
17
|
+
type IFastConfirmationStore,
|
|
18
|
+
getFastConfirmationMetrics,
|
|
19
|
+
} from "./forkChoice/fastConfirmation/fastConfirmationRule.ts";
|
|
9
20
|
export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt} from "./forkChoice/forkChoice.js";
|
|
10
21
|
export {
|
|
11
22
|
type AncestorResult,
|
package/src/metrics.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import {MetricsRegisterExtra} from "@lodestar/utils";
|
|
2
|
+
import {FastConfirmationMetrics, getFastConfirmationMetrics} from "./forkChoice/fastConfirmation/metrics.ts";
|
|
2
3
|
import {UpdateHeadOpt} from "./forkChoice/forkChoice.js";
|
|
3
4
|
import {NotReorgedReason} from "./forkChoice/interface.js";
|
|
4
5
|
|
|
5
|
-
export type ForkChoiceMetrics = ReturnType<typeof getForkChoiceMetrics
|
|
6
|
+
export type ForkChoiceMetrics = ReturnType<typeof getForkChoiceMetrics> & FastConfirmationMetrics;
|
|
6
7
|
|
|
7
8
|
export function getForkChoiceMetrics(register: MetricsRegisterExtra) {
|
|
8
9
|
return {
|
|
10
|
+
...getFastConfirmationMetrics(register),
|
|
9
11
|
forkChoice: {
|
|
10
12
|
findHead: register.histogram<{caller: string}>({
|
|
11
13
|
name: "beacon_fork_choice_find_head_seconds",
|