@lodestar/beacon-node 1.43.0-dev.921c57528b → 1.43.0-dev.958956d6ba
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 +6 -5
- package/lib/api/impl/beacon/blocks/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/chain/blocks/importBlock.d.ts.map +1 -1
- package/lib/chain/blocks/importBlock.js +3 -2
- 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 +4 -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 +5 -3
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +19 -4
- package/lib/chain/chain.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 +5 -3
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.d.ts +2 -2
- package/lib/chain/opPools/payloadAttestationPool.d.ts.map +1 -1
- package/lib/chain/opPools/payloadAttestationPool.js +4 -4
- package/lib/chain/opPools/payloadAttestationPool.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +14 -2
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +35 -16
- 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 +8 -2
- 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/processor/gossipHandlers.d.ts.map +1 -1
- package/lib/network/processor/gossipHandlers.js +10 -6
- 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 +1 -0
- 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 +12 -2
- package/lib/sync/range/batch.d.ts.map +1 -1
- package/lib/sync/range/batch.js +56 -30
- 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 +4 -3
- 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 +24 -1
- package/lib/sync/unknownBlock.d.ts.map +1 -1
- package/lib/sync/unknownBlock.js +649 -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 +147 -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 +6 -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 +16 -15
- package/src/api/impl/beacon/blocks/index.ts +8 -5
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/chain/blocks/importBlock.ts +3 -2
- package/src/chain/blocks/importExecutionPayload.ts +57 -21
- package/src/chain/blocks/index.ts +54 -14
- package/src/chain/blocks/payloadEnvelopeInput/payloadEnvelopeInput.ts +5 -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 +28 -3
- 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 +9 -1
- package/src/chain/opPools/payloadAttestationPool.ts +4 -8
- package/src/chain/prepareNextSlot.ts +24 -3
- package/src/chain/produceBlock/produceBlockBody.ts +41 -14
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/seenCache/index.ts +1 -0
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +13 -3
- 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/processor/gossipHandlers.ts +15 -6
- package/src/network/processor/gossipQueues/index.ts +5 -0
- package/src/network/processor/index.ts +1 -0
- 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 +90 -35
- package/src/sync/range/chain.ts +13 -5
- package/src/sync/range/range.ts +18 -6
- package/src/sync/types.ts +72 -0
- package/src/sync/unknownBlock.ts +810 -57
- package/src/sync/utils/downloadByRange.ts +256 -39
- package/src/sync/utils/downloadByRoot.ts +12 -2
- package/src/sync/utils/pendingBlocksTree.ts +0 -15
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import {ExecutionStatus, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
|
-
import {ForkName, isForkPostFulu} from "@lodestar/params";
|
|
2
|
+
import {ForkName, ForkSeq, isForkPostFulu} from "@lodestar/params";
|
|
3
3
|
import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
4
|
-
import {IndexedAttestation, deneb} from "@lodestar/types";
|
|
4
|
+
import {IndexedAttestation, Slot, deneb} from "@lodestar/types";
|
|
5
|
+
import {getBlobKzgCommitments} from "../../util/dataColumns.js";
|
|
5
6
|
import type {BeaconChain} from "../chain.js";
|
|
6
7
|
import {BlockError, BlockErrorCode} from "../errors/index.js";
|
|
7
8
|
import {BlockProcessOpts} from "../options.js";
|
|
8
9
|
import {RegenCaller} from "../regen/index.js";
|
|
9
10
|
import {DAType, IBlockInput} from "./blockInput/index.js";
|
|
11
|
+
import {PayloadEnvelopeInput} from "./payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
10
12
|
import {ImportBlockOpts} from "./types.js";
|
|
11
13
|
import {DENEB_BLOWFISH_BANNER} from "./utils/blowfishBanner.js";
|
|
12
14
|
import {ELECTRA_GIRAFFE_BANNER} from "./utils/giraffeBanner.js";
|
|
@@ -16,6 +18,7 @@ import {verifyBlocksDataAvailability} from "./verifyBlocksDataAvailability.js";
|
|
|
16
18
|
import {SegmentExecStatus, verifyBlocksExecutionPayload} from "./verifyBlocksExecutionPayloads.js";
|
|
17
19
|
import {verifyBlocksSignatures} from "./verifyBlocksSignatures.js";
|
|
18
20
|
import {verifyBlocksStateTransitionOnly} from "./verifyBlocksStateTransitionOnly.js";
|
|
21
|
+
import {verifyPayloadsDataAvailability} from "./verifyPayloadsDataAvailability.js";
|
|
19
22
|
|
|
20
23
|
/**
|
|
21
24
|
* Verifies 1 or more blocks are fully valid; from a linear sequence of blocks.
|
|
@@ -32,12 +35,14 @@ export async function verifyBlocksInEpoch(
|
|
|
32
35
|
this: BeaconChain,
|
|
33
36
|
parentBlock: ProtoBlock,
|
|
34
37
|
blockInputs: IBlockInput[],
|
|
38
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
35
39
|
opts: BlockProcessOpts & ImportBlockOpts
|
|
36
40
|
): Promise<{
|
|
37
41
|
postStates: IBeaconStateView[];
|
|
38
42
|
proposerBalanceDeltas: number[];
|
|
39
43
|
segmentExecStatus: SegmentExecStatus;
|
|
40
|
-
|
|
44
|
+
blockDAStatuses: DataAvailabilityStatus[];
|
|
45
|
+
payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
|
|
41
46
|
indexedAttestationsByBlock: IndexedAttestation[][];
|
|
42
47
|
}> {
|
|
43
48
|
const blocks = blockInputs.map((blockInput) => blockInput.getBlock());
|
|
@@ -110,17 +115,59 @@ export async function verifyBlocksInEpoch(
|
|
|
110
115
|
});
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
// Pick the data-availability source by fork:
|
|
119
|
+
// - Pre-Gloas: blob/Fulu-column data lives in IBlockInput → verifyBlocksDataAvailability.
|
|
120
|
+
// - Post-Gloas: verifyPayloadsDataAvailability (payload-level DA, keyed by slot).
|
|
121
|
+
const daAvailabilityPromise: Promise<{
|
|
122
|
+
blockDAStatuses: DataAvailabilityStatus[];
|
|
123
|
+
payloadDAStatuses: Map<Slot, DataAvailabilityStatus>;
|
|
124
|
+
availableTime: number;
|
|
125
|
+
}> =
|
|
126
|
+
fork >= ForkSeq.gloas
|
|
127
|
+
? (async () => {
|
|
128
|
+
const payloadInputsForDa: PayloadEnvelopeInput[] = [];
|
|
129
|
+
for (const input of blockInputs) {
|
|
130
|
+
const pi = payloadEnvelopes?.get(input.slot);
|
|
131
|
+
if (pi !== undefined) payloadInputsForDa.push(pi);
|
|
132
|
+
}
|
|
133
|
+
const {dataAvailabilityStatuses, availableTime} = await verifyPayloadsDataAvailability(
|
|
134
|
+
payloadInputsForDa,
|
|
135
|
+
abortController.signal
|
|
136
|
+
);
|
|
137
|
+
const payloadDAStatuses = new Map<Slot, DataAvailabilityStatus>();
|
|
138
|
+
for (let i = 0; i < payloadInputsForDa.length; i++) {
|
|
139
|
+
payloadDAStatuses.set(payloadInputsForDa[i].slot, dataAvailabilityStatuses[i]);
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
// post-gloas, DataAvailabilityStatus is NotRequired for forkChoice.onBlock() ProtoBlock
|
|
143
|
+
blockDAStatuses: blockInputs.map(() => DataAvailabilityStatus.NotRequired),
|
|
144
|
+
payloadDAStatuses,
|
|
145
|
+
availableTime,
|
|
146
|
+
};
|
|
147
|
+
})()
|
|
148
|
+
: (async () => {
|
|
149
|
+
const {dataAvailabilityStatuses, availableTime} = await verifyBlocksDataAvailability(
|
|
150
|
+
blockInputs,
|
|
151
|
+
abortController.signal
|
|
152
|
+
);
|
|
153
|
+
return {
|
|
154
|
+
blockDAStatuses: dataAvailabilityStatuses,
|
|
155
|
+
payloadDAStatuses: new Map<Slot, DataAvailabilityStatus>(),
|
|
156
|
+
availableTime,
|
|
157
|
+
};
|
|
158
|
+
})();
|
|
159
|
+
|
|
113
160
|
// batch all I/O operations to reduce overhead
|
|
114
161
|
const [
|
|
115
162
|
segmentExecStatus,
|
|
116
|
-
{
|
|
163
|
+
{blockDAStatuses, payloadDAStatuses, availableTime},
|
|
117
164
|
{postStates, proposerBalanceDeltas, verifyStateTime},
|
|
118
165
|
{verifySignaturesTime},
|
|
119
166
|
] = await Promise.all([
|
|
120
167
|
verifyExecutionPayloadsPromise,
|
|
121
168
|
|
|
122
|
-
// data availability
|
|
123
|
-
|
|
169
|
+
// data availability (fork-specific; see daAvailabilityPromise above)
|
|
170
|
+
daAvailabilityPromise,
|
|
124
171
|
|
|
125
172
|
// Run state transition only
|
|
126
173
|
// TODO: Ensure it yields to allow flushing to workers and engine API
|
|
@@ -149,6 +196,9 @@ export async function verifyBlocksInEpoch(
|
|
|
149
196
|
opts
|
|
150
197
|
)
|
|
151
198
|
: Promise.resolve({verifySignaturesTime: Date.now()}),
|
|
199
|
+
|
|
200
|
+
// TODO GLOAS: can verify payload signatures in batch too
|
|
201
|
+
// maybe chain with the above verifyBlocksSignatures()
|
|
152
202
|
]);
|
|
153
203
|
|
|
154
204
|
if (opts.verifyOnly !== true) {
|
|
@@ -200,7 +250,9 @@ export async function verifyBlocksInEpoch(
|
|
|
200
250
|
blockInputs.length === 1 &&
|
|
201
251
|
// gossip blocks have seenTimestampSec
|
|
202
252
|
opts.seenTimestampSec !== undefined &&
|
|
253
|
+
// PreData (pre-deneb) and NoData (gloas) carry no blob data on the block — skip metric
|
|
203
254
|
blockInputs[0].type !== DAType.PreData &&
|
|
255
|
+
blockInputs[0].type !== DAType.NoData &&
|
|
204
256
|
executionStatuses[0] === ExecutionStatus.Valid
|
|
205
257
|
) {
|
|
206
258
|
// Find the max time when the block was actually verified
|
|
@@ -209,8 +261,8 @@ export async function verifyBlocksInEpoch(
|
|
|
209
261
|
this.metrics?.gossipBlock.receivedToFullyVerifiedTime.observe(recvTofullyVerifedTime);
|
|
210
262
|
|
|
211
263
|
const verifiedToBlobsAvailabiltyTime = Math.max(availableTime - fullyVerifiedTime, 0) / 1000;
|
|
212
|
-
const block = blockInputs[0].getBlock()
|
|
213
|
-
const numBlobs = block.
|
|
264
|
+
const block = blockInputs[0].getBlock();
|
|
265
|
+
const numBlobs = getBlobKzgCommitments(blockInputs[0].forkName, block as deneb.SignedBeaconBlock).length;
|
|
214
266
|
|
|
215
267
|
this.metrics?.gossipBlock.verifiedToBlobsAvailabiltyTime.observe({numBlobs}, verifiedToBlobsAvailabiltyTime);
|
|
216
268
|
this.logger.verbose("Verified blockInput fully with blobs availability", {
|
|
@@ -229,7 +281,14 @@ export async function verifyBlocksInEpoch(
|
|
|
229
281
|
);
|
|
230
282
|
}
|
|
231
283
|
|
|
232
|
-
return {
|
|
284
|
+
return {
|
|
285
|
+
postStates,
|
|
286
|
+
blockDAStatuses,
|
|
287
|
+
payloadDAStatuses,
|
|
288
|
+
proposerBalanceDeltas,
|
|
289
|
+
segmentExecStatus,
|
|
290
|
+
indexedAttestationsByBlock,
|
|
291
|
+
};
|
|
233
292
|
} finally {
|
|
234
293
|
abortController.abort();
|
|
235
294
|
}
|
|
@@ -90,15 +90,24 @@ export function verifyBlocksSanityChecks(
|
|
|
90
90
|
} else {
|
|
91
91
|
// When importing a block segment, only the first NON-IGNORED block must be known to the fork-choice.
|
|
92
92
|
const parentRoot = toRootHex(block.message.parentRoot);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
parentRoot,
|
|
96
|
-
toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash)
|
|
97
|
-
)
|
|
98
|
-
: chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
99
|
-
if (!parentBlock) {
|
|
93
|
+
const parentBlockDefaultStatus = chain.forkChoice.getBlockHexDefaultStatus(parentRoot);
|
|
94
|
+
if (!parentBlockDefaultStatus) {
|
|
100
95
|
throw new BlockError(block, {code: BlockErrorCode.PARENT_UNKNOWN, parentRoot});
|
|
101
96
|
}
|
|
97
|
+
|
|
98
|
+
parentBlock = parentBlockDefaultStatus;
|
|
99
|
+
if (isGloasBeaconBlock(block.message)) {
|
|
100
|
+
const parentBlockHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
|
|
101
|
+
const parentBlockWithPayload = chain.forkChoice.getBlockHexAndBlockHash(parentRoot, parentBlockHash);
|
|
102
|
+
if (!parentBlockWithPayload) {
|
|
103
|
+
throw new BlockError(block, {
|
|
104
|
+
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
105
|
+
parentRoot,
|
|
106
|
+
parentBlockHash,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
parentBlock = parentBlockWithPayload;
|
|
110
|
+
}
|
|
102
111
|
// Parent is known to the fork-choice
|
|
103
112
|
parentBlockSlot = parentBlock.slot;
|
|
104
113
|
}
|
|
@@ -32,8 +32,8 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
32
32
|
const payload = envelope.payload;
|
|
33
33
|
|
|
34
34
|
// Verify consistency with the beacon block.
|
|
35
|
-
// Compute header root on a
|
|
36
|
-
const headerValue =
|
|
35
|
+
// Compute header root on a clone of latestBlockHeader to avoid mutating state.
|
|
36
|
+
const headerValue = ssz.phase0.BeaconBlockHeader.clone(state.latestBlockHeader);
|
|
37
37
|
if (byteArrayEquals(headerValue.stateRoot, ssz.Root.defaultValue())) {
|
|
38
38
|
headerValue.stateRoot = state.hashTreeRoot();
|
|
39
39
|
}
|
|
@@ -28,11 +28,14 @@ export async function verifyPayloadsDataAvailability(
|
|
|
28
28
|
await Promise.all(promises);
|
|
29
29
|
|
|
30
30
|
const availableTime = Math.max(0, Math.max(...payloadInputs.map((payloadInput) => payloadInput.getTimeComplete())));
|
|
31
|
-
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) =>
|
|
32
|
-
payloadInput.
|
|
31
|
+
const dataAvailabilityStatuses: DataAvailabilityStatus[] = payloadInputs.map((payloadInput) => {
|
|
32
|
+
if (payloadInput.daOutOfRange) {
|
|
33
|
+
return DataAvailabilityStatus.OutOfRange;
|
|
34
|
+
}
|
|
35
|
+
return payloadInput.getBlobKzgCommitments().length === 0
|
|
33
36
|
? DataAvailabilityStatus.NotRequired
|
|
34
|
-
: DataAvailabilityStatus.Available
|
|
35
|
-
);
|
|
37
|
+
: DataAvailabilityStatus.Available;
|
|
38
|
+
});
|
|
36
39
|
|
|
37
40
|
return {dataAvailabilityStatuses, availableTime};
|
|
38
41
|
}
|
package/src/chain/chain.ts
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
ValidatorIndex,
|
|
40
40
|
Wei,
|
|
41
41
|
deneb,
|
|
42
|
+
electra,
|
|
42
43
|
gloas,
|
|
43
44
|
isBlindedBeaconBlock,
|
|
44
45
|
phase0,
|
|
@@ -105,6 +106,7 @@ import {
|
|
|
105
106
|
SeenExecutionPayloadBids,
|
|
106
107
|
SeenPayloadAttesters,
|
|
107
108
|
SeenPayloadEnvelopeInput,
|
|
109
|
+
SeenProposerPreferences,
|
|
108
110
|
SeenSyncCommitteeMessages,
|
|
109
111
|
} from "./seenCache/index.js";
|
|
110
112
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -185,6 +187,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
185
187
|
readonly seenPayloadAttesters = new SeenPayloadAttesters();
|
|
186
188
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
187
189
|
readonly seenExecutionPayloadBids = new SeenExecutionPayloadBids();
|
|
190
|
+
readonly seenProposerPreferences = new SeenProposerPreferences();
|
|
188
191
|
readonly seenBlockProposers = new SeenBlockProposers();
|
|
189
192
|
readonly seenSyncCommitteeMessages = new SeenSyncCommitteeMessages();
|
|
190
193
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -333,6 +336,8 @@ export class BeaconChain implements IBeaconChain {
|
|
|
333
336
|
logger,
|
|
334
337
|
});
|
|
335
338
|
this.seenPayloadEnvelopeInputCache = new SeenPayloadEnvelopeInput({
|
|
339
|
+
config,
|
|
340
|
+
clock,
|
|
336
341
|
chainEvents: emitter,
|
|
337
342
|
signal,
|
|
338
343
|
serializedCache: this.serializedCache,
|
|
@@ -886,6 +891,21 @@ export class BeaconChain implements IBeaconChain {
|
|
|
886
891
|
);
|
|
887
892
|
}
|
|
888
893
|
|
|
894
|
+
async getParentExecutionRequests(
|
|
895
|
+
parentBlockSlot: Slot,
|
|
896
|
+
parentBlockRootHex: RootHex
|
|
897
|
+
): Promise<electra.ExecutionRequests> {
|
|
898
|
+
// at the fork boundary, parent is pre-gloas
|
|
899
|
+
if (!isForkPostGloas(this.config.getForkName(parentBlockSlot))) {
|
|
900
|
+
return ssz.electra.ExecutionRequests.defaultValue();
|
|
901
|
+
}
|
|
902
|
+
const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
|
|
903
|
+
if (envelope === null) {
|
|
904
|
+
throw Error(`Parent execution payload envelope not found slot=${parentBlockSlot}, root=${parentBlockRootHex}`);
|
|
905
|
+
}
|
|
906
|
+
return envelope.message.executionRequests;
|
|
907
|
+
}
|
|
908
|
+
|
|
889
909
|
async getDataColumnSidecars(blockSlot: Slot, blockRootHex: string): Promise<DataColumnSidecar[]> {
|
|
890
910
|
const fork = this.config.getForkName(blockSlot);
|
|
891
911
|
|
|
@@ -1082,11 +1102,15 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1082
1102
|
}
|
|
1083
1103
|
|
|
1084
1104
|
async processBlock(block: IBlockInput, opts?: ImportBlockOpts): Promise<void> {
|
|
1085
|
-
return this.blockProcessor.processBlocksJob([block], opts);
|
|
1105
|
+
return this.blockProcessor.processBlocksJob([block], null, opts);
|
|
1086
1106
|
}
|
|
1087
1107
|
|
|
1088
|
-
async processChainSegment(
|
|
1089
|
-
|
|
1108
|
+
async processChainSegment(
|
|
1109
|
+
blocks: IBlockInput[],
|
|
1110
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
1111
|
+
opts?: ImportBlockOpts
|
|
1112
|
+
): Promise<void> {
|
|
1113
|
+
await this.blockProcessor.processBlocksJob(blocks, payloadEnvelopes, opts);
|
|
1090
1114
|
}
|
|
1091
1115
|
|
|
1092
1116
|
async processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void> {
|
|
@@ -1417,6 +1441,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1417
1441
|
this.payloadAttestationPool.prune(slot);
|
|
1418
1442
|
this.executionPayloadBidPool.prune(slot);
|
|
1419
1443
|
this.seenExecutionPayloadBids.prune(slot);
|
|
1444
|
+
this.seenProposerPreferences.prune(slot);
|
|
1420
1445
|
this.seenAttestationDatas.onSlot(slot);
|
|
1421
1446
|
this.reprocessController.onSlot(slot);
|
|
1422
1447
|
|
|
@@ -74,6 +74,8 @@ export enum BlockErrorCode {
|
|
|
74
74
|
PARENT_EXECUTION_INVALID = "BLOCK_ERROR_PARENT_EXECUTION_INVALID",
|
|
75
75
|
/** The block's parent execution payload (defined by bid.parent_block_hash) has not been seen */
|
|
76
76
|
PARENT_PAYLOAD_UNKNOWN = "BLOCK_ERROR_PARENT_PAYLOAD_UNKNOWN",
|
|
77
|
+
/** An execution payload envelope in the chain segment references a block root that does not match its slot's block */
|
|
78
|
+
ENVELOPE_BLOCK_ROOT_MISMATCH = "BLOCK_ERROR_ENVELOPE_BLOCK_ROOT_MISMATCH",
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
type ExecutionErrorStatus = Exclude<
|
|
@@ -107,6 +109,7 @@ export type BlockErrorType =
|
|
|
107
109
|
| {code: BlockErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
108
110
|
| {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS}
|
|
109
111
|
| {code: BlockErrorCode.NON_LINEAR_SLOTS}
|
|
112
|
+
| {code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH; envelopeBlockRoot: RootHex; blockRoot: RootHex}
|
|
110
113
|
| {code: BlockErrorCode.PER_BLOCK_PROCESSING_ERROR; error: Error}
|
|
111
114
|
| {code: BlockErrorCode.BEACON_CHAIN_ERROR; error: Error}
|
|
112
115
|
| {code: BlockErrorCode.KNOWN_BAD_BLOCK}
|
|
@@ -120,7 +123,7 @@ export type BlockErrorType =
|
|
|
120
123
|
| {code: BlockErrorCode.TOO_MANY_KZG_COMMITMENTS; blobKzgCommitmentsLen: number; commitmentLimit: number}
|
|
121
124
|
| {code: BlockErrorCode.BID_PARENT_ROOT_MISMATCH; bidParentRoot: RootHex; blockParentRoot: RootHex}
|
|
122
125
|
| {code: BlockErrorCode.PARENT_EXECUTION_INVALID; parentRoot: RootHex}
|
|
123
|
-
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentBlockHash: RootHex};
|
|
126
|
+
| {code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN; parentRoot: RootHex; parentBlockHash: RootHex};
|
|
124
127
|
|
|
125
128
|
export class BlockGossipError extends GossipActionError<BlockErrorType> {}
|
|
126
129
|
|
|
@@ -8,6 +8,7 @@ export * from "./executionPayloadBid.js";
|
|
|
8
8
|
export * from "./executionPayloadEnvelope.js";
|
|
9
9
|
export * from "./gossipValidation.js";
|
|
10
10
|
export * from "./payloadAttestation.js";
|
|
11
|
+
export * from "./proposerPreferences.js";
|
|
11
12
|
export * from "./proposerSlashingError.js";
|
|
12
13
|
export * from "./syncCommitteeError.js";
|
|
13
14
|
export * from "./voluntaryExitError.js";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import {Slot, ValidatorIndex} from "@lodestar/types";
|
|
2
|
+
import {GossipActionError} from "./gossipValidation.js";
|
|
3
|
+
|
|
4
|
+
export enum ProposerPreferencesErrorCode {
|
|
5
|
+
INVALID_EPOCH = "PROPOSER_PREFERENCES_ERROR_INVALID_EPOCH",
|
|
6
|
+
PROPOSAL_SLOT_PASSED = "PROPOSER_PREFERENCES_ERROR_PROPOSAL_SLOT_PASSED",
|
|
7
|
+
INVALID_PROPOSER = "PROPOSER_PREFERENCES_ERROR_INVALID_PROPOSER",
|
|
8
|
+
ALREADY_KNOWN = "PROPOSER_PREFERENCES_ERROR_ALREADY_KNOWN",
|
|
9
|
+
INVALID_SIGNATURE = "PROPOSER_PREFERENCES_ERROR_INVALID_SIGNATURE",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ProposerPreferencesErrorType =
|
|
13
|
+
| {
|
|
14
|
+
code: ProposerPreferencesErrorCode.INVALID_EPOCH;
|
|
15
|
+
proposalSlot: Slot;
|
|
16
|
+
currentEpoch: number;
|
|
17
|
+
}
|
|
18
|
+
| {
|
|
19
|
+
code: ProposerPreferencesErrorCode.PROPOSAL_SLOT_PASSED;
|
|
20
|
+
proposalSlot: Slot;
|
|
21
|
+
currentSlot: Slot;
|
|
22
|
+
}
|
|
23
|
+
| {
|
|
24
|
+
code: ProposerPreferencesErrorCode.INVALID_PROPOSER;
|
|
25
|
+
proposalSlot: Slot;
|
|
26
|
+
validatorIndex: ValidatorIndex;
|
|
27
|
+
}
|
|
28
|
+
| {
|
|
29
|
+
code: ProposerPreferencesErrorCode.ALREADY_KNOWN;
|
|
30
|
+
proposalSlot: Slot;
|
|
31
|
+
validatorIndex: ValidatorIndex;
|
|
32
|
+
}
|
|
33
|
+
| {
|
|
34
|
+
code: ProposerPreferencesErrorCode.INVALID_SIGNATURE;
|
|
35
|
+
proposalSlot: Slot;
|
|
36
|
+
validatorIndex: ValidatorIndex;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export class ProposerPreferencesError extends GossipActionError<ProposerPreferencesErrorType> {}
|
package/src/chain/interface.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
altair,
|
|
19
19
|
capella,
|
|
20
20
|
deneb,
|
|
21
|
+
electra,
|
|
21
22
|
gloas,
|
|
22
23
|
phase0,
|
|
23
24
|
rewards,
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
SeenContributionAndProof,
|
|
61
62
|
SeenExecutionPayloadBids,
|
|
62
63
|
SeenPayloadAttesters,
|
|
64
|
+
SeenProposerPreferences,
|
|
63
65
|
SeenSyncCommitteeMessages,
|
|
64
66
|
} from "./seenCache/index.js";
|
|
65
67
|
import {SeenAggregatedAttestations} from "./seenCache/seenAggregateAndProof.js";
|
|
@@ -130,6 +132,7 @@ export interface IBeaconChain {
|
|
|
130
132
|
readonly seenPayloadAttesters: SeenPayloadAttesters;
|
|
131
133
|
readonly seenAggregatedAttestations: SeenAggregatedAttestations;
|
|
132
134
|
readonly seenExecutionPayloadBids: SeenExecutionPayloadBids;
|
|
135
|
+
readonly seenProposerPreferences: SeenProposerPreferences;
|
|
133
136
|
readonly seenBlockProposers: SeenBlockProposers;
|
|
134
137
|
readonly seenSyncCommitteeMessages: SeenSyncCommitteeMessages;
|
|
135
138
|
readonly seenContributionAndProof: SeenContributionAndProof;
|
|
@@ -231,6 +234,7 @@ export interface IBeaconChain {
|
|
|
231
234
|
blockSlot: Slot,
|
|
232
235
|
blockRootHex: string
|
|
233
236
|
): Promise<gloas.SignedExecutionPayloadEnvelope | null>;
|
|
237
|
+
getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<electra.ExecutionRequests>;
|
|
234
238
|
|
|
235
239
|
produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
|
|
236
240
|
produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
|
|
@@ -248,7 +252,11 @@ export interface IBeaconChain {
|
|
|
248
252
|
/** Process a block until complete */
|
|
249
253
|
processBlock(block: IBlockInput, opts?: ImportBlockOpts): Promise<void>;
|
|
250
254
|
/** Process a chain of blocks until complete */
|
|
251
|
-
processChainSegment(
|
|
255
|
+
processChainSegment(
|
|
256
|
+
blocks: IBlockInput[],
|
|
257
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
258
|
+
opts?: ImportBlockOpts
|
|
259
|
+
): Promise<void>;
|
|
252
260
|
|
|
253
261
|
/** Process execution payload envelope: verify, import to fork choice, and persist to DB */
|
|
254
262
|
processExecutionPayload(payloadInput: PayloadEnvelopeInput, opts?: ImportPayloadOpts): Promise<void>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {Signature, aggregateSignatures} from "@chainsafe/blst";
|
|
2
2
|
import {BitArray} from "@chainsafe/ssz";
|
|
3
3
|
import {ChainForkConfig} from "@lodestar/config";
|
|
4
|
-
import {MAX_COMMITTEES_PER_SLOT, PTC_SIZE} from "@lodestar/params";
|
|
4
|
+
import {MAX_COMMITTEES_PER_SLOT, MAX_PAYLOAD_ATTESTATIONS, PTC_SIZE} from "@lodestar/params";
|
|
5
5
|
import {RootHex, Slot, gloas} from "@lodestar/types";
|
|
6
6
|
import {MapDef, toRootHex} from "@lodestar/utils";
|
|
7
7
|
import {Metrics} from "../../metrics/metrics.js";
|
|
@@ -95,13 +95,9 @@ export class PayloadAttestationPool {
|
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
97
|
* Get payload attestations to be included in a block.
|
|
98
|
-
* Pick the top `
|
|
98
|
+
* Pick the top `MAX_PAYLOAD_ATTESTATIONS` aggregates with the most votes.
|
|
99
99
|
*/
|
|
100
|
-
getPayloadAttestationsForBlock(
|
|
101
|
-
beaconBlockRoot: BlockRootHex,
|
|
102
|
-
slot: Slot,
|
|
103
|
-
maxAttestation: number
|
|
104
|
-
): gloas.PayloadAttestation[] {
|
|
100
|
+
getPayloadAttestationsForBlock(beaconBlockRoot: BlockRootHex, slot: Slot): gloas.PayloadAttestation[] {
|
|
105
101
|
const aggregateByDataRootByBlockRoot = this.aggregateByDataRootByBlockRootBySlot.get(slot);
|
|
106
102
|
|
|
107
103
|
if (!aggregateByDataRootByBlockRoot) {
|
|
@@ -119,7 +115,7 @@ export class PayloadAttestationPool {
|
|
|
119
115
|
return Array.from(aggregateByDataRoot.values())
|
|
120
116
|
.slice()
|
|
121
117
|
.sort((a, b) => b.aggregationBits.getTrueBitIndexes().length - a.aggregationBits.getTrueBitIndexes().length)
|
|
122
|
-
.slice(0,
|
|
118
|
+
.slice(0, MAX_PAYLOAD_ATTESTATIONS)
|
|
123
119
|
.map(fastToPayloadAttestation);
|
|
124
120
|
}
|
|
125
121
|
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
isStatePostBellatrix,
|
|
11
11
|
isStatePostGloas,
|
|
12
12
|
} from "@lodestar/state-transition";
|
|
13
|
-
import {Bytes32, Slot} from "@lodestar/types";
|
|
13
|
+
import {Bytes32, Slot, electra} from "@lodestar/types";
|
|
14
14
|
import {Logger, fromHex, isErrorAborted, sleep} from "@lodestar/utils";
|
|
15
15
|
import {GENESIS_SLOT, ZERO_HASH_HEX} from "../constants/constants.js";
|
|
16
16
|
import {BuilderStatus} from "../execution/builder/http.js";
|
|
@@ -165,14 +165,19 @@ export class PrepareNextSlotScheduler {
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
let parentBlockHash: Bytes32;
|
|
168
|
+
let isExtendingPayload = false;
|
|
168
169
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
169
|
-
|
|
170
|
+
isExtendingPayload = this.chain.forkChoice.shouldExtendPayload(updatedHead.blockRoot);
|
|
171
|
+
parentBlockHash = isExtendingPayload
|
|
170
172
|
? updatedPrepareState.latestExecutionPayloadBid.blockHash
|
|
171
173
|
: updatedPrepareState.latestExecutionPayloadBid.parentBlockHash;
|
|
172
174
|
} else {
|
|
173
175
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadHeader.blockHash;
|
|
174
176
|
}
|
|
175
177
|
|
|
178
|
+
// Reused by the SSE emit below to avoid a second DB lookup on cache miss
|
|
179
|
+
let parentExecutionRequests: electra.ExecutionRequests | undefined;
|
|
180
|
+
|
|
176
181
|
if (feeRecipient) {
|
|
177
182
|
const preparationTime =
|
|
178
183
|
computeTimeAtSlot(this.config, prepareSlot, this.chain.genesisTime) - Date.now() / 1000;
|
|
@@ -182,6 +187,13 @@ export class PrepareNextSlotScheduler {
|
|
|
182
187
|
const finalizedBlockHash =
|
|
183
188
|
this.chain.forkChoice.getFinalizedBlock().executionPayloadBlockHash ?? ZERO_HASH_HEX;
|
|
184
189
|
|
|
190
|
+
if (isExtendingPayload) {
|
|
191
|
+
parentExecutionRequests = await this.chain.getParentExecutionRequests(
|
|
192
|
+
updatedHead.slot,
|
|
193
|
+
updatedHead.blockRoot
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
185
197
|
// awaiting here instead of throwing an async call because there is no other task
|
|
186
198
|
// left for scheduler and this gives nice semantics to catch and log errors in the
|
|
187
199
|
// try/catch wrapper here.
|
|
@@ -194,7 +206,8 @@ export class PrepareNextSlotScheduler {
|
|
|
194
206
|
safeBlockHash,
|
|
195
207
|
finalizedBlockHash,
|
|
196
208
|
updatedPrepareState,
|
|
197
|
-
feeRecipient
|
|
209
|
+
feeRecipient,
|
|
210
|
+
parentExecutionRequests
|
|
198
211
|
);
|
|
199
212
|
this.logger.verbose("PrepareNextSlotScheduler prepared new payload", {
|
|
200
213
|
prepareSlot,
|
|
@@ -221,12 +234,20 @@ export class PrepareNextSlotScheduler {
|
|
|
221
234
|
(feeRecipient || this.chain.opts.emitPayloadAttributes === true) &&
|
|
222
235
|
this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
|
|
223
236
|
) {
|
|
237
|
+
// if we didn't fetch above (not proposing), SSE still needs it here
|
|
238
|
+
if (!parentExecutionRequests && isExtendingPayload) {
|
|
239
|
+
parentExecutionRequests = await this.chain.getParentExecutionRequests(
|
|
240
|
+
updatedHead.slot,
|
|
241
|
+
updatedHead.blockRoot
|
|
242
|
+
);
|
|
243
|
+
}
|
|
224
244
|
const data = getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
|
|
225
245
|
prepareState: updatedPrepareState,
|
|
226
246
|
prepareSlot,
|
|
227
247
|
parentBlockRoot: fromHex(updatedHead.blockRoot),
|
|
228
248
|
parentBlockHash,
|
|
229
249
|
feeRecipient: feeRecipient ?? "0x0000000000000000000000000000000000000000",
|
|
250
|
+
parentExecutionRequests,
|
|
230
251
|
});
|
|
231
252
|
this.chain.emitter.emit(routes.events.EventType.payloadAttributes, {data, version: fork});
|
|
232
253
|
}
|
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
ssz,
|
|
50
50
|
} from "@lodestar/types";
|
|
51
51
|
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
52
|
-
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
52
|
+
import {ZERO_HASH, ZERO_HASH_HEX} from "../../constants/index.js";
|
|
53
53
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
54
54
|
import {
|
|
55
55
|
IExecutionBuilder,
|
|
@@ -214,9 +214,19 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
214
214
|
});
|
|
215
215
|
|
|
216
216
|
// Get execution payload from EL
|
|
217
|
-
const
|
|
217
|
+
const isExtendingPayload = this.forkChoice.shouldExtendPayload(toRootHex(parentBlockRoot));
|
|
218
|
+
let parentBlockHash = isExtendingPayload
|
|
218
219
|
? currentState.latestExecutionPayloadBid.blockHash
|
|
219
220
|
: currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
221
|
+
// At gloas genesis the committed bid has no prior EL block to reference
|
|
222
|
+
// (`bid.parentBlockHash` is zero). Fall back to `bid.blockHash` (= eth1 genesis hash) so the
|
|
223
|
+
// FCU to the EL carries a valid head. Post-genesis bids always reference a non-zero parent.
|
|
224
|
+
if (isStatePostGloas(currentState) && byteArrayEquals(parentBlockHash, ZERO_HASH)) {
|
|
225
|
+
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
226
|
+
}
|
|
227
|
+
const parentExecutionRequests = isExtendingPayload
|
|
228
|
+
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
229
|
+
: ssz.electra.ExecutionRequests.defaultValue();
|
|
220
230
|
const prepareRes = await prepareExecutionPayload(
|
|
221
231
|
this,
|
|
222
232
|
this.logger,
|
|
@@ -226,7 +236,8 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
226
236
|
safeBlockHash,
|
|
227
237
|
finalizedBlockHash ?? ZERO_HASH_HEX,
|
|
228
238
|
currentState,
|
|
229
|
-
feeRecipient
|
|
239
|
+
feeRecipient,
|
|
240
|
+
parentExecutionRequests
|
|
230
241
|
);
|
|
231
242
|
|
|
232
243
|
const {prepType, payloadId} = prepareRes;
|
|
@@ -280,9 +291,11 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
280
291
|
const commonBlockBody = await commonBlockBodyPromise;
|
|
281
292
|
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
282
293
|
gloasBody.signedExecutionPayloadBid = signedBid;
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
294
|
+
gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
|
|
295
|
+
parentBlock.blockRoot,
|
|
296
|
+
blockSlot - 1
|
|
297
|
+
);
|
|
298
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
286
299
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
287
300
|
|
|
288
301
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -619,7 +632,8 @@ export async function prepareExecutionPayload(
|
|
|
619
632
|
safeBlockHash: RootHex,
|
|
620
633
|
finalizedBlockHash: RootHex,
|
|
621
634
|
state: IBeaconStateViewBellatrix,
|
|
622
|
-
suggestedFeeRecipient: string
|
|
635
|
+
suggestedFeeRecipient: string,
|
|
636
|
+
parentExecutionRequests?: electra.ExecutionRequests
|
|
623
637
|
): Promise<{prepType: PayloadPreparationType; payloadId: PayloadId}> {
|
|
624
638
|
const timestamp = computeTimeAtSlot(chain.config, state.slot, state.genesisTime);
|
|
625
639
|
const prevRandao = state.getRandaoMix(state.epoch);
|
|
@@ -656,6 +670,7 @@ export async function prepareExecutionPayload(
|
|
|
656
670
|
parentBlockRoot,
|
|
657
671
|
parentBlockHash,
|
|
658
672
|
feeRecipient: suggestedFeeRecipient,
|
|
673
|
+
parentExecutionRequests,
|
|
659
674
|
});
|
|
660
675
|
|
|
661
676
|
payloadId = await chain.executionEngine.notifyForkchoiceUpdate(
|
|
@@ -714,12 +729,14 @@ export function getPayloadAttributesForSSE(
|
|
|
714
729
|
parentBlockRoot,
|
|
715
730
|
parentBlockHash,
|
|
716
731
|
feeRecipient,
|
|
732
|
+
parentExecutionRequests,
|
|
717
733
|
}: {
|
|
718
734
|
prepareState: IBeaconStateViewBellatrix;
|
|
719
735
|
prepareSlot: Slot;
|
|
720
736
|
parentBlockRoot: Root;
|
|
721
737
|
parentBlockHash: Bytes32;
|
|
722
738
|
feeRecipient: string;
|
|
739
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
723
740
|
}
|
|
724
741
|
): SSEPayloadAttributes {
|
|
725
742
|
const payloadAttributes = preparePayloadAttributes(fork, chain, {
|
|
@@ -728,6 +745,7 @@ export function getPayloadAttributesForSSE(
|
|
|
728
745
|
parentBlockRoot,
|
|
729
746
|
parentBlockHash,
|
|
730
747
|
feeRecipient,
|
|
748
|
+
parentExecutionRequests,
|
|
731
749
|
});
|
|
732
750
|
|
|
733
751
|
let parentBlockNumber: number;
|
|
@@ -766,12 +784,14 @@ function preparePayloadAttributes(
|
|
|
766
784
|
parentBlockRoot,
|
|
767
785
|
parentBlockHash,
|
|
768
786
|
feeRecipient,
|
|
787
|
+
parentExecutionRequests,
|
|
769
788
|
}: {
|
|
770
789
|
prepareState: IBeaconStateViewBellatrix;
|
|
771
790
|
prepareSlot: Slot;
|
|
772
791
|
parentBlockRoot: Root;
|
|
773
792
|
parentBlockHash: Bytes32;
|
|
774
793
|
feeRecipient: string;
|
|
794
|
+
parentExecutionRequests?: electra.ExecutionRequests;
|
|
775
795
|
}
|
|
776
796
|
): SSEPayloadAttributes["payloadAttributes"] {
|
|
777
797
|
const timestamp = computeTimeAtSlot(chain.config, prepareSlot, prepareState.genesisTime);
|
|
@@ -789,13 +809,20 @@ function preparePayloadAttributes(
|
|
|
789
809
|
|
|
790
810
|
if (isStatePostGloas(prepareState)) {
|
|
791
811
|
const isExtendingPayload = byteArrayEquals(parentBlockHash, prepareState.latestExecutionPayloadBid.blockHash);
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
812
|
+
if (isExtendingPayload) {
|
|
813
|
+
if (parentExecutionRequests === undefined) {
|
|
814
|
+
throw new Error("parentExecutionRequests required when extending full parent");
|
|
815
|
+
}
|
|
816
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
817
|
+
prepareState.getExpectedWithdrawalsForFullParent(parentExecutionRequests);
|
|
818
|
+
} else {
|
|
819
|
+
// When the parent block is empty, state.payloadExpectedWithdrawals holds a batch
|
|
820
|
+
// already deducted from CL balances but never credited on the EL (the envelope
|
|
821
|
+
// was not delivered). The next payload must carry those same withdrawals to
|
|
822
|
+
// restore CL/EL consistency, otherwise validators permanently lose that balance.
|
|
823
|
+
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
824
|
+
prepareState.payloadExpectedWithdrawals;
|
|
825
|
+
}
|
|
799
826
|
} else {
|
|
800
827
|
// withdrawals logic is now fork aware as it changes on electra fork post capella
|
|
801
828
|
(payloadAttributes as capella.SSEPayloadAttributes["payloadAttributes"]).withdrawals =
|
|
@@ -21,6 +21,7 @@ export enum RegenCaller {
|
|
|
21
21
|
validateGossipAttestation = "validateGossipAttestation",
|
|
22
22
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
23
23
|
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
24
|
+
validateGossipProposerPreferences = "validateGossipProposerPreferences",
|
|
24
25
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
25
26
|
restApi = "restApi",
|
|
26
27
|
}
|