@lodestar/beacon-node 1.44.0-dev.1a8c38ee36 → 1.44.0-dev.1b4e24f1ee
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/beacon/blocks/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/blocks/index.js +43 -5
- package/lib/api/impl/beacon/blocks/index.js.map +1 -1
- package/lib/api/impl/beacon/pool/index.d.ts.map +1 -1
- package/lib/api/impl/beacon/pool/index.js +1 -1
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +1 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +2 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +69 -12
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.d.ts.map +1 -1
- package/lib/api/impl/lodestar/index.js +28 -0
- 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 +97 -45
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/archiveStore/archiveStore.d.ts +0 -1
- package/lib/chain/archiveStore/archiveStore.d.ts.map +1 -1
- package/lib/chain/archiveStore/archiveStore.js +0 -4
- package/lib/chain/archiveStore/archiveStore.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +5 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +1 -1
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +10 -2
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/errors/payloadAttestation.d.ts +6 -0
- package/lib/chain/errors/payloadAttestation.d.ts.map +1 -1
- package/lib/chain/errors/payloadAttestation.js +1 -0
- package/lib/chain/errors/payloadAttestation.js.map +1 -1
- package/lib/chain/forkChoice/index.d.ts +4 -4
- package/lib/chain/forkChoice/index.d.ts.map +1 -1
- package/lib/chain/forkChoice/index.js +7 -7
- package/lib/chain/forkChoice/index.js.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
- package/lib/chain/options.d.ts.map +1 -1
- package/lib/chain/options.js +1 -0
- package/lib/chain/options.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +2 -1
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +59 -16
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -1
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts +0 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +0 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.d.ts.map +1 -1
- package/lib/chain/stateCache/fifoBlockStateCache.js +0 -5
- package/lib/chain/stateCache/fifoBlockStateCache.js.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts +1 -4
- package/lib/chain/stateCache/persistentCheckpointsCache.d.ts.map +1 -1
- package/lib/chain/stateCache/persistentCheckpointsCache.js +5 -2
- package/lib/chain/stateCache/persistentCheckpointsCache.js.map +1 -1
- package/lib/chain/stateCache/types.d.ts +0 -2
- package/lib/chain/stateCache/types.d.ts.map +1 -1
- package/lib/chain/stateCache/types.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +34 -7
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +0 -2
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +24 -4
- package/lib/chain/validation/payloadAttestationMessage.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +19 -766
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/interface.d.ts +1 -0
- package/lib/network/interface.d.ts.map +1 -1
- package/lib/network/network.d.ts +1 -0
- package/lib/network/network.d.ts.map +1 -1
- package/lib/network/network.js +5 -0
- package/lib/network/network.js.map +1 -1
- package/lib/network/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +2 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/index.d.ts +2 -2
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +24 -22
- package/lib/network/processor/index.js.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +9 -5
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +13 -3
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +16 -6
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts +2 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js +15 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRoot.js.map +1 -1
- package/lib/network/reqresp/handlers/index.js +4 -4
- package/lib/network/reqresp/handlers/index.js.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +22 -3
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/util/dataColumns.d.ts.map +1 -1
- package/lib/util/dataColumns.js +16 -11
- package/lib/util/dataColumns.js.map +1 -1
- package/package.json +14 -16
- package/src/api/impl/beacon/blocks/index.ts +49 -5
- package/src/api/impl/beacon/pool/index.ts +3 -1
- package/src/api/impl/config/constants.ts +2 -0
- package/src/api/impl/debug/index.ts +73 -12
- package/src/api/impl/lodestar/index.ts +30 -0
- package/src/api/impl/validator/index.ts +108 -47
- package/src/chain/archiveStore/archiveStore.ts +0 -5
- package/src/chain/blocks/importBlock.ts +10 -2
- package/src/chain/blocks/importExecutionPayload.ts +1 -1
- package/src/chain/chain.ts +12 -1
- package/src/chain/errors/executionPayloadBid.ts +2 -0
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/forkChoice/index.ts +8 -0
- package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
- package/src/chain/options.ts +1 -0
- package/src/chain/prepareNextSlot.ts +2 -1
- package/src/chain/produceBlock/produceBlockBody.ts +83 -18
- package/src/chain/regen/interface.ts +2 -1
- package/src/chain/regen/queued.ts +0 -5
- package/src/chain/stateCache/fifoBlockStateCache.ts +0 -6
- package/src/chain/stateCache/persistentCheckpointsCache.ts +6 -2
- package/src/chain/stateCache/types.ts +0 -2
- package/src/chain/validation/executionPayloadBid.ts +36 -7
- package/src/chain/validation/executionPayloadEnvelope.ts +0 -2
- package/src/chain/validation/payloadAttestationMessage.ts +26 -4
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +4 -2
- package/src/network/processor/index.ts +26 -26
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +12 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -3
- package/src/network/reqresp/handlers/dataColumnSidecarsByRoot.ts +1 -1
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +22 -6
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRoot.ts +20 -1
- package/src/network/reqresp/handlers/index.ts +4 -4
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +21 -3
- package/src/util/dataColumns.ts +17 -12
|
@@ -914,20 +914,38 @@ export function getValidatorApi(
|
|
|
914
914
|
notWhileSyncing();
|
|
915
915
|
await waitForSlot(slot);
|
|
916
916
|
|
|
917
|
-
// TODO GLOAS: support producing blocks from builder bids
|
|
918
|
-
const source = ProducedBlockSource.engine;
|
|
919
|
-
|
|
920
|
-
// TODO GLOAS: needs to be updated after fork choice changes are merged
|
|
921
917
|
const parentBlock = chain.getProposerHead(slot);
|
|
922
918
|
const {blockRoot: parentBlockRootHex, slot: parentSlot} = parentBlock;
|
|
923
919
|
const parentBlockRoot = fromHex(parentBlockRootHex);
|
|
924
920
|
notOnOutOfRangeData(parentBlockRoot);
|
|
925
921
|
metrics?.blockProductionSlotDelta.set(slot - parentSlot);
|
|
926
|
-
metrics?.blockProductionRequests.inc({source});
|
|
927
922
|
|
|
928
923
|
const graffitiBytes = toGraffitiBytes(
|
|
929
924
|
graffiti ?? getDefaultGraffiti(getLodestarClientVersion(opts), chain.executionEngine.clientVersion, opts)
|
|
930
925
|
);
|
|
926
|
+
|
|
927
|
+
// TODO GLOAS: respect builderSelection (MaxProfit, BuilderAlways, ExecutionAlways, etc.) to let
|
|
928
|
+
// the user control bid source preferences and value comparison. Also add external builder api
|
|
929
|
+
// support when it is implemented.
|
|
930
|
+
const isBuildingOnFull = chain.forkChoice.shouldBuildOnFull(parentBlock, slot);
|
|
931
|
+
const bidParentBlockHash = isBuildingOnFull ? parentBlock.executionPayloadBlockHash : parentBlock.parentBlockHash;
|
|
932
|
+
const builderBid = chain.executionPayloadBidPool.getBestBid(slot, bidParentBlockHash, parentBlockRootHex);
|
|
933
|
+
|
|
934
|
+
const logCtx = {
|
|
935
|
+
slot,
|
|
936
|
+
parentSlot,
|
|
937
|
+
parentBlockRoot: parentBlockRootHex,
|
|
938
|
+
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
939
|
+
fork,
|
|
940
|
+
...(builderBid !== null
|
|
941
|
+
? {
|
|
942
|
+
bidValue: builderBid.message.value,
|
|
943
|
+
builderIndex: builderBid.message.builderIndex,
|
|
944
|
+
bidBlockHash: toRootHex(builderBid.message.blockHash),
|
|
945
|
+
}
|
|
946
|
+
: {}),
|
|
947
|
+
};
|
|
948
|
+
|
|
931
949
|
const commonBlockBodyPromise = chain.produceCommonBlockBody({
|
|
932
950
|
slot,
|
|
933
951
|
parentBlock,
|
|
@@ -935,44 +953,76 @@ export function getValidatorApi(
|
|
|
935
953
|
graffiti: graffitiBytes,
|
|
936
954
|
});
|
|
937
955
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
feeRecipient,
|
|
947
|
-
commonBlockBodyPromise,
|
|
948
|
-
});
|
|
956
|
+
const baseAttrs = {
|
|
957
|
+
slot,
|
|
958
|
+
parentBlock,
|
|
959
|
+
randaoReveal,
|
|
960
|
+
graffiti: graffitiBytes,
|
|
961
|
+
feeRecipient,
|
|
962
|
+
commonBlockBodyPromise,
|
|
963
|
+
};
|
|
949
964
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
metrics?.
|
|
953
|
-
|
|
965
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
|
|
966
|
+
if (builderBid !== null) {
|
|
967
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
|
|
968
|
+
}
|
|
954
969
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
970
|
+
const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
|
|
971
|
+
const t = metrics?.blockProductionTime.startTimer();
|
|
972
|
+
return fn().finally(() => t?.({source}));
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
// Always build local block. If builder bid available, also build with it in parallel and prefer it.
|
|
976
|
+
const [engineResult, bidResult] = await Promise.allSettled([
|
|
977
|
+
timed(ProducedBlockSource.engine, () => chain.produceBlock(baseAttrs)),
|
|
978
|
+
builderBid !== null
|
|
979
|
+
? timed(ProducedBlockSource.builder, () => chain.produceBlock({...baseAttrs, builderBid}))
|
|
980
|
+
: Promise.reject(),
|
|
981
|
+
]);
|
|
982
|
+
|
|
983
|
+
let bestResult: typeof engineResult | null = null;
|
|
984
|
+
let source: ProducedBlockSource = ProducedBlockSource.engine;
|
|
985
|
+
if (builderBid !== null && bidResult.status === "fulfilled") {
|
|
986
|
+
source = ProducedBlockSource.builder;
|
|
987
|
+
bestResult = bidResult;
|
|
988
|
+
logger.info("Selected builder bid block", logCtx);
|
|
989
|
+
} else if (engineResult.status === "fulfilled") {
|
|
990
|
+
source = ProducedBlockSource.engine;
|
|
991
|
+
bestResult = engineResult;
|
|
992
|
+
if (builderBid !== null) {
|
|
993
|
+
logger.warn("Builder bid block production failed, using local block", logCtx);
|
|
964
994
|
}
|
|
995
|
+
}
|
|
965
996
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
|
|
997
|
+
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
998
|
+
const engineReason = engineResult.status === "rejected" ? engineResult.reason : undefined;
|
|
999
|
+
const bidReason = builderBid !== null && bidResult.status === "rejected" ? bidResult.reason : undefined;
|
|
1000
|
+
logger.error("Block production failed", {...logCtx, engineReason, bidReason});
|
|
1001
|
+
throw Error(`Block production failed: engine=${engineReason ?? "n/a"} builder=${bidReason ?? "n/a"}`);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
const {block, executionPayloadValue, consensusBlockValue} = bestResult.value;
|
|
1005
|
+
|
|
1006
|
+
metrics?.blockProductionSuccess.inc({source});
|
|
1007
|
+
metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length);
|
|
1008
|
+
metrics?.blockProductionConsensusBlockValue.observe({source}, Number(formatWeiToEth(consensusBlockValue)));
|
|
1009
|
+
metrics?.blockProductionExecutionPayloadValue.observe({source}, Number(formatWeiToEth(executionPayloadValue)));
|
|
1010
|
+
|
|
1011
|
+
const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block));
|
|
1012
|
+
logger.verbose("Produced block", {
|
|
1013
|
+
...logCtx,
|
|
1014
|
+
executionPayloadValue,
|
|
1015
|
+
consensusBlockValue,
|
|
1016
|
+
root: blockRoot,
|
|
1017
|
+
});
|
|
1018
|
+
if (chain.opts.persistProducedBlocks) {
|
|
1019
|
+
void chain.persistBlock(block, "produced_engine_block");
|
|
975
1020
|
}
|
|
1021
|
+
|
|
1022
|
+
return {
|
|
1023
|
+
data: block as gloas.BeaconBlock,
|
|
1024
|
+
meta: {version: fork, consensusBlockValue},
|
|
1025
|
+
};
|
|
976
1026
|
},
|
|
977
1027
|
|
|
978
1028
|
async produceAttestationData({committeeIndex, slot}) {
|
|
@@ -1060,23 +1110,34 @@ export function getValidatorApi(
|
|
|
1060
1110
|
notWhileSyncing();
|
|
1061
1111
|
await waitForSlot(slot);
|
|
1062
1112
|
|
|
1063
|
-
const block = chain.forkChoice.
|
|
1113
|
+
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
1064
1114
|
if (!block) {
|
|
1065
|
-
|
|
1115
|
+
// No block is seen at slot. Return 404 so vc can skip casting payload attestation.
|
|
1116
|
+
throw new ApiError(404, `No canonical block found at slot=${slot}`);
|
|
1066
1117
|
}
|
|
1067
1118
|
|
|
1068
|
-
const blockIsForSlot = block.slot === slot;
|
|
1069
1119
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
1070
1120
|
// Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
|
|
1071
1121
|
// into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
|
|
1072
1122
|
// the input's creation time.
|
|
1073
1123
|
const payloadDueSec = config.getPayloadDueMs() / 1000;
|
|
1074
|
-
const
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
const blobDataAvailable =
|
|
1124
|
+
const payloadSeenSec =
|
|
1125
|
+
payloadInput?.hasPayloadEnvelope() === true
|
|
1126
|
+
? chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec)
|
|
1127
|
+
: null;
|
|
1128
|
+
const payloadPresent = payloadSeenSec !== null && payloadSeenSec < payloadDueSec;
|
|
1129
|
+
const blobDataAvailable = payloadInput?.hasAllData() === true;
|
|
1130
|
+
|
|
1131
|
+
logger.debug("Produced payload attestation data", {
|
|
1132
|
+
slot,
|
|
1133
|
+
blockRoot: block.blockRoot,
|
|
1134
|
+
blockSlot: block.slot,
|
|
1135
|
+
payloadPresent,
|
|
1136
|
+
blobDataAvailable,
|
|
1137
|
+
hasPayloadInput: payloadInput !== undefined,
|
|
1138
|
+
payloadSeenSec,
|
|
1139
|
+
payloadDueSec,
|
|
1140
|
+
});
|
|
1080
1141
|
|
|
1081
1142
|
return {
|
|
1082
1143
|
data: {
|
|
@@ -30,7 +30,6 @@ export enum ArchiveStoreTask {
|
|
|
30
30
|
PruneHistory = "prune_history",
|
|
31
31
|
OnFinalizedCheckpoint = "on_finalized_checkpoint",
|
|
32
32
|
MaybeArchiveState = "maybe_archive_state",
|
|
33
|
-
RegenPruneOnFinalized = "regen_prune_on_finalized",
|
|
34
33
|
ForkchoicePrune = "forkchoice_prune",
|
|
35
34
|
UpdateBackfillRange = "update_backfill_range",
|
|
36
35
|
}
|
|
@@ -229,10 +228,6 @@ export class ArchiveStore {
|
|
|
229
228
|
await this.statesArchiverStrategy.maybeArchiveState(finalized, this.metrics);
|
|
230
229
|
timer?.({source: ArchiveStoreTask.MaybeArchiveState});
|
|
231
230
|
|
|
232
|
-
timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
|
|
233
|
-
this.chain.regen.pruneOnFinalized(finalizedEpoch);
|
|
234
|
-
timer?.({source: ArchiveStoreTask.RegenPruneOnFinalized});
|
|
235
|
-
|
|
236
231
|
// tasks rely on extended fork choice
|
|
237
232
|
timer = this.metrics?.processFinalizedCheckpoint.durationByTask.startTimer();
|
|
238
233
|
const prunedBlocks = this.chain.forkChoice.prune(finalized.rootHex);
|
|
@@ -116,13 +116,19 @@ export async function importBlock(
|
|
|
116
116
|
}
|
|
117
117
|
executionStatus = parentBlock.executionStatus;
|
|
118
118
|
}
|
|
119
|
+
|
|
120
|
+
// getBeaconProposerOrNull will return null if head state is more than one epoch away
|
|
121
|
+
// from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
|
|
122
|
+
const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);
|
|
123
|
+
|
|
119
124
|
const blockSummary = this.forkChoice.onBlock(
|
|
120
125
|
block.message,
|
|
121
126
|
postState,
|
|
122
127
|
blockDelaySec,
|
|
123
128
|
currentSlot,
|
|
124
129
|
executionStatus,
|
|
125
|
-
dataAvailabilityStatus
|
|
130
|
+
dataAvailabilityStatus,
|
|
131
|
+
expectedProposerIndex
|
|
126
132
|
);
|
|
127
133
|
|
|
128
134
|
// This adds the state necessary to process the next block
|
|
@@ -257,8 +263,10 @@ export async function importBlock(
|
|
|
257
263
|
if (ptcIndices.length > 0) {
|
|
258
264
|
this.forkChoice.notifyPtcMessages(
|
|
259
265
|
toRootHex(payloadAttestation.data.beaconBlockRoot),
|
|
266
|
+
payloadAttestation.data.slot,
|
|
260
267
|
ptcIndices,
|
|
261
|
-
payloadAttestation.data.payloadPresent
|
|
268
|
+
payloadAttestation.data.payloadPresent,
|
|
269
|
+
payloadAttestation.data.blobDataAvailable
|
|
262
270
|
);
|
|
263
271
|
}
|
|
264
272
|
} catch (e) {
|
|
@@ -129,7 +129,7 @@ export async function importExecutionPayload(
|
|
|
129
129
|
|
|
130
130
|
// 3. Regenerate state for envelope verification
|
|
131
131
|
const blockState = await this.regen
|
|
132
|
-
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.
|
|
132
|
+
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.importExecutionPayload)
|
|
133
133
|
.catch(() =>
|
|
134
134
|
// only happen at the 1st batch of skipped slot checkpoint sync
|
|
135
135
|
this.regen.getClosestHeadState(protoBlock)
|
package/src/chain/chain.ts
CHANGED
|
@@ -2,7 +2,7 @@ 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 {CheckpointWithHex, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
|
|
5
|
+
import {CheckpointWithHex, ForkChoiceStateGetter, IForkChoice, ProtoBlock, UpdateHeadOpt} from "@lodestar/fork-choice";
|
|
6
6
|
import {LoggerNode} from "@lodestar/logger/node";
|
|
7
7
|
import {
|
|
8
8
|
EFFECTIVE_BALANCE_INCREMENT,
|
|
@@ -382,6 +382,14 @@ export class BeaconChain implements IBeaconChain {
|
|
|
382
382
|
blockStateCache.setHeadState(anchorState);
|
|
383
383
|
checkpointStateCache.add(checkpoint, anchorState);
|
|
384
384
|
|
|
385
|
+
const forkChoiceStateGetter: ForkChoiceStateGetter = ({stateRoot, checkpoint}) => {
|
|
386
|
+
if (stateRoot) return blockStateCache.get(stateRoot);
|
|
387
|
+
|
|
388
|
+
if (checkpoint) return checkpointStateCache.get({epoch: checkpoint.epoch, rootHex: checkpoint.rootHex});
|
|
389
|
+
|
|
390
|
+
return null;
|
|
391
|
+
};
|
|
392
|
+
|
|
385
393
|
const forkChoice = initializeForkChoice(
|
|
386
394
|
config,
|
|
387
395
|
emitter,
|
|
@@ -390,6 +398,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
390
398
|
isAnchorStateFinalized,
|
|
391
399
|
opts,
|
|
392
400
|
this.justifiedBalancesGetter.bind(this),
|
|
401
|
+
forkChoiceStateGetter,
|
|
393
402
|
metrics,
|
|
394
403
|
logger
|
|
395
404
|
);
|
|
@@ -1049,6 +1058,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1049
1058
|
feeRecipient,
|
|
1050
1059
|
commonBlockBodyPromise,
|
|
1051
1060
|
parentBlock,
|
|
1061
|
+
builderBid,
|
|
1052
1062
|
}: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}
|
|
1053
1063
|
): Promise<{
|
|
1054
1064
|
block: AssembledBlockType<T>;
|
|
@@ -1078,6 +1088,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1078
1088
|
proposerIndex,
|
|
1079
1089
|
proposerPubKey,
|
|
1080
1090
|
commonBlockBodyPromise,
|
|
1091
|
+
builderBid,
|
|
1081
1092
|
}
|
|
1082
1093
|
);
|
|
1083
1094
|
|
|
@@ -11,6 +11,7 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
11
11
|
UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
12
12
|
UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
|
|
13
13
|
INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
|
|
14
|
+
NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
|
|
14
15
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
|
|
15
16
|
NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
|
|
16
17
|
PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
|
|
@@ -41,6 +42,7 @@ export type ExecutionPayloadBidErrorType =
|
|
|
41
42
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
|
|
42
43
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
|
|
43
44
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
|
|
45
|
+
| {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
44
46
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
|
|
45
47
|
| {
|
|
46
48
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
|
|
@@ -5,6 +5,7 @@ export enum PayloadAttestationErrorCode {
|
|
|
5
5
|
NOT_CURRENT_SLOT = "PAYLOAD_ATTESTATION_ERROR_NOT_CURRENT_SLOT",
|
|
6
6
|
PAYLOAD_ATTESTATION_ALREADY_KNOWN = "PAYLOAD_ATTESTATION_ERROR_PAYLOAD_ATTESTATION_ALREADY_KNOWN",
|
|
7
7
|
UNKNOWN_BLOCK_ROOT = "PAYLOAD_ATTESTATION_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
8
|
+
INVALID_BLOCK_SLOT = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK_SLOT",
|
|
8
9
|
INVALID_BLOCK = "PAYLOAD_ATTESTATION_ERROR_INVALID_BLOCK",
|
|
9
10
|
INVALID_ATTESTER = "PAYLOAD_ATTESTATION_ERROR_INVALID_ATTESTER",
|
|
10
11
|
INVALID_SIGNATURE = "PAYLOAD_ATTESTATION_ERROR_INVALID_SIGNATURE",
|
|
@@ -18,6 +19,7 @@ export type PayloadAttestationErrorType =
|
|
|
18
19
|
blockRoot: RootHex;
|
|
19
20
|
}
|
|
20
21
|
| {code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT; blockRoot: RootHex}
|
|
22
|
+
| {code: PayloadAttestationErrorCode.INVALID_BLOCK_SLOT; blockRoot: RootHex; blockSlot: Slot; slot: Slot}
|
|
21
23
|
| {code: PayloadAttestationErrorCode.INVALID_BLOCK; blockRoot: RootHex}
|
|
22
24
|
| {code: PayloadAttestationErrorCode.INVALID_ATTESTER; attesterIndex: ValidatorIndex}
|
|
23
25
|
| {code: PayloadAttestationErrorCode.INVALID_SIGNATURE};
|
|
@@ -2,6 +2,7 @@ import {ChainForkConfig} from "@lodestar/config";
|
|
|
2
2
|
import {
|
|
3
3
|
ExecutionStatus,
|
|
4
4
|
ForkChoice,
|
|
5
|
+
ForkChoiceStateGetter,
|
|
5
6
|
ForkChoiceStore,
|
|
6
7
|
JustifiedBalancesGetter,
|
|
7
8
|
PayloadStatus,
|
|
@@ -45,6 +46,7 @@ export function initializeForkChoice(
|
|
|
45
46
|
isFinalizedState: boolean,
|
|
46
47
|
opts: ForkChoiceOpts,
|
|
47
48
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
49
|
+
stateGetter: ForkChoiceStateGetter,
|
|
48
50
|
metrics: Metrics | null,
|
|
49
51
|
logger?: Logger
|
|
50
52
|
): ForkChoice {
|
|
@@ -56,6 +58,7 @@ export function initializeForkChoice(
|
|
|
56
58
|
state,
|
|
57
59
|
opts,
|
|
58
60
|
justifiedBalancesGetter,
|
|
61
|
+
stateGetter,
|
|
59
62
|
metrics,
|
|
60
63
|
logger
|
|
61
64
|
)
|
|
@@ -66,6 +69,7 @@ export function initializeForkChoice(
|
|
|
66
69
|
state,
|
|
67
70
|
opts,
|
|
68
71
|
justifiedBalancesGetter,
|
|
72
|
+
stateGetter,
|
|
69
73
|
metrics,
|
|
70
74
|
logger
|
|
71
75
|
);
|
|
@@ -81,6 +85,7 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
81
85
|
state: IBeaconStateView,
|
|
82
86
|
opts: ForkChoiceOpts,
|
|
83
87
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
88
|
+
stateGetter: ForkChoiceStateGetter,
|
|
84
89
|
metrics: Metrics | null,
|
|
85
90
|
logger?: Logger
|
|
86
91
|
): ForkChoice {
|
|
@@ -112,6 +117,7 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
112
117
|
finalizedCheckpoint,
|
|
113
118
|
justifiedBalances,
|
|
114
119
|
justifiedBalancesGetter,
|
|
120
|
+
stateGetter,
|
|
115
121
|
{
|
|
116
122
|
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
|
|
117
123
|
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
|
|
@@ -172,6 +178,7 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
172
178
|
unfinalizedState: IBeaconStateView,
|
|
173
179
|
opts: ForkChoiceOpts,
|
|
174
180
|
justifiedBalancesGetter: JustifiedBalancesGetter,
|
|
181
|
+
stateGetter: ForkChoiceStateGetter,
|
|
175
182
|
metrics: Metrics | null,
|
|
176
183
|
logger?: Logger
|
|
177
184
|
): ForkChoice {
|
|
@@ -203,6 +210,7 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
203
210
|
finalizedCheckpoint,
|
|
204
211
|
justifiedBalances,
|
|
205
212
|
justifiedBalancesGetter,
|
|
213
|
+
stateGetter,
|
|
206
214
|
{
|
|
207
215
|
onJustified: (cp) => emitter.emit(ChainEvent.forkChoiceJustified, cp),
|
|
208
216
|
onFinalized: (cp) => emitter.emit(ChainEvent.forkChoiceFinalized, cp),
|
|
@@ -12,13 +12,13 @@ type BlockRootHex = string;
|
|
|
12
12
|
type BlockHashHex = string;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Store the best execution payload bid per slot / (parent block root, parent block hash).
|
|
15
|
+
* Store the best signed execution payload bid per slot / (parent block root, parent block hash).
|
|
16
16
|
*/
|
|
17
17
|
export class ExecutionPayloadBidPool {
|
|
18
18
|
private readonly bidByParentHashByParentRootBySlot = new MapDef<
|
|
19
19
|
Slot,
|
|
20
|
-
MapDef<BlockRootHex, Map<BlockHashHex, gloas.
|
|
21
|
-
>(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.
|
|
20
|
+
MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>
|
|
21
|
+
>(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>(() => new Map()));
|
|
22
22
|
private lowestPermissibleSlot = 0;
|
|
23
23
|
|
|
24
24
|
get size(): number {
|
|
@@ -31,8 +31,8 @@ export class ExecutionPayloadBidPool {
|
|
|
31
31
|
return count;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
add(bid: gloas.
|
|
35
|
-
const {slot, parentBlockRoot, parentBlockHash, value} = bid;
|
|
34
|
+
add(bid: gloas.SignedExecutionPayloadBid): InsertOutcome {
|
|
35
|
+
const {slot, parentBlockRoot, parentBlockHash, value} = bid.message;
|
|
36
36
|
const lowestPermissibleSlot = this.lowestPermissibleSlot;
|
|
37
37
|
|
|
38
38
|
if (slot < lowestPermissibleSlot) {
|
|
@@ -45,7 +45,7 @@ export class ExecutionPayloadBidPool {
|
|
|
45
45
|
const existing = bidByParentHash.get(parentHashHex);
|
|
46
46
|
|
|
47
47
|
if (existing) {
|
|
48
|
-
const existingValue = existing.value;
|
|
48
|
+
const existingValue = existing.message.value;
|
|
49
49
|
const newValue = value;
|
|
50
50
|
if (newValue > existingValue) {
|
|
51
51
|
bidByParentHash.set(parentHashHex, bid);
|
|
@@ -59,14 +59,15 @@ export class ExecutionPayloadBidPool {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Return the highest-value bid matching slot, parent block hash, and parent block root.
|
|
62
|
+
* Return the highest-value signed bid matching slot, parent block hash, and parent block root.
|
|
63
63
|
* Used for gossip validation and block production.
|
|
64
64
|
*/
|
|
65
65
|
getBestBid(
|
|
66
66
|
slot: Slot,
|
|
67
|
-
parentBlockHash: BlockHashHex,
|
|
67
|
+
parentBlockHash: BlockHashHex | null,
|
|
68
68
|
parentBlockRoot: BlockRootHex
|
|
69
|
-
): gloas.
|
|
69
|
+
): gloas.SignedExecutionPayloadBid | null {
|
|
70
|
+
if (parentBlockHash === null) return null;
|
|
70
71
|
const bidByParentHash = this.bidByParentHashByParentRootBySlot.get(slot)?.get(parentBlockRoot);
|
|
71
72
|
return bidByParentHash?.get(parentBlockHash) ?? null;
|
|
72
73
|
}
|
package/src/chain/options.ts
CHANGED
|
@@ -105,6 +105,7 @@ export const defaultChainOptions: IChainOptions = {
|
|
|
105
105
|
proposerBoost: true,
|
|
106
106
|
proposerBoostReorg: true,
|
|
107
107
|
computeUnrealized: true,
|
|
108
|
+
fastConfirmation: false,
|
|
108
109
|
suggestedFeeRecipient: defaultValidatorOptions.suggestedFeeRecipient,
|
|
109
110
|
serveHistoricalState: false,
|
|
110
111
|
assertCorrectProgressiveBalances: false,
|
|
@@ -169,7 +169,8 @@ export class PrepareNextSlotScheduler {
|
|
|
169
169
|
// Apply parent payload once here as it's reused by EL prep and SSE emit below
|
|
170
170
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
|
|
171
171
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
172
|
-
|
|
172
|
+
// Spec: should_build_on_full(store, head) — see produceBlockBody.ts for context.
|
|
173
|
+
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead, prepareSlot)) {
|
|
173
174
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
|
|
174
175
|
// Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
|
|
175
176
|
if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
gloas,
|
|
51
51
|
ssz,
|
|
52
52
|
} from "@lodestar/types";
|
|
53
|
-
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
53
|
+
import {GWEI_TO_WEI, Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
54
54
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
55
55
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
56
56
|
import {IExecutionBuilder, IExecutionEngine, PayloadAttributes, PayloadId} from "../../execution/index.js";
|
|
@@ -91,6 +91,8 @@ export type BlockAttributes = {
|
|
|
91
91
|
slot: Slot;
|
|
92
92
|
parentBlock: ProtoBlock;
|
|
93
93
|
feeRecipient?: string;
|
|
94
|
+
/** When provided, build block with this builder bid instead of a self-build bid */
|
|
95
|
+
builderBid?: gloas.SignedExecutionPayloadBid;
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
export enum BlockType {
|
|
@@ -150,6 +152,28 @@ export type ProduceResult =
|
|
|
150
152
|
| ProduceFullPhase0
|
|
151
153
|
| ProduceBlinded;
|
|
152
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Drop voluntary exits that `parent_execution_requests` have invalidated (e.g. a withdrawal
|
|
157
|
+
* request initiating an exit on the same validator). Op pool selected against the unapplied
|
|
158
|
+
* state, so re-validate against the post-apply state to avoid producing an invalid block.
|
|
159
|
+
*
|
|
160
|
+
* `getStateAfterParentPayload` is a thunk so the post-apply state is only materialized when
|
|
161
|
+
* actually needed (i.e. when extending the parent payload and there are exits to filter).
|
|
162
|
+
*/
|
|
163
|
+
function maybeFilterInvalidatedVoluntaryExits(
|
|
164
|
+
commonBlockBody: CommonBlockBody,
|
|
165
|
+
isExtendingPayload: boolean,
|
|
166
|
+
getStateAfterParentPayload: () => IBeaconStateViewBellatrix
|
|
167
|
+
): CommonBlockBody["voluntaryExits"] {
|
|
168
|
+
if (!isExtendingPayload || commonBlockBody.voluntaryExits.length === 0) {
|
|
169
|
+
return commonBlockBody.voluntaryExits;
|
|
170
|
+
}
|
|
171
|
+
const state = getStateAfterParentPayload();
|
|
172
|
+
return commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
|
|
173
|
+
state.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
153
177
|
export async function produceBlockBody<T extends BlockType>(
|
|
154
178
|
this: BeaconChain,
|
|
155
179
|
blockType: T,
|
|
@@ -172,6 +196,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
172
196
|
proposerIndex,
|
|
173
197
|
proposerPubKey,
|
|
174
198
|
commonBlockBodyPromise,
|
|
199
|
+
builderBid,
|
|
175
200
|
} = blockAttr;
|
|
176
201
|
let executionPayloadValue: Wei;
|
|
177
202
|
let blockBody: AssembledBodyType<T>;
|
|
@@ -192,7 +217,43 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
192
217
|
};
|
|
193
218
|
this.logger.verbose("Producing beacon block body", logMeta);
|
|
194
219
|
|
|
195
|
-
if (
|
|
220
|
+
if (builderBid !== undefined) {
|
|
221
|
+
if (!isStatePostGloas(currentState)) {
|
|
222
|
+
throw new Error("Expected Gloas state for builder bid block production");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const isExtendingPayload = byteArrayEquals(
|
|
226
|
+
builderBid.message.parentBlockHash,
|
|
227
|
+
currentState.latestExecutionPayloadBid.blockHash
|
|
228
|
+
);
|
|
229
|
+
const parentExecutionRequests = isExtendingPayload
|
|
230
|
+
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
231
|
+
: ssz.electra.ExecutionRequests.defaultValue();
|
|
232
|
+
executionPayloadValue = BigInt(builderBid.message.value) * GWEI_TO_WEI;
|
|
233
|
+
|
|
234
|
+
const commonBlockBody = await commonBlockBodyPromise;
|
|
235
|
+
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
236
|
+
gloasBody.signedExecutionPayloadBid = builderBid;
|
|
237
|
+
gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
|
|
238
|
+
parentBlock.blockRoot,
|
|
239
|
+
blockSlot - 1
|
|
240
|
+
);
|
|
241
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
242
|
+
gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(commonBlockBody, isExtendingPayload, () =>
|
|
243
|
+
currentState.withParentPayloadApplied(parentExecutionRequests)
|
|
244
|
+
);
|
|
245
|
+
blockBody = gloasBody as AssembledBodyType<T>;
|
|
246
|
+
|
|
247
|
+
this.logger.verbose("Produced block with builder bid", {
|
|
248
|
+
slot: blockSlot,
|
|
249
|
+
builderIndex: builderBid.message.builderIndex,
|
|
250
|
+
bidValue: builderBid.message.value,
|
|
251
|
+
parentBlockHash: toRootHex(builderBid.message.parentBlockHash),
|
|
252
|
+
parentBlockRoot: toRootHex(builderBid.message.parentBlockRoot),
|
|
253
|
+
blockHash: toRootHex(builderBid.message.blockHash),
|
|
254
|
+
isExtendingPayload,
|
|
255
|
+
});
|
|
256
|
+
} else if (isForkPostGloas(fork)) {
|
|
196
257
|
if (!isStatePostGloas(currentState)) {
|
|
197
258
|
throw new Error("Expected Gloas state for Gloas block production");
|
|
198
259
|
}
|
|
@@ -209,19 +270,16 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
209
270
|
|
|
210
271
|
const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
|
|
211
272
|
|
|
212
|
-
this.logger.verbose("Preparing execution payload from engine", {
|
|
213
|
-
slot: blockSlot,
|
|
214
|
-
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
215
|
-
feeRecipient,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
273
|
// Get execution payload from EL
|
|
219
274
|
let parentBlockHash: Bytes32;
|
|
220
275
|
let parentExecutionRequests: electra.ExecutionRequests;
|
|
221
276
|
// Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
|
|
222
277
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
|
|
223
|
-
|
|
224
|
-
|
|
278
|
+
// Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
|
|
279
|
+
// (set by chain.getProposerHead(slot)). Returns false when the PTC majority signalled
|
|
280
|
+
// the blob data is not available or the payload was not timely, forcing a build on EMPTY (reorg).
|
|
281
|
+
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock, blockSlot);
|
|
282
|
+
if (isBuildingOnFull) {
|
|
225
283
|
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
226
284
|
parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
|
|
227
285
|
stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
|
|
@@ -244,6 +302,16 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
244
302
|
const {prepType, payloadId} = prepareRes;
|
|
245
303
|
Object.assign(logMeta, {executionPayloadPrepType: prepType});
|
|
246
304
|
|
|
305
|
+
this.logger.verbose("Prepared execution payload from engine", {
|
|
306
|
+
slot: blockSlot,
|
|
307
|
+
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
308
|
+
parentBlockHash: toRootHex(parentBlockHash),
|
|
309
|
+
feeRecipient,
|
|
310
|
+
prepType,
|
|
311
|
+
payloadId,
|
|
312
|
+
isBuildingOnFull,
|
|
313
|
+
});
|
|
314
|
+
|
|
247
315
|
if (prepType !== PayloadPreparationType.Cached) {
|
|
248
316
|
await sleep(PAYLOAD_GENERATION_TIME_MS);
|
|
249
317
|
}
|
|
@@ -297,14 +365,11 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
297
365
|
blockSlot - 1
|
|
298
366
|
);
|
|
299
367
|
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
306
|
-
);
|
|
307
|
-
}
|
|
368
|
+
gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(
|
|
369
|
+
commonBlockBody,
|
|
370
|
+
isBuildingOnFull,
|
|
371
|
+
() => stateAfterParentPayload
|
|
372
|
+
);
|
|
308
373
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
309
374
|
|
|
310
375
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -17,10 +17,12 @@ export enum RegenCaller {
|
|
|
17
17
|
predictProposerHead = "predictProposerHead",
|
|
18
18
|
produceAttestationData = "produceAttestationData",
|
|
19
19
|
processBlocksInEpoch = "processBlocksInEpoch",
|
|
20
|
+
importExecutionPayload = "importExecutionPayload",
|
|
20
21
|
validateGossipAggregateAndProof = "validateGossipAggregateAndProof",
|
|
21
22
|
validateGossipAttestation = "validateGossipAttestation",
|
|
22
23
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
23
24
|
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
25
|
+
validateGossipPayloadAttestationMessage = "validateGossipPayloadAttestationMessage",
|
|
24
26
|
validateGossipProposerPreferences = "validateGossipProposerPreferences",
|
|
25
27
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
26
28
|
restApi = "restApi",
|
|
@@ -45,7 +47,6 @@ export interface IStateRegenerator extends IStateRegeneratorInternal {
|
|
|
45
47
|
getCheckpointStateSync(cp: CheckpointHex): IBeaconStateView | null;
|
|
46
48
|
getClosestHeadState(head: ProtoBlock): IBeaconStateView | null;
|
|
47
49
|
pruneOnCheckpoint(finalizedEpoch: Epoch, justifiedEpoch: Epoch, headStateRoot: RootHex): void;
|
|
48
|
-
pruneOnFinalized(finalizedEpoch: Epoch): void;
|
|
49
50
|
processState(blockRootHex: RootHex, postState: IBeaconStateView): void;
|
|
50
51
|
addCheckpointState(cp: phase0.Checkpoint, item: IBeaconStateView): void;
|
|
51
52
|
updateHeadState(newHead: ProtoBlock, maybeHeadState: IBeaconStateView): void;
|