@lodestar/fork-choice 1.51.0-dev.fac22802f6 → 1.51.0-dev.fb216e169a
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 +6 -1
- package/lib/forkChoice/errors.d.ts.map +1 -1
- package/lib/forkChoice/errors.js +1 -0
- package/lib/forkChoice/errors.js.map +1 -1
- package/lib/forkChoice/forkChoice.d.ts +8 -9
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +48 -12
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +8 -2
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/metrics.d.ts +3 -0
- package/lib/metrics.d.ts.map +1 -1
- package/lib/metrics.js +5 -0
- package/lib/metrics.js.map +1 -1
- package/lib/protoArray/interface.d.ts +2 -0
- package/lib/protoArray/interface.d.ts.map +1 -1
- package/lib/protoArray/interface.js.map +1 -1
- package/lib/protoArray/protoArray.d.ts +20 -4
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +108 -51
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +7 -7
- package/src/forkChoice/errors.ts +3 -1
- package/src/forkChoice/forkChoice.ts +51 -12
- package/src/forkChoice/interface.ts +6 -2
- package/src/metrics.ts +5 -0
- package/src/protoArray/interface.ts +2 -0
- package/src/protoArray/protoArray.ts +124 -56
package/src/forkChoice/errors.ts
CHANGED
|
@@ -6,13 +6,15 @@ export enum InvalidBlockCode {
|
|
|
6
6
|
FUTURE_SLOT = "FUTURE_SLOT",
|
|
7
7
|
FINALIZED_SLOT = "FINALIZED_SLOT",
|
|
8
8
|
NOT_FINALIZED_DESCENDANT = "NOT_FINALIZED_DESCENDANT",
|
|
9
|
+
PARENT_EXECUTION_INVALID = "PARENT_EXECUTION_INVALID",
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export type InvalidBlock =
|
|
12
13
|
| {code: InvalidBlockCode.UNKNOWN_PARENT; root: RootHex; hash: RootHex | null}
|
|
13
14
|
| {code: InvalidBlockCode.FUTURE_SLOT; currentSlot: Slot; blockSlot: Slot}
|
|
14
15
|
| {code: InvalidBlockCode.FINALIZED_SLOT; finalizedSlot: Slot; blockSlot: Slot}
|
|
15
|
-
| {code: InvalidBlockCode.NOT_FINALIZED_DESCENDANT; finalizedRoot: RootHex; blockAncestor?: RootHex}
|
|
16
|
+
| {code: InvalidBlockCode.NOT_FINALIZED_DESCENDANT; finalizedRoot: RootHex; blockAncestor?: RootHex}
|
|
17
|
+
| {code: InvalidBlockCode.PARENT_EXECUTION_INVALID; root: RootHex; hash: RootHex | null};
|
|
16
18
|
|
|
17
19
|
export enum InvalidAttestationCode {
|
|
18
20
|
/**
|
|
@@ -693,7 +693,20 @@ export class ForkChoice implements IForkChoice {
|
|
|
693
693
|
}
|
|
694
694
|
|
|
695
695
|
// findHead returns the ProtoNode representing the head
|
|
696
|
-
|
|
696
|
+
let head: ProtoNode;
|
|
697
|
+
try {
|
|
698
|
+
head = this.protoArray.findHead(this.fcStore.justified.checkpoint.rootHex, currentSlot);
|
|
699
|
+
} catch (e) {
|
|
700
|
+
// A poisoned tree never recovers, shut the node down instead of failing every head computation
|
|
701
|
+
if (
|
|
702
|
+
e instanceof ProtoArrayError &&
|
|
703
|
+
(e.type.code === ProtoArrayErrorCode.INVALID_LVH_EXECUTION_RESPONSE ||
|
|
704
|
+
e.type.code === ProtoArrayErrorCode.INVALID_JUSTIFIED_EXECUTION_STATUS)
|
|
705
|
+
) {
|
|
706
|
+
this.irrecoverableError = e;
|
|
707
|
+
}
|
|
708
|
+
throw e;
|
|
709
|
+
}
|
|
697
710
|
|
|
698
711
|
this.head = head;
|
|
699
712
|
return this.head;
|
|
@@ -753,6 +766,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
753
766
|
*
|
|
754
767
|
* `justifiedBalances` balances of justified state which is updated synchronously.
|
|
755
768
|
* This ensures that the forkchoice is never out of sync.
|
|
769
|
+
*
|
|
770
|
+
* A block whose parent payload chain was invalidated is rejected. `executionStatus` is ignored for a gloas
|
|
771
|
+
* block: it has no payload of its own and inherits the status of the parent variant it builds on.
|
|
756
772
|
*/
|
|
757
773
|
onBlock(
|
|
758
774
|
block: BeaconBlock,
|
|
@@ -781,6 +797,19 @@ export class ForkChoice implements IForkChoice {
|
|
|
781
797
|
});
|
|
782
798
|
}
|
|
783
799
|
|
|
800
|
+
// An execution engine verdict can invalidate the parent while this block is being verified. Inserting the
|
|
801
|
+
// block would put a viable node below an invalidated one, or poison the tree if this block is VALID
|
|
802
|
+
if (parentBlock.executionStatus === ExecutionStatus.Invalid) {
|
|
803
|
+
throw new ForkChoiceError({
|
|
804
|
+
code: ForkChoiceErrorCode.INVALID_BLOCK,
|
|
805
|
+
err: {
|
|
806
|
+
code: InvalidBlockCode.PARENT_EXECUTION_INVALID,
|
|
807
|
+
root: parentRootHex,
|
|
808
|
+
hash: parentHashHex,
|
|
809
|
+
},
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
|
|
784
813
|
// Blocks cannot be in the future. If they are, their consideration must be delayed until
|
|
785
814
|
// the are in the past.
|
|
786
815
|
//
|
|
@@ -942,7 +971,9 @@ export class ForkChoice implements IForkChoice {
|
|
|
942
971
|
executionPayloadBlockHash: parentBlockHashFromBid,
|
|
943
972
|
executionPayloadNumber: parentMeta.number,
|
|
944
973
|
executionPayloadGasLimit: parentMeta.gasLimit,
|
|
945
|
-
|
|
974
|
+
// The parent's default (PENDING) variant does not reflect a SYNCING parent payload, inheriting
|
|
975
|
+
// from the variant matching the bid's parent block hash keeps that payload from being marked VALID
|
|
976
|
+
executionStatus: this.getPostMergeExecStatus(parentBlock.executionStatus),
|
|
946
977
|
dataAvailabilityStatus,
|
|
947
978
|
};
|
|
948
979
|
})()
|
|
@@ -1003,14 +1034,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
1003
1034
|
*
|
|
1004
1035
|
* https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/fork-choice.md#on_attestation
|
|
1005
1036
|
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
* too expensive to call again.
|
|
1009
|
-
*
|
|
1010
|
-
* ## Notes:
|
|
1011
|
-
*
|
|
1012
|
-
* The supplied `attestation` **must** pass the `in_valid_indexed_attestation` function as it
|
|
1013
|
-
* will not be run here.
|
|
1037
|
+
* Does not run `is_valid_indexed_attestation`. The caller must ensure all of its
|
|
1038
|
+
* validity requirements except index ordering: `attestingIndices` may be unsorted.
|
|
1014
1039
|
*/
|
|
1015
1040
|
onAttestation(attestation: IndexedAttestation, attDataRoot: string, forceImport?: boolean): void {
|
|
1016
1041
|
// Ignore any attestations to the zero hash.
|
|
@@ -1149,6 +1174,19 @@ export class ForkChoice implements IForkChoice {
|
|
|
1149
1174
|
executionStatus: PayloadExecutionStatus,
|
|
1150
1175
|
dataAvailabilityStatus: DataAvailabilityStatus
|
|
1151
1176
|
): void {
|
|
1177
|
+
// Same guard as onBlock: the PENDING variant carries the status of the payload chain this block builds on
|
|
1178
|
+
const pendingBlock = this.getBlockHexDefaultStatus(blockRoot);
|
|
1179
|
+
if (pendingBlock?.executionStatus === ExecutionStatus.Invalid) {
|
|
1180
|
+
throw new ForkChoiceError({
|
|
1181
|
+
code: ForkChoiceErrorCode.INVALID_BLOCK,
|
|
1182
|
+
err: {
|
|
1183
|
+
code: InvalidBlockCode.PARENT_EXECUTION_INVALID,
|
|
1184
|
+
root: pendingBlock.parentRoot,
|
|
1185
|
+
hash: pendingBlock.executionPayloadBlockHash,
|
|
1186
|
+
},
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1152
1190
|
this.protoArray.onExecutionPayload(
|
|
1153
1191
|
blockRoot,
|
|
1154
1192
|
this.fcStore.currentSlot,
|
|
@@ -1595,13 +1633,14 @@ export class ForkChoice implements IForkChoice {
|
|
|
1595
1633
|
* justified's status as well as validate the terminal conditions if terminal block
|
|
1596
1634
|
* becomes valid
|
|
1597
1635
|
*/
|
|
1598
|
-
validateLatestHash(execResponse: LVHExecResponse):
|
|
1636
|
+
validateLatestHash(execResponse: LVHExecResponse, opts?: {strict?: boolean}): boolean {
|
|
1599
1637
|
try {
|
|
1600
|
-
this.protoArray.validateLatestHash(execResponse, this.fcStore.currentSlot);
|
|
1638
|
+
return this.protoArray.validateLatestHash(execResponse, this.fcStore.currentSlot, opts);
|
|
1601
1639
|
} catch (e) {
|
|
1602
1640
|
if (e instanceof ProtoArrayError && e.type.code === ProtoArrayErrorCode.INVALID_LVH_EXECUTION_RESPONSE) {
|
|
1603
1641
|
this.irrecoverableError = e;
|
|
1604
1642
|
}
|
|
1643
|
+
return false;
|
|
1605
1644
|
}
|
|
1606
1645
|
}
|
|
1607
1646
|
|
|
@@ -155,6 +155,9 @@ export interface IForkChoice {
|
|
|
155
155
|
* ## Notes:
|
|
156
156
|
*
|
|
157
157
|
* The supplied block **must** pass the `state_transition` function as it will not be run here.
|
|
158
|
+
*
|
|
159
|
+
* Throws `InvalidBlockCode.PARENT_EXECUTION_INVALID` if the parent payload chain was invalidated.
|
|
160
|
+
* `executionStatus` is ignored for a gloas block, it inherits the status of the parent variant it builds on.
|
|
158
161
|
*/
|
|
159
162
|
onBlock(
|
|
160
163
|
block: BeaconBlock,
|
|
@@ -357,9 +360,10 @@ export interface IForkChoice {
|
|
|
357
360
|
/** Returns the distance of common ancestor of nodes to the max of the newNode and the prevNode. */
|
|
358
361
|
getCommonAncestorDepth(prevBlock: ProtoBlock, newBlock: ProtoBlock): AncestorResult;
|
|
359
362
|
/**
|
|
360
|
-
*
|
|
363
|
+
* Apply an execution engine verdict with a latest valid hash, see `ProtoArray.validateLatestHash`.
|
|
364
|
+
* Returns whether any execution status changed. A consensus failure is recorded in `irrecoverableError`.
|
|
361
365
|
*/
|
|
362
|
-
validateLatestHash(execResponse: LVHExecResponse):
|
|
366
|
+
validateLatestHash(execResponse: LVHExecResponse, opts?: {strict?: boolean}): boolean;
|
|
363
367
|
|
|
364
368
|
/**
|
|
365
369
|
* A dependent root is the block root of the last block before the state transition that decided a specific shuffling
|
package/src/metrics.ts
CHANGED
|
@@ -32,6 +32,11 @@ export function getForkChoiceMetrics(register: MetricsRegisterExtra) {
|
|
|
32
32
|
name: "beacon_fork_choice_changed_head_total",
|
|
33
33
|
help: "Count of occasions fork choice has found a new head",
|
|
34
34
|
}),
|
|
35
|
+
executionInvalidations: register.gauge<{source: string}>({
|
|
36
|
+
name: "beacon_fork_choice_execution_invalidations_total",
|
|
37
|
+
help: "Count of execution engine INVALID verdicts applied to fork choice, by verdict source",
|
|
38
|
+
labelNames: ["source"],
|
|
39
|
+
}),
|
|
35
40
|
reorg: register.gauge({
|
|
36
41
|
name: "beacon_fork_choice_reorg_total",
|
|
37
42
|
help: "Count of occasions fork choice has switched to a different chain",
|
|
@@ -55,6 +55,8 @@ export function isGloasBlock(block: ProtoBlock): block is ProtoBlock & {parentBl
|
|
|
55
55
|
export type LVHValidResponse = {
|
|
56
56
|
executionStatus: ExecutionStatus.Valid;
|
|
57
57
|
latestValidExecHash: RootHex;
|
|
58
|
+
/** Resolves the exact node to validate when several nodes carry the same payload hash (gloas variants) */
|
|
59
|
+
blockRoot?: RootHex;
|
|
58
60
|
};
|
|
59
61
|
export type LVHInvalidResponse = {
|
|
60
62
|
executionStatus: ExecutionStatus.Invalid;
|
|
@@ -1019,12 +1019,23 @@ export class ProtoArray {
|
|
|
1019
1019
|
}
|
|
1020
1020
|
|
|
1021
1021
|
/**
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
1022
|
+
* Apply an execution engine verdict with a latest valid hash (LVH) to the tree. VALID flips Syncing nodes up to the
|
|
1023
|
+
* LVH node. INVALID invalidates the node identified by `invalidateFromParentBlockRoot/Hash` and its ancestors up to
|
|
1024
|
+
* the LVH node (exclusive), then every descendant. Returns whether any status changed.
|
|
1025
|
+
*
|
|
1026
|
+
* A null or unknown LVH means the engine only vouches for the queried payload being invalid: the queried node and
|
|
1027
|
+
* the nodes that inherit that payload (gloas PENDING and EMPTY variants) are invalidated, nothing above them.
|
|
1028
|
+
*
|
|
1029
|
+
* `strict` keeps the consensus failure semantics for verdicts about a payload the engine just processed: a VALID
|
|
1030
|
+
* or PreMerge node on the invalidated chain poisons the tree. Verdicts from forkchoiceUpdated or about an ancestor
|
|
1031
|
+
* chain are applied with `strict: false`: a node the engine already validated is never invalidated and the justified
|
|
1032
|
+
* node is never crossed. If the walk to the LVH meets either, the LVH is treated as unknown.
|
|
1025
1033
|
*/
|
|
1026
|
-
|
|
1027
|
-
|
|
1034
|
+
validateLatestHash(
|
|
1035
|
+
execResponse: LVHExecResponse,
|
|
1036
|
+
currentSlot: Slot,
|
|
1037
|
+
{strict = true}: {strict?: boolean} = {}
|
|
1038
|
+
): boolean {
|
|
1028
1039
|
// Look reverse because its highly likely node with latestValidExecHash is towards the
|
|
1029
1040
|
// the leaves of the forkchoice
|
|
1030
1041
|
//
|
|
@@ -1035,71 +1046,128 @@ export class ProtoArray {
|
|
|
1035
1046
|
// https://github.com/ChainSafe/lodestar/pull/4182#discussion_r914770167
|
|
1036
1047
|
|
|
1037
1048
|
if (execResponse.executionStatus === ExecutionStatus.Valid) {
|
|
1038
|
-
const {latestValidExecHash} = execResponse;
|
|
1049
|
+
const {latestValidExecHash, blockRoot} = execResponse;
|
|
1039
1050
|
// We use -1 for denoting not found
|
|
1040
1051
|
let latestValidHashIndex = -1;
|
|
1041
1052
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1053
|
+
if (blockRoot !== undefined) {
|
|
1054
|
+
latestValidHashIndex = this.getNodeIndexByRootAndBlockHash(blockRoot, latestValidExecHash) ?? -1;
|
|
1055
|
+
} else {
|
|
1056
|
+
for (let nodeIndex = this.nodes.length - 1; nodeIndex >= 0; nodeIndex--) {
|
|
1057
|
+
if (this.nodes[nodeIndex].executionPayloadBlockHash === latestValidExecHash) {
|
|
1058
|
+
latestValidHashIndex = nodeIndex;
|
|
1059
|
+
// We found the block corresponding to latestValidHashIndex, exit the loop
|
|
1060
|
+
break;
|
|
1061
|
+
}
|
|
1047
1062
|
}
|
|
1048
1063
|
}
|
|
1049
1064
|
|
|
1050
1065
|
// We are trying to be as forgiving as possible here because ideally latestValidHashIndex
|
|
1051
1066
|
// should be found in the forkchoice
|
|
1052
|
-
if (latestValidHashIndex
|
|
1053
|
-
|
|
1067
|
+
if (latestValidHashIndex < 0) {
|
|
1068
|
+
return false;
|
|
1054
1069
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
invalidateFromParentBlockHash
|
|
1073
|
-
);
|
|
1074
|
-
if (invalidateFromParentIndex === undefined) {
|
|
1075
|
-
throw Error(
|
|
1076
|
-
`Unable to find invalidateFromParentBlockRoot=${invalidateFromParentBlockRoot} invalidateFromParentBlockHash=${invalidateFromParentBlockHash} in forkChoice`
|
|
1077
|
-
);
|
|
1070
|
+
const {executionStatus} = this.nodes[latestValidHashIndex];
|
|
1071
|
+
if (executionStatus === ExecutionStatus.Valid || executionStatus === ExecutionStatus.PreMerge) {
|
|
1072
|
+
return false;
|
|
1073
|
+
}
|
|
1074
|
+
if (!strict) {
|
|
1075
|
+
// The walk up flips every Syncing ancestor, a lenient verdict must not contradict an invalidated one
|
|
1076
|
+
let nodeIndex: number | undefined = latestValidHashIndex;
|
|
1077
|
+
while (nodeIndex !== undefined) {
|
|
1078
|
+
const node: ProtoNode = this.nodes[nodeIndex];
|
|
1079
|
+
if (node.executionStatus === ExecutionStatus.Valid || node.executionStatus === ExecutionStatus.PreMerge) {
|
|
1080
|
+
break;
|
|
1081
|
+
}
|
|
1082
|
+
if (node.executionStatus === ExecutionStatus.Invalid) {
|
|
1083
|
+
return false;
|
|
1084
|
+
}
|
|
1085
|
+
nodeIndex = node.parent;
|
|
1086
|
+
}
|
|
1078
1087
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1088
|
+
this.propagateValidExecutionStatusByIndex(latestValidHashIndex);
|
|
1089
|
+
return true;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// In case of invalidation, ideally:
|
|
1093
|
+
// i) Find the invalid payload
|
|
1094
|
+
// ii) Obtain a chain [LVH.child, LVH.child.child, ....., invalid_payload]
|
|
1095
|
+
// iii) Obtain a chain [Last_known_valid_node, ...., LVH]
|
|
1096
|
+
//
|
|
1097
|
+
// Mark chain iii) as Valid if LVH is non null but right now LVH can be non null without
|
|
1098
|
+
// gurranteing chain iii) to be valid: for e.g. in following scenario LVH can be returned
|
|
1099
|
+
// as any of SYNCING: SYNCING, SYNCING, SYNCING, INVALID (due to simple check)/
|
|
1100
|
+
// So we currently ignore this chain and hope eventually it gets resolved
|
|
1101
|
+
//
|
|
1102
|
+
// Mark chain ii) as Invalid if LVH is found and non null, else only invalidate invalid_payload
|
|
1103
|
+
// if its in fcU.
|
|
1104
|
+
//
|
|
1105
|
+
const {invalidateFromParentBlockRoot, invalidateFromParentBlockHash, latestValidExecHash} = execResponse;
|
|
1106
|
+
const invalidateFromParentIndex = this.getNodeIndexByRootAndBlockHash(
|
|
1107
|
+
invalidateFromParentBlockRoot,
|
|
1108
|
+
invalidateFromParentBlockHash
|
|
1109
|
+
);
|
|
1110
|
+
if (invalidateFromParentIndex === undefined) {
|
|
1111
|
+
throw Error(
|
|
1112
|
+
`Unable to find invalidateFromParentBlockRoot=${invalidateFromParentBlockRoot} invalidateFromParentBlockHash=${invalidateFromParentBlockHash} in forkChoice`
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
if (!strict && this.nodes[invalidateFromParentIndex].executionStatus !== ExecutionStatus.Syncing) {
|
|
1116
|
+
return false;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
let latestValidHashIndex =
|
|
1120
|
+
latestValidExecHash !== null ? this.getNodeIndexFromLVH(latestValidExecHash, invalidateFromParentIndex) : null;
|
|
1121
|
+
if (latestValidHashIndex === null) {
|
|
1122
|
+
if (strict) {
|
|
1123
|
+
// The strict from node is the parent of the rejected payload and may be valid, never guess here
|
|
1098
1124
|
throw Error(`Unable to find latestValidExecHash=${latestValidExecHash} in the forkchoice`);
|
|
1099
1125
|
}
|
|
1126
|
+
latestValidHashIndex = this.getUnknownLVHIndex(invalidateFromParentIndex);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
if (!strict) {
|
|
1130
|
+
// Never invalidate the justified block, the EMPTY variant sharing its chain, or anything at or below it in
|
|
1131
|
+
// the tree. The justified block's own payload (FULL variant) is not part of the checkpoint
|
|
1132
|
+
const justifiedIndex = this.getDefaultNodeIndex(this.justifiedRoot) ?? -1;
|
|
1133
|
+
let nodeIndex: number | undefined = invalidateFromParentIndex;
|
|
1134
|
+
while (nodeIndex !== undefined && nodeIndex > latestValidHashIndex) {
|
|
1135
|
+
const node: ProtoNode = this.nodes[nodeIndex];
|
|
1136
|
+
if (
|
|
1137
|
+
node.executionStatus !== ExecutionStatus.Syncing ||
|
|
1138
|
+
nodeIndex <= justifiedIndex ||
|
|
1139
|
+
(node.blockRoot === this.justifiedRoot && node.payloadStatus === PayloadStatus.EMPTY)
|
|
1140
|
+
) {
|
|
1141
|
+
// Conflicting LVH: fall back to the unknown LVH bound, but never cross this node
|
|
1142
|
+
latestValidHashIndex = Math.max(nodeIndex, this.getUnknownLVHIndex(invalidateFromParentIndex));
|
|
1143
|
+
break;
|
|
1144
|
+
}
|
|
1145
|
+
nodeIndex = node.parent;
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
if (invalidateFromParentIndex <= latestValidHashIndex) {
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1100
1152
|
|
|
1101
|
-
|
|
1153
|
+
this.propagateInValidExecutionStatusByIndex(invalidateFromParentIndex, latestValidHashIndex, currentSlot);
|
|
1154
|
+
return true;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Upper bound (exclusive) for invalidating from `fromIndex` when the engine gave no usable LVH: the parent of the
|
|
1159
|
+
* last ancestor that still inherits the queried payload. Pre-gloas this is the parent of `fromIndex`. For a gloas
|
|
1160
|
+
* EMPTY or PENDING node it reaches up to the FULL node that owns the payload. -1 when the chain reaches the root.
|
|
1161
|
+
*/
|
|
1162
|
+
private getUnknownLVHIndex(fromIndex: number): number {
|
|
1163
|
+
const payloadBlockHash = this.nodes[fromIndex].executionPayloadBlockHash;
|
|
1164
|
+
let nodeIndex = fromIndex;
|
|
1165
|
+
let parentIndex = this.nodes[nodeIndex].parent;
|
|
1166
|
+
while (parentIndex !== undefined && this.nodes[parentIndex].executionPayloadBlockHash === payloadBlockHash) {
|
|
1167
|
+
nodeIndex = parentIndex;
|
|
1168
|
+
parentIndex = this.nodes[nodeIndex].parent;
|
|
1102
1169
|
}
|
|
1170
|
+
return parentIndex ?? -1;
|
|
1103
1171
|
}
|
|
1104
1172
|
|
|
1105
1173
|
private propagateValidExecutionStatusByIndex(validNodeIndex: number): void {
|