@lodestar/beacon-node 1.44.0-dev.1d0e0b9081 → 1.44.0-dev.2defe36dd3
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 +30 -0
- 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/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +82 -44
- package/lib/api/impl/validator/index.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.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +5 -3
- 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 +2 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +11 -1
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +2 -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.map +1 -1
- package/lib/chain/forkChoice/index.js +14 -4
- 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/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 +78 -24
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -0
- 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/validation/executionPayloadBid.d.ts +7 -3
- package/lib/chain/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +58 -15
- package/lib/chain/validation/executionPayloadBid.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/chain/validatorMonitor.d.ts +1 -0
- package/lib/chain/validatorMonitor.d.ts.map +1 -1
- package/lib/chain/validatorMonitor.js +16 -0
- package/lib/chain/validatorMonitor.js.map +1 -1
- package/lib/execution/builder/index.d.ts +1 -2
- package/lib/execution/builder/index.d.ts.map +1 -1
- package/lib/execution/builder/index.js +0 -1
- package/lib/execution/builder/index.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -1
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -3
- package/lib/metrics/metrics/lodestar.js.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 +12 -4
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/blocks/index.ts +36 -0
- package/src/api/impl/beacon/pool/index.ts +3 -1
- package/src/api/impl/validator/index.ts +95 -45
- package/src/chain/blocks/importBlock.ts +10 -2
- package/src/chain/blocks/importExecutionPayload.ts +8 -2
- package/src/chain/chain.ts +2 -0
- package/src/chain/errors/executionPayloadBid.ts +6 -1
- package/src/chain/errors/payloadAttestation.ts +2 -0
- package/src/chain/forkChoice/index.ts +14 -4
- package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
- package/src/chain/prepareNextSlot.ts +2 -1
- package/src/chain/produceBlock/produceBlockBody.ts +108 -34
- package/src/chain/regen/interface.ts +2 -0
- package/src/chain/validation/executionPayloadBid.ts +66 -19
- package/src/chain/validation/payloadAttestationMessage.ts +26 -4
- package/src/chain/validatorMonitor.ts +18 -0
- package/src/execution/builder/index.ts +1 -4
- package/src/metrics/metrics/lodestar.ts +4 -3
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +17 -4
- package/lib/execution/builder/utils.d.ts +0 -5
- package/lib/execution/builder/utils.d.ts.map +0 -1
- package/lib/execution/builder/utils.js +0 -17
- package/lib/execution/builder/utils.js.map +0 -1
- package/src/execution/builder/utils.ts +0 -19
|
@@ -914,20 +914,40 @@ 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 builderBid = chain.executionPayloadBidPool.getBestBid(
|
|
931
|
+
slot,
|
|
932
|
+
parentBlock.executionPayloadBlockHash,
|
|
933
|
+
parentBlockRootHex
|
|
934
|
+
);
|
|
935
|
+
|
|
936
|
+
const logCtx = {
|
|
937
|
+
slot,
|
|
938
|
+
parentSlot,
|
|
939
|
+
parentBlockRoot: parentBlockRootHex,
|
|
940
|
+
parentBlockHash: parentBlock.executionPayloadBlockHash,
|
|
941
|
+
fork,
|
|
942
|
+
...(builderBid !== null
|
|
943
|
+
? {
|
|
944
|
+
bidValue: builderBid.message.value,
|
|
945
|
+
builderIndex: builderBid.message.builderIndex,
|
|
946
|
+
bidBlockHash: toRootHex(builderBid.message.blockHash),
|
|
947
|
+
}
|
|
948
|
+
: {}),
|
|
949
|
+
};
|
|
950
|
+
|
|
931
951
|
const commonBlockBodyPromise = chain.produceCommonBlockBody({
|
|
932
952
|
slot,
|
|
933
953
|
parentBlock,
|
|
@@ -935,44 +955,76 @@ export function getValidatorApi(
|
|
|
935
955
|
graffiti: graffitiBytes,
|
|
936
956
|
});
|
|
937
957
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
feeRecipient,
|
|
947
|
-
commonBlockBodyPromise,
|
|
948
|
-
});
|
|
958
|
+
const baseAttrs = {
|
|
959
|
+
slot,
|
|
960
|
+
parentBlock,
|
|
961
|
+
randaoReveal,
|
|
962
|
+
graffiti: graffitiBytes,
|
|
963
|
+
feeRecipient,
|
|
964
|
+
commonBlockBodyPromise,
|
|
965
|
+
};
|
|
949
966
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
metrics?.
|
|
953
|
-
|
|
967
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.engine});
|
|
968
|
+
if (builderBid !== null) {
|
|
969
|
+
metrics?.blockProductionRequests.inc({source: ProducedBlockSource.builder});
|
|
970
|
+
}
|
|
954
971
|
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
972
|
+
const timed = <T>(source: ProducedBlockSource, fn: () => Promise<T>): Promise<T> => {
|
|
973
|
+
const t = metrics?.blockProductionTime.startTimer();
|
|
974
|
+
return fn().finally(() => t?.({source}));
|
|
975
|
+
};
|
|
976
|
+
|
|
977
|
+
// Always build local block. If builder bid available, also build with it in parallel and prefer it.
|
|
978
|
+
const [engineResult, bidResult] = await Promise.allSettled([
|
|
979
|
+
timed(ProducedBlockSource.engine, () => chain.produceBlock(baseAttrs)),
|
|
980
|
+
builderBid !== null
|
|
981
|
+
? timed(ProducedBlockSource.builder, () => chain.produceBlock({...baseAttrs, builderBid}))
|
|
982
|
+
: Promise.reject(),
|
|
983
|
+
]);
|
|
984
|
+
|
|
985
|
+
let bestResult: typeof engineResult | null = null;
|
|
986
|
+
let source: ProducedBlockSource = ProducedBlockSource.engine;
|
|
987
|
+
if (builderBid !== null && bidResult.status === "fulfilled") {
|
|
988
|
+
source = ProducedBlockSource.builder;
|
|
989
|
+
bestResult = bidResult;
|
|
990
|
+
logger.info("Selected builder bid block", logCtx);
|
|
991
|
+
} else if (engineResult.status === "fulfilled") {
|
|
992
|
+
source = ProducedBlockSource.engine;
|
|
993
|
+
bestResult = engineResult;
|
|
994
|
+
if (builderBid !== null) {
|
|
995
|
+
logger.warn("Builder bid block production failed, using local block", logCtx);
|
|
964
996
|
}
|
|
997
|
+
}
|
|
965
998
|
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
|
|
999
|
+
if (bestResult === null || bestResult.status !== "fulfilled") {
|
|
1000
|
+
const engineReason = engineResult.status === "rejected" ? engineResult.reason : undefined;
|
|
1001
|
+
const bidReason = builderBid !== null && bidResult.status === "rejected" ? bidResult.reason : undefined;
|
|
1002
|
+
logger.error("Block production failed", {...logCtx, engineReason, bidReason});
|
|
1003
|
+
throw Error(`Block production failed: engine=${engineReason ?? "n/a"} builder=${bidReason ?? "n/a"}`);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const {block, executionPayloadValue, consensusBlockValue} = bestResult.value;
|
|
1007
|
+
|
|
1008
|
+
metrics?.blockProductionSuccess.inc({source});
|
|
1009
|
+
metrics?.blockProductionNumAggregated.observe({source}, block.body.attestations.length);
|
|
1010
|
+
metrics?.blockProductionConsensusBlockValue.observe({source}, Number(formatWeiToEth(consensusBlockValue)));
|
|
1011
|
+
metrics?.blockProductionExecutionPayloadValue.observe({source}, Number(formatWeiToEth(executionPayloadValue)));
|
|
1012
|
+
|
|
1013
|
+
const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block));
|
|
1014
|
+
logger.verbose("Produced block", {
|
|
1015
|
+
...logCtx,
|
|
1016
|
+
executionPayloadValue,
|
|
1017
|
+
consensusBlockValue,
|
|
1018
|
+
root: blockRoot,
|
|
1019
|
+
});
|
|
1020
|
+
if (chain.opts.persistProducedBlocks) {
|
|
1021
|
+
void chain.persistBlock(block, "produced_engine_block");
|
|
975
1022
|
}
|
|
1023
|
+
|
|
1024
|
+
return {
|
|
1025
|
+
data: block as gloas.BeaconBlock,
|
|
1026
|
+
meta: {version: fork, consensusBlockValue},
|
|
1027
|
+
};
|
|
976
1028
|
},
|
|
977
1029
|
|
|
978
1030
|
async produceAttestationData({committeeIndex, slot}) {
|
|
@@ -1060,23 +1112,21 @@ export function getValidatorApi(
|
|
|
1060
1112
|
notWhileSyncing();
|
|
1061
1113
|
await waitForSlot(slot);
|
|
1062
1114
|
|
|
1063
|
-
const block = chain.forkChoice.
|
|
1115
|
+
const block = chain.forkChoice.getCanonicalBlockAtSlot(slot);
|
|
1064
1116
|
if (!block) {
|
|
1065
|
-
|
|
1117
|
+
// No block is seen at slot. Return 404 so vc can skip casting payload attestation.
|
|
1118
|
+
throw new ApiError(404, `No canonical block found at slot=${slot}`);
|
|
1066
1119
|
}
|
|
1067
1120
|
|
|
1068
|
-
const blockIsForSlot = block.slot === slot;
|
|
1069
1121
|
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
1070
1122
|
// Spec: set payload_present only if the envelope was seen before get_payload_due_ms()
|
|
1071
1123
|
// into the slot. Use the envelope's own arrival time (getPayloadEnvelopeSource), not
|
|
1072
1124
|
// the input's creation time.
|
|
1073
1125
|
const payloadDueSec = config.getPayloadDueMs() / 1000;
|
|
1074
1126
|
const payloadPresent =
|
|
1075
|
-
|
|
1076
|
-
payloadInput !== undefined &&
|
|
1077
|
-
payloadInput.hasPayloadEnvelope() &&
|
|
1127
|
+
payloadInput?.hasPayloadEnvelope() === true &&
|
|
1078
1128
|
chain.clock.secFromSlot(slot, payloadInput.getPayloadEnvelopeSource().seenTimestampSec) < payloadDueSec;
|
|
1079
|
-
const blobDataAvailable =
|
|
1129
|
+
const blobDataAvailable = payloadInput?.hasAllData() === true;
|
|
1080
1130
|
|
|
1081
1131
|
return {
|
|
1082
1132
|
data: {
|
|
@@ -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)
|
|
@@ -237,6 +237,7 @@ export async function importExecutionPayload(
|
|
|
237
237
|
blockRootHex,
|
|
238
238
|
blockHashHex,
|
|
239
239
|
envelope.payload.blockNumber,
|
|
240
|
+
envelope.payload.gasLimit,
|
|
240
241
|
execStatus,
|
|
241
242
|
dataAvailabilityStatus
|
|
242
243
|
);
|
|
@@ -254,7 +255,11 @@ export async function importExecutionPayload(
|
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
// 8. Record metrics for payload envelope and column sources
|
|
257
|
-
this.
|
|
258
|
+
const delaySec = this.clock.secFromSlot(slot);
|
|
259
|
+
this.metrics?.importPayload.elapsedTimeTillImported.observe(
|
|
260
|
+
{source: payloadInput.getPayloadEnvelopeSource().source},
|
|
261
|
+
delaySec
|
|
262
|
+
);
|
|
258
263
|
for (const {source} of payloadInput.getSampledColumnsWithSource()) {
|
|
259
264
|
this.metrics?.importPayload.columnsBySource.inc({source});
|
|
260
265
|
}
|
|
@@ -275,6 +280,7 @@ export async function importExecutionPayload(
|
|
|
275
280
|
builderIndex: envelope.builderIndex,
|
|
276
281
|
blockRoot: blockRootHex,
|
|
277
282
|
blockHash: blockHashHex,
|
|
283
|
+
delaySec,
|
|
278
284
|
});
|
|
279
285
|
}
|
|
280
286
|
|
package/src/chain/chain.ts
CHANGED
|
@@ -1049,6 +1049,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1049
1049
|
feeRecipient,
|
|
1050
1050
|
commonBlockBodyPromise,
|
|
1051
1051
|
parentBlock,
|
|
1052
|
+
builderBid,
|
|
1052
1053
|
}: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}
|
|
1053
1054
|
): Promise<{
|
|
1054
1055
|
block: AssembledBlockType<T>;
|
|
@@ -1078,6 +1079,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1078
1079
|
proposerIndex,
|
|
1079
1080
|
proposerPubKey,
|
|
1080
1081
|
commonBlockBodyPromise,
|
|
1082
|
+
builderBid,
|
|
1081
1083
|
}
|
|
1082
1084
|
);
|
|
1083
1085
|
|
|
@@ -9,7 +9,9 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
9
9
|
BID_TOO_HIGH = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_HIGH",
|
|
10
10
|
TOO_MANY_KZG_COMMITMENTS = "EXECUTION_PAYLOAD_BID_ERROR_TOO_MANY_KZG_COMMITMENTS",
|
|
11
11
|
UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
12
|
+
UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
|
|
12
13
|
INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
|
|
14
|
+
NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
|
|
13
15
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
|
|
14
16
|
NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
|
|
15
17
|
PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
|
|
@@ -38,7 +40,9 @@ export type ExecutionPayloadBidErrorType =
|
|
|
38
40
|
commitmentLimit: number;
|
|
39
41
|
}
|
|
40
42
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
|
|
43
|
+
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
|
|
41
44
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
|
|
45
|
+
| {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
42
46
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
|
|
43
47
|
| {
|
|
44
48
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
|
|
@@ -56,7 +60,8 @@ export type ExecutionPayloadBidErrorType =
|
|
|
56
60
|
code: ExecutionPayloadBidErrorCode.PROPOSER_PREFERENCES_GAS_LIMIT_MISMATCH;
|
|
57
61
|
builderIndex: BuilderIndex;
|
|
58
62
|
bidGasLimit: number;
|
|
59
|
-
|
|
63
|
+
parentGasLimit: number;
|
|
64
|
+
targetGasLimit: number;
|
|
60
65
|
};
|
|
61
66
|
|
|
62
67
|
export class ExecutionPayloadBidError extends GossipActionError<ExecutionPayloadBidErrorType> {}
|
|
@@ -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};
|
|
@@ -140,9 +140,11 @@ export function initializeForkChoiceFromFinalizedState(
|
|
|
140
140
|
executionPayloadBlockHash: isStatePostGloas(state)
|
|
141
141
|
? toRootHex(state.latestBlockHash)
|
|
142
142
|
: toRootHex(state.latestExecutionPayloadHeader.blockHash),
|
|
143
|
-
// TODO GLOAS: executionPayloadNumber
|
|
144
|
-
// latestExecutionPayloadHeader). Using 0 as unavailable fallback
|
|
143
|
+
// TODO GLOAS: executionPayloadNumber/GasLimit are not tracked in BeaconState post-gloas
|
|
144
|
+
// (EIP-7732 removed latestExecutionPayloadHeader). Using 0 as unavailable fallback —
|
|
145
|
+
// see initializeForkChoiceFromUnfinalizedState for the same caveat on validation.
|
|
145
146
|
executionPayloadNumber: isStatePostGloas(state) ? 0 : state.payloadBlockNumber,
|
|
147
|
+
executionPayloadGasLimit: isStatePostGloas(state) ? 0 : state.latestExecutionPayloadHeader.gasLimit,
|
|
146
148
|
executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
|
|
147
149
|
}
|
|
148
150
|
: {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),
|
|
@@ -232,9 +234,17 @@ export function initializeForkChoiceFromUnfinalizedState(
|
|
|
232
234
|
executionPayloadBlockHash: isStatePostGloas(unfinalizedState)
|
|
233
235
|
? toRootHex(unfinalizedState.latestBlockHash)
|
|
234
236
|
: toRootHex(unfinalizedState.latestExecutionPayloadHeader.blockHash),
|
|
235
|
-
// TODO GLOAS: executionPayloadNumber
|
|
236
|
-
// latestExecutionPayloadHeader). Using 0 as unavailable fallback until
|
|
237
|
+
// TODO GLOAS: executionPayloadNumber/GasLimit are not tracked in BeaconState post-gloas
|
|
238
|
+
// (EIP-7732 removed latestExecutionPayloadHeader). Using 0 as unavailable fallback until
|
|
239
|
+
// a solution is found. The 0 doesn't gate validation in practice: at boot the head's
|
|
240
|
+
// PENDING variant's `executionPayloadBlockHash` is the *parent's* payload hash (per the
|
|
241
|
+
// PENDING/EMPTY convention), so gossip bids that reference the head's *own* payload
|
|
242
|
+
// hash won't match this variant anyway and will IGNORE until `onExecutionPayload`
|
|
243
|
+
// upgrades the head to FULL with real values.
|
|
237
244
|
executionPayloadNumber: isStatePostGloas(unfinalizedState) ? 0 : unfinalizedState.payloadBlockNumber,
|
|
245
|
+
executionPayloadGasLimit: isStatePostGloas(unfinalizedState)
|
|
246
|
+
? 0
|
|
247
|
+
: unfinalizedState.latestExecutionPayloadHeader.gasLimit,
|
|
238
248
|
executionStatus: blockHeader.slot === GENESIS_SLOT ? ExecutionStatus.Valid : ExecutionStatus.Syncing,
|
|
239
249
|
}
|
|
240
250
|
: {executionPayloadBlockHash: null, executionStatus: ExecutionStatus.PreMerge}),
|
|
@@ -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
|
}
|
|
@@ -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) {
|