@lodestar/fork-choice 1.43.0-dev.9c8becae00 → 1.43.0-dev.9f5db5b9c7
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/forkChoice.d.ts +22 -20
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +56 -75
- 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/interface.d.ts +7 -8
- package/lib/protoArray/interface.d.ts.map +1 -1
- package/lib/protoArray/interface.js +3 -4
- package/lib/protoArray/interface.js.map +1 -1
- package/lib/protoArray/protoArray.d.ts +9 -1
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +55 -47
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +7 -7
- package/src/forkChoice/forkChoice.ts +69 -122
- package/src/forkChoice/interface.ts +27 -9
- package/src/forkChoice/store.ts +20 -52
- package/src/index.ts +3 -9
- package/src/protoArray/interface.ts +8 -7
- package/src/protoArray/protoArray.ts +66 -59
|
@@ -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,
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
getAttesterSlashableIndices,
|
|
12
12
|
isExecutionBlockBodyType,
|
|
13
13
|
isStatePostBellatrix,
|
|
14
|
-
isStatePostGloas,
|
|
15
14
|
} from "@lodestar/state-transition";
|
|
16
15
|
import {
|
|
17
16
|
AttesterSlashing,
|
|
@@ -53,7 +52,7 @@ import {
|
|
|
53
52
|
NotReorgedReason,
|
|
54
53
|
ShouldOverrideForkChoiceUpdateResult,
|
|
55
54
|
} from "./interface.js";
|
|
56
|
-
import {
|
|
55
|
+
import {CheckpointWithHex, IForkChoiceStore, JustifiedBalances, toCheckpointWithHex} from "./store.js";
|
|
57
56
|
|
|
58
57
|
export type ForkChoiceOpts = {
|
|
59
58
|
proposerBoost?: boolean;
|
|
@@ -564,11 +563,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
564
563
|
return this.protoArray.nodes;
|
|
565
564
|
}
|
|
566
565
|
|
|
567
|
-
getFinalizedCheckpoint():
|
|
566
|
+
getFinalizedCheckpoint(): CheckpointWithHex {
|
|
568
567
|
return this.fcStore.finalizedCheckpoint;
|
|
569
568
|
}
|
|
570
569
|
|
|
571
|
-
getJustifiedCheckpoint():
|
|
570
|
+
getJustifiedCheckpoint(): CheckpointWithHex {
|
|
572
571
|
return this.fcStore.justified.checkpoint;
|
|
573
572
|
}
|
|
574
573
|
|
|
@@ -675,18 +674,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
675
674
|
this.proposerBoostRoot = blockRootHex;
|
|
676
675
|
}
|
|
677
676
|
|
|
678
|
-
|
|
679
|
-
const justifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
680
|
-
this.config,
|
|
681
|
-
state,
|
|
682
|
-
state.currentJustifiedCheckpoint.epoch
|
|
683
|
-
);
|
|
684
|
-
const justifiedCheckpoint = toCheckpointWithPayload(state.currentJustifiedCheckpoint, justifiedPayloadStatus);
|
|
677
|
+
const justifiedCheckpoint = toCheckpointWithHex(state.currentJustifiedCheckpoint);
|
|
685
678
|
const stateJustifiedEpoch = justifiedCheckpoint.epoch;
|
|
686
679
|
|
|
687
|
-
|
|
688
|
-
const finalizedPayloadStatus = getCheckpointPayloadStatus(this.config, state, state.finalizedCheckpoint.epoch);
|
|
689
|
-
const finalizedCheckpoint = toCheckpointWithPayload(state.finalizedCheckpoint, finalizedPayloadStatus);
|
|
680
|
+
const finalizedCheckpoint = toCheckpointWithHex(state.finalizedCheckpoint);
|
|
690
681
|
|
|
691
682
|
// Justified balances for `justifiedCheckpoint` are new to the fork-choice. Compute them on demand only if
|
|
692
683
|
// the justified checkpoint changes
|
|
@@ -708,61 +699,29 @@ export class ForkChoice implements IForkChoice {
|
|
|
708
699
|
// This is an optimization. It should reduce the amount of times we run
|
|
709
700
|
// `process_justification_and_finalization` by approximately 1/3rd when the chain is
|
|
710
701
|
// performing optimally.
|
|
711
|
-
let unrealizedJustifiedCheckpoint:
|
|
712
|
-
let unrealizedFinalizedCheckpoint:
|
|
702
|
+
let unrealizedJustifiedCheckpoint: CheckpointWithHex;
|
|
703
|
+
let unrealizedFinalizedCheckpoint: CheckpointWithHex;
|
|
713
704
|
if (this.opts?.computeUnrealized) {
|
|
714
705
|
if (
|
|
715
706
|
parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
|
|
716
707
|
parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
|
|
717
708
|
) {
|
|
718
709
|
// reuse from parent, happens at 1/3 last blocks of epoch as monitored in mainnet
|
|
719
|
-
// Get payload status for unrealized justified checkpoint
|
|
720
|
-
const unrealizedJustifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
721
|
-
this.config,
|
|
722
|
-
state,
|
|
723
|
-
parentBlock.unrealizedJustifiedEpoch
|
|
724
|
-
);
|
|
725
710
|
unrealizedJustifiedCheckpoint = {
|
|
726
711
|
epoch: parentBlock.unrealizedJustifiedEpoch,
|
|
727
712
|
root: fromHex(parentBlock.unrealizedJustifiedRoot),
|
|
728
713
|
rootHex: parentBlock.unrealizedJustifiedRoot,
|
|
729
|
-
payloadStatus: unrealizedJustifiedPayloadStatus,
|
|
730
714
|
};
|
|
731
|
-
// Get payload status for unrealized finalized checkpoint
|
|
732
|
-
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
733
|
-
this.config,
|
|
734
|
-
state,
|
|
735
|
-
parentBlock.unrealizedFinalizedEpoch
|
|
736
|
-
);
|
|
737
715
|
unrealizedFinalizedCheckpoint = {
|
|
738
716
|
epoch: parentBlock.unrealizedFinalizedEpoch,
|
|
739
717
|
root: fromHex(parentBlock.unrealizedFinalizedRoot),
|
|
740
718
|
rootHex: parentBlock.unrealizedFinalizedRoot,
|
|
741
|
-
payloadStatus: unrealizedFinalizedPayloadStatus,
|
|
742
719
|
};
|
|
743
720
|
} else {
|
|
744
721
|
// compute new, happens 2/3 first blocks of epoch as monitored in mainnet
|
|
745
722
|
const unrealized = state.computeUnrealizedCheckpoints();
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
this.config,
|
|
749
|
-
state,
|
|
750
|
-
unrealized.justifiedCheckpoint.epoch
|
|
751
|
-
);
|
|
752
|
-
unrealizedJustifiedCheckpoint = toCheckpointWithPayload(
|
|
753
|
-
unrealized.justifiedCheckpoint,
|
|
754
|
-
unrealizedJustifiedPayloadStatus
|
|
755
|
-
);
|
|
756
|
-
// Get payload status for unrealized finalized checkpoint
|
|
757
|
-
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
758
|
-
this.config,
|
|
759
|
-
state,
|
|
760
|
-
unrealized.finalizedCheckpoint.epoch
|
|
761
|
-
);
|
|
762
|
-
unrealizedFinalizedCheckpoint = toCheckpointWithPayload(
|
|
763
|
-
unrealized.finalizedCheckpoint,
|
|
764
|
-
unrealizedFinalizedPayloadStatus
|
|
765
|
-
);
|
|
723
|
+
unrealizedJustifiedCheckpoint = toCheckpointWithHex(unrealized.justifiedCheckpoint);
|
|
724
|
+
unrealizedFinalizedCheckpoint = toCheckpointWithHex(unrealized.finalizedCheckpoint);
|
|
766
725
|
}
|
|
767
726
|
} else {
|
|
768
727
|
unrealizedJustifiedCheckpoint = justifiedCheckpoint;
|
|
@@ -829,7 +788,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
829
788
|
// Fallback to parent block's number (we know it's post-merge from check above)
|
|
830
789
|
return parentBlock.executionPayloadNumber;
|
|
831
790
|
})(),
|
|
832
|
-
executionStatus: this.
|
|
791
|
+
executionStatus: this.getPostMergeExecStatus(executionStatus),
|
|
833
792
|
dataAvailabilityStatus,
|
|
834
793
|
}
|
|
835
794
|
: isExecutionBlockBodyType(block.body) &&
|
|
@@ -839,7 +798,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
839
798
|
? {
|
|
840
799
|
executionPayloadBlockHash: toRootHex(block.body.executionPayload.blockHash),
|
|
841
800
|
executionPayloadNumber: block.body.executionPayload.blockNumber,
|
|
842
|
-
executionStatus: this.
|
|
801
|
+
executionStatus: this.getPostMergeExecStatus(executionStatus),
|
|
843
802
|
dataAvailabilityStatus,
|
|
844
803
|
}
|
|
845
804
|
: {
|
|
@@ -988,17 +947,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
988
947
|
blockRoot: RootHex,
|
|
989
948
|
executionPayloadBlockHash: RootHex,
|
|
990
949
|
executionPayloadNumber: number,
|
|
991
|
-
|
|
992
|
-
|
|
950
|
+
executionStatus: PayloadExecutionStatus,
|
|
951
|
+
dataAvailabilityStatus: DataAvailabilityStatus
|
|
993
952
|
): void {
|
|
994
953
|
this.protoArray.onExecutionPayload(
|
|
995
954
|
blockRoot,
|
|
996
955
|
this.fcStore.currentSlot,
|
|
997
956
|
executionPayloadBlockHash,
|
|
998
957
|
executionPayloadNumber,
|
|
999
|
-
executionPayloadStateRoot,
|
|
1000
958
|
this.proposerBoostRoot,
|
|
1001
|
-
executionStatus
|
|
959
|
+
executionStatus,
|
|
960
|
+
dataAvailabilityStatus
|
|
1002
961
|
);
|
|
1003
962
|
}
|
|
1004
963
|
|
|
@@ -1084,6 +1043,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1084
1043
|
return this.protoArray.hasPayload(blockRoot);
|
|
1085
1044
|
}
|
|
1086
1045
|
|
|
1046
|
+
getPTCVotes(blockRootHex: RootHex): (boolean | null)[] | null {
|
|
1047
|
+
const votes = this.protoArray.getPTCVotes(blockRootHex);
|
|
1048
|
+
if (votes === null) return null;
|
|
1049
|
+
return votes.toBoolArray().map((v) => v ?? null);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1087
1052
|
/**
|
|
1088
1053
|
* Returns a MUTABLE `ProtoBlock` if the block is known **and** a descendant of the finalized root.
|
|
1089
1054
|
*/
|
|
@@ -1127,8 +1092,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1127
1092
|
}
|
|
1128
1093
|
|
|
1129
1094
|
getJustifiedBlock(): ProtoBlock {
|
|
1130
|
-
const {rootHex
|
|
1131
|
-
const block = this.
|
|
1095
|
+
const {rootHex} = this.fcStore.justified.checkpoint;
|
|
1096
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1132
1097
|
if (!block) {
|
|
1133
1098
|
throw new ForkChoiceError({
|
|
1134
1099
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1139,8 +1104,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1139
1104
|
}
|
|
1140
1105
|
|
|
1141
1106
|
getFinalizedBlock(): ProtoBlock {
|
|
1142
|
-
const {rootHex
|
|
1143
|
-
const block = this.
|
|
1107
|
+
const {rootHex} = this.fcStore.finalizedCheckpoint;
|
|
1108
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1144
1109
|
if (!block) {
|
|
1145
1110
|
throw new ForkChoiceError({
|
|
1146
1111
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1215,13 +1180,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1215
1180
|
}
|
|
1216
1181
|
|
|
1217
1182
|
/**
|
|
1218
|
-
*
|
|
1219
|
-
*
|
|
1183
|
+
* Raw ancestor walk from `blockRoot` back toward the previous finalized block. Includes both
|
|
1184
|
+
* `blockRoot` and the previous-finalized boundary as last element. Mirrors the semantics of
|
|
1185
|
+
* `getAllAncestorAndNonAncestorBlocks.ancestors`
|
|
1220
1186
|
*/
|
|
1221
1187
|
getAllAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[] {
|
|
1222
|
-
|
|
1223
|
-
// the last node is the previous finalized one, it's there to check onBlock finalized checkpoint only.
|
|
1224
|
-
return blocks.slice(0, blocks.length - 1);
|
|
1188
|
+
return this.protoArray.getAllAncestorNodes(blockRoot, payloadStatus);
|
|
1225
1189
|
}
|
|
1226
1190
|
|
|
1227
1191
|
/**
|
|
@@ -1233,18 +1197,33 @@ export class ForkChoice implements IForkChoice {
|
|
|
1233
1197
|
|
|
1234
1198
|
/**
|
|
1235
1199
|
* Returns both ancestor and non-ancestor blocks in a single traversal.
|
|
1200
|
+
*
|
|
1201
|
+
* `ancestors` is the raw walk and includes the previous finalized block as its last element —
|
|
1202
|
+
* callers that don't want the boundary should slice it off themselves.
|
|
1203
|
+
* Post-gloas for each block root, it returns exactly one variant of it.
|
|
1236
1204
|
*/
|
|
1237
1205
|
getAllAncestorAndNonAncestorBlocks(
|
|
1238
1206
|
blockRoot: RootHex,
|
|
1239
1207
|
payloadStatus: PayloadStatus
|
|
1240
1208
|
): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]} {
|
|
1241
|
-
|
|
1209
|
+
return this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot, payloadStatus);
|
|
1210
|
+
}
|
|
1242
1211
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1212
|
+
/**
|
|
1213
|
+
* Same to getAllAncestorAndNonAncestorBlocks with default variant of ${blockRoot} to start with
|
|
1214
|
+
*/
|
|
1215
|
+
getAllAncestorAndNonAncestorBlocksDefaultStatus(blockRoot: RootHex): {
|
|
1216
|
+
ancestors: ProtoBlock[];
|
|
1217
|
+
nonAncestors: ProtoBlock[];
|
|
1218
|
+
} {
|
|
1219
|
+
const defaultStatus = this.protoArray.getDefaultVariant(blockRoot);
|
|
1220
|
+
if (defaultStatus === undefined) {
|
|
1221
|
+
throw new ForkChoiceError({
|
|
1222
|
+
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
1223
|
+
root: blockRoot,
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
return this.getAllAncestorAndNonAncestorBlocks(blockRoot, defaultStatus);
|
|
1248
1227
|
}
|
|
1249
1228
|
|
|
1250
1229
|
getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null {
|
|
@@ -1316,6 +1295,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
1316
1295
|
}
|
|
1317
1296
|
}
|
|
1318
1297
|
|
|
1298
|
+
forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock> {
|
|
1299
|
+
const defaultStatus = this.protoArray.getDefaultVariant(blockRoot);
|
|
1300
|
+
if (defaultStatus === undefined) {
|
|
1301
|
+
throw new ForkChoiceError({
|
|
1302
|
+
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
1303
|
+
root: blockRoot,
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
return this.forwardIterateDescendants(blockRoot, defaultStatus);
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1319
1309
|
/** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
|
|
1320
1310
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[] {
|
|
1321
1311
|
return this.protoArray.nodes.filter((node) => node.parentRoot === parentRoot);
|
|
@@ -1467,24 +1457,16 @@ export class ForkChoice implements IForkChoice {
|
|
|
1467
1457
|
return dataAvailabilityStatus;
|
|
1468
1458
|
}
|
|
1469
1459
|
|
|
1470
|
-
private
|
|
1460
|
+
private getPostMergeExecStatus(
|
|
1471
1461
|
executionStatus: BlockExecutionStatus
|
|
1472
1462
|
): ExecutionStatus.Valid | ExecutionStatus.Syncing {
|
|
1473
|
-
if (executionStatus === ExecutionStatus.PreMerge
|
|
1463
|
+
if (executionStatus === ExecutionStatus.PreMerge)
|
|
1474
1464
|
throw Error(
|
|
1475
1465
|
`Invalid post-merge execution status: expected: ${ExecutionStatus.Syncing} or ${ExecutionStatus.Valid}, got ${executionStatus}`
|
|
1476
1466
|
);
|
|
1477
1467
|
return executionStatus;
|
|
1478
1468
|
}
|
|
1479
1469
|
|
|
1480
|
-
private getPostGloasExecStatus(executionStatus: BlockExecutionStatus): ExecutionStatus.PayloadSeparated {
|
|
1481
|
-
if (executionStatus !== ExecutionStatus.PayloadSeparated)
|
|
1482
|
-
throw Error(
|
|
1483
|
-
`Invalid post-gloas execution status: expected: ${ExecutionStatus.PayloadSeparated}, got ${executionStatus}`
|
|
1484
|
-
);
|
|
1485
|
-
return executionStatus;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
1470
|
/**
|
|
1489
1471
|
* Why `getJustifiedBalances` getter?
|
|
1490
1472
|
* - updateCheckpoints() is called in both on_block and on_tick.
|
|
@@ -1502,12 +1484,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1502
1484
|
*
|
|
1503
1485
|
* **`on_tick`**
|
|
1504
1486
|
* May need the justified balances of:
|
|
1505
|
-
* - unrealizedJustified: Already available in `
|
|
1487
|
+
* - unrealizedJustified: Already available in `CheckpointWithBalance`
|
|
1506
1488
|
* Since this balances are already available the getter is just `() => balances`, without cache interaction
|
|
1507
1489
|
*/
|
|
1508
1490
|
private updateCheckpoints(
|
|
1509
|
-
justifiedCheckpoint:
|
|
1510
|
-
finalizedCheckpoint:
|
|
1491
|
+
justifiedCheckpoint: CheckpointWithHex,
|
|
1492
|
+
finalizedCheckpoint: CheckpointWithHex,
|
|
1511
1493
|
getJustifiedBalances: () => JustifiedBalances
|
|
1512
1494
|
): void {
|
|
1513
1495
|
// Update justified checkpoint.
|
|
@@ -1527,8 +1509,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1527
1509
|
* Update unrealized checkpoints in store if necessary
|
|
1528
1510
|
*/
|
|
1529
1511
|
private updateUnrealizedCheckpoints(
|
|
1530
|
-
unrealizedJustifiedCheckpoint:
|
|
1531
|
-
unrealizedFinalizedCheckpoint:
|
|
1512
|
+
unrealizedJustifiedCheckpoint: CheckpointWithHex,
|
|
1513
|
+
unrealizedFinalizedCheckpoint: CheckpointWithHex,
|
|
1532
1514
|
getJustifiedBalances: () => JustifiedBalances
|
|
1533
1515
|
): void {
|
|
1534
1516
|
if (unrealizedJustifiedCheckpoint.epoch > this.fcStore.unrealizedJustified.checkpoint.epoch) {
|
|
@@ -1903,38 +1885,3 @@ export function getCommitteeFraction(
|
|
|
1903
1885
|
const committeeWeight = Math.floor(justifiedTotalActiveBalanceByIncrement / config.slotsPerEpoch);
|
|
1904
1886
|
return Math.floor((committeeWeight * config.committeePercent) / 100);
|
|
1905
1887
|
}
|
|
1906
|
-
|
|
1907
|
-
/**
|
|
1908
|
-
* Get the payload status for a checkpoint.
|
|
1909
|
-
*
|
|
1910
|
-
* Pre-Gloas: always FULL (payload embedded in block)
|
|
1911
|
-
* Gloas: determined by state.execution_payload_availability
|
|
1912
|
-
*
|
|
1913
|
-
* @param config - The chain fork config to determine fork at checkpoint slot
|
|
1914
|
-
* @param state - The state to check execution_payload_availability
|
|
1915
|
-
* @param checkpointEpoch - The epoch of the checkpoint
|
|
1916
|
-
*/
|
|
1917
|
-
export function getCheckpointPayloadStatus(
|
|
1918
|
-
config: ChainForkConfig,
|
|
1919
|
-
state: IBeaconStateView,
|
|
1920
|
-
checkpointEpoch: number
|
|
1921
|
-
): PayloadStatus {
|
|
1922
|
-
// Compute checkpoint slot first to determine the correct fork
|
|
1923
|
-
const checkpointSlot = computeStartSlotAtEpoch(checkpointEpoch);
|
|
1924
|
-
const fork = config.getForkSeq(checkpointSlot);
|
|
1925
|
-
|
|
1926
|
-
// Pre-Gloas: always FULL
|
|
1927
|
-
if (fork < ForkSeq.gloas) {
|
|
1928
|
-
return PayloadStatus.FULL;
|
|
1929
|
-
}
|
|
1930
|
-
if (!isStatePostGloas(state)) {
|
|
1931
|
-
throw new Error(`Expected gloas+ state for checkpoint payload status, got fork=${state.forkName}`);
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
// For Gloas, check state.execution_payload_availability
|
|
1935
|
-
// - For non-skipped slots at checkpoint: returns false (EMPTY) since payload hasn't arrived yet
|
|
1936
|
-
// - For skipped slots at checkpoint: returns the actual availability status from state
|
|
1937
|
-
const payloadAvailable = state.executionPayloadAvailability.get(checkpointSlot % SLOTS_PER_HISTORICAL_ROOT);
|
|
1938
|
-
|
|
1939
|
-
return payloadAvailable ? PayloadStatus.FULL : PayloadStatus.EMPTY;
|
|
1940
|
-
}
|
|
@@ -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,14 +198,13 @@ 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
|
-
|
|
208
|
-
|
|
206
|
+
executionStatus: PayloadExecutionStatus,
|
|
207
|
+
dataAvailabilityStatus: DataAvailabilityStatus
|
|
209
208
|
): void;
|
|
210
209
|
/**
|
|
211
210
|
* Call `onTick` for all slots between `fcStore.getCurrentSlot()` and the provided `currentSlot`.
|
|
@@ -233,6 +232,7 @@ export interface IForkChoice {
|
|
|
233
232
|
hasPayloadUnsafe(blockRoot: Root): boolean;
|
|
234
233
|
hasPayloadHexUnsafe(blockRoot: RootHex): boolean;
|
|
235
234
|
getSlotsPresent(windowStart: number): number;
|
|
235
|
+
getPTCVotes(blockRootHex: RootHex): (boolean | null)[] | null;
|
|
236
236
|
/**
|
|
237
237
|
* Returns a `ProtoBlock` if the block is known **and** a descendant of the finalized root.
|
|
238
238
|
*/
|
|
@@ -273,11 +273,23 @@ export interface IForkChoice {
|
|
|
273
273
|
getAllNonAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
|
|
274
274
|
/**
|
|
275
275
|
* Returns both ancestor and non-ancestor blocks in a single traversal.
|
|
276
|
+
*
|
|
277
|
+
* `ancestors` is the raw walk and includes the previous finalized block as its last element —
|
|
278
|
+
* callers that don't want the boundary should slice it off themselves.
|
|
276
279
|
*/
|
|
277
280
|
getAllAncestorAndNonAncestorBlocks(
|
|
278
281
|
blockRoot: RootHex,
|
|
279
282
|
payloadStatus: PayloadStatus
|
|
280
283
|
): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]};
|
|
284
|
+
/**
|
|
285
|
+
* Same as `getAllAncestorAndNonAncestorBlocks` but resolves the default payload-status variant
|
|
286
|
+
* (FULL pre-Gloas, PENDING for Gloas) for the given root. Use when the caller holds a
|
|
287
|
+
* `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
|
|
288
|
+
*/
|
|
289
|
+
getAllAncestorAndNonAncestorBlocksDefaultStatus(blockRoot: RootHex): {
|
|
290
|
+
ancestors: ProtoBlock[];
|
|
291
|
+
nonAncestors: ProtoBlock[];
|
|
292
|
+
};
|
|
281
293
|
getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null;
|
|
282
294
|
getCanonicalBlockAtSlot(slot: Slot): ProtoBlock | null;
|
|
283
295
|
getCanonicalBlockClosestLteSlot(slot: Slot): ProtoBlock | null;
|
|
@@ -289,6 +301,12 @@ export interface IForkChoice {
|
|
|
289
301
|
* Iterates forward descendants of blockRoot. Does not yield blockRoot itself
|
|
290
302
|
*/
|
|
291
303
|
forwardIterateDescendants(blockRoot: RootHex, payloadStatus: PayloadStatus): IterableIterator<ProtoBlock>;
|
|
304
|
+
/**
|
|
305
|
+
* Same as `forwardIterateDescendants` but resolves the default payload-status variant
|
|
306
|
+
* (FULL pre-Gloas, PENDING for Gloas) for the given root. Use when the caller holds a
|
|
307
|
+
* `CheckpointWithHex` / finalized root without a specific payload-status variant in mind.
|
|
308
|
+
*/
|
|
309
|
+
forwardIterateDescendantsDefaultStatus(blockRoot: RootHex): IterableIterator<ProtoBlock>;
|
|
292
310
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
|
|
293
311
|
getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
|
|
294
312
|
/** 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,
|
|
@@ -24,16 +24,15 @@ export type VoteIndex = number;
|
|
|
24
24
|
* - Syncing: EL is syncing, payload validity unknown (optimistic sync)
|
|
25
25
|
* - PreMerge: Block is from before The Merge, no execution payload exists
|
|
26
26
|
* - Invalid: Execution payload was invalidated by the EL (post-import status)
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* For gloas blocks the PENDING/EMPTY variants inherit `executionStatus` from the parent's chain
|
|
29
|
+
* (Valid/Syncing/PreMerge); the FULL variant carries the EL response for this block's own payload.
|
|
30
30
|
*/
|
|
31
31
|
export enum ExecutionStatus {
|
|
32
32
|
Valid = "Valid",
|
|
33
33
|
Syncing = "Syncing",
|
|
34
34
|
PreMerge = "PreMerge",
|
|
35
35
|
Invalid = "Invalid",
|
|
36
|
-
PayloadSeparated = "PayloadSeparated",
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/**
|
|
@@ -61,19 +60,21 @@ export type LVHInvalidResponse = {
|
|
|
61
60
|
executionStatus: ExecutionStatus.Invalid;
|
|
62
61
|
latestValidExecHash: RootHex | null;
|
|
63
62
|
invalidateFromParentBlockRoot: RootHex;
|
|
63
|
+
// EL block hash from invalid block's bid (gloas) or payload's parentHash (pre-gloas).
|
|
64
|
+
// Disambiguates which variant of the parent (FULL vs EMPTY) to invalidate from.
|
|
65
|
+
invalidateFromParentBlockHash: RootHex;
|
|
64
66
|
};
|
|
65
67
|
export type LVHExecResponse = LVHValidResponse | LVHInvalidResponse;
|
|
66
68
|
|
|
67
69
|
/**
|
|
68
70
|
* Any execution status that is not definitively invalid.
|
|
69
|
-
*
|
|
70
|
-
* Post-Gloas: execution status must be PayloadSeparated (beacon block imported before its payload arrives via SignedExecutionPayloadEnvelope)
|
|
71
|
+
* Valid | Syncing | PreMerge
|
|
71
72
|
*/
|
|
72
73
|
export type BlockExecutionStatus = Exclude<ExecutionStatus, ExecutionStatus.Invalid>;
|
|
73
74
|
|
|
74
75
|
/**
|
|
75
76
|
* Execution status for a block whose execution payload is present and has been submitted to the EL.
|
|
76
|
-
* Used post-Gloas when transitioning a
|
|
77
|
+
* Used post-Gloas when transitioning a PENDING block to FULL via onExecutionPayload().
|
|
77
78
|
*/
|
|
78
79
|
export type PayloadExecutionStatus = ExecutionStatus.Valid | ExecutionStatus.Syncing;
|
|
79
80
|
|