@lodestar/fork-choice 1.42.0 → 1.43.0-dev.05a33e512f
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 +27 -20
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +102 -84
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +27 -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 +10 -2
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +81 -39
- 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 +119 -132
- package/src/forkChoice/interface.ts +28 -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 +95 -41
|
@@ -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;
|
|
@@ -822,14 +788,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
822
788
|
// Fallback to parent block's number (we know it's post-merge from check above)
|
|
823
789
|
return parentBlock.executionPayloadNumber;
|
|
824
790
|
})(),
|
|
825
|
-
executionStatus: this.
|
|
791
|
+
executionStatus: this.getPostMergeExecStatus(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,
|
|
832
|
-
executionStatus: this.
|
|
801
|
+
executionStatus: this.getPostMergeExecStatus(executionStatus),
|
|
833
802
|
dataAvailabilityStatus,
|
|
834
803
|
}
|
|
835
804
|
: {
|
|
@@ -978,17 +947,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
978
947
|
blockRoot: RootHex,
|
|
979
948
|
executionPayloadBlockHash: RootHex,
|
|
980
949
|
executionPayloadNumber: number,
|
|
981
|
-
|
|
982
|
-
|
|
950
|
+
executionStatus: PayloadExecutionStatus,
|
|
951
|
+
dataAvailabilityStatus: DataAvailabilityStatus
|
|
983
952
|
): void {
|
|
984
953
|
this.protoArray.onExecutionPayload(
|
|
985
954
|
blockRoot,
|
|
986
955
|
this.fcStore.currentSlot,
|
|
987
956
|
executionPayloadBlockHash,
|
|
988
957
|
executionPayloadNumber,
|
|
989
|
-
executionPayloadStateRoot,
|
|
990
958
|
this.proposerBoostRoot,
|
|
991
|
-
executionStatus
|
|
959
|
+
executionStatus,
|
|
960
|
+
dataAvailabilityStatus
|
|
992
961
|
);
|
|
993
962
|
}
|
|
994
963
|
|
|
@@ -1074,6 +1043,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1074
1043
|
return this.protoArray.hasPayload(blockRoot);
|
|
1075
1044
|
}
|
|
1076
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
|
+
|
|
1077
1052
|
/**
|
|
1078
1053
|
* Returns a MUTABLE `ProtoBlock` if the block is known **and** a descendant of the finalized root.
|
|
1079
1054
|
*/
|
|
@@ -1117,8 +1092,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1117
1092
|
}
|
|
1118
1093
|
|
|
1119
1094
|
getJustifiedBlock(): ProtoBlock {
|
|
1120
|
-
const {rootHex
|
|
1121
|
-
const block = this.
|
|
1095
|
+
const {rootHex} = this.fcStore.justified.checkpoint;
|
|
1096
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1122
1097
|
if (!block) {
|
|
1123
1098
|
throw new ForkChoiceError({
|
|
1124
1099
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1129,8 +1104,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1129
1104
|
}
|
|
1130
1105
|
|
|
1131
1106
|
getFinalizedBlock(): ProtoBlock {
|
|
1132
|
-
const {rootHex
|
|
1133
|
-
const block = this.
|
|
1107
|
+
const {rootHex} = this.fcStore.finalizedCheckpoint;
|
|
1108
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1134
1109
|
if (!block) {
|
|
1135
1110
|
throw new ForkChoiceError({
|
|
1136
1111
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1205,13 +1180,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1205
1180
|
}
|
|
1206
1181
|
|
|
1207
1182
|
/**
|
|
1208
|
-
*
|
|
1209
|
-
*
|
|
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`
|
|
1210
1186
|
*/
|
|
1211
1187
|
getAllAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[] {
|
|
1212
|
-
|
|
1213
|
-
// the last node is the previous finalized one, it's there to check onBlock finalized checkpoint only.
|
|
1214
|
-
return blocks.slice(0, blocks.length - 1);
|
|
1188
|
+
return this.protoArray.getAllAncestorNodes(blockRoot, payloadStatus);
|
|
1215
1189
|
}
|
|
1216
1190
|
|
|
1217
1191
|
/**
|
|
@@ -1223,18 +1197,33 @@ export class ForkChoice implements IForkChoice {
|
|
|
1223
1197
|
|
|
1224
1198
|
/**
|
|
1225
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.
|
|
1226
1204
|
*/
|
|
1227
1205
|
getAllAncestorAndNonAncestorBlocks(
|
|
1228
1206
|
blockRoot: RootHex,
|
|
1229
1207
|
payloadStatus: PayloadStatus
|
|
1230
1208
|
): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]} {
|
|
1231
|
-
|
|
1209
|
+
return this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot, payloadStatus);
|
|
1210
|
+
}
|
|
1232
1211
|
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
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);
|
|
1238
1227
|
}
|
|
1239
1228
|
|
|
1240
1229
|
getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null {
|
|
@@ -1306,6 +1295,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
1306
1295
|
}
|
|
1307
1296
|
}
|
|
1308
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
|
+
|
|
1309
1309
|
/** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
|
|
1310
1310
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[] {
|
|
1311
1311
|
return this.protoArray.nodes.filter((node) => node.parentRoot === parentRoot);
|
|
@@ -1457,24 +1457,16 @@ export class ForkChoice implements IForkChoice {
|
|
|
1457
1457
|
return dataAvailabilityStatus;
|
|
1458
1458
|
}
|
|
1459
1459
|
|
|
1460
|
-
private
|
|
1460
|
+
private getPostMergeExecStatus(
|
|
1461
1461
|
executionStatus: BlockExecutionStatus
|
|
1462
1462
|
): ExecutionStatus.Valid | ExecutionStatus.Syncing {
|
|
1463
|
-
if (executionStatus === ExecutionStatus.PreMerge
|
|
1463
|
+
if (executionStatus === ExecutionStatus.PreMerge)
|
|
1464
1464
|
throw Error(
|
|
1465
1465
|
`Invalid post-merge execution status: expected: ${ExecutionStatus.Syncing} or ${ExecutionStatus.Valid}, got ${executionStatus}`
|
|
1466
1466
|
);
|
|
1467
1467
|
return executionStatus;
|
|
1468
1468
|
}
|
|
1469
1469
|
|
|
1470
|
-
private getPostGloasExecStatus(executionStatus: BlockExecutionStatus): ExecutionStatus.PayloadSeparated {
|
|
1471
|
-
if (executionStatus !== ExecutionStatus.PayloadSeparated)
|
|
1472
|
-
throw Error(
|
|
1473
|
-
`Invalid post-gloas execution status: expected: ${ExecutionStatus.PayloadSeparated}, got ${executionStatus}`
|
|
1474
|
-
);
|
|
1475
|
-
return executionStatus;
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
1470
|
/**
|
|
1479
1471
|
* Why `getJustifiedBalances` getter?
|
|
1480
1472
|
* - updateCheckpoints() is called in both on_block and on_tick.
|
|
@@ -1492,12 +1484,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1492
1484
|
*
|
|
1493
1485
|
* **`on_tick`**
|
|
1494
1486
|
* May need the justified balances of:
|
|
1495
|
-
* - unrealizedJustified: Already available in `
|
|
1487
|
+
* - unrealizedJustified: Already available in `CheckpointWithBalance`
|
|
1496
1488
|
* Since this balances are already available the getter is just `() => balances`, without cache interaction
|
|
1497
1489
|
*/
|
|
1498
1490
|
private updateCheckpoints(
|
|
1499
|
-
justifiedCheckpoint:
|
|
1500
|
-
finalizedCheckpoint:
|
|
1491
|
+
justifiedCheckpoint: CheckpointWithHex,
|
|
1492
|
+
finalizedCheckpoint: CheckpointWithHex,
|
|
1501
1493
|
getJustifiedBalances: () => JustifiedBalances
|
|
1502
1494
|
): void {
|
|
1503
1495
|
// Update justified checkpoint.
|
|
@@ -1517,8 +1509,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1517
1509
|
* Update unrealized checkpoints in store if necessary
|
|
1518
1510
|
*/
|
|
1519
1511
|
private updateUnrealizedCheckpoints(
|
|
1520
|
-
unrealizedJustifiedCheckpoint:
|
|
1521
|
-
unrealizedFinalizedCheckpoint:
|
|
1512
|
+
unrealizedJustifiedCheckpoint: CheckpointWithHex,
|
|
1513
|
+
unrealizedFinalizedCheckpoint: CheckpointWithHex,
|
|
1522
1514
|
getJustifiedBalances: () => JustifiedBalances
|
|
1523
1515
|
): void {
|
|
1524
1516
|
if (unrealizedJustifiedCheckpoint.epoch > this.fcStore.unrealizedJustified.checkpoint.epoch) {
|
|
@@ -1680,15 +1672,42 @@ export class ForkChoice implements IForkChoice {
|
|
|
1680
1672
|
});
|
|
1681
1673
|
}
|
|
1682
1674
|
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1675
|
+
if (isGloasBlock(block)) {
|
|
1676
|
+
// For Gloas blocks, attestation index must be 0 or 1
|
|
1677
|
+
if (attestationData.index !== 0 && attestationData.index !== 1) {
|
|
1678
|
+
throw new ForkChoiceError({
|
|
1679
|
+
code: ForkChoiceErrorCode.INVALID_ATTESTATION,
|
|
1680
|
+
err: {
|
|
1681
|
+
code: InvalidAttestationCode.INVALID_DATA_INDEX,
|
|
1682
|
+
index: attestationData.index,
|
|
1683
|
+
},
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
// Same-slot attestations can only vote for the PENDING variant
|
|
1688
|
+
if (block.slot === slot && attestationData.index !== 0) {
|
|
1689
|
+
throw new ForkChoiceError({
|
|
1690
|
+
code: ForkChoiceErrorCode.INVALID_ATTESTATION,
|
|
1691
|
+
err: {
|
|
1692
|
+
code: InvalidAttestationCode.INVALID_DATA_INDEX,
|
|
1693
|
+
index: attestationData.index,
|
|
1694
|
+
},
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// If attesting for a full node, the payload must be known
|
|
1699
|
+
if (attestationData.index === 1) {
|
|
1700
|
+
const fullNodeIndex = this.protoArray.getNodeIndexByRootAndStatus(beaconBlockRootHex, PayloadStatus.FULL);
|
|
1701
|
+
if (fullNodeIndex === undefined) {
|
|
1702
|
+
throw new ForkChoiceError({
|
|
1703
|
+
code: ForkChoiceErrorCode.INVALID_ATTESTATION,
|
|
1704
|
+
err: {
|
|
1705
|
+
code: InvalidAttestationCode.UNKNOWN_PAYLOAD_STATUS,
|
|
1706
|
+
beaconBlockRoot: beaconBlockRootHex,
|
|
1707
|
+
},
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1692
1711
|
}
|
|
1693
1712
|
|
|
1694
1713
|
this.validatedAttestationDatas.add(attDataRoot);
|
|
@@ -1866,35 +1885,3 @@ export function getCommitteeFraction(
|
|
|
1866
1885
|
const committeeWeight = Math.floor(justifiedTotalActiveBalanceByIncrement / config.slotsPerEpoch);
|
|
1867
1886
|
return Math.floor((committeeWeight * config.committeePercent) / 100);
|
|
1868
1887
|
}
|
|
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,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
|
*/
|
|
@@ -241,6 +241,7 @@ export interface IForkChoice {
|
|
|
241
241
|
getBlockDefaultStatus(blockRoot: Root): ProtoBlock | null;
|
|
242
242
|
getBlockHexDefaultStatus(blockRoot: RootHex): ProtoBlock | null;
|
|
243
243
|
getBlockHexAndBlockHash(blockRoot: RootHex, blockHash: RootHex): ProtoBlock | null;
|
|
244
|
+
shouldExtendPayload(blockRoot: RootHex): boolean;
|
|
244
245
|
getFinalizedBlock(): ProtoBlock;
|
|
245
246
|
getJustifiedBlock(): ProtoBlock;
|
|
246
247
|
getFinalizedCheckpointSlot(): Slot;
|
|
@@ -272,11 +273,23 @@ export interface IForkChoice {
|
|
|
272
273
|
getAllNonAncestorBlocks(blockRoot: RootHex, payloadStatus: PayloadStatus): ProtoBlock[];
|
|
273
274
|
/**
|
|
274
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.
|
|
275
279
|
*/
|
|
276
280
|
getAllAncestorAndNonAncestorBlocks(
|
|
277
281
|
blockRoot: RootHex,
|
|
278
282
|
payloadStatus: PayloadStatus
|
|
279
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
|
+
};
|
|
280
293
|
getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null;
|
|
281
294
|
getCanonicalBlockAtSlot(slot: Slot): ProtoBlock | null;
|
|
282
295
|
getCanonicalBlockClosestLteSlot(slot: Slot): ProtoBlock | null;
|
|
@@ -288,6 +301,12 @@ export interface IForkChoice {
|
|
|
288
301
|
* Iterates forward descendants of blockRoot. Does not yield blockRoot itself
|
|
289
302
|
*/
|
|
290
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>;
|
|
291
310
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[];
|
|
292
311
|
getBlockSummariesAtSlot(slot: Slot): ProtoBlock[];
|
|
293
312
|
/** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
|