@lodestar/fork-choice 1.42.0 → 1.43.0-dev.07875b3e0c
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 +8 -1
- package/lib/forkChoice/errors.d.ts.map +1 -1
- package/lib/forkChoice/errors.js +4 -0
- package/lib/forkChoice/errors.js.map +1 -1
- package/lib/forkChoice/forkChoice.d.ts +22 -16
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +88 -70
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +26 -8
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/forkChoice/store.d.ts +16 -40
- package/lib/forkChoice/store.d.ts.map +1 -1
- package/lib/forkChoice/store.js +4 -22
- package/lib/forkChoice/store.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/protoArray/protoArray.d.ts +2 -2
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +26 -12
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +8 -8
- package/src/forkChoice/errors.ts +6 -1
- package/src/forkChoice/forkChoice.ts +101 -113
- package/src/forkChoice/interface.ts +25 -8
- package/src/forkChoice/store.ts +20 -52
- package/src/index.ts +3 -9
- package/src/protoArray/protoArray.ts +27 -12
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {
|
|
2
|
+
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
3
3
|
import {
|
|
4
4
|
DataAvailabilityStatus,
|
|
5
5
|
EffectiveBalanceIncrements,
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
computeStartSlotAtEpoch,
|
|
11
11
|
getAttesterSlashableIndices,
|
|
12
12
|
isExecutionBlockBodyType,
|
|
13
|
+
isStatePostBellatrix,
|
|
13
14
|
} from "@lodestar/state-transition";
|
|
14
15
|
import {
|
|
15
16
|
AttesterSlashing,
|
|
@@ -51,7 +52,7 @@ import {
|
|
|
51
52
|
NotReorgedReason,
|
|
52
53
|
ShouldOverrideForkChoiceUpdateResult,
|
|
53
54
|
} from "./interface.js";
|
|
54
|
-
import {
|
|
55
|
+
import {CheckpointWithHex, IForkChoiceStore, JustifiedBalances, toCheckpointWithHex} from "./store.js";
|
|
55
56
|
|
|
56
57
|
export type ForkChoiceOpts = {
|
|
57
58
|
proposerBoost?: boolean;
|
|
@@ -309,6 +310,14 @@ export class ForkChoice implements IForkChoice {
|
|
|
309
310
|
return this.proposerBoostRoot ?? HEX_ZERO_HASH;
|
|
310
311
|
}
|
|
311
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Decides whether to extend an available payload from the previous slot,
|
|
315
|
+
* corresponding to the beacon block `blockRoot`.
|
|
316
|
+
*/
|
|
317
|
+
shouldExtendPayload(blockRoot: RootHex): boolean {
|
|
318
|
+
return this.protoArray.shouldExtendPayload(blockRoot, this.proposerBoostRoot);
|
|
319
|
+
}
|
|
320
|
+
|
|
312
321
|
/**
|
|
313
322
|
* To predict the proposer head of the next slot. That is, to predict if proposer-boost-reorg could happen.
|
|
314
323
|
* Reason why we can't be certain is because information of the head block is not fully available yet
|
|
@@ -554,11 +563,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
554
563
|
return this.protoArray.nodes;
|
|
555
564
|
}
|
|
556
565
|
|
|
557
|
-
getFinalizedCheckpoint():
|
|
566
|
+
getFinalizedCheckpoint(): CheckpointWithHex {
|
|
558
567
|
return this.fcStore.finalizedCheckpoint;
|
|
559
568
|
}
|
|
560
569
|
|
|
561
|
-
getJustifiedCheckpoint():
|
|
570
|
+
getJustifiedCheckpoint(): CheckpointWithHex {
|
|
562
571
|
return this.fcStore.justified.checkpoint;
|
|
563
572
|
}
|
|
564
573
|
|
|
@@ -639,10 +648,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
639
648
|
// Check block is a descendant of the finalized block at the checkpoint finalized slot.
|
|
640
649
|
const blockAncestorNode = this.getAncestor(parentRootHex, finalizedSlot);
|
|
641
650
|
const fcStoreFinalized = this.fcStore.finalizedCheckpoint;
|
|
642
|
-
if (
|
|
643
|
-
blockAncestorNode.blockRoot !== fcStoreFinalized.rootHex ||
|
|
644
|
-
blockAncestorNode.payloadStatus !== fcStoreFinalized.payloadStatus
|
|
645
|
-
) {
|
|
651
|
+
if (blockAncestorNode.blockRoot !== fcStoreFinalized.rootHex) {
|
|
646
652
|
throw new ForkChoiceError({
|
|
647
653
|
code: ForkChoiceErrorCode.INVALID_BLOCK,
|
|
648
654
|
err: {
|
|
@@ -668,18 +674,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
668
674
|
this.proposerBoostRoot = blockRootHex;
|
|
669
675
|
}
|
|
670
676
|
|
|
671
|
-
|
|
672
|
-
const justifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
673
|
-
this.config,
|
|
674
|
-
state,
|
|
675
|
-
state.currentJustifiedCheckpoint.epoch
|
|
676
|
-
);
|
|
677
|
-
const justifiedCheckpoint = toCheckpointWithPayload(state.currentJustifiedCheckpoint, justifiedPayloadStatus);
|
|
677
|
+
const justifiedCheckpoint = toCheckpointWithHex(state.currentJustifiedCheckpoint);
|
|
678
678
|
const stateJustifiedEpoch = justifiedCheckpoint.epoch;
|
|
679
679
|
|
|
680
|
-
|
|
681
|
-
const finalizedPayloadStatus = getCheckpointPayloadStatus(this.config, state, state.finalizedCheckpoint.epoch);
|
|
682
|
-
const finalizedCheckpoint = toCheckpointWithPayload(state.finalizedCheckpoint, finalizedPayloadStatus);
|
|
680
|
+
const finalizedCheckpoint = toCheckpointWithHex(state.finalizedCheckpoint);
|
|
683
681
|
|
|
684
682
|
// Justified balances for `justifiedCheckpoint` are new to the fork-choice. Compute them on demand only if
|
|
685
683
|
// the justified checkpoint changes
|
|
@@ -701,61 +699,29 @@ export class ForkChoice implements IForkChoice {
|
|
|
701
699
|
// This is an optimization. It should reduce the amount of times we run
|
|
702
700
|
// `process_justification_and_finalization` by approximately 1/3rd when the chain is
|
|
703
701
|
// performing optimally.
|
|
704
|
-
let unrealizedJustifiedCheckpoint:
|
|
705
|
-
let unrealizedFinalizedCheckpoint:
|
|
702
|
+
let unrealizedJustifiedCheckpoint: CheckpointWithHex;
|
|
703
|
+
let unrealizedFinalizedCheckpoint: CheckpointWithHex;
|
|
706
704
|
if (this.opts?.computeUnrealized) {
|
|
707
705
|
if (
|
|
708
706
|
parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
|
|
709
707
|
parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
|
|
710
708
|
) {
|
|
711
709
|
// reuse from parent, happens at 1/3 last blocks of epoch as monitored in mainnet
|
|
712
|
-
// Get payload status for unrealized justified checkpoint
|
|
713
|
-
const unrealizedJustifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
714
|
-
this.config,
|
|
715
|
-
state,
|
|
716
|
-
parentBlock.unrealizedJustifiedEpoch
|
|
717
|
-
);
|
|
718
710
|
unrealizedJustifiedCheckpoint = {
|
|
719
711
|
epoch: parentBlock.unrealizedJustifiedEpoch,
|
|
720
712
|
root: fromHex(parentBlock.unrealizedJustifiedRoot),
|
|
721
713
|
rootHex: parentBlock.unrealizedJustifiedRoot,
|
|
722
|
-
payloadStatus: unrealizedJustifiedPayloadStatus,
|
|
723
714
|
};
|
|
724
|
-
// Get payload status for unrealized finalized checkpoint
|
|
725
|
-
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
726
|
-
this.config,
|
|
727
|
-
state,
|
|
728
|
-
parentBlock.unrealizedFinalizedEpoch
|
|
729
|
-
);
|
|
730
715
|
unrealizedFinalizedCheckpoint = {
|
|
731
716
|
epoch: parentBlock.unrealizedFinalizedEpoch,
|
|
732
717
|
root: fromHex(parentBlock.unrealizedFinalizedRoot),
|
|
733
718
|
rootHex: parentBlock.unrealizedFinalizedRoot,
|
|
734
|
-
payloadStatus: unrealizedFinalizedPayloadStatus,
|
|
735
719
|
};
|
|
736
720
|
} else {
|
|
737
721
|
// compute new, happens 2/3 first blocks of epoch as monitored in mainnet
|
|
738
722
|
const unrealized = state.computeUnrealizedCheckpoints();
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
this.config,
|
|
742
|
-
state,
|
|
743
|
-
unrealized.justifiedCheckpoint.epoch
|
|
744
|
-
);
|
|
745
|
-
unrealizedJustifiedCheckpoint = toCheckpointWithPayload(
|
|
746
|
-
unrealized.justifiedCheckpoint,
|
|
747
|
-
unrealizedJustifiedPayloadStatus
|
|
748
|
-
);
|
|
749
|
-
// Get payload status for unrealized finalized checkpoint
|
|
750
|
-
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
751
|
-
this.config,
|
|
752
|
-
state,
|
|
753
|
-
unrealized.finalizedCheckpoint.epoch
|
|
754
|
-
);
|
|
755
|
-
unrealizedFinalizedCheckpoint = toCheckpointWithPayload(
|
|
756
|
-
unrealized.finalizedCheckpoint,
|
|
757
|
-
unrealizedFinalizedPayloadStatus
|
|
758
|
-
);
|
|
723
|
+
unrealizedJustifiedCheckpoint = toCheckpointWithHex(unrealized.justifiedCheckpoint);
|
|
724
|
+
unrealizedFinalizedCheckpoint = toCheckpointWithHex(unrealized.finalizedCheckpoint);
|
|
759
725
|
}
|
|
760
726
|
} else {
|
|
761
727
|
unrealizedJustifiedCheckpoint = justifiedCheckpoint;
|
|
@@ -825,7 +791,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
825
791
|
executionStatus: this.getPostGloasExecStatus(executionStatus),
|
|
826
792
|
dataAvailabilityStatus,
|
|
827
793
|
}
|
|
828
|
-
: isExecutionBlockBodyType(block.body) &&
|
|
794
|
+
: isExecutionBlockBodyType(block.body) &&
|
|
795
|
+
isStatePostBellatrix(state) &&
|
|
796
|
+
state.isExecutionStateType &&
|
|
797
|
+
state.isExecutionEnabled(block)
|
|
829
798
|
? {
|
|
830
799
|
executionPayloadBlockHash: toRootHex(block.body.executionPayload.blockHash),
|
|
831
800
|
executionPayloadNumber: block.body.executionPayload.blockNumber,
|
|
@@ -978,7 +947,6 @@ export class ForkChoice implements IForkChoice {
|
|
|
978
947
|
blockRoot: RootHex,
|
|
979
948
|
executionPayloadBlockHash: RootHex,
|
|
980
949
|
executionPayloadNumber: number,
|
|
981
|
-
executionPayloadStateRoot: RootHex,
|
|
982
950
|
executionStatus: PayloadExecutionStatus
|
|
983
951
|
): void {
|
|
984
952
|
this.protoArray.onExecutionPayload(
|
|
@@ -986,7 +954,6 @@ export class ForkChoice implements IForkChoice {
|
|
|
986
954
|
this.fcStore.currentSlot,
|
|
987
955
|
executionPayloadBlockHash,
|
|
988
956
|
executionPayloadNumber,
|
|
989
|
-
executionPayloadStateRoot,
|
|
990
957
|
this.proposerBoostRoot,
|
|
991
958
|
executionStatus
|
|
992
959
|
);
|
|
@@ -1117,8 +1084,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1117
1084
|
}
|
|
1118
1085
|
|
|
1119
1086
|
getJustifiedBlock(): ProtoBlock {
|
|
1120
|
-
const {rootHex
|
|
1121
|
-
const block = this.
|
|
1087
|
+
const {rootHex} = this.fcStore.justified.checkpoint;
|
|
1088
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1122
1089
|
if (!block) {
|
|
1123
1090
|
throw new ForkChoiceError({
|
|
1124
1091
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1129,8 +1096,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1129
1096
|
}
|
|
1130
1097
|
|
|
1131
1098
|
getFinalizedBlock(): ProtoBlock {
|
|
1132
|
-
const {rootHex
|
|
1133
|
-
const block = this.
|
|
1099
|
+
const {rootHex} = this.fcStore.finalizedCheckpoint;
|
|
1100
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1134
1101
|
if (!block) {
|
|
1135
1102
|
throw new ForkChoiceError({
|
|
1136
1103
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1223,18 +1190,33 @@ export class ForkChoice implements IForkChoice {
|
|
|
1223
1190
|
|
|
1224
1191
|
/**
|
|
1225
1192
|
* Returns both ancestor and non-ancestor blocks in a single traversal.
|
|
1193
|
+
*
|
|
1194
|
+
* `ancestors` is the raw walk and includes the previous finalized block as its last element —
|
|
1195
|
+
* callers that don't want the boundary should slice it off themselves.
|
|
1196
|
+
* Post-gloas for each block root, it returns exactly one variant of it.
|
|
1226
1197
|
*/
|
|
1227
1198
|
getAllAncestorAndNonAncestorBlocks(
|
|
1228
1199
|
blockRoot: RootHex,
|
|
1229
1200
|
payloadStatus: PayloadStatus
|
|
1230
1201
|
): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]} {
|
|
1231
|
-
|
|
1202
|
+
return this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot, payloadStatus);
|
|
1203
|
+
}
|
|
1232
1204
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1205
|
+
/**
|
|
1206
|
+
* Same to getAllAncestorAndNonAncestorBlocks with default variant of ${blockRoot} to start with
|
|
1207
|
+
*/
|
|
1208
|
+
getAllAncestorAndNonAncestorBlocksDefaultStatus(blockRoot: RootHex): {
|
|
1209
|
+
ancestors: ProtoBlock[];
|
|
1210
|
+
nonAncestors: ProtoBlock[];
|
|
1211
|
+
} {
|
|
1212
|
+
const defaultStatus = this.protoArray.getDefaultVariant(blockRoot);
|
|
1213
|
+
if (defaultStatus === undefined) {
|
|
1214
|
+
throw new ForkChoiceError({
|
|
1215
|
+
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
1216
|
+
root: blockRoot,
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
return this.getAllAncestorAndNonAncestorBlocks(blockRoot, defaultStatus);
|
|
1238
1220
|
}
|
|
1239
1221
|
|
|
1240
1222
|
getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null {
|
|
@@ -1306,6 +1288,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
1306
1288
|
}
|
|
1307
1289
|
}
|
|
1308
1290
|
|
|
1291
|
+
forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock> {
|
|
1292
|
+
const defaultStatus = this.protoArray.getDefaultVariant(blockRoot);
|
|
1293
|
+
if (defaultStatus === undefined) {
|
|
1294
|
+
throw new ForkChoiceError({
|
|
1295
|
+
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
1296
|
+
root: blockRoot,
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
return this.forwardIterateDescendants(blockRoot, defaultStatus);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1309
1302
|
/** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
|
|
1310
1303
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[] {
|
|
1311
1304
|
return this.protoArray.nodes.filter((node) => node.parentRoot === parentRoot);
|
|
@@ -1492,12 +1485,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1492
1485
|
*
|
|
1493
1486
|
* **`on_tick`**
|
|
1494
1487
|
* May need the justified balances of:
|
|
1495
|
-
* - unrealizedJustified: Already available in `
|
|
1488
|
+
* - unrealizedJustified: Already available in `CheckpointWithBalance`
|
|
1496
1489
|
* Since this balances are already available the getter is just `() => balances`, without cache interaction
|
|
1497
1490
|
*/
|
|
1498
1491
|
private updateCheckpoints(
|
|
1499
|
-
justifiedCheckpoint:
|
|
1500
|
-
finalizedCheckpoint:
|
|
1492
|
+
justifiedCheckpoint: CheckpointWithHex,
|
|
1493
|
+
finalizedCheckpoint: CheckpointWithHex,
|
|
1501
1494
|
getJustifiedBalances: () => JustifiedBalances
|
|
1502
1495
|
): void {
|
|
1503
1496
|
// Update justified checkpoint.
|
|
@@ -1517,8 +1510,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1517
1510
|
* Update unrealized checkpoints in store if necessary
|
|
1518
1511
|
*/
|
|
1519
1512
|
private updateUnrealizedCheckpoints(
|
|
1520
|
-
unrealizedJustifiedCheckpoint:
|
|
1521
|
-
unrealizedFinalizedCheckpoint:
|
|
1513
|
+
unrealizedJustifiedCheckpoint: CheckpointWithHex,
|
|
1514
|
+
unrealizedFinalizedCheckpoint: CheckpointWithHex,
|
|
1522
1515
|
getJustifiedBalances: () => JustifiedBalances
|
|
1523
1516
|
): void {
|
|
1524
1517
|
if (unrealizedJustifiedCheckpoint.epoch > this.fcStore.unrealizedJustified.checkpoint.epoch) {
|
|
@@ -1680,15 +1673,42 @@ export class ForkChoice implements IForkChoice {
|
|
|
1680
1673
|
});
|
|
1681
1674
|
}
|
|
1682
1675
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1676
|
+
if (isGloasBlock(block)) {
|
|
1677
|
+
// For Gloas blocks, attestation index must be 0 or 1
|
|
1678
|
+
if (attestationData.index !== 0 && attestationData.index !== 1) {
|
|
1679
|
+
throw new ForkChoiceError({
|
|
1680
|
+
code: ForkChoiceErrorCode.INVALID_ATTESTATION,
|
|
1681
|
+
err: {
|
|
1682
|
+
code: InvalidAttestationCode.INVALID_DATA_INDEX,
|
|
1683
|
+
index: attestationData.index,
|
|
1684
|
+
},
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// Same-slot attestations can only vote for the PENDING variant
|
|
1689
|
+
if (block.slot === slot && attestationData.index !== 0) {
|
|
1690
|
+
throw new ForkChoiceError({
|
|
1691
|
+
code: ForkChoiceErrorCode.INVALID_ATTESTATION,
|
|
1692
|
+
err: {
|
|
1693
|
+
code: InvalidAttestationCode.INVALID_DATA_INDEX,
|
|
1694
|
+
index: attestationData.index,
|
|
1695
|
+
},
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
// If attesting for a full node, the payload must be known
|
|
1700
|
+
if (attestationData.index === 1) {
|
|
1701
|
+
const fullNodeIndex = this.protoArray.getNodeIndexByRootAndStatus(beaconBlockRootHex, PayloadStatus.FULL);
|
|
1702
|
+
if (fullNodeIndex === undefined) {
|
|
1703
|
+
throw new ForkChoiceError({
|
|
1704
|
+
code: ForkChoiceErrorCode.INVALID_ATTESTATION,
|
|
1705
|
+
err: {
|
|
1706
|
+
code: InvalidAttestationCode.UNKNOWN_PAYLOAD_STATUS,
|
|
1707
|
+
beaconBlockRoot: beaconBlockRootHex,
|
|
1708
|
+
},
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1692
1712
|
}
|
|
1693
1713
|
|
|
1694
1714
|
this.validatedAttestationDatas.add(attDataRoot);
|
|
@@ -1866,35 +1886,3 @@ export function getCommitteeFraction(
|
|
|
1866
1886
|
const committeeWeight = Math.floor(justifiedTotalActiveBalanceByIncrement / config.slotsPerEpoch);
|
|
1867
1887
|
return Math.floor((committeeWeight * config.committeePercent) / 100);
|
|
1868
1888
|
}
|
|
1869
|
-
|
|
1870
|
-
/**
|
|
1871
|
-
* Get the payload status for a checkpoint.
|
|
1872
|
-
*
|
|
1873
|
-
* Pre-Gloas: always FULL (payload embedded in block)
|
|
1874
|
-
* Gloas: determined by state.execution_payload_availability
|
|
1875
|
-
*
|
|
1876
|
-
* @param config - The chain fork config to determine fork at checkpoint slot
|
|
1877
|
-
* @param state - The state to check execution_payload_availability
|
|
1878
|
-
* @param checkpointEpoch - The epoch of the checkpoint
|
|
1879
|
-
*/
|
|
1880
|
-
export function getCheckpointPayloadStatus(
|
|
1881
|
-
config: ChainForkConfig,
|
|
1882
|
-
state: IBeaconStateView,
|
|
1883
|
-
checkpointEpoch: number
|
|
1884
|
-
): PayloadStatus {
|
|
1885
|
-
// Compute checkpoint slot first to determine the correct fork
|
|
1886
|
-
const checkpointSlot = computeStartSlotAtEpoch(checkpointEpoch);
|
|
1887
|
-
const fork = config.getForkSeq(checkpointSlot);
|
|
1888
|
-
|
|
1889
|
-
// Pre-Gloas: always FULL
|
|
1890
|
-
if (fork < ForkSeq.gloas) {
|
|
1891
|
-
return PayloadStatus.FULL;
|
|
1892
|
-
}
|
|
1893
|
-
|
|
1894
|
-
// For Gloas, check state.execution_payload_availability
|
|
1895
|
-
// - For non-skipped slots at checkpoint: returns false (EMPTY) since payload hasn't arrived yet
|
|
1896
|
-
// - For skipped slots at checkpoint: returns the actual availability status from state
|
|
1897
|
-
const payloadAvailable = state.executionPayloadAvailability.get(checkpointSlot % SLOTS_PER_HISTORICAL_ROOT);
|
|
1898
|
-
|
|
1899
|
-
return payloadAvailable ? PayloadStatus.FULL : PayloadStatus.EMPTY;
|
|
1900
|
-
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
ProtoNode,
|
|
10
10
|
} from "../protoArray/interface.js";
|
|
11
11
|
import {UpdateAndGetHeadOpt} from "./forkChoice.js";
|
|
12
|
-
import {CheckpointWithHex
|
|
12
|
+
import {CheckpointWithHex} from "./store.js";
|
|
13
13
|
|
|
14
14
|
export type CheckpointHex = {
|
|
15
15
|
epoch: Epoch;
|
|
@@ -21,12 +21,12 @@ export type CheckpointsWithHex = {
|
|
|
21
21
|
finalizedCheckpoint: CheckpointWithHex;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export type
|
|
25
|
-
checkpoint:
|
|
24
|
+
export type CheckpointWithBalance = {
|
|
25
|
+
checkpoint: CheckpointWithHex;
|
|
26
26
|
balances: EffectiveBalanceIncrements;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
export type
|
|
29
|
+
export type CheckpointWithTotalBalance = CheckpointWithBalance & {
|
|
30
30
|
totalBalance: number;
|
|
31
31
|
};
|
|
32
32
|
|
|
@@ -121,8 +121,8 @@ export interface IForkChoice {
|
|
|
121
121
|
* Retrieve all nodes for the debug API.
|
|
122
122
|
*/
|
|
123
123
|
getAllNodes(): ProtoNode[];
|
|
124
|
-
getFinalizedCheckpoint():
|
|
125
|
-
getJustifiedCheckpoint():
|
|
124
|
+
getFinalizedCheckpoint(): CheckpointWithHex;
|
|
125
|
+
getJustifiedCheckpoint(): CheckpointWithHex;
|
|
126
126
|
/**
|
|
127
127
|
* Add `block` to the fork choice DAG.
|
|
128
128
|
*
|
|
@@ -198,13 +198,11 @@ export interface IForkChoice {
|
|
|
198
198
|
* @param blockRoot - The beacon block root for which the payload arrived
|
|
199
199
|
* @param executionPayloadBlockHash - The block hash of the execution payload
|
|
200
200
|
* @param executionPayloadNumber - The block number of the execution payload
|
|
201
|
-
* @param executionPayloadStateRoot - The execution payload state root ie. the root of post-state after processExecutionPayloadEnvelope()
|
|
202
201
|
*/
|
|
203
202
|
onExecutionPayload(
|
|
204
203
|
blockRoot: RootHex,
|
|
205
204
|
executionPayloadBlockHash: RootHex,
|
|
206
205
|
executionPayloadNumber: number,
|
|
207
|
-
executionPayloadStateRoot: RootHex,
|
|
208
206
|
executionStatus: PayloadExecutionStatus
|
|
209
207
|
): void;
|
|
210
208
|
/**
|
|
@@ -241,6 +239,7 @@ export interface IForkChoice {
|
|
|
241
239
|
getBlockDefaultStatus(blockRoot: Root): ProtoBlock | null;
|
|
242
240
|
getBlockHexDefaultStatus(blockRoot: RootHex): ProtoBlock | null;
|
|
243
241
|
getBlockHexAndBlockHash(blockRoot: RootHex, blockHash: RootHex): ProtoBlock | null;
|
|
242
|
+
shouldExtendPayload(blockRoot: RootHex): boolean;
|
|
244
243
|
getFinalizedBlock(): ProtoBlock;
|
|
245
244
|
getJustifiedBlock(): ProtoBlock;
|
|
246
245
|
getFinalizedCheckpointSlot(): Slot;
|
|
@@ -272,11 +271,23 @@ export interface IForkChoice {
|
|
|
272
271
|
getAllNonAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
|
|
273
272
|
/**
|
|
274
273
|
* Returns both ancestor and non-ancestor blocks in a single traversal.
|
|
274
|
+
*
|
|
275
|
+
* `ancestors` is the raw walk and includes the previous finalized block as its last element —
|
|
276
|
+
* callers that don't want the boundary should slice it off themselves.
|
|
275
277
|
*/
|
|
276
278
|
getAllAncestorAndNonAncestorBlocks(
|
|
277
279
|
blockRoot: RootHex,
|
|
278
280
|
payloadStatus: PayloadStatus
|
|
279
281
|
): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]};
|
|
282
|
+
/**
|
|
283
|
+
* Same as `getAllAncestorAndNonAncestorBlocks` but resolves the default payload-status variant
|
|
284
|
+
* (FULL pre-Gloas, PENDING for Gloas) for the given root. Use when the caller holds a
|
|
285
|
+
* `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
|
|
286
|
+
*/
|
|
287
|
+
getAllAncestorAndNonAncestorBlocksDefaultStatus(blockRoot: RootHex): {
|
|
288
|
+
ancestors: ProtoBlock[];
|
|
289
|
+
nonAncestors: ProtoBlock[];
|
|
290
|
+
};
|
|
280
291
|
getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null;
|
|
281
292
|
getCanonicalBlockAtSlot(slot: Slot): ProtoBlock | null;
|
|
282
293
|
getCanonicalBlockClosestLteSlot(slot: Slot): ProtoBlock | null;
|
|
@@ -288,6 +299,12 @@ export interface IForkChoice {
|
|
|
288
299
|
* Iterates forward descendants of blockRoot. Does not yield blockRoot itself
|
|
289
300
|
*/
|
|
290
301
|
forwardIterateDescendants(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock>;
|
|
302
|
+
/**
|
|
303
|
+
* Same as `forwardIterateDescendants` but resolves the default payload-status variant
|
|
304
|
+
* (FULL pre-Gloas, PENDING for Gloas) for the given root. Use when the caller holds a
|
|
305
|
+
* `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
|
|
306
|
+
*/
|
|
307
|
+
forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock>;
|
|
291
308
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
|
|
292
309
|
getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
|
|
293
310
|
/** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
|
package/src/forkChoice/store.ts
CHANGED
|
@@ -1,8 +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 {
|
|
5
|
-
import {CheckpointWithPayloadAndBalance, CheckpointWithPayloadAndTotalBalance} from "./interface.js";
|
|
4
|
+
import {CheckpointWithBalance, CheckpointWithTotalBalance} from "./interface.js";
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Stores checkpoints in a hybrid format:
|
|
@@ -11,15 +10,6 @@ import {CheckpointWithPayloadAndBalance, CheckpointWithPayloadAndTotalBalance} f
|
|
|
11
10
|
*/
|
|
12
11
|
export type CheckpointWithHex = phase0.Checkpoint & {rootHex: RootHex};
|
|
13
12
|
|
|
14
|
-
/**
|
|
15
|
-
* Checkpoint with payload status for Gloas fork choice.
|
|
16
|
-
* Used to track which variant (EMPTY or FULL) of the finalized/justified block to use.
|
|
17
|
-
*
|
|
18
|
-
* Pre-Gloas: payloadStatus is always FULL (payload embedded in block)
|
|
19
|
-
* Gloas: determined by state.execution_payload_availability
|
|
20
|
-
*/
|
|
21
|
-
export type CheckpointWithPayloadStatus = CheckpointWithHex & {payloadStatus: PayloadStatus};
|
|
22
|
-
|
|
23
13
|
export type JustifiedBalances = EffectiveBalanceIncrements;
|
|
24
14
|
|
|
25
15
|
/**
|
|
@@ -29,7 +19,7 @@ export type JustifiedBalances = EffectiveBalanceIncrements;
|
|
|
29
19
|
* @param blockState state that declares justified checkpoint `checkpoint`
|
|
30
20
|
*/
|
|
31
21
|
export type JustifiedBalancesGetter = (
|
|
32
|
-
checkpoint:
|
|
22
|
+
checkpoint: CheckpointWithHex,
|
|
33
23
|
blockState: IBeaconStateView
|
|
34
24
|
) => JustifiedBalances;
|
|
35
25
|
|
|
@@ -47,11 +37,11 @@ export type JustifiedBalancesGetter = (
|
|
|
47
37
|
*/
|
|
48
38
|
export interface IForkChoiceStore {
|
|
49
39
|
currentSlot: Slot;
|
|
50
|
-
get justified():
|
|
51
|
-
set justified(justified:
|
|
52
|
-
unrealizedJustified:
|
|
53
|
-
finalizedCheckpoint:
|
|
54
|
-
unrealizedFinalizedCheckpoint:
|
|
40
|
+
get justified(): CheckpointWithTotalBalance;
|
|
41
|
+
set justified(justified: CheckpointWithBalance);
|
|
42
|
+
unrealizedJustified: CheckpointWithBalance;
|
|
43
|
+
finalizedCheckpoint: CheckpointWithHex;
|
|
44
|
+
unrealizedFinalizedCheckpoint: CheckpointWithHex;
|
|
55
45
|
justifiedBalancesGetter: JustifiedBalancesGetter;
|
|
56
46
|
equivocatingIndices: Set<ValidatorIndex>;
|
|
57
47
|
}
|
|
@@ -60,10 +50,10 @@ export interface IForkChoiceStore {
|
|
|
60
50
|
* IForkChoiceStore implementer which emits forkChoice events on updated justified and finalized checkpoints.
|
|
61
51
|
*/
|
|
62
52
|
export class ForkChoiceStore implements IForkChoiceStore {
|
|
63
|
-
private _justified:
|
|
64
|
-
unrealizedJustified:
|
|
65
|
-
private _finalizedCheckpoint:
|
|
66
|
-
unrealizedFinalizedCheckpoint:
|
|
53
|
+
private _justified: CheckpointWithTotalBalance;
|
|
54
|
+
unrealizedJustified: CheckpointWithBalance;
|
|
55
|
+
private _finalizedCheckpoint: CheckpointWithHex;
|
|
56
|
+
unrealizedFinalizedCheckpoint: CheckpointWithHex;
|
|
67
57
|
equivocatingIndices = new Set<ValidatorIndex>();
|
|
68
58
|
justifiedBalancesGetter: JustifiedBalancesGetter;
|
|
69
59
|
currentSlot: Slot;
|
|
@@ -74,49 +64,37 @@ export class ForkChoiceStore implements IForkChoiceStore {
|
|
|
74
64
|
finalizedCheckpoint: phase0.Checkpoint,
|
|
75
65
|
justifiedBalances: EffectiveBalanceIncrements,
|
|
76
66
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
77
|
-
/**
|
|
78
|
-
* Payload status for justified checkpoint.
|
|
79
|
-
* Pre-Gloas: always FULL
|
|
80
|
-
* Gloas: determined by state.execution_payload_availability
|
|
81
|
-
*/
|
|
82
|
-
justifiedPayloadStatus: PayloadStatus,
|
|
83
|
-
/**
|
|
84
|
-
* Payload status for finalized checkpoint.
|
|
85
|
-
* Pre-Gloas: always FULL
|
|
86
|
-
* Gloas: determined by state.execution_payload_availability
|
|
87
|
-
*/
|
|
88
|
-
finalizedPayloadStatus: PayloadStatus,
|
|
89
67
|
private readonly events?: {
|
|
90
|
-
onJustified: (cp:
|
|
91
|
-
onFinalized: (cp:
|
|
68
|
+
onJustified: (cp: CheckpointWithHex) => void;
|
|
69
|
+
onFinalized: (cp: CheckpointWithHex) => void;
|
|
92
70
|
}
|
|
93
71
|
) {
|
|
94
72
|
this.justifiedBalancesGetter = justifiedBalancesGetter;
|
|
95
73
|
this.currentSlot = currentSlot;
|
|
96
74
|
const justified = {
|
|
97
|
-
checkpoint:
|
|
75
|
+
checkpoint: toCheckpointWithHex(justifiedCheckpoint),
|
|
98
76
|
balances: justifiedBalances,
|
|
99
77
|
totalBalance: computeTotalBalance(justifiedBalances),
|
|
100
78
|
};
|
|
101
79
|
this._justified = justified;
|
|
102
80
|
this.unrealizedJustified = justified;
|
|
103
|
-
this._finalizedCheckpoint =
|
|
81
|
+
this._finalizedCheckpoint = toCheckpointWithHex(finalizedCheckpoint);
|
|
104
82
|
this.unrealizedFinalizedCheckpoint = this._finalizedCheckpoint;
|
|
105
83
|
}
|
|
106
84
|
|
|
107
|
-
get justified():
|
|
85
|
+
get justified(): CheckpointWithTotalBalance {
|
|
108
86
|
return this._justified;
|
|
109
87
|
}
|
|
110
|
-
set justified(justified:
|
|
88
|
+
set justified(justified: CheckpointWithBalance) {
|
|
111
89
|
this._justified = {...justified, totalBalance: computeTotalBalance(justified.balances)};
|
|
112
90
|
this.events?.onJustified(justified.checkpoint);
|
|
113
91
|
}
|
|
114
92
|
|
|
115
|
-
get finalizedCheckpoint():
|
|
93
|
+
get finalizedCheckpoint(): CheckpointWithHex {
|
|
116
94
|
return this._finalizedCheckpoint;
|
|
117
95
|
}
|
|
118
|
-
set finalizedCheckpoint(checkpoint:
|
|
119
|
-
const cp =
|
|
96
|
+
set finalizedCheckpoint(checkpoint: CheckpointWithHex) {
|
|
97
|
+
const cp = toCheckpointWithHex(checkpoint);
|
|
120
98
|
this._finalizedCheckpoint = cp;
|
|
121
99
|
this.events?.onFinalized(cp);
|
|
122
100
|
}
|
|
@@ -133,16 +111,6 @@ export function toCheckpointWithHex(checkpoint: phase0.Checkpoint): CheckpointWi
|
|
|
133
111
|
};
|
|
134
112
|
}
|
|
135
113
|
|
|
136
|
-
export function toCheckpointWithPayload(
|
|
137
|
-
checkpoint: phase0.Checkpoint,
|
|
138
|
-
payloadStatus: PayloadStatus
|
|
139
|
-
): CheckpointWithPayloadStatus {
|
|
140
|
-
return {
|
|
141
|
-
...toCheckpointWithHex(checkpoint),
|
|
142
|
-
payloadStatus,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
114
|
export function equalCheckpointWithHex(a: CheckpointWithHex, b: CheckpointWithHex): boolean {
|
|
147
115
|
return a.epoch === b.epoch && a.rootHex === b.rootHex;
|
|
148
116
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,17 +6,12 @@ export {
|
|
|
6
6
|
type InvalidBlock,
|
|
7
7
|
InvalidBlockCode,
|
|
8
8
|
} from "./forkChoice/errors.js";
|
|
9
|
-
export {
|
|
10
|
-
ForkChoice,
|
|
11
|
-
type ForkChoiceOpts,
|
|
12
|
-
UpdateHeadOpt,
|
|
13
|
-
getCheckpointPayloadStatus,
|
|
14
|
-
} from "./forkChoice/forkChoice.js";
|
|
9
|
+
export {ForkChoice, type ForkChoiceOpts, UpdateHeadOpt} from "./forkChoice/forkChoice.js";
|
|
15
10
|
export {
|
|
16
11
|
type AncestorResult,
|
|
17
12
|
AncestorStatus,
|
|
18
|
-
type
|
|
19
|
-
type
|
|
13
|
+
type CheckpointWithBalance,
|
|
14
|
+
type CheckpointWithTotalBalance,
|
|
20
15
|
EpochDifference,
|
|
21
16
|
type IForkChoice,
|
|
22
17
|
NotReorgedReason,
|
|
@@ -24,7 +19,6 @@ export {
|
|
|
24
19
|
export * from "./forkChoice/safeBlocks.js";
|
|
25
20
|
export {
|
|
26
21
|
type CheckpointWithHex,
|
|
27
|
-
type CheckpointWithPayloadStatus,
|
|
28
22
|
ForkChoiceStore,
|
|
29
23
|
type IForkChoiceStore,
|
|
30
24
|
type JustifiedBalancesGetter,
|