@lodestar/fork-choice 1.43.0-dev.e341cdc614 → 1.43.0-dev.f758dd5f38
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 -16
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +49 -61
- 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 +8 -21
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +8 -8
- package/src/forkChoice/forkChoice.ts +59 -103
- 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 +8 -31
|
@@ -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;
|
|
@@ -311,6 +310,14 @@ export class ForkChoice implements IForkChoice {
|
|
|
311
310
|
return this.proposerBoostRoot ?? HEX_ZERO_HASH;
|
|
312
311
|
}
|
|
313
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
|
+
|
|
314
321
|
/**
|
|
315
322
|
* To predict the proposer head of the next slot. That is, to predict if proposer-boost-reorg could happen.
|
|
316
323
|
* Reason why we can't be certain is because information of the head block is not fully available yet
|
|
@@ -556,11 +563,11 @@ export class ForkChoice implements IForkChoice {
|
|
|
556
563
|
return this.protoArray.nodes;
|
|
557
564
|
}
|
|
558
565
|
|
|
559
|
-
getFinalizedCheckpoint():
|
|
566
|
+
getFinalizedCheckpoint(): CheckpointWithHex {
|
|
560
567
|
return this.fcStore.finalizedCheckpoint;
|
|
561
568
|
}
|
|
562
569
|
|
|
563
|
-
getJustifiedCheckpoint():
|
|
570
|
+
getJustifiedCheckpoint(): CheckpointWithHex {
|
|
564
571
|
return this.fcStore.justified.checkpoint;
|
|
565
572
|
}
|
|
566
573
|
|
|
@@ -667,18 +674,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
667
674
|
this.proposerBoostRoot = blockRootHex;
|
|
668
675
|
}
|
|
669
676
|
|
|
670
|
-
|
|
671
|
-
const justifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
672
|
-
this.config,
|
|
673
|
-
state,
|
|
674
|
-
state.currentJustifiedCheckpoint.epoch
|
|
675
|
-
);
|
|
676
|
-
const justifiedCheckpoint = toCheckpointWithPayload(state.currentJustifiedCheckpoint, justifiedPayloadStatus);
|
|
677
|
+
const justifiedCheckpoint = toCheckpointWithHex(state.currentJustifiedCheckpoint);
|
|
677
678
|
const stateJustifiedEpoch = justifiedCheckpoint.epoch;
|
|
678
679
|
|
|
679
|
-
|
|
680
|
-
const finalizedPayloadStatus = getCheckpointPayloadStatus(this.config, state, state.finalizedCheckpoint.epoch);
|
|
681
|
-
const finalizedCheckpoint = toCheckpointWithPayload(state.finalizedCheckpoint, finalizedPayloadStatus);
|
|
680
|
+
const finalizedCheckpoint = toCheckpointWithHex(state.finalizedCheckpoint);
|
|
682
681
|
|
|
683
682
|
// Justified balances for `justifiedCheckpoint` are new to the fork-choice. Compute them on demand only if
|
|
684
683
|
// the justified checkpoint changes
|
|
@@ -700,61 +699,29 @@ export class ForkChoice implements IForkChoice {
|
|
|
700
699
|
// This is an optimization. It should reduce the amount of times we run
|
|
701
700
|
// `process_justification_and_finalization` by approximately 1/3rd when the chain is
|
|
702
701
|
// performing optimally.
|
|
703
|
-
let unrealizedJustifiedCheckpoint:
|
|
704
|
-
let unrealizedFinalizedCheckpoint:
|
|
702
|
+
let unrealizedJustifiedCheckpoint: CheckpointWithHex;
|
|
703
|
+
let unrealizedFinalizedCheckpoint: CheckpointWithHex;
|
|
705
704
|
if (this.opts?.computeUnrealized) {
|
|
706
705
|
if (
|
|
707
706
|
parentBlock.unrealizedJustifiedEpoch === blockEpoch &&
|
|
708
707
|
parentBlock.unrealizedFinalizedEpoch + 1 >= blockEpoch
|
|
709
708
|
) {
|
|
710
709
|
// reuse from parent, happens at 1/3 last blocks of epoch as monitored in mainnet
|
|
711
|
-
// Get payload status for unrealized justified checkpoint
|
|
712
|
-
const unrealizedJustifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
713
|
-
this.config,
|
|
714
|
-
state,
|
|
715
|
-
parentBlock.unrealizedJustifiedEpoch
|
|
716
|
-
);
|
|
717
710
|
unrealizedJustifiedCheckpoint = {
|
|
718
711
|
epoch: parentBlock.unrealizedJustifiedEpoch,
|
|
719
712
|
root: fromHex(parentBlock.unrealizedJustifiedRoot),
|
|
720
713
|
rootHex: parentBlock.unrealizedJustifiedRoot,
|
|
721
|
-
payloadStatus: unrealizedJustifiedPayloadStatus,
|
|
722
714
|
};
|
|
723
|
-
// Get payload status for unrealized finalized checkpoint
|
|
724
|
-
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
725
|
-
this.config,
|
|
726
|
-
state,
|
|
727
|
-
parentBlock.unrealizedFinalizedEpoch
|
|
728
|
-
);
|
|
729
715
|
unrealizedFinalizedCheckpoint = {
|
|
730
716
|
epoch: parentBlock.unrealizedFinalizedEpoch,
|
|
731
717
|
root: fromHex(parentBlock.unrealizedFinalizedRoot),
|
|
732
718
|
rootHex: parentBlock.unrealizedFinalizedRoot,
|
|
733
|
-
payloadStatus: unrealizedFinalizedPayloadStatus,
|
|
734
719
|
};
|
|
735
720
|
} else {
|
|
736
721
|
// compute new, happens 2/3 first blocks of epoch as monitored in mainnet
|
|
737
722
|
const unrealized = state.computeUnrealizedCheckpoints();
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
this.config,
|
|
741
|
-
state,
|
|
742
|
-
unrealized.justifiedCheckpoint.epoch
|
|
743
|
-
);
|
|
744
|
-
unrealizedJustifiedCheckpoint = toCheckpointWithPayload(
|
|
745
|
-
unrealized.justifiedCheckpoint,
|
|
746
|
-
unrealizedJustifiedPayloadStatus
|
|
747
|
-
);
|
|
748
|
-
// Get payload status for unrealized finalized checkpoint
|
|
749
|
-
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
750
|
-
this.config,
|
|
751
|
-
state,
|
|
752
|
-
unrealized.finalizedCheckpoint.epoch
|
|
753
|
-
);
|
|
754
|
-
unrealizedFinalizedCheckpoint = toCheckpointWithPayload(
|
|
755
|
-
unrealized.finalizedCheckpoint,
|
|
756
|
-
unrealizedFinalizedPayloadStatus
|
|
757
|
-
);
|
|
723
|
+
unrealizedJustifiedCheckpoint = toCheckpointWithHex(unrealized.justifiedCheckpoint);
|
|
724
|
+
unrealizedFinalizedCheckpoint = toCheckpointWithHex(unrealized.finalizedCheckpoint);
|
|
758
725
|
}
|
|
759
726
|
} else {
|
|
760
727
|
unrealizedJustifiedCheckpoint = justifiedCheckpoint;
|
|
@@ -980,7 +947,6 @@ export class ForkChoice implements IForkChoice {
|
|
|
980
947
|
blockRoot: RootHex,
|
|
981
948
|
executionPayloadBlockHash: RootHex,
|
|
982
949
|
executionPayloadNumber: number,
|
|
983
|
-
executionPayloadStateRoot: RootHex,
|
|
984
950
|
executionStatus: PayloadExecutionStatus
|
|
985
951
|
): void {
|
|
986
952
|
this.protoArray.onExecutionPayload(
|
|
@@ -988,7 +954,6 @@ export class ForkChoice implements IForkChoice {
|
|
|
988
954
|
this.fcStore.currentSlot,
|
|
989
955
|
executionPayloadBlockHash,
|
|
990
956
|
executionPayloadNumber,
|
|
991
|
-
executionPayloadStateRoot,
|
|
992
957
|
this.proposerBoostRoot,
|
|
993
958
|
executionStatus
|
|
994
959
|
);
|
|
@@ -1119,8 +1084,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1119
1084
|
}
|
|
1120
1085
|
|
|
1121
1086
|
getJustifiedBlock(): ProtoBlock {
|
|
1122
|
-
const {rootHex
|
|
1123
|
-
const block = this.
|
|
1087
|
+
const {rootHex} = this.fcStore.justified.checkpoint;
|
|
1088
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1124
1089
|
if (!block) {
|
|
1125
1090
|
throw new ForkChoiceError({
|
|
1126
1091
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1131,8 +1096,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1131
1096
|
}
|
|
1132
1097
|
|
|
1133
1098
|
getFinalizedBlock(): ProtoBlock {
|
|
1134
|
-
const {rootHex
|
|
1135
|
-
const block = this.
|
|
1099
|
+
const {rootHex} = this.fcStore.finalizedCheckpoint;
|
|
1100
|
+
const block = this.getBlockHexDefaultStatus(rootHex);
|
|
1136
1101
|
if (!block) {
|
|
1137
1102
|
throw new ForkChoiceError({
|
|
1138
1103
|
code: ForkChoiceErrorCode.MISSING_PROTO_ARRAY_BLOCK,
|
|
@@ -1225,18 +1190,33 @@ export class ForkChoice implements IForkChoice {
|
|
|
1225
1190
|
|
|
1226
1191
|
/**
|
|
1227
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.
|
|
1228
1197
|
*/
|
|
1229
1198
|
getAllAncestorAndNonAncestorBlocks(
|
|
1230
1199
|
blockRoot: RootHex,
|
|
1231
1200
|
payloadStatus: PayloadStatus
|
|
1232
1201
|
): {ancestors: ProtoBlock[]; nonAncestors: ProtoBlock[]} {
|
|
1233
|
-
|
|
1202
|
+
return this.protoArray.getAllAncestorAndNonAncestorNodes(blockRoot, payloadStatus);
|
|
1203
|
+
}
|
|
1234
1204
|
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
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);
|
|
1240
1220
|
}
|
|
1241
1221
|
|
|
1242
1222
|
getCanonicalBlockByRoot(blockRoot: Root): ProtoBlock | null {
|
|
@@ -1308,6 +1288,17 @@ export class ForkChoice implements IForkChoice {
|
|
|
1308
1288
|
}
|
|
1309
1289
|
}
|
|
1310
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
|
+
|
|
1311
1302
|
/** Very expensive function, iterates the entire ProtoArray. TODO: Is this function even necessary? */
|
|
1312
1303
|
getBlockSummariesByParentRoot(parentRoot: RootHex): ProtoBlock[] {
|
|
1313
1304
|
return this.protoArray.nodes.filter((node) => node.parentRoot === parentRoot);
|
|
@@ -1494,12 +1485,12 @@ export class ForkChoice implements IForkChoice {
|
|
|
1494
1485
|
*
|
|
1495
1486
|
* **`on_tick`**
|
|
1496
1487
|
* May need the justified balances of:
|
|
1497
|
-
* - unrealizedJustified: Already available in `
|
|
1488
|
+
* - unrealizedJustified: Already available in `CheckpointWithBalance`
|
|
1498
1489
|
* Since this balances are already available the getter is just `() => balances`, without cache interaction
|
|
1499
1490
|
*/
|
|
1500
1491
|
private updateCheckpoints(
|
|
1501
|
-
justifiedCheckpoint:
|
|
1502
|
-
finalizedCheckpoint:
|
|
1492
|
+
justifiedCheckpoint: CheckpointWithHex,
|
|
1493
|
+
finalizedCheckpoint: CheckpointWithHex,
|
|
1503
1494
|
getJustifiedBalances: () => JustifiedBalances
|
|
1504
1495
|
): void {
|
|
1505
1496
|
// Update justified checkpoint.
|
|
@@ -1519,8 +1510,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1519
1510
|
* Update unrealized checkpoints in store if necessary
|
|
1520
1511
|
*/
|
|
1521
1512
|
private updateUnrealizedCheckpoints(
|
|
1522
|
-
unrealizedJustifiedCheckpoint:
|
|
1523
|
-
unrealizedFinalizedCheckpoint:
|
|
1513
|
+
unrealizedJustifiedCheckpoint: CheckpointWithHex,
|
|
1514
|
+
unrealizedFinalizedCheckpoint: CheckpointWithHex,
|
|
1524
1515
|
getJustifiedBalances: () => JustifiedBalances
|
|
1525
1516
|
): void {
|
|
1526
1517
|
if (unrealizedJustifiedCheckpoint.epoch > this.fcStore.unrealizedJustified.checkpoint.epoch) {
|
|
@@ -1895,38 +1886,3 @@ export function getCommitteeFraction(
|
|
|
1895
1886
|
const committeeWeight = Math.floor(justifiedTotalActiveBalanceByIncrement / config.slotsPerEpoch);
|
|
1896
1887
|
return Math.floor((committeeWeight * config.committeePercent) / 100);
|
|
1897
1888
|
}
|
|
1898
|
-
|
|
1899
|
-
/**
|
|
1900
|
-
* Get the payload status for a checkpoint.
|
|
1901
|
-
*
|
|
1902
|
-
* Pre-Gloas: always FULL (payload embedded in block)
|
|
1903
|
-
* Gloas: determined by state.execution_payload_availability
|
|
1904
|
-
*
|
|
1905
|
-
* @param config - The chain fork config to determine fork at checkpoint slot
|
|
1906
|
-
* @param state - The state to check execution_payload_availability
|
|
1907
|
-
* @param checkpointEpoch - The epoch of the checkpoint
|
|
1908
|
-
*/
|
|
1909
|
-
export function getCheckpointPayloadStatus(
|
|
1910
|
-
config: ChainForkConfig,
|
|
1911
|
-
state: IBeaconStateView,
|
|
1912
|
-
checkpointEpoch: number
|
|
1913
|
-
): PayloadStatus {
|
|
1914
|
-
// Compute checkpoint slot first to determine the correct fork
|
|
1915
|
-
const checkpointSlot = computeStartSlotAtEpoch(checkpointEpoch);
|
|
1916
|
-
const fork = config.getForkSeq(checkpointSlot);
|
|
1917
|
-
|
|
1918
|
-
// Pre-Gloas: always FULL
|
|
1919
|
-
if (fork < ForkSeq.gloas) {
|
|
1920
|
-
return PayloadStatus.FULL;
|
|
1921
|
-
}
|
|
1922
|
-
if (!isStatePostGloas(state)) {
|
|
1923
|
-
throw new Error(`Expected gloas+ state for checkpoint payload status, got fork=${state.forkName}`);
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
// For Gloas, check state.execution_payload_availability
|
|
1927
|
-
// - For non-skipped slots at checkpoint: returns false (EMPTY) since payload hasn't arrived yet
|
|
1928
|
-
// - For skipped slots at checkpoint: returns the actual availability status from state
|
|
1929
|
-
const payloadAvailable = state.executionPayloadAvailability.get(checkpointSlot % SLOTS_PER_HISTORICAL_ROOT);
|
|
1930
|
-
|
|
1931
|
-
return payloadAvailable ? PayloadStatus.FULL : PayloadStatus.EMPTY;
|
|
1932
|
-
}
|
|
@@ -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,
|
|
@@ -109,30 +109,6 @@ export class ProtoArray {
|
|
|
109
109
|
null
|
|
110
110
|
);
|
|
111
111
|
|
|
112
|
-
// Anchor block PTC votes must be all-true per spec get_forkchoice_store:
|
|
113
|
-
// payload_timeliness_vote={anchor_root: Vector[boolean, PTC_SIZE](True for _ in range(PTC_SIZE))}
|
|
114
|
-
// Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.4/specs/gloas/fork-choice.md#modified-get_forkchoice_store
|
|
115
|
-
if (protoArray.ptcVotes.has(block.blockRoot)) {
|
|
116
|
-
protoArray.ptcVotes.set(block.blockRoot, BitArray.fromBoolArray(Array.from({length: PTC_SIZE}, () => true)));
|
|
117
|
-
|
|
118
|
-
// In the spec, we have payload_states = {anchor_root: anchor_state.copy()}
|
|
119
|
-
// which means the anchor's "payload" is considered received
|
|
120
|
-
// Without FULL, blocks extending FULL from the anchor would be orphaned.
|
|
121
|
-
// TODO GLOAS: This is a bug in the spec. Keep this to pass the current spec test
|
|
122
|
-
// for now. Need to remove this when we work on v1.7.0-alpha.5
|
|
123
|
-
if (block.executionPayloadBlockHash !== null) {
|
|
124
|
-
protoArray.onExecutionPayload(
|
|
125
|
-
block.blockRoot,
|
|
126
|
-
currentSlot,
|
|
127
|
-
block.executionPayloadBlockHash,
|
|
128
|
-
(block as {executionPayloadNumber?: number}).executionPayloadNumber ?? 0,
|
|
129
|
-
block.stateRoot,
|
|
130
|
-
null,
|
|
131
|
-
ExecutionStatus.Valid
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
112
|
return protoArray;
|
|
137
113
|
}
|
|
138
114
|
|
|
@@ -572,7 +548,6 @@ export class ProtoArray {
|
|
|
572
548
|
currentSlot: Slot,
|
|
573
549
|
executionPayloadBlockHash: RootHex,
|
|
574
550
|
executionPayloadNumber: number,
|
|
575
|
-
executionPayloadStateRoot: RootHex,
|
|
576
551
|
proposerBoostRoot: RootHex | null,
|
|
577
552
|
executionStatus: PayloadExecutionStatus
|
|
578
553
|
): void {
|
|
@@ -628,7 +603,6 @@ export class ProtoArray {
|
|
|
628
603
|
executionStatus,
|
|
629
604
|
executionPayloadBlockHash,
|
|
630
605
|
executionPayloadNumber,
|
|
631
|
-
stateRoot: executionPayloadStateRoot,
|
|
632
606
|
};
|
|
633
607
|
|
|
634
608
|
const fullIndex = this.nodes.length;
|
|
@@ -707,7 +681,7 @@ export class ProtoArray {
|
|
|
707
681
|
* Determine if we should extend the payload (prefer FULL over EMPTY)
|
|
708
682
|
* Spec: gloas/fork-choice.md#new-should_extend_payload
|
|
709
683
|
*
|
|
710
|
-
* Returns true if:
|
|
684
|
+
* Returns true if payload is verified (FULL variant exists) AND:
|
|
711
685
|
* 1. Payload is timely, OR
|
|
712
686
|
* 2. No proposer boost root (empty/zero hash), OR
|
|
713
687
|
* 3. Proposer boost root's parent is not this block, OR
|
|
@@ -717,6 +691,10 @@ export class ProtoArray {
|
|
|
717
691
|
* @param proposerBoostRoot - Current proposer boost root (from ForkChoice)
|
|
718
692
|
*/
|
|
719
693
|
shouldExtendPayload(blockRoot: RootHex, proposerBoostRoot: RootHex | null): boolean {
|
|
694
|
+
if (!this.hasPayload(blockRoot)) {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
|
|
720
698
|
// Condition 1: Payload is timely
|
|
721
699
|
if (this.isPayloadTimely(blockRoot)) {
|
|
722
700
|
return true;
|
|
@@ -1672,10 +1650,9 @@ export class ProtoArray {
|
|
|
1672
1650
|
const ancestors: ProtoNode[] = [];
|
|
1673
1651
|
const nonAncestors: ProtoNode[] = [];
|
|
1674
1652
|
|
|
1675
|
-
//
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
}
|
|
1653
|
+
// caller of this method may pass default status
|
|
1654
|
+
// this is the only node that we accept PENDING
|
|
1655
|
+
ancestors.push(node);
|
|
1679
1656
|
|
|
1680
1657
|
let nodeIndex = startIndex;
|
|
1681
1658
|
while (node.parent !== undefined) {
|