@lodestar/fork-choice 1.42.0-dev.4411584fd8 → 1.42.0-dev.47afaa6bb7
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 +8 -1
- package/lib/forkChoice/errors.d.ts.map +1 -1
- package/lib/forkChoice/errors.js +4 -0
- package/lib/forkChoice/errors.js.map +1 -1
- package/lib/forkChoice/forkChoice.d.ts +17 -7
- package/lib/forkChoice/forkChoice.d.ts.map +1 -1
- package/lib/forkChoice/forkChoice.js +50 -23
- package/lib/forkChoice/forkChoice.js.map +1 -1
- package/lib/forkChoice/interface.d.ts +10 -4
- package/lib/forkChoice/interface.d.ts.map +1 -1
- package/lib/forkChoice/interface.js.map +1 -1
- package/lib/forkChoice/store.d.ts +2 -2
- package/lib/forkChoice/store.d.ts.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/protoArray/interface.d.ts +11 -3
- package/lib/protoArray/interface.d.ts.map +1 -1
- package/lib/protoArray/protoArray.d.ts +7 -2
- package/lib/protoArray/protoArray.d.ts.map +1 -1
- package/lib/protoArray/protoArray.js +21 -9
- package/lib/protoArray/protoArray.js.map +1 -1
- package/package.json +7 -7
- package/src/forkChoice/errors.ts +6 -1
- package/src/forkChoice/forkChoice.ts +72 -28
- package/src/forkChoice/interface.ts +13 -9
- package/src/forkChoice/store.ts +2 -2
- package/src/index.ts +2 -1
- package/src/protoArray/interface.ts +12 -7
- package/src/protoArray/protoArray.ts +26 -9
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
2
|
import {ForkSeq, SLOTS_PER_EPOCH, SLOTS_PER_HISTORICAL_ROOT} from "@lodestar/params";
|
|
3
3
|
import {
|
|
4
|
-
CachedBeaconStateAllForks,
|
|
5
|
-
CachedBeaconStateGloas,
|
|
6
4
|
DataAvailabilityStatus,
|
|
7
5
|
EffectiveBalanceIncrements,
|
|
6
|
+
IBeaconStateView,
|
|
8
7
|
ZERO_HASH,
|
|
9
8
|
computeEpochAtSlot,
|
|
10
9
|
computeSlotsSinceEpochStart,
|
|
11
10
|
computeStartSlotAtEpoch,
|
|
12
11
|
getAttesterSlashableIndices,
|
|
13
12
|
isExecutionBlockBodyType,
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
isStatePostBellatrix,
|
|
14
|
+
isStatePostGloas,
|
|
16
15
|
} from "@lodestar/state-transition";
|
|
17
|
-
import {computeUnrealizedCheckpoints} from "@lodestar/state-transition/epoch";
|
|
18
16
|
import {
|
|
19
17
|
AttesterSlashing,
|
|
20
18
|
BeaconBlock,
|
|
@@ -33,11 +31,12 @@ import {ForkChoiceMetrics} from "../metrics.js";
|
|
|
33
31
|
import {computeDeltas} from "../protoArray/computeDeltas.js";
|
|
34
32
|
import {ProtoArrayError, ProtoArrayErrorCode} from "../protoArray/errors.js";
|
|
35
33
|
import {
|
|
34
|
+
BlockExecutionStatus,
|
|
36
35
|
ExecutionStatus,
|
|
37
36
|
HEX_ZERO_HASH,
|
|
38
37
|
LVHExecResponse,
|
|
39
|
-
MaybeValidExecutionStatus,
|
|
40
38
|
NULL_VOTE_INDEX,
|
|
39
|
+
PayloadExecutionStatus,
|
|
41
40
|
PayloadStatus,
|
|
42
41
|
ProtoBlock,
|
|
43
42
|
ProtoNode,
|
|
@@ -586,10 +585,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
586
585
|
*/
|
|
587
586
|
onBlock(
|
|
588
587
|
block: BeaconBlock,
|
|
589
|
-
state:
|
|
588
|
+
state: IBeaconStateView,
|
|
590
589
|
blockDelaySec: number,
|
|
591
590
|
currentSlot: Slot,
|
|
592
|
-
executionStatus:
|
|
591
|
+
executionStatus: BlockExecutionStatus,
|
|
593
592
|
dataAvailabilityStatus: DataAvailabilityStatus
|
|
594
593
|
): ProtoBlock {
|
|
595
594
|
const {parentRoot, slot} = block;
|
|
@@ -672,12 +671,16 @@ export class ForkChoice implements IForkChoice {
|
|
|
672
671
|
}
|
|
673
672
|
|
|
674
673
|
// Get justified checkpoint with payload status for Gloas
|
|
675
|
-
const justifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
674
|
+
const justifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
675
|
+
this.config,
|
|
676
|
+
state,
|
|
677
|
+
state.currentJustifiedCheckpoint.epoch
|
|
678
|
+
);
|
|
676
679
|
const justifiedCheckpoint = toCheckpointWithPayload(state.currentJustifiedCheckpoint, justifiedPayloadStatus);
|
|
677
680
|
const stateJustifiedEpoch = justifiedCheckpoint.epoch;
|
|
678
681
|
|
|
679
682
|
// Get finalized checkpoint with payload status for Gloas
|
|
680
|
-
const finalizedPayloadStatus = getCheckpointPayloadStatus(state, state.finalizedCheckpoint.epoch);
|
|
683
|
+
const finalizedPayloadStatus = getCheckpointPayloadStatus(this.config, state, state.finalizedCheckpoint.epoch);
|
|
681
684
|
const finalizedCheckpoint = toCheckpointWithPayload(state.finalizedCheckpoint, finalizedPayloadStatus);
|
|
682
685
|
|
|
683
686
|
// Justified balances for `justifiedCheckpoint` are new to the fork-choice. Compute them on demand only if
|
|
@@ -710,6 +713,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
710
713
|
// reuse from parent, happens at 1/3 last blocks of epoch as monitored in mainnet
|
|
711
714
|
// Get payload status for unrealized justified checkpoint
|
|
712
715
|
const unrealizedJustifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
716
|
+
this.config,
|
|
713
717
|
state,
|
|
714
718
|
parentBlock.unrealizedJustifiedEpoch
|
|
715
719
|
);
|
|
@@ -721,6 +725,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
721
725
|
};
|
|
722
726
|
// Get payload status for unrealized finalized checkpoint
|
|
723
727
|
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
728
|
+
this.config,
|
|
724
729
|
state,
|
|
725
730
|
parentBlock.unrealizedFinalizedEpoch
|
|
726
731
|
);
|
|
@@ -732,9 +737,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
732
737
|
};
|
|
733
738
|
} else {
|
|
734
739
|
// compute new, happens 2/3 first blocks of epoch as monitored in mainnet
|
|
735
|
-
const unrealized = computeUnrealizedCheckpoints(
|
|
740
|
+
const unrealized = state.computeUnrealizedCheckpoints();
|
|
736
741
|
// Get payload status for unrealized justified checkpoint
|
|
737
742
|
const unrealizedJustifiedPayloadStatus = getCheckpointPayloadStatus(
|
|
743
|
+
this.config,
|
|
738
744
|
state,
|
|
739
745
|
unrealized.justifiedCheckpoint.epoch
|
|
740
746
|
);
|
|
@@ -744,6 +750,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
744
750
|
);
|
|
745
751
|
// Get payload status for unrealized finalized checkpoint
|
|
746
752
|
const unrealizedFinalizedPayloadStatus = getCheckpointPayloadStatus(
|
|
753
|
+
this.config,
|
|
747
754
|
state,
|
|
748
755
|
unrealized.finalizedCheckpoint.epoch
|
|
749
756
|
);
|
|
@@ -771,7 +778,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
771
778
|
}
|
|
772
779
|
|
|
773
780
|
const targetSlot = computeStartSlotAtEpoch(blockEpoch);
|
|
774
|
-
const targetRoot = slot === targetSlot ? blockRoot : state.
|
|
781
|
+
const targetRoot = slot === targetSlot ? blockRoot : state.getBlockRootAtSlot(targetSlot);
|
|
775
782
|
|
|
776
783
|
// This does not apply a vote to the block, it just makes fork choice aware of the block so
|
|
777
784
|
// it can still be identified as the head even if it doesn't have any votes.
|
|
@@ -820,7 +827,10 @@ export class ForkChoice implements IForkChoice {
|
|
|
820
827
|
executionStatus: this.getPostGloasExecStatus(executionStatus),
|
|
821
828
|
dataAvailabilityStatus,
|
|
822
829
|
}
|
|
823
|
-
: isExecutionBlockBodyType(block.body) &&
|
|
830
|
+
: isExecutionBlockBodyType(block.body) &&
|
|
831
|
+
isStatePostBellatrix(state) &&
|
|
832
|
+
state.isExecutionStateType &&
|
|
833
|
+
state.isExecutionEnabled(block)
|
|
824
834
|
? {
|
|
825
835
|
executionPayloadBlockHash: toRootHex(block.body.executionPayload.blockHash),
|
|
826
836
|
executionPayloadNumber: block.body.executionPayload.blockNumber,
|
|
@@ -834,10 +844,6 @@ export class ForkChoice implements IForkChoice {
|
|
|
834
844
|
}),
|
|
835
845
|
|
|
836
846
|
payloadStatus: isGloasBeaconBlock(block) ? PayloadStatus.PENDING : PayloadStatus.FULL,
|
|
837
|
-
builderIndex: isGloasBeaconBlock(block) ? block.body.signedExecutionPayloadBid.message.builderIndex : null,
|
|
838
|
-
blockHashFromBid: isGloasBeaconBlock(block)
|
|
839
|
-
? toRootHex(block.body.signedExecutionPayloadBid.message.blockHash)
|
|
840
|
-
: null,
|
|
841
847
|
parentBlockHash: parentHashHex,
|
|
842
848
|
};
|
|
843
849
|
|
|
@@ -977,7 +983,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
977
983
|
blockRoot: RootHex,
|
|
978
984
|
executionPayloadBlockHash: RootHex,
|
|
979
985
|
executionPayloadNumber: number,
|
|
980
|
-
executionPayloadStateRoot: RootHex
|
|
986
|
+
executionPayloadStateRoot: RootHex,
|
|
987
|
+
executionStatus: PayloadExecutionStatus
|
|
981
988
|
): void {
|
|
982
989
|
this.protoArray.onExecutionPayload(
|
|
983
990
|
blockRoot,
|
|
@@ -985,7 +992,8 @@ export class ForkChoice implements IForkChoice {
|
|
|
985
992
|
executionPayloadBlockHash,
|
|
986
993
|
executionPayloadNumber,
|
|
987
994
|
executionPayloadStateRoot,
|
|
988
|
-
this.proposerBoostRoot
|
|
995
|
+
this.proposerBoostRoot,
|
|
996
|
+
executionStatus
|
|
989
997
|
);
|
|
990
998
|
}
|
|
991
999
|
|
|
@@ -1043,19 +1051,34 @@ export class ForkChoice implements IForkChoice {
|
|
|
1043
1051
|
}
|
|
1044
1052
|
|
|
1045
1053
|
/**
|
|
1046
|
-
* Same
|
|
1054
|
+
* Same as hasBlock but without checking if the block is a descendant of the finalized root.
|
|
1047
1055
|
*/
|
|
1048
1056
|
hasBlockUnsafe(blockRoot: Root): boolean {
|
|
1049
1057
|
return this.hasBlockHexUnsafe(toRootHex(blockRoot));
|
|
1050
1058
|
}
|
|
1051
1059
|
|
|
1052
1060
|
/**
|
|
1053
|
-
* Same
|
|
1061
|
+
* Same as hasBlockHex but without checking if the block is a descendant of the finalized root.
|
|
1054
1062
|
*/
|
|
1055
1063
|
hasBlockHexUnsafe(blockRoot: RootHex): boolean {
|
|
1056
1064
|
return this.protoArray.hasBlock(blockRoot);
|
|
1057
1065
|
}
|
|
1058
1066
|
|
|
1067
|
+
/**
|
|
1068
|
+
* Returns true if the FULL payload variant (execution payload envelope) exists for this block root,
|
|
1069
|
+
* without checking if the block is a descendant of the finalized root.
|
|
1070
|
+
*/
|
|
1071
|
+
hasPayloadUnsafe(blockRoot: Root): boolean {
|
|
1072
|
+
return this.hasPayloadHexUnsafe(toRootHex(blockRoot));
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Same as hasPayloadUnsafe but accepts a hex-encoded block root.
|
|
1077
|
+
*/
|
|
1078
|
+
hasPayloadHexUnsafe(blockRoot: RootHex): boolean {
|
|
1079
|
+
return this.protoArray.hasPayload(blockRoot);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1059
1082
|
/**
|
|
1060
1083
|
* Returns a MUTABLE `ProtoBlock` if the block is known **and** a descendant of the finalized root.
|
|
1061
1084
|
*/
|
|
@@ -1425,7 +1448,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
1425
1448
|
return secFromSlot * 1000 <= proposerReorgCutoff;
|
|
1426
1449
|
}
|
|
1427
1450
|
|
|
1428
|
-
private getPreMergeExecStatus(executionStatus:
|
|
1451
|
+
private getPreMergeExecStatus(executionStatus: BlockExecutionStatus): ExecutionStatus.PreMerge {
|
|
1429
1452
|
if (executionStatus !== ExecutionStatus.PreMerge)
|
|
1430
1453
|
throw Error(`Invalid pre-merge execution status: expected: ${ExecutionStatus.PreMerge}, got ${executionStatus}`);
|
|
1431
1454
|
return executionStatus;
|
|
@@ -1440,7 +1463,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
1440
1463
|
}
|
|
1441
1464
|
|
|
1442
1465
|
private getPreGloasExecStatus(
|
|
1443
|
-
executionStatus:
|
|
1466
|
+
executionStatus: BlockExecutionStatus
|
|
1444
1467
|
): ExecutionStatus.Valid | ExecutionStatus.Syncing {
|
|
1445
1468
|
if (executionStatus === ExecutionStatus.PreMerge || executionStatus === ExecutionStatus.PayloadSeparated)
|
|
1446
1469
|
throw Error(
|
|
@@ -1449,7 +1472,7 @@ export class ForkChoice implements IForkChoice {
|
|
|
1449
1472
|
return executionStatus;
|
|
1450
1473
|
}
|
|
1451
1474
|
|
|
1452
|
-
private getPostGloasExecStatus(executionStatus:
|
|
1475
|
+
private getPostGloasExecStatus(executionStatus: BlockExecutionStatus): ExecutionStatus.PayloadSeparated {
|
|
1453
1476
|
if (executionStatus !== ExecutionStatus.PayloadSeparated)
|
|
1454
1477
|
throw Error(
|
|
1455
1478
|
`Invalid post-gloas execution status: expected: ${ExecutionStatus.PayloadSeparated}, got ${executionStatus}`
|
|
@@ -1673,6 +1696,20 @@ export class ForkChoice implements IForkChoice {
|
|
|
1673
1696
|
});
|
|
1674
1697
|
}
|
|
1675
1698
|
|
|
1699
|
+
// If attesting for a full node, the payload must be known
|
|
1700
|
+
if (isGloasBlock(block) && attestationData.index === 1) {
|
|
1701
|
+
const fullNodeIndex = this.protoArray.getNodeIndexByRootAndStatus(beaconBlockRootHex, PayloadStatus.FULL);
|
|
1702
|
+
if (fullNodeIndex === undefined) {
|
|
1703
|
+
throw new ForkChoiceError({
|
|
1704
|
+
code: ForkChoiceErrorCode.INVALID_ATTESTATION,
|
|
1705
|
+
err: {
|
|
1706
|
+
code: InvalidAttestationCode.UNKNOWN_PAYLOAD_STATUS,
|
|
1707
|
+
beaconBlockRoot: beaconBlockRootHex,
|
|
1708
|
+
},
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1676
1713
|
this.validatedAttestationDatas.add(attDataRoot);
|
|
1677
1714
|
}
|
|
1678
1715
|
|
|
@@ -1855,24 +1892,31 @@ export function getCommitteeFraction(
|
|
|
1855
1892
|
* Pre-Gloas: always FULL (payload embedded in block)
|
|
1856
1893
|
* Gloas: determined by state.execution_payload_availability
|
|
1857
1894
|
*
|
|
1895
|
+
* @param config - The chain fork config to determine fork at checkpoint slot
|
|
1858
1896
|
* @param state - The state to check execution_payload_availability
|
|
1859
1897
|
* @param checkpointEpoch - The epoch of the checkpoint
|
|
1860
1898
|
*/
|
|
1861
|
-
export function getCheckpointPayloadStatus(
|
|
1899
|
+
export function getCheckpointPayloadStatus(
|
|
1900
|
+
config: ChainForkConfig,
|
|
1901
|
+
state: IBeaconStateView,
|
|
1902
|
+
checkpointEpoch: number
|
|
1903
|
+
): PayloadStatus {
|
|
1862
1904
|
// Compute checkpoint slot first to determine the correct fork
|
|
1863
1905
|
const checkpointSlot = computeStartSlotAtEpoch(checkpointEpoch);
|
|
1864
|
-
const fork =
|
|
1906
|
+
const fork = config.getForkSeq(checkpointSlot);
|
|
1865
1907
|
|
|
1866
1908
|
// Pre-Gloas: always FULL
|
|
1867
1909
|
if (fork < ForkSeq.gloas) {
|
|
1868
1910
|
return PayloadStatus.FULL;
|
|
1869
1911
|
}
|
|
1912
|
+
if (!isStatePostGloas(state)) {
|
|
1913
|
+
throw new Error(`Expected gloas+ state for checkpoint payload status, got fork=${state.forkName}`);
|
|
1914
|
+
}
|
|
1870
1915
|
|
|
1871
1916
|
// For Gloas, check state.execution_payload_availability
|
|
1872
1917
|
// - For non-skipped slots at checkpoint: returns false (EMPTY) since payload hasn't arrived yet
|
|
1873
1918
|
// - For skipped slots at checkpoint: returns the actual availability status from state
|
|
1874
|
-
const
|
|
1875
|
-
const payloadAvailable = gloasState.executionPayloadAvailability.get(checkpointSlot % SLOTS_PER_HISTORICAL_ROOT);
|
|
1919
|
+
const payloadAvailable = state.executionPayloadAvailability.get(checkpointSlot % SLOTS_PER_HISTORICAL_ROOT);
|
|
1876
1920
|
|
|
1877
1921
|
return payloadAvailable ? PayloadStatus.FULL : PayloadStatus.EMPTY;
|
|
1878
1922
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CachedBeaconStateAllForks,
|
|
3
|
-
DataAvailabilityStatus,
|
|
4
|
-
EffectiveBalanceIncrements,
|
|
5
|
-
} from "@lodestar/state-transition";
|
|
1
|
+
import {DataAvailabilityStatus, EffectiveBalanceIncrements, IBeaconStateView} from "@lodestar/state-transition";
|
|
6
2
|
import {AttesterSlashing, BeaconBlock, Epoch, IndexedAttestation, Root, RootHex, Slot} from "@lodestar/types";
|
|
7
3
|
import {
|
|
4
|
+
BlockExecutionStatus,
|
|
8
5
|
LVHExecResponse,
|
|
9
|
-
|
|
6
|
+
PayloadExecutionStatus,
|
|
10
7
|
PayloadStatus,
|
|
11
8
|
ProtoBlock,
|
|
12
9
|
ProtoNode,
|
|
@@ -144,10 +141,10 @@ export interface IForkChoice {
|
|
|
144
141
|
*/
|
|
145
142
|
onBlock(
|
|
146
143
|
block: BeaconBlock,
|
|
147
|
-
state:
|
|
144
|
+
state: IBeaconStateView,
|
|
148
145
|
blockDelaySec: number,
|
|
149
146
|
currentSlot: Slot,
|
|
150
|
-
executionStatus:
|
|
147
|
+
executionStatus: BlockExecutionStatus,
|
|
151
148
|
dataAvailabilityStatus: DataAvailabilityStatus
|
|
152
149
|
): ProtoBlock;
|
|
153
150
|
/**
|
|
@@ -207,7 +204,8 @@ export interface IForkChoice {
|
|
|
207
204
|
blockRoot: RootHex,
|
|
208
205
|
executionPayloadBlockHash: RootHex,
|
|
209
206
|
executionPayloadNumber: number,
|
|
210
|
-
executionPayloadStateRoot: RootHex
|
|
207
|
+
executionPayloadStateRoot: RootHex,
|
|
208
|
+
executionStatus: PayloadExecutionStatus
|
|
211
209
|
): void;
|
|
212
210
|
/**
|
|
213
211
|
* Call `onTick` for all slots between `fcStore.getCurrentSlot()` and the provided `currentSlot`.
|
|
@@ -228,6 +226,12 @@ export interface IForkChoice {
|
|
|
228
226
|
*/
|
|
229
227
|
hasBlockUnsafe(blockRoot: Root): boolean;
|
|
230
228
|
hasBlockHexUnsafe(blockRoot: RootHex): boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Returns true if the FULL payload variant (execution payload envelope) exists for this block root,
|
|
231
|
+
* without checking if the block is a descendant of the finalized root.
|
|
232
|
+
*/
|
|
233
|
+
hasPayloadUnsafe(blockRoot: Root): boolean;
|
|
234
|
+
hasPayloadHexUnsafe(blockRoot: RootHex): boolean;
|
|
231
235
|
getSlotsPresent(windowStart: number): number;
|
|
232
236
|
/**
|
|
233
237
|
* Returns a `ProtoBlock` if the block is known **and** a descendant of the finalized root.
|
package/src/forkChoice/store.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
4
|
import {PayloadStatus} from "../protoArray/interface.js";
|
|
@@ -30,7 +30,7 @@ export type JustifiedBalances = EffectiveBalanceIncrements;
|
|
|
30
30
|
*/
|
|
31
31
|
export type JustifiedBalancesGetter = (
|
|
32
32
|
checkpoint: CheckpointWithPayloadStatus,
|
|
33
|
-
blockState:
|
|
33
|
+
blockState: IBeaconStateView
|
|
34
34
|
) => JustifiedBalances;
|
|
35
35
|
|
|
36
36
|
/**
|
package/src/index.ts
CHANGED
|
@@ -31,10 +31,11 @@ export {
|
|
|
31
31
|
} from "./forkChoice/store.js";
|
|
32
32
|
export {type ForkChoiceMetrics, getForkChoiceMetrics} from "./metrics.js";
|
|
33
33
|
export type {
|
|
34
|
+
BlockExecutionStatus,
|
|
34
35
|
BlockExtraMeta,
|
|
35
36
|
LVHInvalidResponse,
|
|
36
37
|
LVHValidResponse,
|
|
37
|
-
|
|
38
|
+
PayloadExecutionStatus,
|
|
38
39
|
ProtoBlock,
|
|
39
40
|
ProtoNode,
|
|
40
41
|
} from "./protoArray/interface.js";
|
|
@@ -64,7 +64,18 @@ export type LVHInvalidResponse = {
|
|
|
64
64
|
};
|
|
65
65
|
export type LVHExecResponse = LVHValidResponse | LVHInvalidResponse;
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Any execution status that is not definitively invalid.
|
|
69
|
+
* Pre-Gloas: Valid | Syncing | PreMerge
|
|
70
|
+
* Post-Gloas: execution status must be PayloadSeparated (beacon block imported before its payload arrives via SignedExecutionPayloadEnvelope)
|
|
71
|
+
*/
|
|
72
|
+
export type BlockExecutionStatus = Exclude<ExecutionStatus, ExecutionStatus.Invalid>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Execution status for a block whose execution payload is present and has been submitted to the EL.
|
|
76
|
+
* Used post-Gloas when transitioning a PayloadSeparated block to FULL via onExecutionPayload().
|
|
77
|
+
*/
|
|
78
|
+
export type PayloadExecutionStatus = ExecutionStatus.Valid | ExecutionStatus.Syncing;
|
|
68
79
|
|
|
69
80
|
export type BlockExtraMeta =
|
|
70
81
|
| {
|
|
@@ -127,12 +138,6 @@ export type ProtoBlock = BlockExtraMeta & {
|
|
|
127
138
|
/** Payload status for this node (Gloas fork). Always FULL in pre-gloas */
|
|
128
139
|
payloadStatus: PayloadStatus;
|
|
129
140
|
|
|
130
|
-
// GLOAS: The followings are from bids. They are null in pre-gloas
|
|
131
|
-
// Used for execution payload gossip validation
|
|
132
|
-
builderIndex: number | null;
|
|
133
|
-
// Used for execution payload gossip validation. Not to be confused with executionPayloadBlockHash
|
|
134
|
-
blockHashFromBid: RootHex | null;
|
|
135
|
-
|
|
136
141
|
// Used to determine if this block extends EMPTY or FULL parent variant
|
|
137
142
|
// Spec: gloas/fork-choice.md#new-get_parent_payload_status
|
|
138
143
|
parentBlockHash: RootHex | null;
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
ExecutionStatus,
|
|
10
10
|
HEX_ZERO_HASH,
|
|
11
11
|
LVHExecResponse,
|
|
12
|
+
PayloadExecutionStatus,
|
|
12
13
|
PayloadStatus,
|
|
13
14
|
ProtoBlock,
|
|
14
15
|
ProtoNode,
|
|
@@ -107,6 +108,14 @@ export class ProtoArray {
|
|
|
107
108
|
currentSlot,
|
|
108
109
|
null
|
|
109
110
|
);
|
|
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
|
+
|
|
110
119
|
return protoArray;
|
|
111
120
|
}
|
|
112
121
|
|
|
@@ -541,7 +550,8 @@ export class ProtoArray {
|
|
|
541
550
|
executionPayloadBlockHash: RootHex,
|
|
542
551
|
executionPayloadNumber: number,
|
|
543
552
|
executionPayloadStateRoot: RootHex,
|
|
544
|
-
proposerBoostRoot: RootHex | null
|
|
553
|
+
proposerBoostRoot: RootHex | null,
|
|
554
|
+
executionStatus: PayloadExecutionStatus
|
|
545
555
|
): void {
|
|
546
556
|
// First check if block exists
|
|
547
557
|
const variants = this.indices.get(blockRoot);
|
|
@@ -591,7 +601,8 @@ export class ProtoArray {
|
|
|
591
601
|
weight: 0,
|
|
592
602
|
bestChild: undefined,
|
|
593
603
|
bestDescendant: undefined,
|
|
594
|
-
|
|
604
|
+
// TODO GLOAS: handle optimistic sync
|
|
605
|
+
executionStatus,
|
|
595
606
|
executionPayloadBlockHash,
|
|
596
607
|
executionPayloadNumber,
|
|
597
608
|
stateRoot: executionPayloadStateRoot,
|
|
@@ -650,9 +661,7 @@ export class ProtoArray {
|
|
|
650
661
|
}
|
|
651
662
|
|
|
652
663
|
// If payload is not locally available, it's not timely
|
|
653
|
-
|
|
654
|
-
const fullNodeIndex = this.getNodeIndexByRootAndStatus(blockRoot, PayloadStatus.FULL);
|
|
655
|
-
if (fullNodeIndex === undefined) {
|
|
664
|
+
if (!this.hasPayload(blockRoot)) {
|
|
656
665
|
return false;
|
|
657
666
|
}
|
|
658
667
|
|
|
@@ -1059,10 +1068,8 @@ export class ProtoArray {
|
|
|
1059
1068
|
});
|
|
1060
1069
|
}
|
|
1061
1070
|
|
|
1062
|
-
//
|
|
1063
|
-
const finalizedIndex = Array.isArray(variants)
|
|
1064
|
-
? Math.min(...variants.filter((idx) => idx !== undefined))
|
|
1065
|
-
: variants;
|
|
1071
|
+
// For Gloas, PENDING variant (index 0) is always the smallest since it's inserted first
|
|
1072
|
+
const finalizedIndex = Array.isArray(variants) ? variants[PayloadStatus.PENDING] : variants;
|
|
1066
1073
|
|
|
1067
1074
|
if (finalizedIndex < this.pruneThreshold) {
|
|
1068
1075
|
// Pruning at small numbers incurs more cost than benefit
|
|
@@ -1672,6 +1679,16 @@ export class ProtoArray {
|
|
|
1672
1679
|
return this.getDefaultNodeIndex(blockRoot) !== undefined;
|
|
1673
1680
|
}
|
|
1674
1681
|
|
|
1682
|
+
/**
|
|
1683
|
+
* Check if a FULL payload variant (execution payload envelope) exists for this block root.
|
|
1684
|
+
* Returns true once the SignedExecutionPayloadEnvelope for this block has been received and processed.
|
|
1685
|
+
*/
|
|
1686
|
+
hasPayload(blockRoot: RootHex): boolean {
|
|
1687
|
+
// we should also make sure this blockRoot is a gloas block, however we only call this function
|
|
1688
|
+
// starting from GLOAS_FORK_EPOCH, so we can assume the blockRoot is from gloas block
|
|
1689
|
+
return this.getNodeIndexByRootAndStatus(blockRoot, PayloadStatus.FULL) !== undefined;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1675
1692
|
/**
|
|
1676
1693
|
* Return ProtoNode for blockRoot with explicit payload status
|
|
1677
1694
|
*
|