@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,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";
|
|
@@ -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
|
|
|
@@ -276,38 +252,43 @@ export class ProtoArray {
|
|
|
276
252
|
}
|
|
277
253
|
|
|
278
254
|
/**
|
|
279
|
-
* 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.
|
|
280
257
|
*/
|
|
281
|
-
|
|
258
|
+
getNodeIndexByRootAndBlockHash(blockRoot: RootHex, blockHash: RootHex): number | undefined {
|
|
282
259
|
const variantIndices = this.indices.get(blockRoot);
|
|
283
260
|
if (variantIndices === undefined) {
|
|
284
|
-
return
|
|
261
|
+
return undefined;
|
|
285
262
|
}
|
|
286
263
|
|
|
287
264
|
// Pre-Gloas
|
|
288
265
|
if (!Array.isArray(variantIndices)) {
|
|
289
|
-
|
|
290
|
-
return node.executionPayloadBlockHash === blockHash ? node : null;
|
|
266
|
+
return this.nodes[variantIndices].executionPayloadBlockHash === blockHash ? variantIndices : undefined;
|
|
291
267
|
}
|
|
292
268
|
|
|
293
|
-
// Post-Gloas,
|
|
269
|
+
// Post-Gloas, prefer FULL then EMPTY
|
|
294
270
|
const fullNodeIndex = variantIndices[PayloadStatus.FULL];
|
|
295
|
-
if (fullNodeIndex !== undefined) {
|
|
296
|
-
|
|
297
|
-
if (fullNode && fullNode.executionPayloadBlockHash === blockHash) {
|
|
298
|
-
return fullNode;
|
|
299
|
-
}
|
|
271
|
+
if (fullNodeIndex !== undefined && this.nodes[fullNodeIndex].executionPayloadBlockHash === blockHash) {
|
|
272
|
+
return fullNodeIndex;
|
|
300
273
|
}
|
|
301
274
|
|
|
302
|
-
const
|
|
303
|
-
if (
|
|
304
|
-
return
|
|
275
|
+
const emptyNodeIndex = variantIndices[PayloadStatus.EMPTY];
|
|
276
|
+
if (this.nodes[emptyNodeIndex].executionPayloadBlockHash === blockHash) {
|
|
277
|
+
return emptyNodeIndex;
|
|
305
278
|
}
|
|
306
279
|
|
|
307
280
|
// PENDING is the same to EMPTY so not likely we can return it
|
|
308
281
|
// also it's only specific for fork-choice
|
|
309
282
|
|
|
310
|
-
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;
|
|
311
292
|
}
|
|
312
293
|
|
|
313
294
|
/**
|
|
@@ -572,9 +553,9 @@ export class ProtoArray {
|
|
|
572
553
|
currentSlot: Slot,
|
|
573
554
|
executionPayloadBlockHash: RootHex,
|
|
574
555
|
executionPayloadNumber: number,
|
|
575
|
-
executionPayloadStateRoot: RootHex,
|
|
576
556
|
proposerBoostRoot: RootHex | null,
|
|
577
|
-
executionStatus: PayloadExecutionStatus
|
|
557
|
+
executionStatus: PayloadExecutionStatus,
|
|
558
|
+
dataAvailabilityStatus: DataAvailabilityStatus
|
|
578
559
|
): void {
|
|
579
560
|
// First check if block exists
|
|
580
561
|
const variants = this.indices.get(blockRoot);
|
|
@@ -616,7 +597,7 @@ export class ProtoArray {
|
|
|
616
597
|
});
|
|
617
598
|
}
|
|
618
599
|
|
|
619
|
-
// Create FULL variant as a child of PENDING (sibling to EMPTY)
|
|
600
|
+
// Create FULL variant as a child of PENDING (sibling to EMPTY).
|
|
620
601
|
const fullNode: ProtoNode = {
|
|
621
602
|
...pendingNode,
|
|
622
603
|
parent: pendingIndex, // Points to own PENDING (same as EMPTY)
|
|
@@ -624,11 +605,10 @@ export class ProtoArray {
|
|
|
624
605
|
weight: 0,
|
|
625
606
|
bestChild: undefined,
|
|
626
607
|
bestDescendant: undefined,
|
|
627
|
-
// TODO GLOAS: handle optimistic sync
|
|
628
608
|
executionStatus,
|
|
629
609
|
executionPayloadBlockHash,
|
|
630
610
|
executionPayloadNumber,
|
|
631
|
-
|
|
611
|
+
dataAvailabilityStatus,
|
|
632
612
|
};
|
|
633
613
|
|
|
634
614
|
const fullIndex = this.nodes.length;
|
|
@@ -637,6 +617,12 @@ export class ProtoArray {
|
|
|
637
617
|
// Add FULL variant to the indices array
|
|
638
618
|
variants[PayloadStatus.FULL] = fullIndex;
|
|
639
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
|
+
|
|
640
626
|
// Update bestChild for PENDING node (may now prefer FULL over EMPTY)
|
|
641
627
|
this.maybeUpdateBestChildAndDescendant(pendingIndex, fullIndex, currentSlot, proposerBoostRoot);
|
|
642
628
|
}
|
|
@@ -665,6 +651,16 @@ export class ProtoArray {
|
|
|
665
651
|
}
|
|
666
652
|
}
|
|
667
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
|
+
|
|
668
664
|
/**
|
|
669
665
|
* Check if execution payload for a block is timely
|
|
670
666
|
* Spec: gloas/fork-choice.md#new-is_payload_timely
|
|
@@ -801,11 +797,15 @@ export class ProtoArray {
|
|
|
801
797
|
// Mark chain ii) as Invalid if LVH is found and non null, else only invalidate invalid_payload
|
|
802
798
|
// if its in fcU.
|
|
803
799
|
//
|
|
804
|
-
const {invalidateFromParentBlockRoot, latestValidExecHash} = execResponse;
|
|
805
|
-
|
|
806
|
-
|
|
800
|
+
const {invalidateFromParentBlockRoot, invalidateFromParentBlockHash, latestValidExecHash} = execResponse;
|
|
801
|
+
const invalidateFromParentIndex = this.getNodeIndexByRootAndBlockHash(
|
|
802
|
+
invalidateFromParentBlockRoot,
|
|
803
|
+
invalidateFromParentBlockHash
|
|
804
|
+
);
|
|
807
805
|
if (invalidateFromParentIndex === undefined) {
|
|
808
|
-
throw Error(
|
|
806
|
+
throw Error(
|
|
807
|
+
`Unable to find invalidateFromParentBlockRoot=${invalidateFromParentBlockRoot} invalidateFromParentBlockHash=${invalidateFromParentBlockHash} in forkChoice`
|
|
808
|
+
);
|
|
809
809
|
}
|
|
810
810
|
const latestValidHashIndex =
|
|
811
811
|
latestValidExecHash !== null ? this.getNodeIndexFromLVH(latestValidExecHash, invalidateFromParentIndex) : null;
|
|
@@ -841,12 +841,6 @@ export class ProtoArray {
|
|
|
841
841
|
if (node.executionStatus === ExecutionStatus.PreMerge || node.executionStatus === ExecutionStatus.Valid) {
|
|
842
842
|
break;
|
|
843
843
|
}
|
|
844
|
-
// If PayloadSeparated, that means the node is either PENDING or EMPTY, there could be
|
|
845
|
-
// some ancestor still has syncing status.
|
|
846
|
-
if (node.executionStatus === ExecutionStatus.PayloadSeparated) {
|
|
847
|
-
nodeIndex = node.parent;
|
|
848
|
-
continue;
|
|
849
|
-
}
|
|
850
844
|
this.validateNodeByIndex(nodeIndex);
|
|
851
845
|
nodeIndex = node.parent;
|
|
852
846
|
}
|
|
@@ -944,6 +938,13 @@ export class ProtoArray {
|
|
|
944
938
|
invalidNode.executionStatus = ExecutionStatus.Invalid;
|
|
945
939
|
invalidNode.bestChild = undefined;
|
|
946
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
|
+
}
|
|
947
948
|
|
|
948
949
|
return invalidNode;
|
|
949
950
|
}
|
|
@@ -965,6 +966,13 @@ export class ProtoArray {
|
|
|
965
966
|
if (validNode.executionStatus === ExecutionStatus.Syncing) {
|
|
966
967
|
validNode.executionStatus = ExecutionStatus.Valid;
|
|
967
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
|
+
}
|
|
968
976
|
return validNode;
|
|
969
977
|
}
|
|
970
978
|
|
|
@@ -1676,10 +1684,9 @@ export class ProtoArray {
|
|
|
1676
1684
|
const ancestors: ProtoNode[] = [];
|
|
1677
1685
|
const nonAncestors: ProtoNode[] = [];
|
|
1678
1686
|
|
|
1679
|
-
//
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
}
|
|
1687
|
+
// caller of this method may pass default status
|
|
1688
|
+
// this is the only node that we accept PENDING
|
|
1689
|
+
ancestors.push(node);
|
|
1683
1690
|
|
|
1684
1691
|
let nodeIndex = startIndex;
|
|
1685
1692
|
while (node.parent !== undefined) {
|