@lodestar/fork-choice 1.51.0-dev.837363b869 → 1.51.0-dev.83aae9f53e
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/utils.d.ts.map +1 -1
- package/lib/forkChoice/fastConfirmation/utils.js +19 -24
- package/lib/forkChoice/fastConfirmation/utils.js.map +1 -1
- package/lib/forkChoice/forkChoice.d.ts +18 -18
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +51 -35
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +10 -2
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/metrics.d.ts +1 -0
- package/lib/metrics.d.ts.map +1 -1
- package/lib/metrics.js +4 -0
- package/lib/metrics.js.map +1 -1
- package/lib/protoArray/protoArray.d.ts +15 -0
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +34 -0
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +8 -8
- package/src/forkChoice/fastConfirmation/utils.ts +21 -20
- package/src/forkChoice/forkChoice.ts +58 -37
- package/src/forkChoice/interface.ts +11 -3
- package/src/metrics.ts +4 -0
- package/src/protoArray/protoArray.ts +40 -0
|
@@ -86,8 +86,8 @@ export enum UpdateHeadOpt {
|
|
|
86
86
|
|
|
87
87
|
export type UpdateAndGetHeadOpt =
|
|
88
88
|
| {mode: UpdateHeadOpt.GetCanonicalHead}
|
|
89
|
-
| {mode: UpdateHeadOpt.GetProposerHead;
|
|
90
|
-
| {mode: UpdateHeadOpt.GetPredictedProposerHead;
|
|
89
|
+
| {mode: UpdateHeadOpt.GetProposerHead; msFromSlot: number; slot: Slot}
|
|
90
|
+
| {mode: UpdateHeadOpt.GetPredictedProposerHead; msFromSlot: number; slot: Slot};
|
|
91
91
|
|
|
92
92
|
// the initial vote epoch for all validators
|
|
93
93
|
const INIT_VOTE_SLOT: Slot = 0;
|
|
@@ -315,13 +315,13 @@ export class ForkChoice implements IForkChoice {
|
|
|
315
315
|
const canonicalHeadBlock = mode === UpdateHeadOpt.GetPredictedProposerHead ? this.getHead() : this.updateHead();
|
|
316
316
|
switch (mode) {
|
|
317
317
|
case UpdateHeadOpt.GetPredictedProposerHead:
|
|
318
|
-
return {head: this.predictProposerHead(canonicalHeadBlock, opt.
|
|
318
|
+
return {head: this.predictProposerHead(canonicalHeadBlock, opt.msFromSlot, opt.slot)};
|
|
319
319
|
case UpdateHeadOpt.GetProposerHead: {
|
|
320
320
|
const {
|
|
321
321
|
proposerHead: head,
|
|
322
322
|
isHeadTimely,
|
|
323
323
|
notReorgedReason,
|
|
324
|
-
} = this.getProposerHead(canonicalHeadBlock, opt.
|
|
324
|
+
} = this.getProposerHead(canonicalHeadBlock, opt.msFromSlot, opt.slot);
|
|
325
325
|
return {head, isHeadTimely, notReorgedReason};
|
|
326
326
|
}
|
|
327
327
|
case UpdateHeadOpt.GetCanonicalHead:
|
|
@@ -338,7 +338,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
338
338
|
// Note when proposer boost reorg is disabled, it always returns false
|
|
339
339
|
shouldOverrideForkChoiceUpdate(
|
|
340
340
|
headBlock: ProtoBlock,
|
|
341
|
-
|
|
341
|
+
msFromSlot: number,
|
|
342
342
|
currentSlot: Slot
|
|
343
343
|
): ShouldOverrideForkChoiceUpdateResult {
|
|
344
344
|
if (headBlock === null) {
|
|
@@ -369,7 +369,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
369
369
|
|
|
370
370
|
const currentTimeOk =
|
|
371
371
|
headBlock.slot === currentSlot ||
|
|
372
|
-
(proposalSlot === currentSlot && this.isProposingOnTime(
|
|
372
|
+
(proposalSlot === currentSlot && this.isProposingOnTime(msFromSlot, currentSlot));
|
|
373
373
|
|
|
374
374
|
// Mirror the proposer equivocation branch of getProposerHead(). The head slot's attestations are
|
|
375
375
|
// still queued at this point so the head is assumed weak, same as for the regular branch below.
|
|
@@ -422,6 +422,24 @@ export class ForkChoice implements IForkChoice {
|
|
|
422
422
|
return this.protoArray.shouldExtendPayload(blockRoot, this.proposerBoostRoot);
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
/** Inclusion list verdict for the payload of `blockRoot`, as reported by the execution engine */
|
|
426
|
+
recordPayloadInclusionListSatisfaction(blockRoot: RootHex, satisfied: boolean): boolean {
|
|
427
|
+
const changed = this.protoArray.recordPayloadInclusionListSatisfaction(blockRoot, satisfied);
|
|
428
|
+
if (changed && !satisfied) {
|
|
429
|
+
this.metrics?.forkChoice.unsatisfiedInclusionListBlocks.inc();
|
|
430
|
+
this.logger?.verbose("Payload does not satisfy the inclusion list constraints, not extending it", {blockRoot});
|
|
431
|
+
}
|
|
432
|
+
return changed;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
isPayloadInclusionListSatisfied(blockRoot: RootHex): boolean {
|
|
436
|
+
return this.protoArray.isPayloadInclusionListSatisfied(blockRoot);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
getPayloadInclusionListSatisfaction(blockRoot: RootHex): boolean | null {
|
|
440
|
+
return this.protoArray.getPayloadInclusionListSatisfaction(blockRoot);
|
|
441
|
+
}
|
|
442
|
+
|
|
425
443
|
/** Spec: should_build_on_full(store, head, slot) */
|
|
426
444
|
shouldBuildOnFull(head: ProtoBlock, slot: Slot): boolean {
|
|
427
445
|
return this.protoArray.shouldBuildOnFull(head, slot);
|
|
@@ -437,7 +455,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
437
455
|
* By calling this function, we assume we are the proposer of next slot
|
|
438
456
|
*
|
|
439
457
|
*/
|
|
440
|
-
predictProposerHead(headBlock: ProtoBlock,
|
|
458
|
+
predictProposerHead(headBlock: ProtoBlock, msFromSlot: number, currentSlot: Slot): ProtoBlock {
|
|
441
459
|
const {proposerBoost, proposerBoostReorg} = this.opts ?? {};
|
|
442
460
|
// Skip re-org attempt if proposer boost (reorg) are disabled
|
|
443
461
|
if (!proposerBoost || !proposerBoostReorg) {
|
|
@@ -450,7 +468,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
450
468
|
}
|
|
451
469
|
|
|
452
470
|
const blockRoot = headBlock.blockRoot;
|
|
453
|
-
const result = this.shouldOverrideForkChoiceUpdate(headBlock,
|
|
471
|
+
const result = this.shouldOverrideForkChoiceUpdate(headBlock, msFromSlot, currentSlot);
|
|
454
472
|
|
|
455
473
|
if (result.shouldOverrideFcu) {
|
|
456
474
|
this.logger?.verbose("Current head is weak. Predicting next block to be built on parent of head.", {
|
|
@@ -481,7 +499,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
481
499
|
*/
|
|
482
500
|
getProposerHead(
|
|
483
501
|
headBlock: ProtoBlock,
|
|
484
|
-
|
|
502
|
+
msFromSlot: number,
|
|
485
503
|
slot: Slot
|
|
486
504
|
): {proposerHead: ProtoBlock; isHeadTimely: boolean; notReorgedReason?: NotReorgedReason} {
|
|
487
505
|
const isHeadTimely = headBlock.timeliness;
|
|
@@ -538,7 +556,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
538
556
|
}
|
|
539
557
|
|
|
540
558
|
// Only re-org if we are proposing on-time
|
|
541
|
-
if (!this.isProposingOnTime(
|
|
559
|
+
if (!this.isProposingOnTime(msFromSlot, slot)) {
|
|
542
560
|
return {proposerHead, isHeadTimely, notReorgedReason: NotReorgedReason.NotProposingOnTime};
|
|
543
561
|
}
|
|
544
562
|
|
|
@@ -739,8 +757,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
739
757
|
onBlock(
|
|
740
758
|
block: BeaconBlock,
|
|
741
759
|
state: IBeaconStateView,
|
|
742
|
-
|
|
743
|
-
|
|
760
|
+
receiveDelayMs: number,
|
|
761
|
+
importDelayMs: number,
|
|
744
762
|
currentSlot: Slot,
|
|
745
763
|
executionStatus: BlockExecutionStatus,
|
|
746
764
|
dataAvailabilityStatus: DataAvailabilityStatus
|
|
@@ -812,7 +830,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
812
830
|
// Decide whether this block should receive proposer boost, and whether the block is timely.
|
|
813
831
|
// The store field `this.proposerBoostRoot` and `updateCheckpoints()` are mutated only after
|
|
814
832
|
// `protoArray.onBlock()` succeeds
|
|
815
|
-
const isTimely = this.isBlockReceivedTimely(block,
|
|
833
|
+
const isTimely = this.isBlockReceivedTimely(block, receiveDelayMs);
|
|
816
834
|
const isProposerBoostBlock =
|
|
817
835
|
this.opts?.proposerBoost === true &&
|
|
818
836
|
isTimely &&
|
|
@@ -831,14 +849,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
831
849
|
const blockEpoch = computeEpochAtSlot(slot);
|
|
832
850
|
|
|
833
851
|
// same logic to compute_pulled_up_tip in the spec, making it inline because of reusing variables
|
|
834
|
-
// If the parent checkpoints are already at the same epoch as the
|
|
835
|
-
// it's impossible for the unrealized checkpoints to differ from the parent's.
|
|
836
|
-
// holds true because:
|
|
852
|
+
// If the block has no slashings and the parent checkpoints are already at the same epoch as the
|
|
853
|
+
// block being imported, it's impossible for the unrealized checkpoints to differ from the parent's.
|
|
854
|
+
// This holds true because:
|
|
837
855
|
//
|
|
838
856
|
// 1. A child block cannot have lower FFG checkpoints than its parent.
|
|
839
857
|
// 2. A block in epoch `N` cannot contain attestations which would justify an epoch higher than `N`.
|
|
840
858
|
// 3. A block in epoch `N` cannot contain attestations which would finalize an epoch higher than `N - 1`.
|
|
841
859
|
//
|
|
860
|
+
// Slashings are excluded because they can reduce the unslashed target balance of the child state
|
|
861
|
+
// and therefore lower its unrealized checkpoints.
|
|
862
|
+
//
|
|
842
863
|
// This is an optimization. It should reduce the amount of times we run
|
|
843
864
|
// `process_justification_and_finalization` by approximately 1/3rd when the chain is
|
|
844
865
|
// performing optimally.
|
|
@@ -846,6 +867,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
846
867
|
let unrealizedFinalizedCheckpoint: CheckpointWithHex;
|
|
847
868
|
if (this.opts?.computeUnrealized) {
|
|
848
869
|
if (
|
|
870
|
+
block.body.proposerSlashings.length === 0 &&
|
|
871
|
+
block.body.attesterSlashings.length === 0 &&
|
|
849
872
|
parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
|
|
850
873
|
parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
|
|
851
874
|
) {
|
|
@@ -883,8 +906,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
883
906
|
targetRoot: toRootHex(targetRoot),
|
|
884
907
|
stateRoot: toRootHex(block.stateRoot),
|
|
885
908
|
timeliness: isTimely,
|
|
886
|
-
ptcTimeliness: this.isBlockPtcTimely(block,
|
|
887
|
-
importedTimely: this.isBlockImportedTimely(block,
|
|
909
|
+
ptcTimeliness: this.isBlockPtcTimely(block, receiveDelayMs),
|
|
910
|
+
importedTimely: this.isBlockImportedTimely(block, importDelayMs),
|
|
888
911
|
proposerIndex: block.proposerIndex,
|
|
889
912
|
|
|
890
913
|
justifiedEpoch: stateJustifiedEpoch,
|
|
@@ -980,14 +1003,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
980
1003
|
*
|
|
981
1004
|
* https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#on_attestation
|
|
982
1005
|
*
|
|
983
|
-
*
|
|
984
|
-
*
|
|
985
|
-
* too expensive to call again.
|
|
986
|
-
*
|
|
987
|
-
* ## Notes:
|
|
988
|
-
*
|
|
989
|
-
* The supplied `attestation` **must** pass the `in_valid_indexed_attestation` function as it
|
|
990
|
-
* will not be run here.
|
|
1006
|
+
* Does not run `is_valid_indexed_attestation`. The caller must ensure all of its
|
|
1007
|
+
* validity requirements except index ordering: `attestingIndices` may be unsorted.
|
|
991
1008
|
*/
|
|
992
1009
|
onAttestation(attestation: IndexedAttestation, attDataRoot: string, forceImport?: boolean): void {
|
|
993
1010
|
// Ignore any attestations to the zero hash.
|
|
@@ -1414,7 +1431,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
1414
1431
|
*
|
|
1415
1432
|
* `ancestors` is the raw walk and includes the previous finalized block as its last element —
|
|
1416
1433
|
* callers that don't want the boundary should slice it off themselves.
|
|
1417
|
-
*
|
|
1434
|
+
* For Gloas parent blocks, `ancestors` contains the selected EMPTY or FULL variant.
|
|
1435
|
+
* The other variant, if present in the traversed range, goes into `nonAncestors` with the same root.
|
|
1436
|
+
* Exclude roots present in `ancestors` before pruning by block root.
|
|
1418
1437
|
*/
|
|
1419
1438
|
getAllAncestorAndNonAncestorBlocks(
|
|
1420
1439
|
blockRoot: RootHex,
|
|
@@ -1522,7 +1541,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
1522
1541
|
|
|
1523
1542
|
/** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
|
|
1524
1543
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[] {
|
|
1525
|
-
return this.protoArray.nodes.filter(
|
|
1544
|
+
return this.protoArray.nodes.filter(
|
|
1545
|
+
(node) => node.parentRoot === parentRoot && (!isGloasBlock(node) || node.payloadStatus === PayloadStatus.PENDING)
|
|
1546
|
+
);
|
|
1526
1547
|
}
|
|
1527
1548
|
|
|
1528
1549
|
/** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
|
|
@@ -1531,7 +1552,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
1531
1552
|
const blocksAtSlot: ProtoBlock[] = [];
|
|
1532
1553
|
for (let i = 0, len = nodes.length; i < len; i++) {
|
|
1533
1554
|
const node = nodes[i];
|
|
1534
|
-
if (node.slot === slot) {
|
|
1555
|
+
if (node.slot === slot && (!isGloasBlock(node) || node.payloadStatus === PayloadStatus.PENDING)) {
|
|
1535
1556
|
blocksAtSlot.push(node);
|
|
1536
1557
|
}
|
|
1537
1558
|
}
|
|
@@ -1757,9 +1778,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
1757
1778
|
* Return true if the block is timely for the current slot.
|
|
1758
1779
|
* Child class can overwrite this for testing purpose.
|
|
1759
1780
|
*/
|
|
1760
|
-
protected isBlockReceivedTimely(block: BeaconBlock,
|
|
1781
|
+
protected isBlockReceivedTimely(block: BeaconBlock, blockDelayMs: number): boolean {
|
|
1761
1782
|
const fork = this.config.getForkName(block.slot);
|
|
1762
|
-
const isBeforeLateBlockCutoff =
|
|
1783
|
+
const isBeforeLateBlockCutoff = blockDelayMs < this.config.getAttestationDueMs(fork);
|
|
1763
1784
|
return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
|
|
1764
1785
|
}
|
|
1765
1786
|
|
|
@@ -1773,12 +1794,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1773
1794
|
*
|
|
1774
1795
|
* Child class can overwrite this for testing purpose.
|
|
1775
1796
|
*/
|
|
1776
|
-
protected isBlockPtcTimely(block: BeaconBlock,
|
|
1797
|
+
protected isBlockPtcTimely(block: BeaconBlock, blockDelayMs: number): boolean {
|
|
1777
1798
|
const ptcThresholdMs = this.config.getSlotComponentDurationMs(
|
|
1778
1799
|
this.config.getForkName(block.slot),
|
|
1779
1800
|
this.config.PAYLOAD_ATTESTATION_DUE_BPS
|
|
1780
1801
|
);
|
|
1781
|
-
return this.fcStore.currentSlot === block.slot &&
|
|
1802
|
+
return this.fcStore.currentSlot === block.slot && blockDelayMs < ptcThresholdMs;
|
|
1782
1803
|
}
|
|
1783
1804
|
|
|
1784
1805
|
/**
|
|
@@ -1786,9 +1807,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
1786
1807
|
* block was imported in a timely manner for its own slot.
|
|
1787
1808
|
* This is not part of the spec, we use this to determine late canonical blocks.
|
|
1788
1809
|
*/
|
|
1789
|
-
protected isBlockImportedTimely(block: BeaconBlock,
|
|
1810
|
+
protected isBlockImportedTimely(block: BeaconBlock, importDelayMs: number): boolean {
|
|
1790
1811
|
const fork = this.config.getForkName(block.slot);
|
|
1791
|
-
const isBeforeLateBlockCutoff =
|
|
1812
|
+
const isBeforeLateBlockCutoff = importDelayMs < this.config.getAttestationDueMs(fork);
|
|
1792
1813
|
return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
|
|
1793
1814
|
}
|
|
1794
1815
|
|
|
@@ -1873,10 +1894,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
1873
1894
|
/**
|
|
1874
1895
|
* https://github.com/ethereum/consensus-specs/blob/v1.5.0/specs/phase0/fork-choice.md#is_proposing_on_time
|
|
1875
1896
|
*/
|
|
1876
|
-
private isProposingOnTime(
|
|
1897
|
+
private isProposingOnTime(msFromSlot: number, slot: Slot): boolean {
|
|
1877
1898
|
const fork = this.config.getForkName(slot);
|
|
1878
1899
|
const proposerReorgCutoff = this.config.getProposerReorgCutoffMs(fork);
|
|
1879
|
-
return
|
|
1900
|
+
return msFromSlot <= proposerReorgCutoff;
|
|
1880
1901
|
}
|
|
1881
1902
|
|
|
1882
1903
|
private getPreMergeExecStatus(executionStatus: BlockExecutionStatus): ExecutionStatus.PreMerge {
|
|
@@ -123,7 +123,7 @@ export interface IForkChoice {
|
|
|
123
123
|
*/
|
|
124
124
|
shouldOverrideForkChoiceUpdate(
|
|
125
125
|
headBlock: ProtoBlock,
|
|
126
|
-
|
|
126
|
+
msFromSlot: number,
|
|
127
127
|
currentSlot: Slot
|
|
128
128
|
): ShouldOverrideForkChoiceUpdateResult;
|
|
129
129
|
/**
|
|
@@ -159,8 +159,8 @@ export interface IForkChoice {
|
|
|
159
159
|
onBlock(
|
|
160
160
|
block: BeaconBlock,
|
|
161
161
|
state: IBeaconStateView,
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
receiveDelayMs: number,
|
|
163
|
+
importDelayMs: number,
|
|
164
164
|
currentSlot: Slot,
|
|
165
165
|
executionStatus: BlockExecutionStatus,
|
|
166
166
|
dataAvailabilityStatus: DataAvailabilityStatus
|
|
@@ -279,6 +279,12 @@ export interface IForkChoice {
|
|
|
279
279
|
shouldExtendPayload(blockRoot: RootHex): boolean;
|
|
280
280
|
/** Spec: should_build_on_full(store, head, slot) */
|
|
281
281
|
shouldBuildOnFull(head: ProtoBlock, slot: Slot): boolean;
|
|
282
|
+
/** Inclusion list verdict for the payload of `blockRoot`, as reported by the execution engine. True if it changed */
|
|
283
|
+
recordPayloadInclusionListSatisfaction(blockRoot: RootHex, satisfied: boolean): boolean;
|
|
284
|
+
/** True once the payload is delivered and not recorded unsatisfied */
|
|
285
|
+
isPayloadInclusionListSatisfied(blockRoot: RootHex): boolean;
|
|
286
|
+
/** Engine verdict recorded for the payload of `blockRoot`, null when none was recorded */
|
|
287
|
+
getPayloadInclusionListSatisfaction(blockRoot: RootHex): boolean | null;
|
|
282
288
|
getFinalizedBlock(): ProtoBlock;
|
|
283
289
|
getJustifiedBlock(): ProtoBlock;
|
|
284
290
|
getFinalizedCheckpointSlot(): Slot;
|
|
@@ -344,7 +350,9 @@ export interface IForkChoice {
|
|
|
344
350
|
* `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
|
|
345
351
|
*/
|
|
346
352
|
forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock>;
|
|
353
|
+
/** One summary per block, its default variant (FULL pre-Gloas, PENDING for Gloas) */
|
|
347
354
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
|
|
355
|
+
/** One summary per block, its default variant (FULL pre-Gloas, PENDING for Gloas) */
|
|
348
356
|
getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
|
|
349
357
|
/** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
|
|
350
358
|
getCommonAncestorDepth(prevBlock: ProtoBlock, newBlock: ProtoBlock): AncestorResult;
|
package/src/metrics.ts
CHANGED
|
@@ -19,6 +19,10 @@ export function getForkChoiceMetrics(register: MetricsRegisterExtra) {
|
|
|
19
19
|
name: "beacon_fork_choice_requests_total",
|
|
20
20
|
help: "Count of occasions where fork choice has tried to find a head",
|
|
21
21
|
}),
|
|
22
|
+
unsatisfiedInclusionListBlocks: register.gauge({
|
|
23
|
+
name: "beacon_fork_choice_unsatisfied_inclusion_list_blocks_total",
|
|
24
|
+
help: "Count of payloads not extended because they did not satisfy the inclusion list constraints",
|
|
25
|
+
}),
|
|
22
26
|
errors: register.gauge<{entrypoint: UpdateHeadOpt}>({
|
|
23
27
|
name: "beacon_fork_choice_errors_total",
|
|
24
28
|
help: "Count of occasions where fork choice has returned an error when trying to find a head",
|
|
@@ -122,6 +122,11 @@ export class ProtoArray {
|
|
|
122
122
|
* when called with the negative parameter value.
|
|
123
123
|
*/
|
|
124
124
|
private ptcAttested = new Map<RootHex, BitArray>();
|
|
125
|
+
/**
|
|
126
|
+
* Execution engine inclusion list verdict per block, recorded on payload import from heze.
|
|
127
|
+
* Spec: heze/fork-choice.md#modified-store (payload_inclusion_list_satisfaction)
|
|
128
|
+
*/
|
|
129
|
+
private payloadInclusionListSatisfaction = new Map<RootHex, boolean>();
|
|
125
130
|
|
|
126
131
|
constructor({
|
|
127
132
|
pruneThreshold,
|
|
@@ -938,6 +943,27 @@ export class ProtoArray {
|
|
|
938
943
|
return this.getParentPayloadStatus(block) === PayloadStatus.FULL;
|
|
939
944
|
}
|
|
940
945
|
|
|
946
|
+
/** Spec: heze/fork-choice.md#new-record_payload_inclusion_list_satisfaction. Returns whether the verdict changed */
|
|
947
|
+
recordPayloadInclusionListSatisfaction(blockRoot: RootHex, satisfied: boolean): boolean {
|
|
948
|
+
const previous = this.payloadInclusionListSatisfaction.get(blockRoot);
|
|
949
|
+
this.payloadInclusionListSatisfaction.set(blockRoot, satisfied);
|
|
950
|
+
return previous !== satisfied;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Spec: heze/fork-choice.md#new-is_payload_inclusion_list_satisfied
|
|
955
|
+
*
|
|
956
|
+
* False until the payload is delivered. A delivered payload without a verdict is pre-heze, where
|
|
957
|
+
* no inclusion list constraints apply.
|
|
958
|
+
*/
|
|
959
|
+
isPayloadInclusionListSatisfied(blockRoot: RootHex): boolean {
|
|
960
|
+
return this.hasPayload(blockRoot) && this.payloadInclusionListSatisfaction.get(blockRoot) !== false;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
getPayloadInclusionListSatisfaction(blockRoot: RootHex): boolean | null {
|
|
964
|
+
return this.payloadInclusionListSatisfaction.get(blockRoot) ?? null;
|
|
965
|
+
}
|
|
966
|
+
|
|
941
967
|
/**
|
|
942
968
|
* Determine if we should extend the payload (prefer FULL over EMPTY)
|
|
943
969
|
* Spec: gloas/fork-choice.md#new-should_extend_payload
|
|
@@ -956,6 +982,10 @@ export class ProtoArray {
|
|
|
956
982
|
return false;
|
|
957
983
|
}
|
|
958
984
|
|
|
985
|
+
if (this.payloadInclusionListSatisfaction.get(blockRoot) === false) {
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
|
|
959
989
|
// Condition 1: Payload is timely AND data is available
|
|
960
990
|
if (this.isPayloadTimely(blockRoot) && this.isPayloadDataAvailable(blockRoot)) {
|
|
961
991
|
return true;
|
|
@@ -1265,6 +1295,15 @@ export class ProtoArray {
|
|
|
1265
1295
|
|
|
1266
1296
|
// For Gloas: check if from previous slot
|
|
1267
1297
|
if (node.slot + 1 !== currentSlot) {
|
|
1298
|
+
// During the block's own slot a payload known to not satisfy the inclusion list
|
|
1299
|
+
// constraints ranks below its EMPTY variant, as it will from the next slot on
|
|
1300
|
+
if (
|
|
1301
|
+
node.slot === currentSlot &&
|
|
1302
|
+
node.payloadStatus === PayloadStatus.FULL &&
|
|
1303
|
+
this.payloadInclusionListSatisfaction.get(node.blockRoot) === false
|
|
1304
|
+
) {
|
|
1305
|
+
return PayloadStatus.PENDING;
|
|
1306
|
+
}
|
|
1268
1307
|
return node.payloadStatus;
|
|
1269
1308
|
}
|
|
1270
1309
|
|
|
@@ -1399,6 +1438,7 @@ export class ProtoArray {
|
|
|
1399
1438
|
this.payloadTimelinessVotes.delete(root);
|
|
1400
1439
|
this.ptcAttested.delete(root);
|
|
1401
1440
|
this.payloadDataAvailabilityVotes.delete(root);
|
|
1441
|
+
this.payloadInclusionListSatisfaction.delete(root);
|
|
1402
1442
|
}
|
|
1403
1443
|
|
|
1404
1444
|
// Store nodes prior to finalization
|