@lodestar/beacon-node 1.43.0-dev.a140dd987e → 1.43.0-dev.a45ba75824
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 +16 -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 +45 -2
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/lodestar/index.js +1 -1
- package/lib/api/impl/lodestar/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +66 -1
- 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 +10 -18
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.d.ts +19 -6
- package/lib/chain/blocks/importExecutionPayload.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +43 -19
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/blocks/index.d.ts +5 -3
- package/lib/chain/blocks/index.d.ts.map +1 -1
- package/lib/chain/blocks/index.js +31 -10
- package/lib/chain/blocks/index.js.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts +1 -0
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.d.ts.map +1 -1
- package/lib/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.js +5 -1
- 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/payloadEnvelopeProcessor.js +2 -2
- package/lib/chain/blocks/payloadEnvelopeProcessor.js.map +1 -1
- package/lib/chain/blocks/types.d.ts +2 -2
- package/lib/chain/blocks/types.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.d.ts +23 -2
- package/lib/chain/blocks/utils/chainSegment.d.ts.map +1 -1
- package/lib/chain/blocks/utils/chainSegment.js +81 -12
- package/lib/chain/blocks/utils/chainSegment.js.map +1 -1
- package/lib/chain/blocks/verifyBlock.d.ts +5 -3
- package/lib/chain/blocks/verifyBlock.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlock.js +51 -7
- package/lib/chain/blocks/verifyBlock.js.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.d.ts.map +1 -1
- package/lib/chain/blocks/verifyBlocksSanityChecks.js +15 -4
- package/lib/chain/blocks/verifyBlocksSanityChecks.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +2 -2
- 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 +3 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +12 -4
- 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/blockError.d.ts +8 -1
- package/lib/chain/errors/blockError.d.ts.map +1 -1
- package/lib/chain/errors/blockError.js +2 -0
- package/lib/chain/errors/blockError.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 +33 -0
- package/lib/chain/errors/proposerPreferences.d.ts.map +1 -0
- package/lib/chain/errors/proposerPreferences.js +13 -0
- package/lib/chain/errors/proposerPreferences.js.map +1 -0
- package/lib/chain/interface.d.ts +3 -2
- 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 +11 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +41 -18
- 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/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 +15 -0
- package/lib/chain/seenCache/seenProposerPreferences.d.ts.map +1 -0
- package/lib/chain/seenCache/seenProposerPreferences.js +25 -0
- package/lib/chain/seenCache/seenProposerPreferences.js.map +1 -0
- package/lib/chain/validation/block.d.ts.map +1 -1
- package/lib/chain/validation/block.js +1 -0
- package/lib/chain/validation/block.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 +69 -0
- package/lib/chain/validation/proposerPreferences.js.map +1 -0
- package/lib/metrics/metrics/lodestar.d.ts +1 -0
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -0
- package/lib/metrics/metrics/lodestar.js.map +1 -1
- 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 +8 -0
- 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 +24 -16
- 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/network/reqresp/handlers/beaconBlocksByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js +14 -6
- package/lib/network/reqresp/handlers/beaconBlocksByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js +11 -5
- package/lib/network/reqresp/handlers/blobSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js +17 -5
- package/lib/network/reqresp/handlers/dataColumnSidecarsByRange.js.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.d.ts.map +1 -1
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js +7 -4
- package/lib/network/reqresp/handlers/executionPayloadEnvelopesByRange.js.map +1 -1
- package/lib/node/notifier.js +7 -1
- package/lib/node/notifier.js.map +1 -1
- package/lib/sync/range/batch.d.ts +23 -2
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +84 -33
- package/lib/sync/range/batch.js.map +1 -1
- package/lib/sync/range/chain.d.ts +6 -2
- package/lib/sync/range/chain.d.ts.map +1 -1
- package/lib/sync/range/chain.js +26 -7
- package/lib/sync/range/chain.js.map +1 -1
- package/lib/sync/range/range.d.ts.map +1 -1
- package/lib/sync/range/range.js +17 -6
- package/lib/sync/range/range.js.map +1 -1
- package/lib/sync/types.d.ts +34 -0
- package/lib/sync/types.d.ts.map +1 -1
- package/lib/sync/types.js +34 -0
- package/lib/sync/types.js.map +1 -1
- package/lib/sync/unknownBlock.d.ts +22 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +602 -53
- package/lib/sync/unknownBlock.js.map +1 -1
- package/lib/sync/utils/downloadByRange.d.ts +46 -10
- package/lib/sync/utils/downloadByRange.d.ts.map +1 -1
- package/lib/sync/utils/downloadByRange.js +153 -24
- 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 +16 -2
- package/lib/sync/utils/downloadByRoot.js.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts +0 -1
- package/lib/sync/utils/pendingBlocksTree.d.ts.map +1 -1
- package/lib/sync/utils/pendingBlocksTree.js +0 -9
- package/lib/sync/utils/pendingBlocksTree.js.map +1 -1
- package/package.json +15 -15
- package/src/api/impl/beacon/blocks/index.ts +21 -5
- package/src/api/impl/beacon/pool/index.ts +83 -1
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +80 -0
- package/src/chain/blocks/importBlock.ts +10 -35
- package/src/chain/blocks/importExecutionPayload.ts +57 -21
- package/src/chain/blocks/index.ts +54 -14
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +6 -1
- package/src/chain/blocks/payloadEnvelopeInput/types.ts +1 -0
- package/src/chain/blocks/payloadEnvelopeProcessor.ts +2 -2
- package/src/chain/blocks/types.ts +2 -2
- package/src/chain/blocks/utils/chainSegment.ts +106 -17
- package/src/chain/blocks/verifyBlock.ts +68 -9
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +16 -7
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +2 -2
- package/src/chain/blocks/verifyPayloadsDataAvailability.ts +7 -4
- package/src/chain/chain.ts +16 -3
- package/src/chain/emitter.ts +0 -11
- package/src/chain/errors/blockError.ts +4 -1
- package/src/chain/errors/index.ts +1 -0
- package/src/chain/errors/proposerPreferences.ts +39 -0
- package/src/chain/interface.ts +7 -1
- package/src/chain/opPools/payloadAttestationPool.ts +29 -8
- package/src/chain/prepareNextSlot.ts +20 -28
- package/src/chain/produceBlock/produceBlockBody.ts +51 -23
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +25 -5
- package/src/chain/seenCache/seenProposerPreferences.ts +29 -0
- package/src/chain/validation/block.ts +1 -0
- package/src/chain/validation/proposerPreferences.ts +91 -0
- package/src/metrics/metrics/lodestar.ts +4 -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 +35 -17
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +6 -5
- package/src/network/reqresp/handlers/beaconBlocksByRange.ts +14 -6
- package/src/network/reqresp/handlers/blobSidecarsByRange.ts +11 -5
- package/src/network/reqresp/handlers/dataColumnSidecarsByRange.ts +17 -5
- package/src/network/reqresp/handlers/executionPayloadEnvelopesByRange.ts +7 -4
- package/src/node/notifier.ts +8 -1
- package/src/sync/range/batch.ts +142 -38
- package/src/sync/range/chain.ts +37 -9
- package/src/sync/range/range.ts +18 -6
- package/src/sync/types.ts +72 -0
- package/src/sync/unknownBlock.ts +760 -57
- package/src/sync/utils/downloadByRange.ts +262 -39
- package/src/sync/utils/downloadByRoot.ts +24 -2
- package/src/sync/utils/pendingBlocksTree.ts +0 -15
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
BUILDER_INDEX_SELF_BUILD,
|
|
6
6
|
ForkPostBellatrix,
|
|
7
7
|
ForkPostFulu,
|
|
8
|
+
ForkPostGloas,
|
|
8
9
|
ForkPreGloas,
|
|
9
10
|
NUMBER_OF_COLUMNS,
|
|
10
11
|
SLOTS_PER_HISTORICAL_ROOT,
|
|
@@ -109,6 +110,18 @@ export function getBeaconBlockApi({
|
|
|
109
110
|
seenTimestampSec,
|
|
110
111
|
blockRootHex: blockRoot,
|
|
111
112
|
});
|
|
113
|
+
|
|
114
|
+
if (isForkPostGloas(fork)) {
|
|
115
|
+
chain.seenPayloadEnvelopeInputCache.add({
|
|
116
|
+
blockRootHex: blockRoot,
|
|
117
|
+
block: signedBlock as SignedBeaconBlock<ForkPostGloas>,
|
|
118
|
+
forkName: fork,
|
|
119
|
+
sampledColumns: chain.custodyConfig.sampledColumns,
|
|
120
|
+
custodyColumns: chain.custodyConfig.custodyColumns,
|
|
121
|
+
timeCreatedSec: seenTimestampSec,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
112
125
|
let blobSidecars: deneb.BlobSidecars, dataColumnSidecars: fulu.DataColumnSidecar[];
|
|
113
126
|
|
|
114
127
|
if (isDenebBlockContents(signedBlockContents)) {
|
|
@@ -234,7 +247,7 @@ export function getBeaconBlockApi({
|
|
|
234
247
|
}
|
|
235
248
|
|
|
236
249
|
try {
|
|
237
|
-
await verifyBlocksInEpoch.call(chain as BeaconChain, parentBlock, [blockForImport], {
|
|
250
|
+
await verifyBlocksInEpoch.call(chain as BeaconChain, parentBlock, [blockForImport], null, {
|
|
238
251
|
...opts,
|
|
239
252
|
verifyOnly: true,
|
|
240
253
|
skipVerifyBlockSignatures: true,
|
|
@@ -311,7 +324,10 @@ export function getBeaconBlockApi({
|
|
|
311
324
|
chain
|
|
312
325
|
.processBlock(blockForImport, opts)
|
|
313
326
|
.catch((e) => {
|
|
314
|
-
if (
|
|
327
|
+
if (
|
|
328
|
+
e instanceof BlockError &&
|
|
329
|
+
(e.type.code === BlockErrorCode.PARENT_UNKNOWN || e.type.code === BlockErrorCode.PARENT_PAYLOAD_UNKNOWN)
|
|
330
|
+
) {
|
|
315
331
|
chain.emitter.emit(ChainEvent.blockUnknownParent, {
|
|
316
332
|
blockInput: blockForImport,
|
|
317
333
|
peer: IDENTITY_PEER_ID,
|
|
@@ -773,9 +789,9 @@ export function getBeaconBlockApi({
|
|
|
773
789
|
// Track metrics for data column publishing
|
|
774
790
|
if (dataColumnSidecars.length > 0) {
|
|
775
791
|
let columnsPublishedWithZeroPeers = 0;
|
|
776
|
-
// Skip first entry (envelope),
|
|
777
|
-
for (let i =
|
|
778
|
-
const sentPeers = sentPeersArr[i] as number;
|
|
792
|
+
// Skip first entry (envelope); the final entry is processExecutionPayload(), which returns void.
|
|
793
|
+
for (let i = 0; i < dataColumnSidecars.length; i++) {
|
|
794
|
+
const sentPeers = sentPeersArr[i + 1] as number;
|
|
779
795
|
metrics?.dataColumns.sentPeersPerSubnet.observe(sentPeers);
|
|
780
796
|
if (sentPeers === 0) {
|
|
781
797
|
columnsPublishedWithZeroPeers++;
|
|
@@ -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
|
*
|
|
@@ -118,7 +118,7 @@ export function getLodestarApi({
|
|
|
118
118
|
return {
|
|
119
119
|
// biome-ignore lint/complexity/useLiteralKeys: The `blockProcessor` is a protected attribute
|
|
120
120
|
data: (chain as BeaconChain)["blockProcessor"].jobQueue.getItems().map((item) => {
|
|
121
|
-
const [blockInputs, opts] = item.args;
|
|
121
|
+
const [blockInputs, _payloadEnvelopes, opts] = item.args;
|
|
122
122
|
return {
|
|
123
123
|
blockSlots: blockInputs.map((blockInput) => blockInput.slot),
|
|
124
124
|
jobOpts: opts,
|
|
@@ -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
|
*
|
|
@@ -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";
|
|
@@ -135,22 +118,14 @@ export async function importBlock(
|
|
|
135
118
|
// Some block event handlers require state being in state cache so need to do this before emitting EventType.block
|
|
136
119
|
this.regen.processState(blockRootHex, postState);
|
|
137
120
|
|
|
138
|
-
// For
|
|
139
|
-
if (fork >= ForkSeq.gloas) {
|
|
140
|
-
const payloadInput = this.seenPayloadEnvelopeInputCache.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
timeCreatedSec: fullyVerifiedBlock.seenTimestampSec,
|
|
147
|
-
});
|
|
148
|
-
this.logger.debug("Created PayloadEnvelopeInput for block", {
|
|
149
|
-
slot: blockSlot,
|
|
150
|
-
root: blockRootHex,
|
|
151
|
-
source: source.source,
|
|
152
|
-
...(opts.seenTimestampSec !== undefined ? {recvToImport: Date.now() / 1000 - opts.seenTimestampSec} : {}),
|
|
153
|
-
});
|
|
121
|
+
// For range sync we skip triggerGetBlobs because column fetching is handled in the range path.
|
|
122
|
+
if (fork >= ForkSeq.gloas && !opts.fromRangeSync) {
|
|
123
|
+
const payloadInput = this.seenPayloadEnvelopeInputCache.get(blockRootHex);
|
|
124
|
+
// PayloadEnvelopeInput is supposed to have right after we have block
|
|
125
|
+
// there are 4 sources of them: gossip, by root, by range and api
|
|
126
|
+
if (!payloadInput) {
|
|
127
|
+
throw Error(`PayloadEnvelopeInput not seeded for block ${blockRootHex} before importBlock`);
|
|
128
|
+
}
|
|
154
129
|
|
|
155
130
|
// Immediately attempt fetch of data columns from execution engine as the bid contains kzg commitments
|
|
156
131
|
// 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 {fromHex} from "@lodestar/utils";
|
|
2
|
+
import {ExecutionStatus, PayloadExecutionStatus, getSafeExecutionBlockHash} from "@lodestar/fork-choice";
|
|
3
|
+
import {DataAvailabilityStatus, isStatePostGloas} from "@lodestar/state-transition";
|
|
4
|
+
import {fromHex, 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";
|
|
@@ -75,21 +76,24 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
|
|
|
75
76
|
* The envelope is only verified here, no state mutation. State effects from the payload
|
|
76
77
|
* are applied on the next block via processParentExecutionPayload.
|
|
77
78
|
*
|
|
79
|
+
* The DA wait must have run upstream (range sync awaits DA in `verifyBlocksInEpoch` for the
|
|
80
|
+
* whole segment; gossip / API path uses the `processExecutionPayload` wrapper below).
|
|
81
|
+
*
|
|
78
82
|
* Steps:
|
|
79
83
|
* 1. Emit `execution_payload_available` event for payload attestation
|
|
80
84
|
* 2. Get the ProtoBlock from fork choice
|
|
81
|
-
* 3.
|
|
82
|
-
* 4.
|
|
83
|
-
* 5.
|
|
84
|
-
* 6.
|
|
85
|
-
* 7.
|
|
85
|
+
* 3. Regenerate state for envelope verification
|
|
86
|
+
* 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
|
|
87
|
+
* 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
|
|
88
|
+
* 6. Update fork choice (transitions the block's PENDING variant to FULL)
|
|
89
|
+
* 7. Queue notifyForkchoiceUpdate to engine api
|
|
86
90
|
* 8. Record metrics for payload envelope and column sources
|
|
87
91
|
* 9. Emit `execution_payload` event
|
|
88
92
|
*/
|
|
89
93
|
export async function importExecutionPayload(
|
|
90
94
|
this: BeaconChain,
|
|
91
95
|
payloadInput: PayloadEnvelopeInput,
|
|
92
|
-
|
|
96
|
+
dataAvailabilityStatus: DataAvailabilityStatus,
|
|
93
97
|
opts: ImportPayloadOpts = {}
|
|
94
98
|
): Promise<void> {
|
|
95
99
|
const signedEnvelope = payloadInput.getPayloadEnvelope();
|
|
@@ -119,11 +123,7 @@ export async function importExecutionPayload(
|
|
|
119
123
|
});
|
|
120
124
|
}
|
|
121
125
|
|
|
122
|
-
// 3.
|
|
123
|
-
// The helper is shared with future gloas sync services; take the single-item batch form here.
|
|
124
|
-
await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
125
|
-
|
|
126
|
-
// 4. Regenerate state for envelope verification
|
|
126
|
+
// 3. Regenerate state for envelope verification
|
|
127
127
|
const blockState = await this.regen.getBlockSlotState(
|
|
128
128
|
protoBlock,
|
|
129
129
|
protoBlock.slot,
|
|
@@ -137,7 +137,7 @@ export async function importExecutionPayload(
|
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
//
|
|
140
|
+
// 4. Verify envelope fields against state first to fail fast before the EL + BLS work.
|
|
141
141
|
// When validSignature is true, gossip/API has already verified both the signature and the
|
|
142
142
|
// executionRequestsRoot, so we skip those checks here.
|
|
143
143
|
try {
|
|
@@ -154,7 +154,7 @@ export async function importExecutionPayload(
|
|
|
154
154
|
);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
//
|
|
157
|
+
// 4a. Run EL and signature verification in parallel
|
|
158
158
|
const [execResult, signatureValid] = await Promise.all([
|
|
159
159
|
this.executionEngine.notifyNewPayload(
|
|
160
160
|
fork,
|
|
@@ -176,12 +176,12 @@ export async function importExecutionPayload(
|
|
|
176
176
|
),
|
|
177
177
|
]);
|
|
178
178
|
|
|
179
|
-
//
|
|
179
|
+
// 4b. Check signature verification result
|
|
180
180
|
if (!signatureValid) {
|
|
181
181
|
throw new PayloadError({code: PayloadErrorCode.INVALID_SIGNATURE});
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
-
//
|
|
184
|
+
// 4c. Handle EL response
|
|
185
185
|
switch (execResult.status) {
|
|
186
186
|
case ExecutionPayloadStatus.VALID:
|
|
187
187
|
break;
|
|
@@ -207,7 +207,7 @@ export async function importExecutionPayload(
|
|
|
207
207
|
});
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
//
|
|
210
|
+
// 5. Persist payload envelope to hot DB. Wait for write-queue space here to apply backpressure
|
|
211
211
|
// on the import pipeline during sync, then perform the write asynchronously to avoid blocking.
|
|
212
212
|
await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
|
|
213
213
|
this.unfinalizedPayloadEnvelopeWrites.push(payloadInput).catch((e) => {
|
|
@@ -220,9 +220,27 @@ export async function importExecutionPayload(
|
|
|
220
220
|
}
|
|
221
221
|
});
|
|
222
222
|
|
|
223
|
-
//
|
|
223
|
+
// 6. Update fork choice, transitions the block's PENDING variant to FULL
|
|
224
224
|
const execStatus = toForkChoiceExecutionStatus(execResult.status);
|
|
225
|
-
this.forkChoice.onExecutionPayload(
|
|
225
|
+
this.forkChoice.onExecutionPayload(
|
|
226
|
+
blockRootHex,
|
|
227
|
+
blockHashHex,
|
|
228
|
+
envelope.payload.blockNumber,
|
|
229
|
+
execStatus,
|
|
230
|
+
dataAvailabilityStatus
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// 7. Queue notifyForkchoiceUpdate to engine api
|
|
234
|
+
const head = this.forkChoice.getHead();
|
|
235
|
+
if (!this.opts.disableImportExecutionFcU && blockRootHex === head.blockRoot) {
|
|
236
|
+
const safeBlockHash = getSafeExecutionBlockHash(this.forkChoice);
|
|
237
|
+
const finalizedBlockHash = this.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
238
|
+
this.executionEngine.notifyForkchoiceUpdate(fork, blockHashHex, safeBlockHash, finalizedBlockHash).catch((e) => {
|
|
239
|
+
if (!isErrorAborted(e) && !isQueueErrorAborted(e)) {
|
|
240
|
+
this.logger.error("Error pushing notifyForkchoiceUpdate()", {blockHashHex, finalizedBlockHash}, e);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
226
244
|
|
|
227
245
|
// 8. Record metrics for payload envelope and column sources
|
|
228
246
|
this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
|
|
@@ -249,3 +267,21 @@ export async function importExecutionPayload(
|
|
|
249
267
|
blockHash: blockHashHex,
|
|
250
268
|
});
|
|
251
269
|
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Process an execution payload envelope end-to-end: wait for DA, then import.
|
|
273
|
+
*
|
|
274
|
+
* Used by the PayloadEnvelopeProcessor queue (gossip / API / unknown-payload sync) — i.e.
|
|
275
|
+
* callers that have NOT already awaited DA themselves. Range sync's inline dispatch in
|
|
276
|
+
* processBlocks skips this wrapper and calls `importExecutionPayload` directly, since
|
|
277
|
+
* `verifyBlocksInEpoch` already awaited DA for the segment.
|
|
278
|
+
*/
|
|
279
|
+
export async function processExecutionPayload(
|
|
280
|
+
this: BeaconChain,
|
|
281
|
+
payloadInput: PayloadEnvelopeInput,
|
|
282
|
+
signal: AbortSignal,
|
|
283
|
+
opts: ImportPayloadOpts = {}
|
|
284
|
+
): Promise<void> {
|
|
285
|
+
const {dataAvailabilityStatuses} = await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
286
|
+
await importExecutionPayload.call(this, payloadInput, dataAvailabilityStatuses[0], opts);
|
|
287
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {SignedBeaconBlock} from "@lodestar/types";
|
|
1
|
+
import {SignedBeaconBlock, Slot} from "@lodestar/types";
|
|
2
2
|
import {isErrorAborted, toRootHex} from "@lodestar/utils";
|
|
3
3
|
import {Metrics} from "../../metrics/metrics.js";
|
|
4
4
|
import {nextEventLoop} from "../../util/eventLoop.js";
|
|
@@ -8,6 +8,8 @@ import {BlockError, BlockErrorCode, isBlockErrorAborted} from "../errors/index.j
|
|
|
8
8
|
import {BlockProcessOpts} from "../options.js";
|
|
9
9
|
import {IBlockInput} from "./blockInput/types.js";
|
|
10
10
|
import {importBlock} from "./importBlock.js";
|
|
11
|
+
import {importExecutionPayload} from "./importExecutionPayload.js";
|
|
12
|
+
import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
11
13
|
import {FullyVerifiedBlock, ImportBlockOpts} from "./types.js";
|
|
12
14
|
import {assertLinearChainSegment} from "./utils/chainSegment.js";
|
|
13
15
|
import {verifyBlocksInEpoch} from "./verifyBlock.js";
|
|
@@ -21,20 +23,24 @@ const QUEUE_MAX_LENGTH = 256;
|
|
|
21
23
|
* BlockProcessor processes block jobs in a queued fashion, one after the other.
|
|
22
24
|
*/
|
|
23
25
|
export class BlockProcessor {
|
|
24
|
-
readonly jobQueue: JobItemQueue<[IBlockInput[], ImportBlockOpts], void>;
|
|
26
|
+
readonly jobQueue: JobItemQueue<[IBlockInput[], Map<Slot, PayloadEnvelopeInput> | null, ImportBlockOpts], void>;
|
|
25
27
|
|
|
26
28
|
constructor(chain: BeaconChain, metrics: Metrics | null, opts: BlockProcessOpts, signal: AbortSignal) {
|
|
27
|
-
this.jobQueue = new JobItemQueue<[IBlockInput[], ImportBlockOpts], void>(
|
|
28
|
-
(job, importOpts) => {
|
|
29
|
-
return processBlocks.call(chain, job, {...opts, ...importOpts});
|
|
29
|
+
this.jobQueue = new JobItemQueue<[IBlockInput[], Map<Slot, PayloadEnvelopeInput> | null, ImportBlockOpts], void>(
|
|
30
|
+
(job, payloadEnvelopes, importOpts) => {
|
|
31
|
+
return processBlocks.call(chain, job, payloadEnvelopes, {...opts, ...importOpts});
|
|
30
32
|
},
|
|
31
33
|
{maxLength: QUEUE_MAX_LENGTH, noYieldIfOneItem: true, signal},
|
|
32
34
|
metrics?.blockProcessorQueue ?? undefined
|
|
33
35
|
);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
async processBlocksJob(
|
|
37
|
-
|
|
38
|
+
async processBlocksJob(
|
|
39
|
+
job: IBlockInput[],
|
|
40
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
41
|
+
opts: ImportBlockOpts = {}
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
await this.jobQueue.push(job, payloadEnvelopes, opts);
|
|
38
44
|
}
|
|
39
45
|
}
|
|
40
46
|
|
|
@@ -51,16 +57,13 @@ export class BlockProcessor {
|
|
|
51
57
|
export async function processBlocks(
|
|
52
58
|
this: BeaconChain,
|
|
53
59
|
blocks: IBlockInput[],
|
|
60
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
54
61
|
opts: BlockProcessOpts & ImportBlockOpts
|
|
55
62
|
): Promise<void> {
|
|
56
63
|
if (blocks.length === 0) {
|
|
57
64
|
return; // TODO: or throw?
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
if (blocks.length > 1) {
|
|
61
|
-
assertLinearChainSegment(this.config, blocks);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
67
|
try {
|
|
65
68
|
const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
|
|
66
69
|
|
|
@@ -70,10 +73,31 @@ export async function processBlocks(
|
|
|
70
73
|
return;
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
const {warnings: orphanedPayloads} = assertLinearChainSegment(
|
|
77
|
+
this.config,
|
|
78
|
+
relevantBlocks,
|
|
79
|
+
payloadEnvelopes,
|
|
80
|
+
parentBlock
|
|
81
|
+
);
|
|
82
|
+
if (orphanedPayloads != null) {
|
|
83
|
+
for (const orphaned of orphanedPayloads) {
|
|
84
|
+
this.logger.debug("Orphaned payload envelope in chain segment", {
|
|
85
|
+
slot: orphaned.slot,
|
|
86
|
+
blockRoot: orphaned.payloadEnvelopeInput.blockRootHex,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
73
91
|
// Fully verify a block to be imported immediately after. Does not produce any side-effects besides adding intermediate
|
|
74
92
|
// states in the state cache through regen.
|
|
75
|
-
const {
|
|
76
|
-
|
|
93
|
+
const {
|
|
94
|
+
postStates,
|
|
95
|
+
blockDAStatuses,
|
|
96
|
+
payloadDAStatuses,
|
|
97
|
+
proposerBalanceDeltas,
|
|
98
|
+
segmentExecStatus,
|
|
99
|
+
indexedAttestationsByBlock,
|
|
100
|
+
} = await verifyBlocksInEpoch.call(this, parentBlock, relevantBlocks, payloadEnvelopes, opts);
|
|
77
101
|
|
|
78
102
|
// If segmentExecStatus has lvhForkchoice then, the entire segment should be invalid
|
|
79
103
|
// and we need to further propagate
|
|
@@ -92,7 +116,7 @@ export async function processBlocks(
|
|
|
92
116
|
parentBlockSlot: parentSlots[i],
|
|
93
117
|
executionStatus: executionStatuses[i],
|
|
94
118
|
// start supporting optimistic syncing/processing
|
|
95
|
-
dataAvailabilityStatus:
|
|
119
|
+
dataAvailabilityStatus: blockDAStatuses[i],
|
|
96
120
|
proposerBalanceDelta: proposerBalanceDeltas[i],
|
|
97
121
|
indexedAttestations: indexedAttestationsByBlock[i],
|
|
98
122
|
// TODO: Make this param mandatory and capture in gossip
|
|
@@ -103,6 +127,22 @@ export async function processBlocks(
|
|
|
103
127
|
for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
|
|
104
128
|
// TODO: Consider batching importBlock too if it takes significant time
|
|
105
129
|
await importBlock.call(this, fullyVerifiedBlock, opts);
|
|
130
|
+
|
|
131
|
+
const slot = fullyVerifiedBlock.blockInput.getBlock().message.slot;
|
|
132
|
+
const payloadInput = payloadEnvelopes?.get(slot);
|
|
133
|
+
if (payloadInput?.hasPayloadEnvelope()) {
|
|
134
|
+
if (!payloadInput.isComplete()) {
|
|
135
|
+
// we validated DA before reaching this
|
|
136
|
+
throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
|
|
137
|
+
}
|
|
138
|
+
// we already awaited DA in verifyBlocksInEpoch for this segment
|
|
139
|
+
const payloadDA = payloadDAStatuses.get(slot);
|
|
140
|
+
if (payloadDA === undefined) {
|
|
141
|
+
throw new Error(`Missing payload DA status for slot ${slot}`);
|
|
142
|
+
}
|
|
143
|
+
await importExecutionPayload.call(this, payloadInput, payloadDA, {validSignature: false});
|
|
144
|
+
}
|
|
145
|
+
|
|
106
146
|
await nextEventLoop();
|
|
107
147
|
}
|
|
108
148
|
} catch (e) {
|
|
@@ -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,
|