@lodestar/fork-choice 1.51.0-dev.e22f67e7bc → 1.51.0-dev.e8f7876d67
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 +12 -10
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +32 -25
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +2 -2
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/forkChoice/fastConfirmation/utils.ts +21 -20
- package/src/forkChoice/forkChoice.ts +34 -27
- package/src/forkChoice/interface.ts +3 -3
|
@@ -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.
|
|
@@ -455,7 +455,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
455
455
|
* By calling this function, we assume we are the proposer of next slot
|
|
456
456
|
*
|
|
457
457
|
*/
|
|
458
|
-
predictProposerHead(headBlock: ProtoBlock,
|
|
458
|
+
predictProposerHead(headBlock: ProtoBlock, msFromSlot: number, currentSlot: Slot): ProtoBlock {
|
|
459
459
|
const {proposerBoost, proposerBoostReorg} = this.opts ?? {};
|
|
460
460
|
// Skip re-org attempt if proposer boost (reorg) are disabled
|
|
461
461
|
if (!proposerBoost || !proposerBoostReorg) {
|
|
@@ -468,7 +468,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
468
468
|
}
|
|
469
469
|
|
|
470
470
|
const blockRoot = headBlock.blockRoot;
|
|
471
|
-
const result = this.shouldOverrideForkChoiceUpdate(headBlock,
|
|
471
|
+
const result = this.shouldOverrideForkChoiceUpdate(headBlock, msFromSlot, currentSlot);
|
|
472
472
|
|
|
473
473
|
if (result.shouldOverrideFcu) {
|
|
474
474
|
this.logger?.verbose("Current head is weak. Predicting next block to be built on parent of head.", {
|
|
@@ -499,7 +499,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
499
499
|
*/
|
|
500
500
|
getProposerHead(
|
|
501
501
|
headBlock: ProtoBlock,
|
|
502
|
-
|
|
502
|
+
msFromSlot: number,
|
|
503
503
|
slot: Slot
|
|
504
504
|
): {proposerHead: ProtoBlock; isHeadTimely: boolean; notReorgedReason?: NotReorgedReason} {
|
|
505
505
|
const isHeadTimely = headBlock.timeliness;
|
|
@@ -556,7 +556,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
556
556
|
}
|
|
557
557
|
|
|
558
558
|
// Only re-org if we are proposing on-time
|
|
559
|
-
if (!this.isProposingOnTime(
|
|
559
|
+
if (!this.isProposingOnTime(msFromSlot, slot)) {
|
|
560
560
|
return {proposerHead, isHeadTimely, notReorgedReason: NotReorgedReason.NotProposingOnTime};
|
|
561
561
|
}
|
|
562
562
|
|
|
@@ -757,8 +757,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
757
757
|
onBlock(
|
|
758
758
|
block: BeaconBlock,
|
|
759
759
|
state: IBeaconStateView,
|
|
760
|
-
|
|
761
|
-
|
|
760
|
+
receiveDelayMs: number,
|
|
761
|
+
importDelayMs: number,
|
|
762
762
|
currentSlot: Slot,
|
|
763
763
|
executionStatus: BlockExecutionStatus,
|
|
764
764
|
dataAvailabilityStatus: DataAvailabilityStatus
|
|
@@ -830,7 +830,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
830
830
|
// Decide whether this block should receive proposer boost, and whether the block is timely.
|
|
831
831
|
// The store field `this.proposerBoostRoot` and `updateCheckpoints()` are mutated only after
|
|
832
832
|
// `protoArray.onBlock()` succeeds
|
|
833
|
-
const isTimely = this.isBlockReceivedTimely(block,
|
|
833
|
+
const isTimely = this.isBlockReceivedTimely(block, receiveDelayMs);
|
|
834
834
|
const isProposerBoostBlock =
|
|
835
835
|
this.opts?.proposerBoost === true &&
|
|
836
836
|
isTimely &&
|
|
@@ -849,14 +849,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
849
849
|
const blockEpoch = computeEpochAtSlot(slot);
|
|
850
850
|
|
|
851
851
|
// same logic to compute_pulled_up_tip in the spec, making it inline because of reusing variables
|
|
852
|
-
// If the parent checkpoints are already at the same epoch as the
|
|
853
|
-
// it's impossible for the unrealized checkpoints to differ from the parent's.
|
|
854
|
-
// 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:
|
|
855
855
|
//
|
|
856
856
|
// 1. A child block cannot have lower FFG checkpoints than its parent.
|
|
857
857
|
// 2. A block in epoch `N` cannot contain attestations which would justify an epoch higher than `N`.
|
|
858
858
|
// 3. A block in epoch `N` cannot contain attestations which would finalize an epoch higher than `N - 1`.
|
|
859
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
|
+
//
|
|
860
863
|
// This is an optimization. It should reduce the amount of times we run
|
|
861
864
|
// `process_justification_and_finalization` by approximately 1/3rd when the chain is
|
|
862
865
|
// performing optimally.
|
|
@@ -864,6 +867,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
864
867
|
let unrealizedFinalizedCheckpoint: CheckpointWithHex;
|
|
865
868
|
if (this.opts?.computeUnrealized) {
|
|
866
869
|
if (
|
|
870
|
+
block.body.proposerSlashings.length === 0 &&
|
|
871
|
+
block.body.attesterSlashings.length === 0 &&
|
|
867
872
|
parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
|
|
868
873
|
parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
|
|
869
874
|
) {
|
|
@@ -901,8 +906,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
901
906
|
targetRoot: toRootHex(targetRoot),
|
|
902
907
|
stateRoot: toRootHex(block.stateRoot),
|
|
903
908
|
timeliness: isTimely,
|
|
904
|
-
ptcTimeliness: this.isBlockPtcTimely(block,
|
|
905
|
-
importedTimely: this.isBlockImportedTimely(block,
|
|
909
|
+
ptcTimeliness: this.isBlockPtcTimely(block, receiveDelayMs),
|
|
910
|
+
importedTimely: this.isBlockImportedTimely(block, importDelayMs),
|
|
906
911
|
proposerIndex: block.proposerIndex,
|
|
907
912
|
|
|
908
913
|
justifiedEpoch: stateJustifiedEpoch,
|
|
@@ -1432,7 +1437,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
1432
1437
|
*
|
|
1433
1438
|
* `ancestors` is the raw walk and includes the previous finalized block as its last element —
|
|
1434
1439
|
* callers that don't want the boundary should slice it off themselves.
|
|
1435
|
-
*
|
|
1440
|
+
* For Gloas parent blocks, `ancestors` contains the selected EMPTY or FULL variant.
|
|
1441
|
+
* The other variant, if present in the traversed range, goes into `nonAncestors` with the same root.
|
|
1442
|
+
* Exclude roots present in `ancestors` before pruning by block root.
|
|
1436
1443
|
*/
|
|
1437
1444
|
getAllAncestorAndNonAncestorBlocks(
|
|
1438
1445
|
blockRoot: RootHex,
|
|
@@ -1775,9 +1782,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
1775
1782
|
* Return true if the block is timely for the current slot.
|
|
1776
1783
|
* Child class can overwrite this for testing purpose.
|
|
1777
1784
|
*/
|
|
1778
|
-
protected isBlockReceivedTimely(block: BeaconBlock,
|
|
1785
|
+
protected isBlockReceivedTimely(block: BeaconBlock, blockDelayMs: number): boolean {
|
|
1779
1786
|
const fork = this.config.getForkName(block.slot);
|
|
1780
|
-
const isBeforeLateBlockCutoff =
|
|
1787
|
+
const isBeforeLateBlockCutoff = blockDelayMs < this.config.getAttestationDueMs(fork);
|
|
1781
1788
|
return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
|
|
1782
1789
|
}
|
|
1783
1790
|
|
|
@@ -1791,12 +1798,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1791
1798
|
*
|
|
1792
1799
|
* Child class can overwrite this for testing purpose.
|
|
1793
1800
|
*/
|
|
1794
|
-
protected isBlockPtcTimely(block: BeaconBlock,
|
|
1801
|
+
protected isBlockPtcTimely(block: BeaconBlock, blockDelayMs: number): boolean {
|
|
1795
1802
|
const ptcThresholdMs = this.config.getSlotComponentDurationMs(
|
|
1796
1803
|
this.config.getForkName(block.slot),
|
|
1797
1804
|
this.config.PAYLOAD_ATTESTATION_DUE_BPS
|
|
1798
1805
|
);
|
|
1799
|
-
return this.fcStore.currentSlot === block.slot &&
|
|
1806
|
+
return this.fcStore.currentSlot === block.slot && blockDelayMs < ptcThresholdMs;
|
|
1800
1807
|
}
|
|
1801
1808
|
|
|
1802
1809
|
/**
|
|
@@ -1804,9 +1811,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
1804
1811
|
* block was imported in a timely manner for its own slot.
|
|
1805
1812
|
* This is not part of the spec, we use this to determine late canonical blocks.
|
|
1806
1813
|
*/
|
|
1807
|
-
protected isBlockImportedTimely(block: BeaconBlock,
|
|
1814
|
+
protected isBlockImportedTimely(block: BeaconBlock, importDelayMs: number): boolean {
|
|
1808
1815
|
const fork = this.config.getForkName(block.slot);
|
|
1809
|
-
const isBeforeLateBlockCutoff =
|
|
1816
|
+
const isBeforeLateBlockCutoff = importDelayMs < this.config.getAttestationDueMs(fork);
|
|
1810
1817
|
return this.fcStore.currentSlot === block.slot && isBeforeLateBlockCutoff;
|
|
1811
1818
|
}
|
|
1812
1819
|
|
|
@@ -1891,10 +1898,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
1891
1898
|
/**
|
|
1892
1899
|
* https://github.com/ethereum/consensus-specs/blob/v1.5.0/specs/phase0/fork-choice.md#is_proposing_on_time
|
|
1893
1900
|
*/
|
|
1894
|
-
private isProposingOnTime(
|
|
1901
|
+
private isProposingOnTime(msFromSlot: number, slot: Slot): boolean {
|
|
1895
1902
|
const fork = this.config.getForkName(slot);
|
|
1896
1903
|
const proposerReorgCutoff = this.config.getProposerReorgCutoffMs(fork);
|
|
1897
|
-
return
|
|
1904
|
+
return msFromSlot <= proposerReorgCutoff;
|
|
1898
1905
|
}
|
|
1899
1906
|
|
|
1900
1907
|
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
|