@lodestar/beacon-node 1.43.0-dev.78c66bac71 → 1.43.0-dev.87cbe69c66
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/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/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 +68 -2
- 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 +24 -20
- 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 +45 -23
- 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 +59 -25
- 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 +4 -3
- 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 +89 -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 +50 -7
- 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 +25 -5
- 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 +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 +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 +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 +12 -3
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +35 -17
- 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/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/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/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 +10 -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/nodejs.js +2 -2
- package/lib/node/nodejs.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 +164 -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/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 +16 -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/debug/index.ts +0 -1
- package/src/api/impl/lodestar/index.ts +1 -1
- package/src/api/impl/validator/index.ts +82 -1
- package/src/chain/blocks/importBlock.ts +24 -38
- package/src/chain/blocks/importExecutionPayload.ts +59 -25
- package/src/chain/blocks/index.ts +73 -22
- 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 +4 -3
- package/src/chain/blocks/utils/chainSegment.ts +114 -17
- package/src/chain/blocks/verifyBlock.ts +70 -9
- package/src/chain/blocks/verifyBlocksExecutionPayloads.ts +6 -4
- package/src/chain/blocks/verifyBlocksSanityChecks.ts +26 -7
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +9 -4
- 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 +47 -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 +46 -22
- 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/block.ts +1 -0
- package/src/chain/validation/executionPayloadBid.ts +11 -8
- package/src/chain/validation/proposerPreferences.ts +110 -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/nodejs.ts +2 -2
- 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 +274 -39
- package/src/sync/utils/downloadByRoot.ts +24 -2
- package/src/sync/utils/pendingBlocksTree.ts +0 -15
- package/src/util/sszBytes.ts +8 -6
|
@@ -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;
|
|
@@ -75,21 +75,24 @@ function toForkChoiceExecutionStatus(status: ExecutionPayloadStatus): PayloadExe
|
|
|
75
75
|
* The envelope is only verified here, no state mutation. State effects from the payload
|
|
76
76
|
* are applied on the next block via processParentExecutionPayload.
|
|
77
77
|
*
|
|
78
|
+
* The DA wait must have run upstream (range sync awaits DA in `verifyBlocksInEpoch` for the
|
|
79
|
+
* whole segment; gossip / API path uses the `processExecutionPayload` wrapper below).
|
|
80
|
+
*
|
|
78
81
|
* Steps:
|
|
79
82
|
* 1. Emit `execution_payload_available` event for payload attestation
|
|
80
83
|
* 2. Get the ProtoBlock from fork choice
|
|
81
|
-
* 3.
|
|
82
|
-
* 4.
|
|
83
|
-
* 5.
|
|
84
|
-
* 6.
|
|
85
|
-
* 7.
|
|
84
|
+
* 3. Regenerate state for envelope verification
|
|
85
|
+
* 4. Verify envelope (fields against state, signature, and EL in parallel where possible)
|
|
86
|
+
* 5. Persist verified payload envelope to hot DB (waits for write-queue space for backpressure)
|
|
87
|
+
* 6. Update fork choice (transitions the block's PENDING variant to FULL)
|
|
88
|
+
* 7. Queue notifyForkchoiceUpdate to engine api
|
|
86
89
|
* 8. Record metrics for payload envelope and column sources
|
|
87
90
|
* 9. Emit `execution_payload` event
|
|
88
91
|
*/
|
|
89
92
|
export async function importExecutionPayload(
|
|
90
93
|
this: BeaconChain,
|
|
91
94
|
payloadInput: PayloadEnvelopeInput,
|
|
92
|
-
|
|
95
|
+
dataAvailabilityStatus: DataAvailabilityStatus,
|
|
93
96
|
opts: ImportPayloadOpts = {}
|
|
94
97
|
): Promise<void> {
|
|
95
98
|
const signedEnvelope = payloadInput.getPayloadEnvelope();
|
|
@@ -119,11 +122,7 @@ export async function importExecutionPayload(
|
|
|
119
122
|
});
|
|
120
123
|
}
|
|
121
124
|
|
|
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
|
|
125
|
+
// 3. Regenerate state for envelope verification
|
|
127
126
|
const blockState = await this.regen.getBlockSlotState(
|
|
128
127
|
protoBlock,
|
|
129
128
|
protoBlock.slot,
|
|
@@ -137,7 +136,7 @@ export async function importExecutionPayload(
|
|
|
137
136
|
});
|
|
138
137
|
}
|
|
139
138
|
|
|
140
|
-
//
|
|
139
|
+
// 4. Verify envelope fields against state first to fail fast before the EL + BLS work.
|
|
141
140
|
// When validSignature is true, gossip/API has already verified both the signature and the
|
|
142
141
|
// executionRequestsRoot, so we skip those checks here.
|
|
143
142
|
try {
|
|
@@ -154,13 +153,13 @@ export async function importExecutionPayload(
|
|
|
154
153
|
);
|
|
155
154
|
}
|
|
156
155
|
|
|
157
|
-
//
|
|
156
|
+
// 4a. Run EL and signature verification in parallel
|
|
158
157
|
const [execResult, signatureValid] = await Promise.all([
|
|
159
158
|
this.executionEngine.notifyNewPayload(
|
|
160
159
|
fork,
|
|
161
160
|
envelope.payload,
|
|
162
161
|
payloadInput.getVersionedHashes(),
|
|
163
|
-
|
|
162
|
+
envelope.parentBeaconBlockRoot,
|
|
164
163
|
envelope.executionRequests
|
|
165
164
|
),
|
|
166
165
|
|
|
@@ -176,12 +175,12 @@ export async function importExecutionPayload(
|
|
|
176
175
|
),
|
|
177
176
|
]);
|
|
178
177
|
|
|
179
|
-
//
|
|
178
|
+
// 4b. Check signature verification result
|
|
180
179
|
if (!signatureValid) {
|
|
181
180
|
throw new PayloadError({code: PayloadErrorCode.INVALID_SIGNATURE});
|
|
182
181
|
}
|
|
183
182
|
|
|
184
|
-
//
|
|
183
|
+
// 4c. Handle EL response
|
|
185
184
|
switch (execResult.status) {
|
|
186
185
|
case ExecutionPayloadStatus.VALID:
|
|
187
186
|
break;
|
|
@@ -207,7 +206,7 @@ export async function importExecutionPayload(
|
|
|
207
206
|
});
|
|
208
207
|
}
|
|
209
208
|
|
|
210
|
-
//
|
|
209
|
+
// 5. Persist payload envelope to hot DB. Wait for write-queue space here to apply backpressure
|
|
211
210
|
// on the import pipeline during sync, then perform the write asynchronously to avoid blocking.
|
|
212
211
|
await this.unfinalizedPayloadEnvelopeWrites.waitForSpace();
|
|
213
212
|
this.unfinalizedPayloadEnvelopeWrites.push(payloadInput).catch((e) => {
|
|
@@ -220,9 +219,27 @@ export async function importExecutionPayload(
|
|
|
220
219
|
}
|
|
221
220
|
});
|
|
222
221
|
|
|
223
|
-
//
|
|
222
|
+
// 6. Update fork choice, transitions the block's PENDING variant to FULL
|
|
224
223
|
const execStatus = toForkChoiceExecutionStatus(execResult.status);
|
|
225
|
-
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
|
+
}
|
|
226
243
|
|
|
227
244
|
// 8. Record metrics for payload envelope and column sources
|
|
228
245
|
this.metrics?.importPayload.bySource.inc({source: payloadInput.getPayloadEnvelopeSource().source});
|
|
@@ -237,8 +254,7 @@ export async function importExecutionPayload(
|
|
|
237
254
|
builderIndex: envelope.builderIndex,
|
|
238
255
|
blockHash: blockHashHex,
|
|
239
256
|
blockRoot: blockRootHex,
|
|
240
|
-
|
|
241
|
-
executionOptimistic: false,
|
|
257
|
+
executionOptimistic: execStatus === ExecutionStatus.Syncing,
|
|
242
258
|
});
|
|
243
259
|
}
|
|
244
260
|
|
|
@@ -249,3 +265,21 @@ export async function importExecutionPayload(
|
|
|
249
265
|
blockHash: blockHashHex,
|
|
250
266
|
});
|
|
251
267
|
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Process an execution payload envelope end-to-end: wait for DA, then import.
|
|
271
|
+
*
|
|
272
|
+
* Used by the PayloadEnvelopeProcessor queue (gossip / API / unknown-payload sync) — i.e.
|
|
273
|
+
* callers that have NOT already awaited DA themselves. Range sync's inline dispatch in
|
|
274
|
+
* processBlocks skips this wrapper and calls `importExecutionPayload` directly, since
|
|
275
|
+
* `verifyBlocksInEpoch` already awaited DA for the segment.
|
|
276
|
+
*/
|
|
277
|
+
export async function processExecutionPayload(
|
|
278
|
+
this: BeaconChain,
|
|
279
|
+
payloadInput: PayloadEnvelopeInput,
|
|
280
|
+
signal: AbortSignal,
|
|
281
|
+
opts: ImportPayloadOpts = {}
|
|
282
|
+
): Promise<void> {
|
|
283
|
+
const {dataAvailabilityStatuses} = await verifyPayloadsDataAvailability([payloadInput], signal);
|
|
284
|
+
await importExecutionPayload.call(this, payloadInput, dataAvailabilityStatuses[0], opts);
|
|
285
|
+
}
|
|
@@ -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,18 +57,15 @@ 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
|
-
const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, opts);
|
|
68
|
+
const {relevantBlocks, parentSlots, parentBlock} = verifyBlocksSanityChecks(this, blocks, payloadEnvelopes, opts);
|
|
66
69
|
|
|
67
70
|
// No relevant blocks, skip verifyBlocksInEpoch()
|
|
68
71
|
if (relevantBlocks.length === 0 || parentBlock === null) {
|
|
@@ -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
|
|
@@ -85,24 +109,51 @@ export async function processBlocks(
|
|
|
85
109
|
}
|
|
86
110
|
|
|
87
111
|
const {executionStatuses} = segmentExecStatus;
|
|
88
|
-
const
|
|
89
|
-
|
|
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, {
|
|
90
116
|
blockInput: block,
|
|
91
117
|
postState: postStates[i],
|
|
92
118
|
parentBlockSlot: parentSlots[i],
|
|
93
119
|
executionStatus: executionStatuses[i],
|
|
94
120
|
// start supporting optimistic syncing/processing
|
|
95
|
-
dataAvailabilityStatus:
|
|
121
|
+
dataAvailabilityStatus: blockDAStatuses[i],
|
|
96
122
|
proposerBalanceDelta: proposerBalanceDeltas[i],
|
|
97
123
|
indexedAttestations: indexedAttestationsByBlock[i],
|
|
98
124
|
// TODO: Make this param mandatory and capture in gossip
|
|
99
125
|
seenTimestampSec: opts.seenTimestampSec ?? Math.floor(Date.now() / 1000),
|
|
100
|
-
})
|
|
101
|
-
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
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
|
+
}
|
|
142
|
+
|
|
143
|
+
const payloadInput = payloadEnvelopes?.get(slot);
|
|
144
|
+
if (payloadInput?.hasPayloadEnvelope()) {
|
|
145
|
+
if (!payloadInput.isComplete()) {
|
|
146
|
+
// we validated DA before reaching this
|
|
147
|
+
throw new Error(`Payload envelope for slot ${slot} not complete after DA verification`);
|
|
148
|
+
}
|
|
149
|
+
// we already awaited DA in verifyBlocksInEpoch for this segment
|
|
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});
|
|
155
|
+
}
|
|
102
156
|
|
|
103
|
-
for (const fullyVerifiedBlock of fullyVerifiedBlocks) {
|
|
104
|
-
// TODO: Consider batching importBlock too if it takes significant time
|
|
105
|
-
await importBlock.call(this, fullyVerifiedBlock, opts);
|
|
106
157
|
await nextEventLoop();
|
|
107
158
|
}
|
|
108
159
|
} 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,
|
|
@@ -2,7 +2,7 @@ import {Metrics} from "../../metrics/metrics.js";
|
|
|
2
2
|
import {JobItemQueue} from "../../util/queue/index.js";
|
|
3
3
|
import type {BeaconChain} from "../chain.js";
|
|
4
4
|
import {PayloadEnvelopeInput} from "../seenCache/seenPayloadEnvelopeInput.js";
|
|
5
|
-
import {
|
|
5
|
+
import {processExecutionPayload} from "./importExecutionPayload.js";
|
|
6
6
|
import {ImportPayloadOpts} from "./types.js";
|
|
7
7
|
|
|
8
8
|
// TODO GLOAS: Set to be equal to DEFAULT_MAX_PENDING_UNFINALIZED_PAYLOAD_ENVELOPE_WRITES for now
|
|
@@ -30,7 +30,7 @@ export class PayloadEnvelopeProcessor {
|
|
|
30
30
|
this.jobQueue = new JobItemQueue<[PayloadEnvelopeInput, ImportPayloadOpts], void>(
|
|
31
31
|
(payloadInput, opts) => {
|
|
32
32
|
this.importStatus.set(payloadInput, PayloadEnvelopeImportStatus.importing);
|
|
33
|
-
return
|
|
33
|
+
return processExecutionPayload.call(chain, payloadInput, signal, opts);
|
|
34
34
|
},
|
|
35
35
|
{maxLength: QUEUE_MAX_LENGTH, noYieldIfOneItem: true, signal},
|
|
36
36
|
metrics?.payloadEnvelopeProcessorQueue ?? undefined
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {BlockExecutionStatus} from "@lodestar/fork-choice";
|
|
2
|
+
import type {BlockExecutionStatus, PayloadExecutionStatus} from "@lodestar/fork-choice";
|
|
3
3
|
import {ForkSeq} from "@lodestar/params";
|
|
4
4
|
import {DataAvailabilityStatus, IBeaconStateView, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
5
5
|
import type {IndexedAttestation, Slot, fulu} from "@lodestar/types";
|
|
@@ -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;
|
|
@@ -107,5 +108,5 @@ export type FullyVerifiedBlock = {
|
|
|
107
108
|
/** Seen timestamp seconds */
|
|
108
109
|
seenTimestampSec: number;
|
|
109
110
|
/** If the execution payload couldn't be verified because of EL syncing status, used in optimistic sync */
|
|
110
|
-
executionStatus: BlockExecutionStatus;
|
|
111
|
+
executionStatus: BlockExecutionStatus | PayloadExecutionStatus;
|
|
111
112
|
};
|
|
@@ -1,29 +1,126 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {
|
|
2
|
+
import {ProtoBlock} from "@lodestar/fork-choice";
|
|
3
|
+
import {Slot, isGloasBeaconBlock, ssz} from "@lodestar/types";
|
|
4
|
+
import {toRootHex} from "@lodestar/utils";
|
|
3
5
|
import {BlockError, BlockErrorCode} from "../../errors/index.js";
|
|
4
6
|
import {IBlockInput} from "../blockInput/types.js";
|
|
7
|
+
import {PayloadEnvelopeInput} from "../payloadEnvelopeInput/payloadEnvelopeInput.js";
|
|
8
|
+
|
|
9
|
+
export type OrphanedPayloadEnvelope = {
|
|
10
|
+
slot: Slot;
|
|
11
|
+
payloadEnvelopeInput: PayloadEnvelopeInput;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type ChainSegmentResult = {warnings: OrphanedPayloadEnvelope[] | null};
|
|
5
15
|
|
|
6
16
|
/**
|
|
7
|
-
* Assert this chain segment of blocks is linear with slot numbers and hashes
|
|
17
|
+
* Assert this chain segment of blocks is linear with slot numbers and hashes,
|
|
18
|
+
* and that the provided envelopes are consistent with their respective blocks.
|
|
19
|
+
*
|
|
20
|
+
* Must be called after verifyBlocksSanityChecks so that parentBlock (from forkchoice)
|
|
21
|
+
* is available to seed the execution hash chain.
|
|
22
|
+
*
|
|
23
|
+
* For each block:
|
|
24
|
+
* - Verifies parent root + slot linearity
|
|
25
|
+
* - For gloas: verifies bid.parentBlockHash matches the tracked execution hash; if not, the
|
|
26
|
+
* previous FULL envelope is treated as orphaned (segment continues as if previous slot was EMPTY)
|
|
27
|
+
* - If an envelope exists for this slot: verifies it references this block's root
|
|
28
|
+
* - Advances the tracked execution hash (FULL if envelope present, EMPTY if not)
|
|
8
29
|
*/
|
|
30
|
+
export function assertLinearChainSegment(
|
|
31
|
+
config: ChainForkConfig,
|
|
32
|
+
blocks: IBlockInput[],
|
|
33
|
+
payloadEnvelopes: Map<Slot, PayloadEnvelopeInput> | null,
|
|
34
|
+
parentBlock: ProtoBlock
|
|
35
|
+
): ChainSegmentResult {
|
|
36
|
+
const warnings: OrphanedPayloadEnvelope[] = [];
|
|
9
37
|
|
|
10
|
-
|
|
11
|
-
|
|
38
|
+
// Track the expected execution payload block hash through the segment.
|
|
39
|
+
// Starts from the known forkchoice parent's execution hash.
|
|
40
|
+
// - FULL variant (envelope present for slot): advances to envelope.payload.blockHash
|
|
41
|
+
// - EMPTY variant (no envelope for slot): execution hash is unchanged
|
|
42
|
+
// null only for pre-merge parents, which cannot precede gloas blocks.
|
|
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
|
+
}
|
|
52
|
+
// Track the execution hash before the last FULL advancement so we can recover
|
|
53
|
+
// if the next block reveals that envelope was orphaned.
|
|
54
|
+
let prevExecHash: string | null = currentExecHash;
|
|
55
|
+
// The slot whose envelope last advanced currentExecHash (for warning context).
|
|
56
|
+
let lastFullSlot: Slot | null = null;
|
|
57
|
+
|
|
58
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
12
59
|
const block = blocks[i].getBlock();
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
60
|
+
const slot = block.message.slot;
|
|
61
|
+
|
|
62
|
+
if (i > 0) {
|
|
63
|
+
const prevBlock = blocks[i - 1].getBlock();
|
|
64
|
+
// Ensure parent root matches the previous block's root
|
|
65
|
+
if (
|
|
66
|
+
!ssz.Root.equals(
|
|
67
|
+
config.getForkTypes(prevBlock.message.slot).BeaconBlock.hashTreeRoot(prevBlock.message),
|
|
68
|
+
block.message.parentRoot
|
|
69
|
+
)
|
|
70
|
+
) {
|
|
71
|
+
throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_PARENT_ROOTS});
|
|
72
|
+
}
|
|
73
|
+
// Ensure slots are strictly increasing
|
|
74
|
+
if (slot <= prevBlock.message.slot) {
|
|
75
|
+
throw new BlockError(block, {code: BlockErrorCode.NON_LINEAR_SLOTS});
|
|
76
|
+
}
|
|
23
77
|
}
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
|
|
78
|
+
|
|
79
|
+
if (isGloasBeaconBlock(block.message) && currentExecHash !== null) {
|
|
80
|
+
// Verify the bid's parentBlockHash matches the tracked execution hash.
|
|
81
|
+
// This ensures the block was built on the correct FULL or EMPTY variant of its parent.
|
|
82
|
+
const bidParentHash = toRootHex(block.message.body.signedExecutionPayloadBid.message.parentBlockHash);
|
|
83
|
+
if (bidParentHash !== currentExecHash) {
|
|
84
|
+
// Maybe the previous slot's FULL envelope was orphaned — try falling back.
|
|
85
|
+
// If even prevExecHash doesn't match, the segment is non-linear.
|
|
86
|
+
if (bidParentHash !== prevExecHash) {
|
|
87
|
+
throw new BlockError(block, {
|
|
88
|
+
code: BlockErrorCode.PARENT_PAYLOAD_UNKNOWN,
|
|
89
|
+
parentRoot: toRootHex(block.message.parentRoot),
|
|
90
|
+
parentBlockHash: bidParentHash,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (lastFullSlot !== null && payloadEnvelopes !== null) {
|
|
94
|
+
const orphanedInput = payloadEnvelopes.get(lastFullSlot);
|
|
95
|
+
if (orphanedInput != null) {
|
|
96
|
+
warnings.push({slot: lastFullSlot, payloadEnvelopeInput: orphanedInput});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
currentExecHash = prevExecHash;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const payloadInput = payloadEnvelopes?.get(slot) ?? null;
|
|
103
|
+
const payloadEnvelope = payloadInput?.hasPayloadEnvelope() ? payloadInput.getPayloadEnvelope() : null;
|
|
104
|
+
if (payloadEnvelope !== null) {
|
|
105
|
+
// Verify the envelope references this block's root
|
|
106
|
+
const blockRoot = toRootHex(config.getForkTypes(slot).BeaconBlock.hashTreeRoot(block.message));
|
|
107
|
+
const envelopeBlockRoot = toRootHex(payloadEnvelope.message.beaconBlockRoot);
|
|
108
|
+
if (blockRoot !== envelopeBlockRoot) {
|
|
109
|
+
throw new BlockError(block, {
|
|
110
|
+
code: BlockErrorCode.ENVELOPE_BLOCK_ROOT_MISMATCH,
|
|
111
|
+
envelopeBlockRoot,
|
|
112
|
+
blockRoot,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// FULL variant: save state before advancing, then advance
|
|
117
|
+
prevExecHash = currentExecHash;
|
|
118
|
+
lastFullSlot = slot;
|
|
119
|
+
currentExecHash = toRootHex(payloadEnvelope.message.payload.blockHash);
|
|
120
|
+
}
|
|
121
|
+
// EMPTY variant: currentExecHash unchanged
|
|
27
122
|
}
|
|
28
123
|
}
|
|
124
|
+
|
|
125
|
+
return {warnings: warnings.length > 0 ? warnings : null};
|
|
29
126
|
}
|