@lodestar/fork-choice 1.43.0-dev.3d6ae250a4 → 1.43.0-dev.3fe3b04cbd
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 +6 -5
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +16 -16
- 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 +52 -28
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +7 -7
- package/src/forkChoice/forkChoice.ts +18 -19
- package/src/forkChoice/interface.ts +3 -1
- package/src/protoArray/interface.ts +8 -7
- package/src/protoArray/protoArray.ts +63 -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";
|
|
@@ -252,38 +252,43 @@ export class ProtoArray {
|
|
|
252
252
|
}
|
|
253
253
|
|
|
254
254
|
/**
|
|
255
|
-
* Returns an EMPTY or FULL
|
|
255
|
+
* Returns the node index of an EMPTY or FULL variant matching block root and block hash.
|
|
256
|
+
* Pre-gloas: checks the single variant. Post-gloas: prefers FULL, falls back to EMPTY.
|
|
256
257
|
*/
|
|
257
|
-
|
|
258
|
+
getNodeIndexByRootAndBlockHash(blockRoot: RootHex, blockHash: RootHex): number | undefined {
|
|
258
259
|
const variantIndices = this.indices.get(blockRoot);
|
|
259
260
|
if (variantIndices === undefined) {
|
|
260
|
-
return
|
|
261
|
+
return undefined;
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
// Pre-Gloas
|
|
264
265
|
if (!Array.isArray(variantIndices)) {
|
|
265
|
-
|
|
266
|
-
return node.executionPayloadBlockHash === blockHash ? node : null;
|
|
266
|
+
return this.nodes[variantIndices].executionPayloadBlockHash === blockHash ? variantIndices : undefined;
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
// Post-Gloas,
|
|
269
|
+
// Post-Gloas, prefer FULL then EMPTY
|
|
270
270
|
const fullNodeIndex = variantIndices[PayloadStatus.FULL];
|
|
271
|
-
if (fullNodeIndex !== undefined) {
|
|
272
|
-
|
|
273
|
-
if (fullNode && fullNode.executionPayloadBlockHash === blockHash) {
|
|
274
|
-
return fullNode;
|
|
275
|
-
}
|
|
271
|
+
if (fullNodeIndex !== undefined && this.nodes[fullNodeIndex].executionPayloadBlockHash === blockHash) {
|
|
272
|
+
return fullNodeIndex;
|
|
276
273
|
}
|
|
277
274
|
|
|
278
|
-
const
|
|
279
|
-
if (
|
|
280
|
-
return
|
|
275
|
+
const emptyNodeIndex = variantIndices[PayloadStatus.EMPTY];
|
|
276
|
+
if (this.nodes[emptyNodeIndex].executionPayloadBlockHash === blockHash) {
|
|
277
|
+
return emptyNodeIndex;
|
|
281
278
|
}
|
|
282
279
|
|
|
283
280
|
// PENDING is the same to EMPTY so not likely we can return it
|
|
284
281
|
// also it's only specific for fork-choice
|
|
285
282
|
|
|
286
|
-
return
|
|
283
|
+
return undefined;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Returns an EMPTY or FULL `ProtoBlock` that has matching block root and block hash
|
|
288
|
+
*/
|
|
289
|
+
getBlockHexAndBlockHash(blockRoot: RootHex, blockHash: RootHex): ProtoBlock | null {
|
|
290
|
+
const idx = this.getNodeIndexByRootAndBlockHash(blockRoot, blockHash);
|
|
291
|
+
return idx !== undefined ? this.nodes[idx] : null;
|
|
287
292
|
}
|
|
288
293
|
|
|
289
294
|
/**
|
|
@@ -549,7 +554,8 @@ export class ProtoArray {
|
|
|
549
554
|
executionPayloadBlockHash: RootHex,
|
|
550
555
|
executionPayloadNumber: number,
|
|
551
556
|
proposerBoostRoot: RootHex | null,
|
|
552
|
-
executionStatus: PayloadExecutionStatus
|
|
557
|
+
executionStatus: PayloadExecutionStatus,
|
|
558
|
+
dataAvailabilityStatus: DataAvailabilityStatus
|
|
553
559
|
): void {
|
|
554
560
|
// First check if block exists
|
|
555
561
|
const variants = this.indices.get(blockRoot);
|
|
@@ -591,7 +597,7 @@ export class ProtoArray {
|
|
|
591
597
|
});
|
|
592
598
|
}
|
|
593
599
|
|
|
594
|
-
// Create FULL variant as a child of PENDING (sibling to EMPTY)
|
|
600
|
+
// Create FULL variant as a child of PENDING (sibling to EMPTY).
|
|
595
601
|
const fullNode: ProtoNode = {
|
|
596
602
|
...pendingNode,
|
|
597
603
|
parent: pendingIndex, // Points to own PENDING (same as EMPTY)
|
|
@@ -599,10 +605,10 @@ export class ProtoArray {
|
|
|
599
605
|
weight: 0,
|
|
600
606
|
bestChild: undefined,
|
|
601
607
|
bestDescendant: undefined,
|
|
602
|
-
// TODO GLOAS: handle optimistic sync
|
|
603
608
|
executionStatus,
|
|
604
609
|
executionPayloadBlockHash,
|
|
605
610
|
executionPayloadNumber,
|
|
611
|
+
dataAvailabilityStatus,
|
|
606
612
|
};
|
|
607
613
|
|
|
608
614
|
const fullIndex = this.nodes.length;
|
|
@@ -611,6 +617,12 @@ export class ProtoArray {
|
|
|
611
617
|
// Add FULL variant to the indices array
|
|
612
618
|
variants[PayloadStatus.FULL] = fullIndex;
|
|
613
619
|
|
|
620
|
+
if (executionStatus === ExecutionStatus.Valid) {
|
|
621
|
+
// Walk up from FULL's parent (its own PENDING). FULL is already Valid; the loop breaks
|
|
622
|
+
// immediately if we start at FULL. Same pattern as pre-gloas onBlock at line ~533.
|
|
623
|
+
this.propagateValidExecutionStatusByIndex(pendingIndex);
|
|
624
|
+
}
|
|
625
|
+
|
|
614
626
|
// Update bestChild for PENDING node (may now prefer FULL over EMPTY)
|
|
615
627
|
this.maybeUpdateBestChildAndDescendant(pendingIndex, fullIndex, currentSlot, proposerBoostRoot);
|
|
616
628
|
}
|
|
@@ -639,6 +651,16 @@ export class ProtoArray {
|
|
|
639
651
|
}
|
|
640
652
|
}
|
|
641
653
|
|
|
654
|
+
getPTCVotes(blockRootHex: RootHex): BitArray | null {
|
|
655
|
+
const votes = this.ptcVotes.get(blockRootHex);
|
|
656
|
+
if (votes === undefined) {
|
|
657
|
+
// Block not found or not a Gloas block
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return votes;
|
|
662
|
+
}
|
|
663
|
+
|
|
642
664
|
/**
|
|
643
665
|
* Check if execution payload for a block is timely
|
|
644
666
|
* Spec: gloas/fork-choice.md#new-is_payload_timely
|
|
@@ -775,11 +797,15 @@ export class ProtoArray {
|
|
|
775
797
|
// Mark chain ii) as Invalid if LVH is found and non null, else only invalidate invalid_payload
|
|
776
798
|
// if its in fcU.
|
|
777
799
|
//
|
|
778
|
-
const {invalidateFromParentBlockRoot, latestValidExecHash} = execResponse;
|
|
779
|
-
|
|
780
|
-
|
|
800
|
+
const {invalidateFromParentBlockRoot, invalidateFromParentBlockHash, latestValidExecHash} = execResponse;
|
|
801
|
+
const invalidateFromParentIndex = this.getNodeIndexByRootAndBlockHash(
|
|
802
|
+
invalidateFromParentBlockRoot,
|
|
803
|
+
invalidateFromParentBlockHash
|
|
804
|
+
);
|
|
781
805
|
if (invalidateFromParentIndex === undefined) {
|
|
782
|
-
throw Error(
|
|
806
|
+
throw Error(
|
|
807
|
+
`Unable to find invalidateFromParentBlockRoot=${invalidateFromParentBlockRoot} invalidateFromParentBlockHash=${invalidateFromParentBlockHash} in forkChoice`
|
|
808
|
+
);
|
|
783
809
|
}
|
|
784
810
|
const latestValidHashIndex =
|
|
785
811
|
latestValidExecHash !== null ? this.getNodeIndexFromLVH(latestValidExecHash, invalidateFromParentIndex) : null;
|
|
@@ -815,12 +841,6 @@ export class ProtoArray {
|
|
|
815
841
|
if (node.executionStatus === ExecutionStatus.PreMerge || node.executionStatus === ExecutionStatus.Valid) {
|
|
816
842
|
break;
|
|
817
843
|
}
|
|
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
844
|
this.validateNodeByIndex(nodeIndex);
|
|
825
845
|
nodeIndex = node.parent;
|
|
826
846
|
}
|
|
@@ -918,6 +938,13 @@ export class ProtoArray {
|
|
|
918
938
|
invalidNode.executionStatus = ExecutionStatus.Invalid;
|
|
919
939
|
invalidNode.bestChild = undefined;
|
|
920
940
|
invalidNode.bestDescendant = undefined;
|
|
941
|
+
// Gloas: PENDING and sibling EMPTY share chain status, flip together
|
|
942
|
+
if (invalidNode.payloadStatus === PayloadStatus.PENDING) {
|
|
943
|
+
const variants = this.indices.get(invalidNode.blockRoot);
|
|
944
|
+
if (Array.isArray(variants)) {
|
|
945
|
+
this.invalidateNodeByIndex(variants[PayloadStatus.EMPTY]);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
921
948
|
|
|
922
949
|
return invalidNode;
|
|
923
950
|
}
|
|
@@ -939,6 +966,13 @@ export class ProtoArray {
|
|
|
939
966
|
if (validNode.executionStatus === ExecutionStatus.Syncing) {
|
|
940
967
|
validNode.executionStatus = ExecutionStatus.Valid;
|
|
941
968
|
}
|
|
969
|
+
// Gloas: PENDING and sibling EMPTY share chain status, flip together
|
|
970
|
+
if (validNode.payloadStatus === PayloadStatus.PENDING) {
|
|
971
|
+
const variants = this.indices.get(validNode.blockRoot);
|
|
972
|
+
if (Array.isArray(variants)) {
|
|
973
|
+
this.validateNodeByIndex(variants[PayloadStatus.EMPTY]);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
942
976
|
return validNode;
|
|
943
977
|
}
|
|
944
978
|
|