@lodestar/beacon-node 1.43.0-dev.6641fd750e → 1.43.0-dev.9fa9f08ef6
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/api/impl/lodestar/attesterSlashing.d.ts +8 -0
- package/lib/api/impl/lodestar/attesterSlashing.d.ts.map +1 -0
- package/lib/api/impl/lodestar/attesterSlashing.js +29 -0
- package/lib/api/impl/lodestar/attesterSlashing.js.map +1 -0
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +36 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +4 -3
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/GetBlobsTracker.d.ts +1 -1
- package/lib/chain/GetBlobsTracker.d.ts.map +1 -1
- package/lib/chain/GetBlobsTracker.js +1 -2
- package/lib/chain/GetBlobsTracker.js.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.d.ts.map +1 -1
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js +2 -4
- package/lib/chain/archiveStore/strategies/frequencyStateArchiveStrategy.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +27 -35
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +10 -8
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.js +1 -1
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +3 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +20 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts +5 -0
- package/lib/chain/blocks/payloadEnvelopeProcessor.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeProcessor.js +6 -4
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +1 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts +14 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +25 -0
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -0
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +17 -37
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +13 -1
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +5 -0
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/attestationError.d.ts +8 -1
- package/lib/chain/errors/attestationError.d.ts.map +1 -1
- package/lib/chain/errors/attestationError.js +4 -0
- package/lib/chain/errors/attestationError.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +12 -4
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +22 -16
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts +3 -9
- package/lib/chain/produceBlock/computeNewStateRoot.d.ts.map +1 -1
- package/lib/chain/produceBlock/computeNewStateRoot.js +5 -32
- package/lib/chain/produceBlock/computeNewStateRoot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -8
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +24 -19
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/errors.d.ts +1 -11
- package/lib/chain/regen/errors.d.ts.map +1 -1
- package/lib/chain/regen/errors.js +0 -2
- package/lib/chain/regen/errors.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +6 -12
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/queued.d.ts +6 -11
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +8 -40
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts +0 -5
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +7 -34
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/stateCache/datastore/db.d.ts +5 -4
- package/lib/chain/stateCache/datastore/db.d.ts.map +1 -1
- package/lib/chain/stateCache/datastore/db.js +10 -32
- package/lib/chain/stateCache/datastore/db.js.map +1 -1
- package/lib/chain/stateCache/datastore/file.d.ts +1 -1
- package/lib/chain/stateCache/datastore/file.d.ts.map +1 -1
- package/lib/chain/stateCache/datastore/file.js +5 -5
- package/lib/chain/stateCache/datastore/file.js.map +1 -1
- package/lib/chain/stateCache/datastore/types.d.ts +1 -1
- package/lib/chain/stateCache/datastore/types.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +1 -7
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +0 -8
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +13 -30
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +116 -215
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +8 -15
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/aggregateAndProof.js +12 -0
- package/lib/chain/validation/aggregateAndProof.js.map +1 -1
- package/lib/chain/validation/attestation.d.ts.map +1 -1
- package/lib/chain/validation/attestation.js +12 -0
- package/lib/chain/validation/attestation.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +2 -2
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +19 -3
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/node/nodejs.d.ts.map +1 -1
- package/lib/node/nodejs.js +4 -2
- package/lib/node/nodejs.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/lodestar/attesterSlashing.ts +43 -0
- package/src/api/impl/lodestar/index.ts +48 -2
- package/src/api/impl/validator/index.ts +6 -5
- package/src/chain/GetBlobsTracker.ts +1 -2
- package/src/chain/archiveStore/strategies/frequencyStateArchiveStrategy.ts +2 -4
- package/src/chain/blocks/importBlock.ts +26 -39
- package/src/chain/blocks/importExecutionPayload.ts +11 -7
- package/src/chain/blocks/index.ts +1 -1
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +27 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +6 -5
- package/src/chain/blocks/types.ts +1 -1
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +38 -0
- package/src/chain/chain.ts +16 -47
- package/src/chain/emitter.ts +12 -0
- package/src/chain/errors/attestationError.ts +6 -1
- package/src/chain/forkChoice/index.ts +12 -4
- package/src/chain/prepareNextSlot.ts +25 -16
- package/src/chain/produceBlock/computeNewStateRoot.ts +6 -43
- package/src/chain/produceBlock/produceBlockBody.ts +34 -20
- package/src/chain/regen/errors.ts +1 -6
- package/src/chain/regen/interface.ts +6 -12
- package/src/chain/regen/queued.ts +12 -48
- package/src/chain/regen/regen.ts +8 -36
- package/src/chain/stateCache/datastore/db.ts +10 -33
- package/src/chain/stateCache/datastore/file.ts +5 -6
- package/src/chain/stateCache/datastore/types.ts +2 -3
- package/src/chain/stateCache/fifoBlockStateCache.ts +1 -10
- package/src/chain/stateCache/persistentCheckpointsCache.ts +135 -246
- package/src/chain/stateCache/types.ts +8 -14
- package/src/chain/validation/aggregateAndProof.ts +13 -0
- package/src/chain/validation/attestation.ts +13 -0
- package/src/chain/validation/executionPayloadEnvelope.ts +2 -2
- package/src/network/processor/gossipHandlers.ts +23 -7
- package/src/node/nodejs.ts +4 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {DataAvailabilityStatus} from "@lodestar/state-transition";
|
|
2
|
+
import {gloas} from "@lodestar/types";
|
|
3
|
+
import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
|
|
4
|
+
|
|
5
|
+
// we can now wait for full 12 seconds because sync and reconstruction will try pulling
|
|
6
|
+
// the data columns from the network anyway while the envelope is being processed
|
|
7
|
+
export const PAYLOAD_DATA_AVAILABILITY_TIMEOUT = 12_000;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Verifies that all payload envelope inputs have their data columns available.
|
|
11
|
+
* - Waits a max of PAYLOAD_DATA_AVAILABILITY_TIMEOUT for all data to be available
|
|
12
|
+
* - Returns the time at which all data was available
|
|
13
|
+
* - Returns the data availability status for each payload input
|
|
14
|
+
*/
|
|
15
|
+
export async function verifyPayloadsDataAvailability(
|
|
16
|
+
payloadInputs: PayloadEnvelopeInput[],
|
|
17
|
+
signal: AbortSignal
|
|
18
|
+
): Promise<{
|
|
19
|
+
dataAvailabilityStatuses: DataAvailabilityStatus[];
|
|
20
|
+
availableTime: number;
|
|
21
|
+
}> {
|
|
22
|
+
const promises: Promise<gloas.DataColumnSidecar[]>[] = [];
|
|
23
|
+
for (const payloadInput of payloadInputs) {
|
|
24
|
+
if (!payloadInput.hasAllData()) {
|
|
25
|
+
promises.push(payloadInput.waitForAllData(PAYLOAD_DATA_AVAILABILITY_TIMEOUT, signal));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await Promise.all(promises);
|
|
29
|
+
|
|
30
|
+
const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
|
|
31
|
+
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
|
|
32
|
+
payloadInput.getBlobKzgCommitments().length === 0
|
|
33
|
+
? DataAvailabilityStatus.NotRequired
|
|
34
|
+
: DataAvailabilityStatus.Available
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return {dataAvailabilityStatuses, availableTime};
|
|
38
|
+
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -2,17 +2,9 @@ import path from "node:path";
|
|
|
2
2
|
import {PrivateKey} from "@libp2p/interface";
|
|
3
3
|
import {Type} from "@chainsafe/ssz";
|
|
4
4
|
import {BeaconConfig} from "@lodestar/config";
|
|
5
|
-
import {
|
|
6
|
-
CheckpointWithPayloadStatus,
|
|
7
|
-
IForkChoice,
|
|
8
|
-
PayloadStatus,
|
|
9
|
-
ProtoBlock,
|
|
10
|
-
UpdateHeadOpt,
|
|
11
|
-
getCheckpointPayloadStatus,
|
|
12
|
-
} from "@lodestar/fork-choice";
|
|
5
|
+
import {CheckpointWithPayloadStatus, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
|
|
13
6
|
import {LoggerNode} from "@lodestar/logger/node";
|
|
14
7
|
import {
|
|
15
|
-
BUILDER_INDEX_SELF_BUILD,
|
|
16
8
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
17
9
|
type ForkPostFulu,
|
|
18
10
|
type ForkPostGloas,
|
|
@@ -31,7 +23,6 @@ import {
|
|
|
31
23
|
getEffectiveBalancesFromStateBytes,
|
|
32
24
|
isStatePostAltair,
|
|
33
25
|
isStatePostElectra,
|
|
34
|
-
isStatePostGloas,
|
|
35
26
|
} from "@lodestar/state-transition";
|
|
36
27
|
import {
|
|
37
28
|
BeaconBlock,
|
|
@@ -100,8 +91,8 @@ import {
|
|
|
100
91
|
} from "./opPools/index.js";
|
|
101
92
|
import {IChainOptions} from "./options.js";
|
|
102
93
|
import {PrepareNextSlotScheduler} from "./prepareNextSlot.js";
|
|
103
|
-
import {computeNewStateRoot
|
|
104
|
-
import {AssembledBlockType, BlockType,
|
|
94
|
+
import {computeNewStateRoot} from "./produceBlock/computeNewStateRoot.js";
|
|
95
|
+
import {AssembledBlockType, BlockType, ProduceResult} from "./produceBlock/index.js";
|
|
105
96
|
import {BlockAttributes, produceBlockBody, produceCommonBlockBody} from "./produceBlock/produceBlockBody.js";
|
|
106
97
|
import {QueuedStateRegenerator, RegenCaller} from "./regen/index.js";
|
|
107
98
|
import {ReprocessController} from "./reprocess.js";
|
|
@@ -125,7 +116,7 @@ import {DbCPStateDatastore, checkpointToDatastoreKey} from "./stateCache/datasto
|
|
|
125
116
|
import {FileCPStateDatastore} from "./stateCache/datastore/file.js";
|
|
126
117
|
import {CPStateDatastore} from "./stateCache/datastore/types.js";
|
|
127
118
|
import {FIFOBlockStateCache} from "./stateCache/fifoBlockStateCache.js";
|
|
128
|
-
import {PersistentCheckpointStateCache
|
|
119
|
+
import {PersistentCheckpointStateCache} from "./stateCache/persistentCheckpointsCache.js";
|
|
129
120
|
import {CheckpointStateCache} from "./stateCache/types.js";
|
|
130
121
|
import {ValidatorMonitor} from "./validatorMonitor.js";
|
|
131
122
|
|
|
@@ -390,8 +381,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
390
381
|
const {checkpoint} = anchorState.computeAnchorCheckpoint();
|
|
391
382
|
blockStateCache.add(anchorState);
|
|
392
383
|
blockStateCache.setHeadState(anchorState);
|
|
393
|
-
|
|
394
|
-
checkpointStateCache.add(checkpoint, anchorState, payloadPresent);
|
|
384
|
+
checkpointStateCache.add(checkpoint, anchorState);
|
|
395
385
|
|
|
396
386
|
const forkChoice = initializeForkChoice(
|
|
397
387
|
config,
|
|
@@ -685,7 +675,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
685
675
|
|
|
686
676
|
// TODO GLOAS: Need to revisit the design of this api. Currently we just retrieve FULL state of the checkpoint for backwards compatibility.
|
|
687
677
|
// because pre-gloas we always store FULL checkpoint state.
|
|
688
|
-
const persistedKey = checkpointToDatastoreKey(checkpoint
|
|
678
|
+
const persistedKey = checkpointToDatastoreKey(checkpoint);
|
|
689
679
|
return this.cpStateDatastore.read(persistedKey);
|
|
690
680
|
}
|
|
691
681
|
|
|
@@ -693,8 +683,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
693
683
|
checkpoint: CheckpointWithPayloadStatus
|
|
694
684
|
): {state: IBeaconStateView; executionOptimistic: boolean; finalized: boolean} | null {
|
|
695
685
|
// finalized or justified checkpoint states maynot be available with PersistentCheckpointStateCache, use getCheckpointStateOrBytes() api to get Uint8Array
|
|
696
|
-
const
|
|
697
|
-
const cachedStateCtx = this.regen.getCheckpointStateSync(
|
|
686
|
+
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
687
|
+
const cachedStateCtx = this.regen.getCheckpointStateSync(checkpointHex);
|
|
698
688
|
if (cachedStateCtx) {
|
|
699
689
|
const block = this.forkChoice.getBlockDefaultStatus(
|
|
700
690
|
ssz.phase0.BeaconBlockHeader.hashTreeRoot(cachedStateCtx.latestBlockHeader)
|
|
@@ -713,8 +703,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
713
703
|
async getStateOrBytesByCheckpoint(
|
|
714
704
|
checkpoint: CheckpointWithPayloadStatus
|
|
715
705
|
): Promise<{state: IBeaconStateView | Uint8Array; executionOptimistic: boolean; finalized: boolean} | null> {
|
|
716
|
-
const
|
|
717
|
-
const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(
|
|
706
|
+
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
707
|
+
const cachedStateCtx = await this.regen.getCheckpointStateOrBytes(checkpointHex);
|
|
718
708
|
if (cachedStateCtx) {
|
|
719
709
|
const block = this.forkChoice.getBlockDefaultStatus(checkpoint.root);
|
|
720
710
|
const finalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
@@ -1070,7 +1060,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1070
1060
|
body,
|
|
1071
1061
|
} as AssembledBlockType<T>;
|
|
1072
1062
|
|
|
1073
|
-
const {newStateRoot, proposerReward
|
|
1063
|
+
const {newStateRoot, proposerReward} = computeNewStateRoot(this.metrics, state, block);
|
|
1074
1064
|
block.stateRoot = newStateRoot;
|
|
1075
1065
|
const blockRoot =
|
|
1076
1066
|
produceResult.type === BlockType.Full
|
|
@@ -1079,26 +1069,9 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1079
1069
|
const blockRootHex = toRootHex(blockRoot);
|
|
1080
1070
|
|
|
1081
1071
|
const fork = this.config.getForkName(slot);
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
const gloasResult = produceResult as ProduceFullGloas;
|
|
1089
|
-
const envelope: gloas.ExecutionPayloadEnvelope = {
|
|
1090
|
-
payload: gloasResult.executionPayload,
|
|
1091
|
-
executionRequests: gloasResult.executionRequests,
|
|
1092
|
-
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
1093
|
-
beaconBlockRoot: blockRoot,
|
|
1094
|
-
slot,
|
|
1095
|
-
stateRoot: ZERO_HASH,
|
|
1096
|
-
};
|
|
1097
|
-
if (!isStatePostGloas(postBlockState)) {
|
|
1098
|
-
throw Error(`Expected gloas+ post-state for execution payload envelope, got fork=${postBlockState.forkName}`);
|
|
1099
|
-
}
|
|
1100
|
-
const payloadEnvelopeStateRoot = computePayloadEnvelopeStateRoot(this.metrics, postBlockState, envelope);
|
|
1101
|
-
gloasResult.payloadEnvelopeStateRoot = payloadEnvelopeStateRoot;
|
|
1072
|
+
// TODO GLOAS: we should retire BlockType post-gloas, may need a new enum for self vs non-self built
|
|
1073
|
+
if (isForkPostGloas(fork) && produceResult.type !== BlockType.Full) {
|
|
1074
|
+
throw Error(`Unexpected block type=${produceResult.type} for post-gloas fork=${fork}`);
|
|
1102
1075
|
}
|
|
1103
1076
|
|
|
1104
1077
|
// Track the produced block for consensus broadcast validations, later validation, etc.
|
|
@@ -1346,8 +1319,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1346
1319
|
checkpoint: CheckpointWithPayloadStatus,
|
|
1347
1320
|
blockState: IBeaconStateView
|
|
1348
1321
|
): {state: IBeaconStateView; stateId: string; shouldWarn: boolean} {
|
|
1349
|
-
const
|
|
1350
|
-
const state = this.regen.getCheckpointStateSync(
|
|
1322
|
+
const checkpointHex = {epoch: checkpoint.epoch, rootHex: checkpoint.rootHex};
|
|
1323
|
+
const state = this.regen.getCheckpointStateSync(checkpointHex);
|
|
1351
1324
|
if (state) {
|
|
1352
1325
|
return {state, stateId: "checkpoint_state", shouldWarn: false};
|
|
1353
1326
|
}
|
|
@@ -1471,10 +1444,6 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1471
1444
|
private onClockEpoch(epoch: Epoch): void {
|
|
1472
1445
|
this.metrics?.clockEpoch.set(epoch);
|
|
1473
1446
|
|
|
1474
|
-
if (epoch === this.config.GLOAS_FORK_EPOCH) {
|
|
1475
|
-
this.regen.upgradeForGloas(epoch);
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
1447
|
this.seenAttesters.prune(epoch);
|
|
1479
1448
|
this.seenAggregators.prune(epoch);
|
|
1480
1449
|
this.seenPayloadAttesters.prune(epoch);
|
package/src/chain/emitter.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {DataColumnSidecar, RootHex, deneb, phase0} from "@lodestar/types";
|
|
|
7
7
|
import {SignedExecutionPayloadEnvelope} from "@lodestar/types/gloas";
|
|
8
8
|
import {PeerIdStr} from "../util/peerId.js";
|
|
9
9
|
import {BlockInputSource, IBlockInput} from "./blocks/blockInput/types.js";
|
|
10
|
+
import {PayloadEnvelopeInput} from "./blocks/payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Important chain events that occur during normal chain operation.
|
|
@@ -76,6 +77,11 @@ export enum ChainEvent {
|
|
|
76
77
|
* cut-off window passes for waiting on gossip
|
|
77
78
|
*/
|
|
78
79
|
incompleteBlockInput = "incompleteBlockInput",
|
|
80
|
+
/**
|
|
81
|
+
* Post-gloas: trigger BlockInputSync for payload envelopes whose envelope and/or sampled columns are partially
|
|
82
|
+
* received via gossip but are not complete by time the cut-off window passes for waiting on gossip
|
|
83
|
+
*/
|
|
84
|
+
incompletePayloadEnvelope = "incompletePayloadEnvelope",
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
export type HeadEventData = routes.events.EventData[routes.events.EventType.head];
|
|
@@ -93,6 +99,11 @@ export type ChainEventData = {
|
|
|
93
99
|
};
|
|
94
100
|
[ChainEvent.unknownBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
|
|
95
101
|
[ChainEvent.incompleteBlockInput]: {blockInput: IBlockInput; peer: PeerIdStr; source: BlockInputSource};
|
|
102
|
+
[ChainEvent.incompletePayloadEnvelope]: {
|
|
103
|
+
payloadInput: PayloadEnvelopeInput;
|
|
104
|
+
peer: PeerIdStr;
|
|
105
|
+
source: BlockInputSource;
|
|
106
|
+
};
|
|
96
107
|
[ChainEvent.unknownEnvelopeBlockRoot]: {rootHex: RootHex; peer?: PeerIdStr; source: BlockInputSource};
|
|
97
108
|
};
|
|
98
109
|
|
|
@@ -116,6 +127,7 @@ export type IChainEvents = ApiEvents & {
|
|
|
116
127
|
[ChainEvent.envelopeUnknownBlock]: (data: ChainEventData[ChainEvent.envelopeUnknownBlock]) => void;
|
|
117
128
|
[ChainEvent.unknownBlockRoot]: (data: ChainEventData[ChainEvent.unknownBlockRoot]) => void;
|
|
118
129
|
[ChainEvent.incompleteBlockInput]: (data: ChainEventData[ChainEvent.incompleteBlockInput]) => void;
|
|
130
|
+
[ChainEvent.incompletePayloadEnvelope]: (data: ChainEventData[ChainEvent.incompletePayloadEnvelope]) => void;
|
|
119
131
|
[ChainEvent.unknownEnvelopeBlockRoot]: (data: ChainEventData[ChainEvent.unknownEnvelopeBlockRoot]) => void;
|
|
120
132
|
};
|
|
121
133
|
|
|
@@ -147,6 +147,10 @@ export enum AttestationErrorCode {
|
|
|
147
147
|
* Gloas: Current slot attestation is marking payload as present
|
|
148
148
|
*/
|
|
149
149
|
PREMATURELY_INDICATED_PAYLOAD_PRESENT = "ATTESTATION_ERROR_PREMATURELY_INDICATED_PAYLOAD_PRESENT",
|
|
150
|
+
/**
|
|
151
|
+
* Gloas: index-1 attestation but the execution payload has not been seen yet
|
|
152
|
+
*/
|
|
153
|
+
EXECUTION_PAYLOAD_NOT_SEEN = "ATTESTATION_ERROR_EXECUTION_PAYLOAD_NOT_SEEN",
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
export type AttestationErrorType =
|
|
@@ -185,7 +189,8 @@ export type AttestationErrorType =
|
|
|
185
189
|
| {code: AttestationErrorCode.NON_ZERO_ATTESTATION_DATA_INDEX}
|
|
186
190
|
| {code: AttestationErrorCode.ATTESTER_NOT_IN_COMMITTEE}
|
|
187
191
|
| {code: AttestationErrorCode.INVALID_PAYLOAD_STATUS_VALUE; attDataIndex: number}
|
|
188
|
-
| {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT}
|
|
192
|
+
| {code: AttestationErrorCode.PREMATURELY_INDICATED_PAYLOAD_PRESENT}
|
|
193
|
+
| {code: AttestationErrorCode.EXECUTION_PAYLOAD_NOT_SEEN; beaconBlockRoot: RootHex};
|
|
189
194
|
|
|
190
195
|
export class AttestationError extends GossipActionError<AttestationErrorType> {
|
|
191
196
|
getMetadata(): Record<string, string | number | null> {
|
|
@@ -105,10 +105,14 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
105
105
|
const isForkPostGloas = computeEpochAtSlot(state.slot) >= config.GLOAS_FORK_EPOCH;
|
|
106
106
|
|
|
107
107
|
// Determine justified checkpoint payload status
|
|
108
|
-
const justifiedPayloadStatus =
|
|
108
|
+
const justifiedPayloadStatus = isForkPostGloas
|
|
109
|
+
? PayloadStatus.PENDING
|
|
110
|
+
: getCheckpointPayloadStatus(config, state, justifiedCheckpoint.epoch);
|
|
109
111
|
|
|
110
112
|
// Determine finalized checkpoint payload status
|
|
111
|
-
const finalizedPayloadStatus =
|
|
113
|
+
const finalizedPayloadStatus = isForkPostGloas
|
|
114
|
+
? PayloadStatus.PENDING
|
|
115
|
+
: getCheckpointPayloadStatus(config, state, finalizedCheckpoint.epoch);
|
|
112
116
|
|
|
113
117
|
return new forkchoiceConstructor(
|
|
114
118
|
config,
|
|
@@ -146,7 +150,9 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
146
150
|
|
|
147
151
|
...(isStatePostBellatrix(state) && state.isExecutionStateType && state.isMergeTransitionComplete
|
|
148
152
|
? {
|
|
149
|
-
executionPayloadBlockHash:
|
|
153
|
+
executionPayloadBlockHash: isStatePostGloas(state)
|
|
154
|
+
? toRootHex(state.latestBlockHash)
|
|
155
|
+
: toRootHex(state.latestExecutionPayloadHeader.blockHash),
|
|
150
156
|
// TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
|
|
151
157
|
// latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
|
|
152
158
|
executionPayloadNumber: isStatePostGloas(state) ? 0 : state.payloadBlockNumber,
|
|
@@ -243,7 +249,9 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
243
249
|
unfinalizedState.isExecutionStateType &&
|
|
244
250
|
unfinalizedState.isMergeTransitionComplete
|
|
245
251
|
? {
|
|
246
|
-
executionPayloadBlockHash:
|
|
252
|
+
executionPayloadBlockHash: isStatePostGloas(unfinalizedState)
|
|
253
|
+
? toRootHex(unfinalizedState.latestBlockHash)
|
|
254
|
+
: toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash),
|
|
247
255
|
// TODO GLOAS: executionPayloadNumber is not tracked in BeaconState post-gloas (EIP-7732 removed
|
|
248
256
|
// latestExecutionPayloadHeader). Using 0 as unavailable fallback until a solution is found.
|
|
249
257
|
executionPayloadNumber: isStatePostGloas(unfinalizedState) ? 0 : unfinalizedState.payloadBlockNumber,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
-
import {
|
|
3
|
+
import {getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
4
4
|
import {ForkPostBellatrix, ForkSeq, SLOTS_PER_EPOCH, isForkPostBellatrix} from "@lodestar/params";
|
|
5
5
|
import {
|
|
6
6
|
IBeaconStateView,
|
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
computeEpochAtSlot,
|
|
9
9
|
computeTimeAtSlot,
|
|
10
10
|
isStatePostBellatrix,
|
|
11
|
+
isStatePostGloas,
|
|
11
12
|
} from "@lodestar/state-transition";
|
|
12
|
-
import {Slot} from "@lodestar/types";
|
|
13
|
+
import {Bytes32, Slot} from "@lodestar/types";
|
|
13
14
|
import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
|
|
14
15
|
import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
|
|
15
16
|
import {BuilderStatus} from "../execution/builder/http.js";
|
|
@@ -81,6 +82,8 @@ export class PrepareNextSlotScheduler {
|
|
|
81
82
|
// calling updateHead() here before we produce a block to reduce reorg possibility
|
|
82
83
|
const headBlock = this.chain.recomputeForkChoiceHead(ForkchoiceCaller.prepareNextSlot);
|
|
83
84
|
const {slot: headSlot, blockRoot: headRoot} = headBlock;
|
|
85
|
+
// may be updated below if we predict a proposer-boost-reorg
|
|
86
|
+
let updatedHeadRoot = headRoot;
|
|
84
87
|
|
|
85
88
|
// PS: previously this was comparing slots, but that gave no leway on the skipped
|
|
86
89
|
// slots on epoch bounday. Making it more fluid.
|
|
@@ -123,7 +126,6 @@ export class PrepareNextSlotScheduler {
|
|
|
123
126
|
const proposerIndex = prepareState.getBeaconProposer(prepareSlot);
|
|
124
127
|
const feeRecipient = this.chain.beaconProposerCache.get(proposerIndex);
|
|
125
128
|
let updatedPrepareState = prepareState;
|
|
126
|
-
let updatedHeadRoot = headRoot;
|
|
127
129
|
|
|
128
130
|
if (feeRecipient) {
|
|
129
131
|
// If we are proposing next slot, we need to predict if we can proposer-boost-reorg or not
|
|
@@ -156,25 +158,39 @@ export class PrepareNextSlotScheduler {
|
|
|
156
158
|
this.logger.error("Builder disabled as the check status api failed", {prepareSlot}, e as Error);
|
|
157
159
|
});
|
|
158
160
|
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!isStatePostBellatrix(updatedPrepareState)) {
|
|
164
|
+
throw new Error("Expected Bellatrix state for payload attributes");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let parentBlockHash: Bytes32;
|
|
168
|
+
if (isStatePostGloas(updatedPrepareState)) {
|
|
169
|
+
parentBlockHash = this.chain.forkChoice.shouldExtendPayload(updatedHeadRoot)
|
|
170
|
+
? updatedPrepareState.latestExecutionPayloadBid.blockHash
|
|
171
|
+
: updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
|
|
172
|
+
} else {
|
|
173
|
+
parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
|
|
174
|
+
}
|
|
159
175
|
|
|
176
|
+
if (feeRecipient) {
|
|
160
177
|
const preparationTime =
|
|
161
178
|
computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
|
|
162
179
|
this.metrics?.blockPayload.payloadAdvancePrepTime.observe(preparationTime);
|
|
163
|
-
if (!isStatePostBellatrix(updatedPrepareState)) {
|
|
164
|
-
throw new Error("Expected Bellatrix state for payload preparation");
|
|
165
|
-
}
|
|
166
180
|
|
|
167
181
|
const safeBlockHash = getSafeExecutionBlockHash(this.chain.forkChoice);
|
|
168
182
|
const finalizedBlockHash =
|
|
169
183
|
this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
184
|
+
|
|
170
185
|
// awaiting here instead of throwing an async call because there is no other task
|
|
171
|
-
// left for scheduler and this gives nice
|
|
186
|
+
// left for scheduler and this gives nice semantics to catch and log errors in the
|
|
172
187
|
// try/catch wrapper here.
|
|
173
188
|
await prepareExecutionPayload(
|
|
174
189
|
this.chain,
|
|
175
190
|
this.logger,
|
|
176
191
|
fork as ForkPostBellatrix, // State is of execution type
|
|
177
192
|
fromHex(updatedHeadRoot),
|
|
193
|
+
parentBlockHash,
|
|
178
194
|
safeBlockHash,
|
|
179
195
|
finalizedBlockHash,
|
|
180
196
|
updatedPrepareState,
|
|
@@ -187,10 +203,6 @@ export class PrepareNextSlotScheduler {
|
|
|
187
203
|
});
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
if (!isStatePostBellatrix(updatedPrepareState)) {
|
|
191
|
-
throw new Error("Expected Bellatrix state for payload attributes");
|
|
192
|
-
}
|
|
193
|
-
|
|
194
206
|
this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
|
|
195
207
|
|
|
196
208
|
// If emitPayloadAttributes is true emit a SSE payloadAttributes event
|
|
@@ -202,6 +214,7 @@ export class PrepareNextSlotScheduler {
|
|
|
202
214
|
prepareState: updatedPrepareState,
|
|
203
215
|
prepareSlot,
|
|
204
216
|
parentBlockRoot: fromHex(headRoot),
|
|
217
|
+
parentBlockHash,
|
|
205
218
|
// The likely consumers of this API are builders and will anyway ignore the
|
|
206
219
|
// feeRecipient, so just pass zero hash for now till a real use case arises
|
|
207
220
|
feeRecipient: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
@@ -217,11 +230,7 @@ export class PrepareNextSlotScheduler {
|
|
|
217
230
|
// + if next slot is a skipped slot, it'd help getting target checkpoint state faster to validate attestations
|
|
218
231
|
if (isEpochTransition) {
|
|
219
232
|
this.metrics?.precomputeNextEpochTransition.count.inc({result: "success"}, 1);
|
|
220
|
-
|
|
221
|
-
// Pre-Gloas: payloadStatus is always FULL → payloadPresent = true
|
|
222
|
-
// Post-Gloas: FULL → true, EMPTY → false, PENDING → false (conservative, treat as block state)
|
|
223
|
-
const payloadPresent = headBlock.payloadStatus === PayloadStatus.FULL;
|
|
224
|
-
const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch, payloadPresent);
|
|
233
|
+
const previousHits = this.chain.regen.updatePreComputedCheckpoint(headRoot, nextEpoch);
|
|
225
234
|
if (previousHits === 0) {
|
|
226
235
|
this.metrics?.precomputeNextEpochTransition.waste.inc();
|
|
227
236
|
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DataAvailabilityStatus,
|
|
3
3
|
ExecutionPayloadStatus,
|
|
4
|
-
G2_POINT_AT_INFINITY,
|
|
5
4
|
IBeaconStateView,
|
|
6
|
-
IBeaconStateViewGloas,
|
|
7
5
|
StateHashTreeRootSource,
|
|
8
6
|
} from "@lodestar/state-transition";
|
|
9
|
-
import {BeaconBlock, BlindedBeaconBlock, Gwei, Root
|
|
7
|
+
import {BeaconBlock, BlindedBeaconBlock, Gwei, Root} from "@lodestar/types";
|
|
10
8
|
import {ZERO_HASH} from "../../constants/index.js";
|
|
11
9
|
import {Metrics} from "../../metrics/index.js";
|
|
12
10
|
|
|
@@ -19,11 +17,11 @@ export function computeNewStateRoot(
|
|
|
19
17
|
metrics: Metrics | null,
|
|
20
18
|
state: IBeaconStateView,
|
|
21
19
|
block: BeaconBlock | BlindedBeaconBlock
|
|
22
|
-
): {newStateRoot: Root; proposerReward: Gwei;
|
|
20
|
+
): {newStateRoot: Root; proposerReward: Gwei; postState: IBeaconStateView} {
|
|
23
21
|
// Set signature to zero to re-use stateTransition() function which requires the SignedBeaconBlock type
|
|
24
22
|
const blockEmptySig = {message: block, signature: ZERO_HASH};
|
|
25
23
|
|
|
26
|
-
const
|
|
24
|
+
const postState = state.stateTransition(
|
|
27
25
|
blockEmptySig,
|
|
28
26
|
{
|
|
29
27
|
// ExecutionPayloadStatus.valid: Assume payload valid, it has been produced by a trusted EL
|
|
@@ -42,49 +40,14 @@ export function computeNewStateRoot(
|
|
|
42
40
|
{metrics}
|
|
43
41
|
);
|
|
44
42
|
|
|
45
|
-
const {attestations, syncAggregate, slashing} =
|
|
43
|
+
const {attestations, syncAggregate, slashing} = postState.proposerRewards;
|
|
46
44
|
const proposerReward = BigInt(attestations + syncAggregate + slashing);
|
|
47
45
|
|
|
48
46
|
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
|
|
49
47
|
source: StateHashTreeRootSource.computeNewStateRoot,
|
|
50
48
|
});
|
|
51
|
-
const newStateRoot =
|
|
49
|
+
const newStateRoot = postState.hashTreeRoot();
|
|
52
50
|
hashTreeRootTimer?.();
|
|
53
51
|
|
|
54
|
-
return {newStateRoot, proposerReward,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Compute the state root after processing an execution payload envelope.
|
|
59
|
-
* Similar to `computeNewStateRoot` but for payload envelope processing.
|
|
60
|
-
*
|
|
61
|
-
*/
|
|
62
|
-
export function computePayloadEnvelopeStateRoot(
|
|
63
|
-
metrics: Metrics | null,
|
|
64
|
-
postBlockState: IBeaconStateViewGloas,
|
|
65
|
-
envelope: gloas.ExecutionPayloadEnvelope
|
|
66
|
-
): Root {
|
|
67
|
-
const signedEnvelope: gloas.SignedExecutionPayloadEnvelope = {
|
|
68
|
-
message: envelope,
|
|
69
|
-
signature: G2_POINT_AT_INFINITY,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const processEnvelopeTimer = metrics?.blockPayload.executionPayloadEnvelopeProcessingTime.startTimer();
|
|
73
|
-
const postPayloadState = postBlockState.processExecutionPayloadEnvelope(signedEnvelope, {
|
|
74
|
-
// Signature is zero-ed (G2_POINT_AT_INFINITY), skip verification
|
|
75
|
-
verifySignature: false,
|
|
76
|
-
// State root is being computed here, the envelope doesn't have it yet
|
|
77
|
-
verifyStateRoot: false,
|
|
78
|
-
// Preserve cache in source state, since the resulting state is not added to the state cache
|
|
79
|
-
dontTransferCache: true,
|
|
80
|
-
});
|
|
81
|
-
processEnvelopeTimer?.();
|
|
82
|
-
|
|
83
|
-
const hashTreeRootTimer = metrics?.stateHashTreeRootTime.startTimer({
|
|
84
|
-
source: StateHashTreeRootSource.computePayloadEnvelopeStateRoot,
|
|
85
|
-
});
|
|
86
|
-
const stateRoot = postPayloadState.hashTreeRoot();
|
|
87
|
-
hashTreeRootTimer?.();
|
|
88
|
-
|
|
89
|
-
return stateRoot;
|
|
52
|
+
return {newStateRoot, proposerReward, postState};
|
|
90
53
|
}
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
IBeaconStateView,
|
|
20
20
|
type IBeaconStateViewBellatrix,
|
|
21
21
|
computeTimeAtSlot,
|
|
22
|
-
isParentBlockFull,
|
|
23
22
|
isStatePostBellatrix,
|
|
24
23
|
isStatePostCapella,
|
|
25
24
|
isStatePostGloas,
|
|
@@ -48,7 +47,7 @@ import {
|
|
|
48
47
|
fulu,
|
|
49
48
|
gloas,
|
|
50
49
|
} from "@lodestar/types";
|
|
51
|
-
import {Logger, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
50
|
+
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
52
51
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
53
52
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
54
53
|
import {
|
|
@@ -111,12 +110,6 @@ export type ProduceFullGloas = {
|
|
|
111
110
|
executionRequests: electra.ExecutionRequests;
|
|
112
111
|
blobsBundle: BlobsBundle<ForkPostGloas>;
|
|
113
112
|
cells: fulu.Cell[][];
|
|
114
|
-
/**
|
|
115
|
-
* Cached payload envelope state root computed during block production.
|
|
116
|
-
* This is the state root after running `processExecutionPayloadEnvelope` on the
|
|
117
|
-
* post-block state, and later used to construct the `ExecutionPayloadEnvelope`.
|
|
118
|
-
*/
|
|
119
|
-
payloadEnvelopeStateRoot: Root;
|
|
120
113
|
};
|
|
121
114
|
export type ProduceFullFulu = {
|
|
122
115
|
type: BlockType.Full;
|
|
@@ -220,11 +213,15 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
220
213
|
});
|
|
221
214
|
|
|
222
215
|
// Get execution payload from EL
|
|
216
|
+
const parentBlockHash = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot))
|
|
217
|
+
? currentState.latestExecutionPayloadBid.blockHash
|
|
218
|
+
: currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
223
219
|
const prepareRes = await prepareExecutionPayload(
|
|
224
220
|
this,
|
|
225
221
|
this.logger,
|
|
226
222
|
fork,
|
|
227
223
|
parentBlockRoot,
|
|
224
|
+
parentBlockHash,
|
|
228
225
|
safeBlockHash,
|
|
229
226
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
230
227
|
currentState,
|
|
@@ -261,8 +258,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
261
258
|
|
|
262
259
|
// Create self-build execution payload bid
|
|
263
260
|
const bid: gloas.ExecutionPayloadBid = {
|
|
264
|
-
parentBlockHash
|
|
265
|
-
parentBlockRoot
|
|
261
|
+
parentBlockHash,
|
|
262
|
+
parentBlockRoot,
|
|
266
263
|
blockHash: executionPayload.blockHash,
|
|
267
264
|
prevRandao: currentState.getRandaoMix(currentState.epoch),
|
|
268
265
|
feeRecipient: executionPayload.feeRecipient,
|
|
@@ -340,6 +337,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
340
337
|
this.logger,
|
|
341
338
|
fork,
|
|
342
339
|
parentBlockRoot,
|
|
340
|
+
currentState.latestExecutionPayloadHeader.blockHash,
|
|
343
341
|
safeBlockHash,
|
|
344
342
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
345
343
|
currentState,
|
|
@@ -448,6 +446,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
448
446
|
this.logger,
|
|
449
447
|
fork,
|
|
450
448
|
parentBlockRoot,
|
|
449
|
+
currentState.latestExecutionPayloadHeader.blockHash,
|
|
451
450
|
safeBlockHash,
|
|
452
451
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
453
452
|
currentState,
|
|
@@ -613,17 +612,17 @@ export async function prepareExecutionPayload(
|
|
|
613
612
|
logger: Logger,
|
|
614
613
|
fork: ForkPostBellatrix,
|
|
615
614
|
parentBlockRoot: Root,
|
|
615
|
+
parentBlockHash: Bytes32,
|
|
616
616
|
safeBlockHash: RootHex,
|
|
617
617
|
finalizedBlockHash: RootHex,
|
|
618
618
|
state: IBeaconStateViewBellatrix,
|
|
619
619
|
suggestedFeeRecipient: string
|
|
620
620
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
621
|
-
const parentHash = state.latestBlockHash;
|
|
622
621
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
623
622
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
624
623
|
|
|
625
624
|
const payloadIdCached = chain.executionEngine.payloadIdCache.get({
|
|
626
|
-
headBlockHash: toRootHex(
|
|
625
|
+
headBlockHash: toRootHex(parentBlockHash),
|
|
627
626
|
finalizedBlockHash,
|
|
628
627
|
timestamp: numToQuantity(timestamp),
|
|
629
628
|
prevRandao: toHex(prevRandao),
|
|
@@ -652,12 +651,13 @@ export async function prepareExecutionPayload(
|
|
|
652
651
|
prepareState: state,
|
|
653
652
|
prepareSlot: state.slot,
|
|
654
653
|
parentBlockRoot,
|
|
654
|
+
parentBlockHash,
|
|
655
655
|
feeRecipient: suggestedFeeRecipient,
|
|
656
656
|
});
|
|
657
657
|
|
|
658
658
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
659
659
|
fork,
|
|
660
|
-
toRootHex(
|
|
660
|
+
toRootHex(parentBlockHash),
|
|
661
661
|
safeBlockHash,
|
|
662
662
|
finalizedBlockHash,
|
|
663
663
|
attributes
|
|
@@ -709,20 +709,30 @@ export function getPayloadAttributesForSSE(
|
|
|
709
709
|
prepareState,
|
|
710
710
|
prepareSlot,
|
|
711
711
|
parentBlockRoot,
|
|
712
|
+
parentBlockHash,
|
|
712
713
|
feeRecipient,
|
|
713
|
-
}: {
|
|
714
|
+
}: {
|
|
715
|
+
prepareState: IBeaconStateViewBellatrix;
|
|
716
|
+
prepareSlot: Slot;
|
|
717
|
+
parentBlockRoot: Root;
|
|
718
|
+
parentBlockHash: Bytes32;
|
|
719
|
+
feeRecipient: string;
|
|
720
|
+
}
|
|
714
721
|
): SSEPayloadAttributes {
|
|
715
|
-
const parentHash = prepareState.latestBlockHash;
|
|
716
722
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
717
723
|
prepareState,
|
|
718
724
|
prepareSlot,
|
|
719
725
|
parentBlockRoot,
|
|
726
|
+
parentBlockHash,
|
|
720
727
|
feeRecipient,
|
|
721
728
|
});
|
|
722
729
|
|
|
723
730
|
let parentBlockNumber: number;
|
|
724
731
|
if (isForkPostGloas(fork)) {
|
|
725
|
-
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
|
|
732
|
+
const parentBlock = chain.forkChoice.getBlockHexAndBlockHash(
|
|
733
|
+
toRootHex(parentBlockRoot),
|
|
734
|
+
toRootHex(parentBlockHash)
|
|
735
|
+
);
|
|
726
736
|
if (parentBlock?.executionPayloadBlockHash == null) {
|
|
727
737
|
throw Error(`Parent block not found in fork choice root=${toRootHex(parentBlockRoot)}`);
|
|
728
738
|
}
|
|
@@ -736,7 +746,7 @@ export function getPayloadAttributesForSSE(
|
|
|
736
746
|
proposalSlot: prepareSlot,
|
|
737
747
|
parentBlockNumber,
|
|
738
748
|
parentBlockRoot,
|
|
739
|
-
parentBlockHash
|
|
749
|
+
parentBlockHash,
|
|
740
750
|
payloadAttributes,
|
|
741
751
|
};
|
|
742
752
|
return ssePayloadAttributes;
|
|
@@ -751,11 +761,13 @@ function preparePayloadAttributes(
|
|
|
751
761
|
prepareState,
|
|
752
762
|
prepareSlot,
|
|
753
763
|
parentBlockRoot,
|
|
764
|
+
parentBlockHash,
|
|
754
765
|
feeRecipient,
|
|
755
766
|
}: {
|
|
756
767
|
prepareState: IBeaconStateViewBellatrix;
|
|
757
768
|
prepareSlot: Slot;
|
|
758
769
|
parentBlockRoot: Root;
|
|
770
|
+
parentBlockHash: Bytes32;
|
|
759
771
|
feeRecipient: string;
|
|
760
772
|
}
|
|
761
773
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
@@ -772,13 +784,15 @@ function preparePayloadAttributes(
|
|
|
772
784
|
throw new Error("Expected Capella state for withdrawals");
|
|
773
785
|
}
|
|
774
786
|
|
|
775
|
-
if (isStatePostGloas(prepareState)
|
|
787
|
+
if (isStatePostGloas(prepareState)) {
|
|
788
|
+
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
776
789
|
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
777
790
|
// already deducted from CL balances but never credited on the EL (the envelope
|
|
778
791
|
// was not delivered). The next payload must carry those same withdrawals to
|
|
779
792
|
// restore CL/EL consistency, otherwise validators permanently lose that balance.
|
|
780
|
-
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
781
|
-
prepareState.
|
|
793
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals = isExtendingPayload
|
|
794
|
+
? prepareState.getExpectedWithdrawals().expectedWithdrawals
|
|
795
|
+
: prepareState.payloadExpectedWithdrawals;
|
|
782
796
|
} else {
|
|
783
797
|
// withdrawals logic is now fork aware as it changes on electra fork post capella
|
|
784
798
|
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|