@lodestar/beacon-node 1.43.0-dev.a142c56215 → 1.43.0-dev.a691e9b4dd
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 +13 -3
- 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 +45 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/debug/index.d.ts.map +1 -1
- package/lib/api/impl/debug/index.js +0 -1
- package/lib/api/impl/debug/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +68 -2
- package/lib/api/impl/validator/index.js.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.d.ts +3 -0
- package/lib/chain/blocks/blockInput/blockInput.d.ts.map +1 -1
- package/lib/chain/blocks/blockInput/blockInput.js +4 -1
- package/lib/chain/blocks/blockInput/blockInput.js.map +1 -1
- package/lib/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +23 -22
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +5 -3
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +25 -14
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +36 -21
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +4 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +9 -2
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/types.d.ts.map +1 -1
- package/lib/chain/blocks/types.d.ts +2 -1
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +8 -0
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +2 -1
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +30 -12
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts +0 -4
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js +5 -2
- package/lib/chain/blocks/verifyBlocksExecutionPayloads.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts +2 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +16 -7
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts +2 -2
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +7 -4
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.d.ts.map +1 -1
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js +8 -3
- package/lib/chain/blocks/verifyPayloadsDataAvailability.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +5 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/emitter.d.ts +0 -11
- package/lib/chain/emitter.d.ts.map +1 -1
- package/lib/chain/emitter.js +0 -4
- package/lib/chain/emitter.js.map +1 -1
- package/lib/chain/errors/index.d.ts +1 -0
- package/lib/chain/errors/index.d.ts.map +1 -1
- package/lib/chain/errors/index.js +1 -0
- package/lib/chain/errors/index.js.map +1 -1
- package/lib/chain/errors/proposerPreferences.d.ts +40 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/errors/proposerPreferences.js +14 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -0
- package/lib/chain/interface.d.ts +2 -1
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +3 -2
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +26 -4
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +15 -17
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +12 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +34 -22
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/regen/queued.d.ts.map +1 -1
- package/lib/chain/regen/queued.js +1 -4
- package/lib/chain/regen/queued.js.map +1 -1
- package/lib/chain/regen/regen.d.ts.map +1 -1
- package/lib/chain/regen/regen.js +1 -4
- package/lib/chain/regen/regen.js.map +1 -1
- package/lib/chain/seenCache/index.d.ts +1 -0
- package/lib/chain/seenCache/index.d.ts.map +1 -1
- package/lib/chain/seenCache/index.js +1 -0
- package/lib/chain/seenCache/index.js.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts +8 -2
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js +20 -4
- package/lib/chain/seenCache/seenPayloadEnvelopeInput.js.map +1 -1
- package/lib/chain/seenCache/seenProposerPreferences.d.ts +16 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
- package/lib/chain/seenCache/seenProposerPreferences.js +31 -0
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
- package/lib/chain/validation/executionPayloadBid.js +11 -8
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/proposerPreferences.d.ts +8 -0
- package/lib/chain/validation/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/validation/proposerPreferences.js +91 -0
- package/lib/chain/validation/proposerPreferences.js.map +1 -0
- package/lib/network/gossip/interface.d.ts +7 -1
- package/lib/network/gossip/interface.d.ts.map +1 -1
- package/lib/network/gossip/interface.js +1 -0
- package/lib/network/gossip/interface.js.map +1 -1
- package/lib/network/gossip/scoringParameters.d.ts.map +1 -1
- package/lib/network/gossip/scoringParameters.js +12 -1
- package/lib/network/gossip/scoringParameters.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +29 -766
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/gossip/topic.js +6 -0
- package/lib/network/gossip/topic.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 +22 -15
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/lib/network/processor/gossipQueues/index.d.ts.map +1 -1
- package/lib/network/processor/gossipQueues/index.js +5 -0
- package/lib/network/processor/gossipQueues/index.js.map +1 -1
- package/lib/network/processor/index.d.ts.map +1 -1
- package/lib/network/processor/index.js +6 -5
- package/lib/network/processor/index.js.map +1 -1
- package/lib/node/nodejs.js +2 -2
- package/lib/node/nodejs.js.map +1 -1
- package/lib/node/notifier.js +1 -7
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/range/batch.d.ts +11 -0
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +83 -21
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +23 -5
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +0 -2
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +0 -47
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +36 -21
- package/lib/sync/utils/downloadByRange.js.map +1 -1
- package/lib/sync/utils/downloadByRoot.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRoot.js +10 -0
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/util/sszBytes.d.ts.map +1 -1
- package/lib/util/sszBytes.js +8 -6
- package/lib/util/sszBytes.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +16 -3
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/debug/index.ts +0 -1
- package/src/api/impl/validator/index.ts +82 -1
- package/src/chain/blocks/blockInput/blockInput.ts +4 -1
- package/src/chain/blocks/importBlock.ts +23 -39
- package/src/chain/blocks/importExecutionPayload.ts +33 -14
- package/src/chain/blocks/index.ts +34 -15
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +10 -2
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
- package/src/chain/blocks/types.ts +2 -1
- package/src/chain/blocks/utils/chainSegment.ts +8 -0
- package/src/chain/blocks/verifyBlock.ts +45 -13
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -6
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +9 -4
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
- package/src/chain/chain.ts +5 -0
- package/src/chain/emitter.ts +0 -11
- package/src/chain/errors/index.ts +1 -0
- package/src/chain/errors/proposerPreferences.ts +47 -0
- package/src/chain/interface.ts +2 -0
- package/src/chain/opPools/payloadAttestationPool.ts +29 -8
- package/src/chain/prepareNextSlot.ts +20 -28
- package/src/chain/produceBlock/produceBlockBody.ts +45 -27
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/regen/queued.ts +2 -7
- package/src/chain/regen/regen.ts +2 -7
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +25 -5
- package/src/chain/seenCache/seenProposerPreferences.ts +37 -0
- package/src/chain/validation/executionPayloadBid.ts +11 -8
- package/src/chain/validation/proposerPreferences.ts +110 -0
- package/src/network/gossip/interface.ts +6 -0
- package/src/network/gossip/scoringParameters.ts +14 -1
- package/src/network/gossip/topic.ts +6 -0
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +31 -16
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +6 -5
- package/src/node/nodejs.ts +2 -2
- package/src/node/notifier.ts +1 -8
- package/src/sync/range/batch.ts +108 -24
- package/src/sync/range/chain.ts +25 -5
- package/src/sync/unknownBlock.ts +0 -50
- package/src/sync/utils/downloadByRange.ts +37 -21
- package/src/sync/utils/downloadByRoot.ts +12 -0
- package/src/util/sszBytes.ts +8 -6
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {ApplicationMethods} from "@lodestar/api/server";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ForkPostElectra,
|
|
5
|
+
ForkPreElectra,
|
|
6
|
+
SYNC_COMMITTEE_SUBNET_SIZE,
|
|
7
|
+
isForkPostElectra,
|
|
8
|
+
isForkPostGloas,
|
|
9
|
+
} from "@lodestar/params";
|
|
4
10
|
import {isStatePostAltair} from "@lodestar/state-transition";
|
|
5
11
|
import {Attestation, Epoch, SingleAttestation, isElectraAttestation, ssz, sszTypesFor} from "@lodestar/types";
|
|
12
|
+
import {toRootHex} from "@lodestar/utils";
|
|
6
13
|
import {
|
|
7
14
|
AttestationError,
|
|
8
15
|
AttestationErrorCode,
|
|
9
16
|
GossipAction,
|
|
17
|
+
PayloadAttestationError,
|
|
18
|
+
PayloadAttestationErrorCode,
|
|
10
19
|
SyncCommitteeError,
|
|
11
20
|
} from "../../../../chain/errors/index.js";
|
|
12
21
|
import {validateApiAttesterSlashing} from "../../../../chain/validation/attesterSlashing.js";
|
|
13
22
|
import {validateApiBlsToExecutionChange} from "../../../../chain/validation/blsToExecutionChange.js";
|
|
14
23
|
import {toElectraSingleAttestation, validateApiAttestation} from "../../../../chain/validation/index.js";
|
|
24
|
+
import {validateApiPayloadAttestationMessage} from "../../../../chain/validation/payloadAttestationMessage.js";
|
|
15
25
|
import {validateApiProposerSlashing} from "../../../../chain/validation/proposerSlashing.js";
|
|
16
26
|
import {validateApiSyncCommittee} from "../../../../chain/validation/syncCommittee.js";
|
|
17
27
|
import {validateApiVoluntaryExit} from "../../../../chain/validation/voluntaryExit.js";
|
|
@@ -62,6 +72,15 @@ export function getBeaconPoolApi({
|
|
|
62
72
|
return {data: attestations, meta: {version: fork}};
|
|
63
73
|
},
|
|
64
74
|
|
|
75
|
+
async getPoolPayloadAttestations({slot}) {
|
|
76
|
+
const fork = chain.config.getForkName(slot ?? chain.clock.currentSlot);
|
|
77
|
+
if (!isForkPostGloas(fork)) {
|
|
78
|
+
throw new ApiError(400, `Payload attestation pool is not supported before Gloas fork=${fork}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {data: chain.payloadAttestationPool.getAll(slot), meta: {version: fork}};
|
|
82
|
+
},
|
|
83
|
+
|
|
65
84
|
async getPoolAttesterSlashings() {
|
|
66
85
|
const fork = chain.config.getForkName(chain.clock.currentSlot);
|
|
67
86
|
|
|
@@ -231,6 +250,69 @@ export function getBeaconPoolApi({
|
|
|
231
250
|
}
|
|
232
251
|
},
|
|
233
252
|
|
|
253
|
+
async submitPayloadAttestationMessages({payloadAttestationMessages}) {
|
|
254
|
+
const failures: FailureList = [];
|
|
255
|
+
|
|
256
|
+
await Promise.all(
|
|
257
|
+
payloadAttestationMessages.map(async (payloadAttestationMessage, i) => {
|
|
258
|
+
try {
|
|
259
|
+
const validateFn = () => validateApiPayloadAttestationMessage(chain, payloadAttestationMessage);
|
|
260
|
+
const {slot, beaconBlockRoot} = payloadAttestationMessage.data;
|
|
261
|
+
const {attDataRootHex, validatorCommitteeIndex} = await validateGossipFnRetryUnknownRoot(
|
|
262
|
+
validateFn,
|
|
263
|
+
network,
|
|
264
|
+
chain,
|
|
265
|
+
slot,
|
|
266
|
+
beaconBlockRoot
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const insertOutcome = chain.payloadAttestationPool.add(
|
|
270
|
+
payloadAttestationMessage,
|
|
271
|
+
attDataRootHex,
|
|
272
|
+
validatorCommitteeIndex
|
|
273
|
+
);
|
|
274
|
+
metrics?.opPool.payloadAttestationPool.apiInsertOutcome.inc({insertOutcome});
|
|
275
|
+
|
|
276
|
+
chain.forkChoice.notifyPtcMessages(
|
|
277
|
+
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
278
|
+
[validatorCommitteeIndex],
|
|
279
|
+
payloadAttestationMessage.data.payloadPresent
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
await network.publishPayloadAttestationMessage(payloadAttestationMessage);
|
|
283
|
+
} catch (e) {
|
|
284
|
+
const logCtx = {
|
|
285
|
+
slot: payloadAttestationMessage.data.slot,
|
|
286
|
+
validatorIndex: payloadAttestationMessage.validatorIndex,
|
|
287
|
+
beaconBlockRoot: toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
if (
|
|
291
|
+
e instanceof PayloadAttestationError &&
|
|
292
|
+
e.type.code === PayloadAttestationErrorCode.PAYLOAD_ATTESTATION_ALREADY_KNOWN
|
|
293
|
+
) {
|
|
294
|
+
logger.debug("Ignoring known payload attestation message", logCtx);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
failures.push({index: i, message: (e as Error).message});
|
|
299
|
+
logger.verbose(`Error on submitPayloadAttestationMessages [${i}]`, logCtx, e as Error);
|
|
300
|
+
if (e instanceof PayloadAttestationError && e.action === GossipAction.REJECT) {
|
|
301
|
+
chain.persistInvalidSszValue(
|
|
302
|
+
ssz.gloas.PayloadAttestationMessage,
|
|
303
|
+
payloadAttestationMessage,
|
|
304
|
+
"api_reject"
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
if (failures.length > 0) {
|
|
312
|
+
throw new IndexedError("Error processing payload attestation messages", failures);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
|
|
234
316
|
/**
|
|
235
317
|
* POST `/eth/v1/beacon/pool/sync_committees`
|
|
236
318
|
*
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
computeTimeAtSlot,
|
|
27
27
|
getCurrentSlot,
|
|
28
28
|
isStatePostAltair,
|
|
29
|
+
isStatePostGloas,
|
|
29
30
|
proposerShufflingDecisionRoot,
|
|
30
31
|
} from "@lodestar/state-transition";
|
|
31
32
|
import {
|
|
@@ -1049,6 +1050,36 @@ export function getValidatorApi(
|
|
|
1049
1050
|
};
|
|
1050
1051
|
},
|
|
1051
1052
|
|
|
1053
|
+
async producePayloadAttestationData({slot}) {
|
|
1054
|
+
const fork = config.getForkName(slot);
|
|
1055
|
+
if (!isForkPostGloas(fork)) {
|
|
1056
|
+
throw new ApiError(400, `producePayloadAttestationData is not supported before Gloas fork=${fork}`);
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
notWhileSyncing();
|
|
1060
|
+
await waitForSlot(slot);
|
|
1061
|
+
|
|
1062
|
+
const block = chain.forkChoice.getCanonicalBlockClosestLteSlot(slot);
|
|
1063
|
+
if (!block) {
|
|
1064
|
+
throw new ApiError(404, `No canonical block found at or before slot=${slot}`);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
const blockIsForSlot = block.slot === slot;
|
|
1068
|
+
const payloadInput = chain.seenPayloadEnvelopeInputCache.get(block.blockRoot);
|
|
1069
|
+
const payloadPresent = blockIsForSlot && (payloadInput?.hasPayloadEnvelope() ?? false);
|
|
1070
|
+
const blobDataAvailable = blockIsForSlot && (payloadInput?.hasAllData() ?? false);
|
|
1071
|
+
|
|
1072
|
+
return {
|
|
1073
|
+
data: {
|
|
1074
|
+
beaconBlockRoot: fromHex(block.blockRoot),
|
|
1075
|
+
slot,
|
|
1076
|
+
payloadPresent,
|
|
1077
|
+
blobDataAvailable,
|
|
1078
|
+
},
|
|
1079
|
+
meta: {version: fork},
|
|
1080
|
+
};
|
|
1081
|
+
},
|
|
1082
|
+
|
|
1052
1083
|
/**
|
|
1053
1084
|
* GET `/eth/v1/validator/sync_committee_contribution`
|
|
1054
1085
|
*
|
|
@@ -1263,6 +1294,55 @@ export function getValidatorApi(
|
|
|
1263
1294
|
};
|
|
1264
1295
|
},
|
|
1265
1296
|
|
|
1297
|
+
async getPtcDuties({epoch, indices}) {
|
|
1298
|
+
notWhileSyncing();
|
|
1299
|
+
|
|
1300
|
+
if (indices.length === 0) {
|
|
1301
|
+
throw new ApiError(400, "No validator to get PTC duties");
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
const startSlot = computeStartSlotAtEpoch(epoch);
|
|
1305
|
+
const fork = config.getForkName(startSlot);
|
|
1306
|
+
if (!isForkPostGloas(fork)) {
|
|
1307
|
+
throw new ApiError(400, `PTC duties are not supported before Gloas fork=${fork}`);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
await waitForNextClosestEpoch();
|
|
1311
|
+
|
|
1312
|
+
if (epoch > chain.clock.currentEpoch + 1) {
|
|
1313
|
+
throw new ApiError(400, "Cannot get PTC duties for epoch more than one ahead");
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
const head = chain.forkChoice.getHead();
|
|
1317
|
+
const state = await chain.getHeadStateAtCurrentEpoch(RegenCaller.getDuties);
|
|
1318
|
+
if (!isStatePostGloas(state)) {
|
|
1319
|
+
throw new ApiError(400, `PTC duties are not available before Gloas fork=${state.forkName}`);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
const pubkeys = getPubkeysForIndices(state, indices);
|
|
1323
|
+
const ptcs = state.getEpochPTCs(epoch);
|
|
1324
|
+
const duties: routes.validator.PtcDuty[] = [];
|
|
1325
|
+
for (let i = 0, len = indices.length; i < len; i++) {
|
|
1326
|
+
const validatorIndex = indices[i];
|
|
1327
|
+
for (let j = 0; j < SLOTS_PER_EPOCH; j++) {
|
|
1328
|
+
if (ptcs[j].indexOf(validatorIndex) !== -1) {
|
|
1329
|
+
duties.push({pubkey: pubkeys[i], validatorIndex, slot: j + startSlot});
|
|
1330
|
+
break;
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const dependentRoot = fromHex(state.getShufflingDecisionRoot(epoch)) || (await getGenesisBlockRoot(state));
|
|
1336
|
+
|
|
1337
|
+
return {
|
|
1338
|
+
data: duties,
|
|
1339
|
+
meta: {
|
|
1340
|
+
dependentRoot: toRootHex(dependentRoot),
|
|
1341
|
+
executionOptimistic: isOptimisticBlock(head),
|
|
1342
|
+
},
|
|
1343
|
+
};
|
|
1344
|
+
},
|
|
1345
|
+
|
|
1266
1346
|
/**
|
|
1267
1347
|
* `POST /eth/v1/validator/duties/sync/{epoch}`
|
|
1268
1348
|
*
|
|
@@ -1641,13 +1721,14 @@ export function getValidatorApi(
|
|
|
1641
1721
|
throw Error("Cached block production result is not full block");
|
|
1642
1722
|
}
|
|
1643
1723
|
|
|
1644
|
-
const {executionPayload, executionRequests} = produceResult as ProduceFullGloas;
|
|
1724
|
+
const {executionPayload, executionRequests, parentBlockRoot} = produceResult as ProduceFullGloas;
|
|
1645
1725
|
|
|
1646
1726
|
const envelope: gloas.ExecutionPayloadEnvelope = {
|
|
1647
1727
|
payload: executionPayload,
|
|
1648
1728
|
executionRequests: executionRequests,
|
|
1649
1729
|
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
1650
1730
|
beaconBlockRoot,
|
|
1731
|
+
parentBeaconBlockRoot: parentBlockRoot,
|
|
1651
1732
|
};
|
|
1652
1733
|
|
|
1653
1734
|
logger.info("Produced execution payload envelope", {
|
|
@@ -895,8 +895,11 @@ export class BlockInputColumns extends AbstractBlockInput<ForkColumnsDA, fulu.Da
|
|
|
895
895
|
return this.getAllColumnsWithSource().map(({columnSidecar}) => columnSidecar);
|
|
896
896
|
}
|
|
897
897
|
|
|
898
|
+
/**
|
|
899
|
+
* Strictly checks missing sampled columns. Does NOT short-circuit on `state.hasAllData`.
|
|
900
|
+
*/
|
|
898
901
|
getMissingSampledColumnMeta(): MissingColumnMeta {
|
|
899
|
-
if (this.state.
|
|
902
|
+
if (this.state.hasComputedAllData) {
|
|
900
903
|
return {
|
|
901
904
|
missing: [],
|
|
902
905
|
versionedHashes: this.state.versionedHashes,
|
|
@@ -9,14 +9,7 @@ import {
|
|
|
9
9
|
NotReorgedReason,
|
|
10
10
|
getSafeExecutionBlockHash,
|
|
11
11
|
} from "@lodestar/fork-choice";
|
|
12
|
-
import {
|
|
13
|
-
ForkPostAltair,
|
|
14
|
-
ForkPostElectra,
|
|
15
|
-
ForkPostGloas,
|
|
16
|
-
ForkSeq,
|
|
17
|
-
MAX_SEED_LOOKAHEAD,
|
|
18
|
-
SLOTS_PER_EPOCH,
|
|
19
|
-
} from "@lodestar/params";
|
|
12
|
+
import {ForkPostAltair, ForkPostElectra, ForkSeq, MAX_SEED_LOOKAHEAD, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
20
13
|
import {
|
|
21
14
|
IBeaconStateView,
|
|
22
15
|
RootCache,
|
|
@@ -27,17 +20,7 @@ import {
|
|
|
27
20
|
isStatePostAltair,
|
|
28
21
|
isStatePostBellatrix,
|
|
29
22
|
} from "@lodestar/state-transition";
|
|
30
|
-
import {
|
|
31
|
-
Attestation,
|
|
32
|
-
BeaconBlock,
|
|
33
|
-
SignedBeaconBlock,
|
|
34
|
-
altair,
|
|
35
|
-
capella,
|
|
36
|
-
electra,
|
|
37
|
-
isGloasBeaconBlock,
|
|
38
|
-
phase0,
|
|
39
|
-
ssz,
|
|
40
|
-
} from "@lodestar/types";
|
|
23
|
+
import {Attestation, BeaconBlock, altair, capella, electra, isGloasBeaconBlock, phase0, ssz} from "@lodestar/types";
|
|
41
24
|
import {isErrorAborted, toRootHex} from "@lodestar/utils";
|
|
42
25
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
43
26
|
import {callInNextEventLoop} from "../../util/eventLoop.js";
|
|
@@ -86,8 +69,8 @@ export async function importBlock(
|
|
|
86
69
|
fullyVerifiedBlock: FullyVerifiedBlock,
|
|
87
70
|
opts: ImportBlockOpts
|
|
88
71
|
): Promise<void> {
|
|
89
|
-
const {blockInput, postState, parentBlockSlot,
|
|
90
|
-
|
|
72
|
+
const {blockInput, postState, parentBlockSlot, dataAvailabilityStatus, indexedAttestations} = fullyVerifiedBlock;
|
|
73
|
+
let {executionStatus} = fullyVerifiedBlock;
|
|
91
74
|
const block = blockInput.getBlock();
|
|
92
75
|
const source = blockInput.getBlockSource();
|
|
93
76
|
const {slot: blockSlot} = block.message;
|
|
@@ -122,13 +105,23 @@ export async function importBlock(
|
|
|
122
105
|
|
|
123
106
|
// Should compute checkpoint balances before forkchoice.onBlock
|
|
124
107
|
this.checkpointBalancesCache.processState(blockRootHex, postState);
|
|
108
|
+
if (fork >= ForkSeq.gloas) {
|
|
109
|
+
const parentRootHex = toRootHex(block.message.parentRoot);
|
|
110
|
+
const parentBlock = this.forkChoice.getBlockHexDefaultStatus(parentRootHex);
|
|
111
|
+
if (parentBlock === null) {
|
|
112
|
+
throw Error(`Parent block not found in forkChoice, parentRoot=${parentRootHex}`);
|
|
113
|
+
}
|
|
114
|
+
if (parentBlock.executionStatus === ExecutionStatus.Invalid) {
|
|
115
|
+
throw Error(`Parent block has invalid execution status, parentRoot=${parentRootHex}`);
|
|
116
|
+
}
|
|
117
|
+
executionStatus = parentBlock.executionStatus;
|
|
118
|
+
}
|
|
125
119
|
const blockSummary = this.forkChoice.onBlock(
|
|
126
120
|
block.message,
|
|
127
121
|
postState,
|
|
128
122
|
blockDelaySec,
|
|
129
123
|
currentSlot,
|
|
130
|
-
|
|
131
|
-
// TODO GLOAS: this is not useful post-gloas, may need to remove it?
|
|
124
|
+
executionStatus,
|
|
132
125
|
dataAvailabilityStatus
|
|
133
126
|
);
|
|
134
127
|
|
|
@@ -136,23 +129,14 @@ export async function importBlock(
|
|
|
136
129
|
// Some block event handlers require state being in state cache so need to do this before emitting EventType.block
|
|
137
130
|
this.regen.processState(blockRootHex, postState);
|
|
138
131
|
|
|
139
|
-
// For range sync
|
|
140
|
-
// we also don't need to trigger getBlobs() in that case
|
|
132
|
+
// For range sync we skip triggerGetBlobs because column fetching is handled in the range path.
|
|
141
133
|
if (fork >= ForkSeq.gloas && !opts.fromRangeSync) {
|
|
142
|
-
const payloadInput = this.seenPayloadEnvelopeInputCache.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
timeCreatedSec: fullyVerifiedBlock.seenTimestampSec,
|
|
149
|
-
});
|
|
150
|
-
this.logger.debug("Created PayloadEnvelopeInput for block", {
|
|
151
|
-
slot: blockSlot,
|
|
152
|
-
root: blockRootHex,
|
|
153
|
-
source: source.source,
|
|
154
|
-
...(opts.seenTimestampSec !== undefined ? {recvToImport: Date.now() / 1000 - opts.seenTimestampSec} : {}),
|
|
155
|
-
});
|
|
134
|
+
const payloadInput = this.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
135
|
+
// PayloadEnvelopeInput is supposed to have right after we have block
|
|
136
|
+
// there are 4 sources of them: gossip, by root, by range and api
|
|
137
|
+
if (!payloadInput) {
|
|
138
|
+
throw Error(`PayloadEnvelopeInput not seeded for block ${blockRootHex} before importBlock`);
|
|
139
|
+
}
|
|
156
140
|
|
|
157
141
|
// Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
|
|
158
142
|
// which is all the information we need so there is no reason to delay until execution payload arrives
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
|
-
import {ExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
|
|
3
|
-
import {isStatePostGloas} from "@lodestar/state-transition";
|
|
4
|
-
import {
|
|
2
|
+
import {ExecutionStatus, PayloadExecutionStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
3
|
+
import {DataAvailabilityStatus, isStatePostGloas} from "@lodestar/state-transition";
|
|
4
|
+
import {isErrorAborted} from "@lodestar/utils";
|
|
5
|
+
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
5
6
|
import {ExecutionPayloadStatus} from "../../execution/index.js";
|
|
6
7
|
import {isQueueErrorAborted} from "../../util/queue/index.js";
|
|
7
8
|
import {BeaconChain} from "../chain.js";
|
|
@@ -60,7 +61,6 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
|
|
|
60
61
|
switch (status) {
|
|
61
62
|
case ExecutionPayloadStatus.VALID:
|
|
62
63
|
return ExecutionStatus.Valid;
|
|
63
|
-
// TODO GLOAS: Handle optimistic import for payload
|
|
64
64
|
case ExecutionPayloadStatus.SYNCING:
|
|
65
65
|
case ExecutionPayloadStatus.ACCEPTED:
|
|
66
66
|
return ExecutionStatus.Syncing;
|
|
@@ -85,12 +85,14 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
|
|
|
85
85
|
* 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
|
|
86
86
|
* 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
|
|
87
87
|
* 6. Update fork choice (transitions the block's PENDING variant to FULL)
|
|
88
|
-
* 7.
|
|
89
|
-
* 8.
|
|
88
|
+
* 7. Queue notifyForkchoiceUpdate to engine api
|
|
89
|
+
* 8. Record metrics for payload envelope and column sources
|
|
90
|
+
* 9. Emit `execution_payload` event
|
|
90
91
|
*/
|
|
91
92
|
export async function importExecutionPayload(
|
|
92
93
|
this: BeaconChain,
|
|
93
94
|
payloadInput: PayloadEnvelopeInput,
|
|
95
|
+
dataAvailabilityStatus: DataAvailabilityStatus,
|
|
94
96
|
opts: ImportPayloadOpts = {}
|
|
95
97
|
): Promise<void> {
|
|
96
98
|
const signedEnvelope = payloadInput.getPayloadEnvelope();
|
|
@@ -157,7 +159,7 @@ export async function importExecutionPayload(
|
|
|
157
159
|
fork,
|
|
158
160
|
envelope.payload,
|
|
159
161
|
payloadInput.getVersionedHashes(),
|
|
160
|
-
|
|
162
|
+
envelope.parentBeaconBlockRoot,
|
|
161
163
|
envelope.executionRequests
|
|
162
164
|
),
|
|
163
165
|
|
|
@@ -219,23 +221,40 @@ export async function importExecutionPayload(
|
|
|
219
221
|
|
|
220
222
|
// 6. Update fork choice, transitions the block's PENDING variant to FULL
|
|
221
223
|
const execStatus = toForkChoiceExecutionStatus(execResult.status);
|
|
222
|
-
this.forkChoice.onExecutionPayload(
|
|
224
|
+
this.forkChoice.onExecutionPayload(
|
|
225
|
+
blockRootHex,
|
|
226
|
+
blockHashHex,
|
|
227
|
+
envelope.payload.blockNumber,
|
|
228
|
+
execStatus,
|
|
229
|
+
dataAvailabilityStatus
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
// 7. Queue notifyForkchoiceUpdate to engine api
|
|
233
|
+
const head = this.forkChoice.getHead();
|
|
234
|
+
if (!this.opts.disableImportExecutionFcU && blockRootHex === head.blockRoot) {
|
|
235
|
+
const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
|
|
236
|
+
const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
237
|
+
this.executionEngine.notifyForkchoiceUpdate(fork, blockHashHex, safeBlockHash, finalizedBlockHash).catch((e) => {
|
|
238
|
+
if (!isErrorAborted(e) && !isQueueErrorAborted(e)) {
|
|
239
|
+
this.logger.error("Error pushing notifyForkchoiceUpdate()", {blockHashHex, finalizedBlockHash}, e);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
223
243
|
|
|
224
|
-
//
|
|
244
|
+
// 8. Record metrics for payload envelope and column sources
|
|
225
245
|
this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
|
|
226
246
|
for (const {source} of payloadInput.getSampledColumnsWithSource()) {
|
|
227
247
|
this.metrics?.importPayload.columnsBySource.inc({source});
|
|
228
248
|
}
|
|
229
249
|
|
|
230
|
-
//
|
|
250
|
+
// 9. Emit event after payload is fully verified and imported to fork choice, only for recent enough payloads
|
|
231
251
|
if (this.clock.currentSlot - slot < EVENTSTREAM_EMIT_RECENT_EXECUTION_PAYLOAD_SLOTS) {
|
|
232
252
|
this.emitter.emit(routes.events.EventType.executionPayload, {
|
|
233
253
|
slot,
|
|
234
254
|
builderIndex: envelope.builderIndex,
|
|
235
255
|
blockHash: blockHashHex,
|
|
236
256
|
blockRoot: blockRootHex,
|
|
237
|
-
|
|
238
|
-
executionOptimistic: false,
|
|
257
|
+
executionOptimistic: execStatus === ExecutionStatus.Syncing,
|
|
239
258
|
});
|
|
240
259
|
}
|
|
241
260
|
|
|
@@ -261,6 +280,6 @@ export async function processExecutionPayload(
|
|
|
261
280
|
signal: AbortSignal,
|
|
262
281
|
opts: ImportPayloadOpts = {}
|
|
263
282
|
): Promise<void> {
|
|
264
|
-
await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
265
|
-
await importExecutionPayload.call(this, payloadInput, opts);
|
|
283
|
+
const {dataAvailabilityStatuses} = await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
284
|
+
await importExecutionPayload.call(this, payloadInput, dataAvailabilityStatuses[0], opts);
|
|
266
285
|
}
|
|
@@ -65,7 +65,7 @@ export async function processBlocks(
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
try {
|
|
68
|
-
const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
|
|
68
|
+
const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, payloadEnvelopes, opts);
|
|
69
69
|
|
|
70
70
|
// No relevant blocks, skip verifyBlocksInEpoch()
|
|
71
71
|
if (relevantBlocks.length === 0 || parentBlock === null) {
|
|
@@ -90,8 +90,14 @@ export async function processBlocks(
|
|
|
90
90
|
|
|
91
91
|
// Fully verify a block to be imported immediately after. Does not produce any side-effects besides adding intermediate
|
|
92
92
|
// states in the state cache through regen.
|
|
93
|
-
const {
|
|
94
|
-
|
|
93
|
+
const {
|
|
94
|
+
postStates,
|
|
95
|
+
blockDAStatuses,
|
|
96
|
+
payloadDAStatuses,
|
|
97
|
+
proposerBalanceDeltas,
|
|
98
|
+
segmentExecStatus,
|
|
99
|
+
indexedAttestationsByBlock,
|
|
100
|
+
} = await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
|
|
95
101
|
|
|
96
102
|
// If segmentExecStatus has lvhForkchoice then, the entire segment should be invalid
|
|
97
103
|
// and we need to further propagate
|
|
@@ -103,26 +109,37 @@ export async function processBlocks(
|
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
const {executionStatuses} = segmentExecStatus;
|
|
106
|
-
const
|
|
107
|
-
|
|
112
|
+
const verifiedBlocksBySlot = new Map<Slot, FullyVerifiedBlock>();
|
|
113
|
+
for (let i = 0; i < relevantBlocks.length; i++) {
|
|
114
|
+
const block = relevantBlocks[i];
|
|
115
|
+
verifiedBlocksBySlot.set(block.getBlock().message.slot, {
|
|
108
116
|
blockInput: block,
|
|
109
117
|
postState: postStates[i],
|
|
110
118
|
parentBlockSlot: parentSlots[i],
|
|
111
119
|
executionStatus: executionStatuses[i],
|
|
112
120
|
// start supporting optimistic syncing/processing
|
|
113
|
-
dataAvailabilityStatus:
|
|
121
|
+
dataAvailabilityStatus: blockDAStatuses[i],
|
|
114
122
|
proposerBalanceDelta: proposerBalanceDeltas[i],
|
|
115
123
|
indexedAttestations: indexedAttestationsByBlock[i],
|
|
116
124
|
// TODO: Make this param mandatory and capture in gossip
|
|
117
125
|
seenTimestampSec: opts.seenTimestampSec ?? Math.floor(Date.now() / 1000),
|
|
118
|
-
})
|
|
119
|
-
|
|
126
|
+
});
|
|
127
|
+
}
|
|
120
128
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
129
|
+
// Iterate slots from the original `blocks` input (which spans the entire batch including
|
|
130
|
+
// slots filtered out of `relevantBlocks`). The first batch of a checkpoint sync may contain
|
|
131
|
+
// a payload at the anchor slot whose block is already in fork-choice (added by
|
|
132
|
+
// initializeForkChoice as PENDING+EMPTY) and therefore not in verifiedBlocksBySlot — the
|
|
133
|
+
// payload still needs to be imported here to populate the anchor's FULL variant so
|
|
134
|
+
// subsequent slots can find their parent payload.
|
|
135
|
+
const slots = Array.from(new Set(blocks.map((b) => b.getBlock().message.slot)));
|
|
136
|
+
for (const slot of slots) {
|
|
137
|
+
const fullyVerifiedBlock = verifiedBlocksBySlot.get(slot);
|
|
138
|
+
if (fullyVerifiedBlock !== undefined) {
|
|
139
|
+
// TODO: Consider batching importBlock too if it takes significant time
|
|
140
|
+
await importBlock.call(this, fullyVerifiedBlock, opts);
|
|
141
|
+
}
|
|
124
142
|
|
|
125
|
-
const slot = fullyVerifiedBlock.blockInput.getBlock().message.slot;
|
|
126
143
|
const payloadInput = payloadEnvelopes?.get(slot);
|
|
127
144
|
if (payloadInput?.hasPayloadEnvelope()) {
|
|
128
145
|
if (!payloadInput.isComplete()) {
|
|
@@ -130,9 +147,11 @@ export async function processBlocks(
|
|
|
130
147
|
throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
|
|
131
148
|
}
|
|
132
149
|
// we already awaited DA in verifyBlocksInEpoch for this segment
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
150
|
+
const payloadDA = payloadDAStatuses.get(slot);
|
|
151
|
+
if (payloadDA === undefined) {
|
|
152
|
+
throw new Error(`Missing payload DA status for slot ${slot}`);
|
|
153
|
+
}
|
|
154
|
+
await importExecutionPayload.call(this, payloadInput, payloadDA, {validSignature: false});
|
|
136
155
|
}
|
|
137
156
|
|
|
138
157
|
await nextEventLoop();
|
|
@@ -64,6 +64,7 @@ export class PayloadEnvelopeInput {
|
|
|
64
64
|
readonly proposerIndex: ValidatorIndex;
|
|
65
65
|
readonly bid: gloas.ExecutionPayloadBid;
|
|
66
66
|
readonly versionedHashes: VersionedHashes;
|
|
67
|
+
readonly daOutOfRange: boolean;
|
|
67
68
|
|
|
68
69
|
private columnsCache = new Map<ColumnIndex, ColumnWithSource>();
|
|
69
70
|
|
|
@@ -87,6 +88,7 @@ export class PayloadEnvelopeInput {
|
|
|
87
88
|
sampledColumns: ColumnIndex[];
|
|
88
89
|
custodyColumns: ColumnIndex[];
|
|
89
90
|
timeCreatedSec: number;
|
|
91
|
+
daOutOfRange: boolean;
|
|
90
92
|
}) {
|
|
91
93
|
this.blockRootHex = props.blockRootHex;
|
|
92
94
|
this.slot = props.slot;
|
|
@@ -97,13 +99,14 @@ export class PayloadEnvelopeInput {
|
|
|
97
99
|
this.sampledColumns = props.sampledColumns;
|
|
98
100
|
this.custodyColumns = props.custodyColumns;
|
|
99
101
|
this.timeCreatedSec = props.timeCreatedSec;
|
|
102
|
+
this.daOutOfRange = props.daOutOfRange;
|
|
100
103
|
this.payloadEnvelopeDataPromise = createPromise();
|
|
101
104
|
this.allDataPromise = createPromise();
|
|
102
105
|
this.columnsDataPromise = createPromise();
|
|
103
106
|
|
|
104
107
|
const noBlobs = props.bid.blobKzgCommitments.length === 0;
|
|
105
108
|
const noSampledColumns = props.sampledColumns.length === 0;
|
|
106
|
-
const hasAllData = noBlobs || noSampledColumns;
|
|
109
|
+
const hasAllData = props.daOutOfRange || noBlobs || noSampledColumns;
|
|
107
110
|
|
|
108
111
|
if (hasAllData) {
|
|
109
112
|
this.state = {hasPayload: false, hasAllData: true, hasComputedAllData: true};
|
|
@@ -125,6 +128,7 @@ export class PayloadEnvelopeInput {
|
|
|
125
128
|
sampledColumns: props.sampledColumns,
|
|
126
129
|
custodyColumns: props.custodyColumns,
|
|
127
130
|
timeCreatedSec: props.timeCreatedSec,
|
|
131
|
+
daOutOfRange: props.daOutOfRange,
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
134
|
|
|
@@ -152,6 +156,7 @@ export class PayloadEnvelopeInput {
|
|
|
152
156
|
throw new Error("Payload envelope beacon_block_root mismatch");
|
|
153
157
|
}
|
|
154
158
|
|
|
159
|
+
// TODO GLOAS: track source by metrics, maybe inside the seen cache
|
|
155
160
|
const source: SourceMeta = {
|
|
156
161
|
source: props.source,
|
|
157
162
|
seenTimestampSec: props.seenTimestampSec,
|
|
@@ -306,8 +311,11 @@ export class PayloadEnvelopeInput {
|
|
|
306
311
|
return this.state.hasAllData;
|
|
307
312
|
}
|
|
308
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Strictly checks missing sampled columns. Does NOT short-circuit on `state.hasAllData`.
|
|
316
|
+
*/
|
|
309
317
|
getMissingSampledColumnMeta(): MissingColumnMeta {
|
|
310
|
-
if (this.state.
|
|
318
|
+
if (this.state.hasComputedAllData) {
|
|
311
319
|
return {missing: [], versionedHashes: this.versionedHashes};
|
|
312
320
|
}
|
|
313
321
|
|
|
@@ -94,7 +94,8 @@ export type ImportBlockOpts = {
|
|
|
94
94
|
*
|
|
95
95
|
* `executionStatus` reflects the outcome of execution payload verification at block-import time:
|
|
96
96
|
* - pre-gloas: Valid | Syncing | PreMerge (from EL notifyNewPayload against the in-block payload)
|
|
97
|
-
* - post-gloas:
|
|
97
|
+
* - post-gloas: inherited from parent's chain (Valid/Syncing) by importBlock; payload arrives
|
|
98
|
+
* separately as an envelope and creates the FULL variant later via onExecutionPayload
|
|
98
99
|
*/
|
|
99
100
|
export type FullyVerifiedBlock = {
|
|
100
101
|
blockInput: IBlockInput;
|
|
@@ -41,6 +41,14 @@ export function assertLinearChainSegment(
|
|
|
41
41
|
// - EMPTY variant (no envelope for slot): execution hash is unchanged
|
|
42
42
|
// null only for pre-merge parents, which cannot precede gloas blocks.
|
|
43
43
|
let currentExecHash: string | null = parentBlock.executionPayloadBlockHash;
|
|
44
|
+
// Checkpoint sync first batch: parent is the anchor PENDING whose executionPayloadBlockHash
|
|
45
|
+
// is the inherited parentBlockHash semantic (= grandparent's payload), not its own payload.
|
|
46
|
+
// If parent's own payload envelope arrives in this batch, advance currentExecHash to that
|
|
47
|
+
// payload's blockHash so the segment validation sees the true EL chain head.
|
|
48
|
+
const parentPayloadInput = payloadEnvelopes?.get(parentBlock.slot);
|
|
49
|
+
if (parentPayloadInput?.hasPayloadEnvelope()) {
|
|
50
|
+
currentExecHash = parentPayloadInput.getBlockHashHex();
|
|
51
|
+
}
|
|
44
52
|
// Track the execution hash before the last FULL advancement so we can recover
|
|
45
53
|
// if the next block reveals that envelope was orphaned.
|
|
46
54
|
let prevExecHash: string | null = currentExecHash;
|