@lodestar/fork-choice 1.43.0-dev.dfb984e779 → 1.43.0-dev.e3c96e7a79
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 +3 -3
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +12 -19
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +2 -1
- package/lib/forkChoice/interface.d.ts.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 +56 -28
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +7 -7
- package/src/forkChoice/forkChoice.ts +14 -24
- package/src/forkChoice/interface.ts +3 -1
- package/src/protoArray/interface.ts +8 -7
- package/src/protoArray/protoArray.ts +68 -29
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {BitArray} from "@chainsafe/ssz";
|
|
2
2
|
import {GENESIS_EPOCH, PTC_SIZE} from "@lodestar/params";
|
|
3
|
-
import {computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
3
|
+
import {DataAvailabilityStatus, computeEpochAtSlot, computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
4
|
import {Epoch, RootHex, Slot} from "@lodestar/types";
|
|
5
5
|
import {bitCount, toRootHex} from "@lodestar/utils";
|
|
6
6
|
import {ForkChoiceError, ForkChoiceErrorCode} from "../forkChoice/errors.js";
|
|
@@ -214,6 +214,11 @@ export class ProtoArray {
|
|
|
214
214
|
return PayloadStatus.FULL;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
// Genesis block has no parent in the proto array
|
|
218
|
+
if (block.parentRoot === HEX_ZERO_HASH) {
|
|
219
|
+
return PayloadStatus.FULL;
|
|
220
|
+
}
|
|
221
|
+
|
|
217
222
|
const parentBlock = this.getBlockHexAndBlockHash(block.parentRoot, parentBlockHash);
|
|
218
223
|
if (parentBlock == null) {
|
|
219
224
|
throw new ProtoArrayError({
|
|
@@ -252,38 +257,43 @@ export class ProtoArray {
|
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
/**
|
|
255
|
-
* Returns an EMPTY or FULL
|
|
260
|
+
* Returns the node index of an EMPTY or FULL variant matching block root and block hash.
|
|
261
|
+
* Pre-gloas: checks the single variant. Post-gloas: prefers FULL, falls back to EMPTY.
|
|
256
262
|
*/
|
|
257
|
-
|
|
263
|
+
getNodeIndexByRootAndBlockHash(blockRoot: RootHex, blockHash: RootHex): number | undefined {
|
|
258
264
|
const variantIndices = this.indices.get(blockRoot);
|
|
259
265
|
if (variantIndices === undefined) {
|
|
260
|
-
return
|
|
266
|
+
return undefined;
|
|
261
267
|
}
|
|
262
268
|
|
|
263
269
|
// Pre-Gloas
|
|
264
270
|
if (!Array.isArray(variantIndices)) {
|
|
265
|
-
|
|
266
|
-
return node.executionPayloadBlockHash === blockHash ? node : null;
|
|
271
|
+
return this.nodes[variantIndices].executionPayloadBlockHash === blockHash ? variantIndices : undefined;
|
|
267
272
|
}
|
|
268
273
|
|
|
269
|
-
// Post-Gloas,
|
|
274
|
+
// Post-Gloas, prefer FULL then EMPTY
|
|
270
275
|
const fullNodeIndex = variantIndices[PayloadStatus.FULL];
|
|
271
|
-
if (fullNodeIndex !== undefined) {
|
|
272
|
-
|
|
273
|
-
if (fullNode && fullNode.executionPayloadBlockHash === blockHash) {
|
|
274
|
-
return fullNode;
|
|
275
|
-
}
|
|
276
|
+
if (fullNodeIndex !== undefined && this.nodes[fullNodeIndex].executionPayloadBlockHash === blockHash) {
|
|
277
|
+
return fullNodeIndex;
|
|
276
278
|
}
|
|
277
279
|
|
|
278
|
-
const
|
|
279
|
-
if (
|
|
280
|
-
return
|
|
280
|
+
const emptyNodeIndex = variantIndices[PayloadStatus.EMPTY];
|
|
281
|
+
if (this.nodes[emptyNodeIndex].executionPayloadBlockHash === blockHash) {
|
|
282
|
+
return emptyNodeIndex;
|
|
281
283
|
}
|
|
282
284
|
|
|
283
285
|
// PENDING is the same to EMPTY so not likely we can return it
|
|
284
286
|
// also it's only specific for fork-choice
|
|
285
287
|
|
|
286
|
-
return
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Returns an EMPTY or FULL `ProtoBlock` that has matching block root and block hash
|
|
293
|
+
*/
|
|
294
|
+
getBlockHexAndBlockHash(blockRoot: RootHex, blockHash: RootHex): ProtoBlock | null {
|
|
295
|
+
const idx = this.getNodeIndexByRootAndBlockHash(blockRoot, blockHash);
|
|
296
|
+
return idx !== undefined ? this.nodes[idx] : null;
|
|
287
297
|
}
|
|
288
298
|
|
|
289
299
|
/**
|
|
@@ -549,7 +559,8 @@ export class ProtoArray {
|
|
|
549
559
|
executionPayloadBlockHash: RootHex,
|
|
550
560
|
executionPayloadNumber: number,
|
|
551
561
|
proposerBoostRoot: RootHex | null,
|
|
552
|
-
executionStatus: PayloadExecutionStatus
|
|
562
|
+
executionStatus: PayloadExecutionStatus,
|
|
563
|
+
dataAvailabilityStatus: DataAvailabilityStatus
|
|
553
564
|
): void {
|
|
554
565
|
// First check if block exists
|
|
555
566
|
const variants = this.indices.get(blockRoot);
|
|
@@ -591,7 +602,7 @@ export class ProtoArray {
|
|
|
591
602
|
});
|
|
592
603
|
}
|
|
593
604
|
|
|
594
|
-
// Create FULL variant as a child of PENDING (sibling to EMPTY)
|
|
605
|
+
// Create FULL variant as a child of PENDING (sibling to EMPTY).
|
|
595
606
|
const fullNode: ProtoNode = {
|
|
596
607
|
...pendingNode,
|
|
597
608
|
parent: pendingIndex, // Points to own PENDING (same as EMPTY)
|
|
@@ -599,10 +610,10 @@ export class ProtoArray {
|
|
|
599
610
|
weight: 0,
|
|
600
611
|
bestChild: undefined,
|
|
601
612
|
bestDescendant: undefined,
|
|
602
|
-
// TODO GLOAS: handle optimistic sync
|
|
603
613
|
executionStatus,
|
|
604
614
|
executionPayloadBlockHash,
|
|
605
615
|
executionPayloadNumber,
|
|
616
|
+
dataAvailabilityStatus,
|
|
606
617
|
};
|
|
607
618
|
|
|
608
619
|
const fullIndex = this.nodes.length;
|
|
@@ -611,6 +622,12 @@ export class ProtoArray {
|
|
|
611
622
|
// Add FULL variant to the indices array
|
|
612
623
|
variants[PayloadStatus.FULL] = fullIndex;
|
|
613
624
|
|
|
625
|
+
if (executionStatus === ExecutionStatus.Valid) {
|
|
626
|
+
// Walk up from FULL's parent (its own PENDING). FULL is already Valid; the loop breaks
|
|
627
|
+
// immediately if we start at FULL. Same pattern as pre-gloas onBlock at line ~533.
|
|
628
|
+
this.propagateValidExecutionStatusByIndex(pendingIndex);
|
|
629
|
+
}
|
|
630
|
+
|
|
614
631
|
// Update bestChild for PENDING node (may now prefer FULL over EMPTY)
|
|
615
632
|
this.maybeUpdateBestChildAndDescendant(pendingIndex, fullIndex, currentSlot, proposerBoostRoot);
|
|
616
633
|
}
|
|
@@ -639,6 +656,16 @@ export class ProtoArray {
|
|
|
639
656
|
}
|
|
640
657
|
}
|
|
641
658
|
|
|
659
|
+
getPTCVotes(blockRootHex: RootHex): BitArray | null {
|
|
660
|
+
const votes = this.ptcVotes.get(blockRootHex);
|
|
661
|
+
if (votes === undefined) {
|
|
662
|
+
// Block not found or not a Gloas block
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
return votes;
|
|
667
|
+
}
|
|
668
|
+
|
|
642
669
|
/**
|
|
643
670
|
* Check if execution payload for a block is timely
|
|
644
671
|
* Spec: gloas/fork-choice.md#new-is_payload_timely
|
|
@@ -775,11 +802,15 @@ export class ProtoArray {
|
|
|
775
802
|
// Mark chain ii) as Invalid if LVH is found and non null, else only invalidate invalid_payload
|
|
776
803
|
// if its in fcU.
|
|
777
804
|
//
|
|
778
|
-
const {invalidateFromParentBlockRoot, latestValidExecHash} = execResponse;
|
|
779
|
-
|
|
780
|
-
|
|
805
|
+
const {invalidateFromParentBlockRoot, invalidateFromParentBlockHash, latestValidExecHash} = execResponse;
|
|
806
|
+
const invalidateFromParentIndex = this.getNodeIndexByRootAndBlockHash(
|
|
807
|
+
invalidateFromParentBlockRoot,
|
|
808
|
+
invalidateFromParentBlockHash
|
|
809
|
+
);
|
|
781
810
|
if (invalidateFromParentIndex === undefined) {
|
|
782
|
-
throw Error(
|
|
811
|
+
throw Error(
|
|
812
|
+
`Unable to find invalidateFromParentBlockRoot=${invalidateFromParentBlockRoot} invalidateFromParentBlockHash=${invalidateFromParentBlockHash} in forkChoice`
|
|
813
|
+
);
|
|
783
814
|
}
|
|
784
815
|
const latestValidHashIndex =
|
|
785
816
|
latestValidExecHash !== null ? this.getNodeIndexFromLVH(latestValidExecHash, invalidateFromParentIndex) : null;
|
|
@@ -815,12 +846,6 @@ export class ProtoArray {
|
|
|
815
846
|
if (node.executionStatus === ExecutionStatus.PreMerge || node.executionStatus === ExecutionStatus.Valid) {
|
|
816
847
|
break;
|
|
817
848
|
}
|
|
818
|
-
// If PayloadSeparated, that means the node is either PENDING or EMPTY, there could be
|
|
819
|
-
// some ancestor still has syncing status.
|
|
820
|
-
if (node.executionStatus === ExecutionStatus.PayloadSeparated) {
|
|
821
|
-
nodeIndex = node.parent;
|
|
822
|
-
continue;
|
|
823
|
-
}
|
|
824
849
|
this.validateNodeByIndex(nodeIndex);
|
|
825
850
|
nodeIndex = node.parent;
|
|
826
851
|
}
|
|
@@ -918,6 +943,13 @@ export class ProtoArray {
|
|
|
918
943
|
invalidNode.executionStatus = ExecutionStatus.Invalid;
|
|
919
944
|
invalidNode.bestChild = undefined;
|
|
920
945
|
invalidNode.bestDescendant = undefined;
|
|
946
|
+
// Gloas: PENDING and sibling EMPTY share chain status, flip together
|
|
947
|
+
if (invalidNode.payloadStatus === PayloadStatus.PENDING) {
|
|
948
|
+
const variants = this.indices.get(invalidNode.blockRoot);
|
|
949
|
+
if (Array.isArray(variants)) {
|
|
950
|
+
this.invalidateNodeByIndex(variants[PayloadStatus.EMPTY]);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
921
953
|
|
|
922
954
|
return invalidNode;
|
|
923
955
|
}
|
|
@@ -939,6 +971,13 @@ export class ProtoArray {
|
|
|
939
971
|
if (validNode.executionStatus === ExecutionStatus.Syncing) {
|
|
940
972
|
validNode.executionStatus = ExecutionStatus.Valid;
|
|
941
973
|
}
|
|
974
|
+
// Gloas: PENDING and sibling EMPTY share chain status, flip together
|
|
975
|
+
if (validNode.payloadStatus === PayloadStatus.PENDING) {
|
|
976
|
+
const variants = this.indices.get(validNode.blockRoot);
|
|
977
|
+
if (Array.isArray(variants)) {
|
|
978
|
+
this.validateNodeByIndex(variants[PayloadStatus.EMPTY]);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
942
981
|
return validNode;
|
|
943
982
|
}
|
|
944
983
|
|