@lodestar/beacon-node 1.44.0-rc.1 → 1.44.0-rc.3
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 +23 -28
- 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 +0 -25
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/config/constants.d.ts +4 -0
- package/lib/api/impl/config/constants.d.ts.map +1 -1
- package/lib/api/impl/config/constants.js +5 -1
- package/lib/api/impl/config/constants.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +0 -20
- 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 +14 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.d.ts.map +1 -1
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js +3 -3
- package/lib/chain/blocks/verifyExecutionPayloadEnvelope.js.map +1 -1
- package/lib/chain/chain.d.ts +2 -2
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +1 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +12 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +2 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/interface.d.ts +2 -2
- package/lib/chain/interface.d.ts.map +1 -1
- package/lib/chain/interface.js.map +1 -1
- package/lib/chain/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +0 -9
- package/lib/chain/prepareNextSlot.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +2 -2
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +4 -4
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- 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/validation/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +23 -1
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js +1 -1
- package/lib/chain/validation/executionPayloadEnvelope.js.map +1 -1
- package/lib/execution/engine/http.js +1 -1
- package/lib/execution/engine/http.js.map +1 -1
- package/lib/execution/engine/interface.d.ts +2 -2
- package/lib/execution/engine/interface.d.ts.map +1 -1
- package/lib/execution/engine/interface.js +6 -2
- package/lib/execution/engine/interface.js.map +1 -1
- package/lib/execution/engine/mock.d.ts.map +1 -1
- package/lib/execution/engine/mock.js +3 -5
- package/lib/execution/engine/mock.js.map +1 -1
- package/lib/execution/engine/types.d.ts +15 -7
- package/lib/execution/engine/types.d.ts.map +1 -1
- package/lib/execution/engine/types.js +47 -9
- package/lib/execution/engine/types.js.map +1 -1
- package/lib/network/gossip/topic.d.ts +22 -2
- package/lib/network/gossip/topic.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.d.ts.map +1 -1
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js +1 -2
- package/lib/network/reqresp/utils/dataColumnResponseValidation.js.map +1 -1
- package/lib/util/sszBytes.js +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/blocks/index.ts +27 -37
- package/src/api/impl/beacon/pool/index.ts +1 -41
- package/src/api/impl/config/constants.ts +8 -0
- package/src/api/impl/validator/index.ts +0 -31
- package/src/chain/blocks/importBlock.ts +19 -5
- package/src/chain/blocks/verifyExecutionPayloadEnvelope.ts +3 -5
- package/src/chain/chain.ts +2 -3
- package/src/chain/errors/executionPayloadBid.ts +14 -0
- package/src/chain/interface.ts +1 -2
- package/src/chain/prepareNextSlot.ts +0 -10
- package/src/chain/produceBlock/produceBlockBody.ts +7 -7
- package/src/chain/seenCache/seenPayloadEnvelopeInput.ts +8 -3
- package/src/chain/validation/executionPayloadBid.ts +25 -1
- package/src/chain/validation/executionPayloadEnvelope.ts +1 -1
- package/src/execution/engine/http.ts +1 -1
- package/src/execution/engine/interface.ts +12 -2
- package/src/execution/engine/mock.ts +6 -5
- package/src/execution/engine/types.ts +75 -14
- package/src/network/reqresp/utils/dataColumnResponseValidation.ts +1 -2
- package/src/util/sszBytes.ts +1 -1
|
@@ -1501,33 +1501,6 @@ export function getValidatorApi(
|
|
|
1501
1501
|
};
|
|
1502
1502
|
},
|
|
1503
1503
|
|
|
1504
|
-
async getAggregatedAttestation({attestationDataRoot, slot}) {
|
|
1505
|
-
notWhileSyncing();
|
|
1506
|
-
|
|
1507
|
-
await waitForSlot(slot); // Must never request for a future slot > currentSlot
|
|
1508
|
-
|
|
1509
|
-
const dataRootHex = toRootHex(attestationDataRoot);
|
|
1510
|
-
const aggregate = chain.attestationPool.getAggregate(slot, dataRootHex, null);
|
|
1511
|
-
const fork = chain.config.getForkName(slot);
|
|
1512
|
-
|
|
1513
|
-
if (isForkPostElectra(fork)) {
|
|
1514
|
-
throw new ApiError(
|
|
1515
|
-
400,
|
|
1516
|
-
`Use getAggregatedAttestationV2 to retrieve aggregated attestations for post-electra fork=${fork}`
|
|
1517
|
-
);
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
if (!aggregate) {
|
|
1521
|
-
throw new ApiError(404, `No aggregated attestation for slot=${slot}, dataRoot=${dataRootHex}`);
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
|
-
metrics?.production.producedAggregateParticipants.observe(aggregate.aggregationBits.getTrueBitIndexes().length);
|
|
1525
|
-
|
|
1526
|
-
return {
|
|
1527
|
-
data: aggregate,
|
|
1528
|
-
};
|
|
1529
|
-
},
|
|
1530
|
-
|
|
1531
1504
|
async getAggregatedAttestationV2({attestationDataRoot, slot, committeeIndex}) {
|
|
1532
1505
|
notWhileSyncing();
|
|
1533
1506
|
|
|
@@ -1551,10 +1524,6 @@ export function getValidatorApi(
|
|
|
1551
1524
|
};
|
|
1552
1525
|
},
|
|
1553
1526
|
|
|
1554
|
-
async publishAggregateAndProofs({signedAggregateAndProofs}) {
|
|
1555
|
-
await this.publishAggregateAndProofsV2({signedAggregateAndProofs});
|
|
1556
|
-
},
|
|
1557
|
-
|
|
1558
1527
|
async publishAggregateAndProofsV2({signedAggregateAndProofs}) {
|
|
1559
1528
|
notWhileSyncing();
|
|
1560
1529
|
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from "@lodestar/state-transition";
|
|
23
23
|
import {Attestation, BeaconBlock, altair, capella, electra, isGloasBeaconBlock, phase0, ssz} from "@lodestar/types";
|
|
24
24
|
import {isErrorAborted, toRootHex} from "@lodestar/utils";
|
|
25
|
-
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
25
|
+
import {GENESIS_SLOT, ZERO_HASH_HEX} from "../../constants/index.js";
|
|
26
26
|
import {callInNextEventLoop} from "../../util/eventLoop.js";
|
|
27
27
|
import {isOptimisticBlock} from "../../util/forkChoice.js";
|
|
28
28
|
import {isQueueErrorAborted} from "../../util/queue/index.js";
|
|
@@ -189,10 +189,12 @@ export async function importBlock(
|
|
|
189
189
|
this.seenBlockAttesters.addIndices(blockEpoch, indexedAttestation.attestingIndices);
|
|
190
190
|
|
|
191
191
|
const correctHead = ssz.Root.equals(rootCache.getBlockRootAtSlot(attestation.data.slot), beaconBlockRoot);
|
|
192
|
-
const missedSlotVote =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
192
|
+
const missedSlotVote =
|
|
193
|
+
attestation.data.slot > GENESIS_SLOT &&
|
|
194
|
+
ssz.Root.equals(
|
|
195
|
+
rootCache.getBlockRootAtSlot(attestation.data.slot - 1),
|
|
196
|
+
rootCache.getBlockRootAtSlot(attestation.data.slot)
|
|
197
|
+
);
|
|
196
198
|
this.validatorMonitor?.registerAttestationInBlock(
|
|
197
199
|
indexedAttestation,
|
|
198
200
|
parentBlockSlot,
|
|
@@ -281,6 +283,18 @@ export async function importBlock(
|
|
|
281
283
|
const newHead = this.recomputeForkChoiceHead(ForkchoiceCaller.importBlock);
|
|
282
284
|
const currFinalizedEpoch = this.forkChoice.getFinalizedCheckpoint().epoch;
|
|
283
285
|
|
|
286
|
+
// Prune the gloas payload-envelope cache below the new head's parent so it stays bounded during
|
|
287
|
+
// syncing. On a synced node, cache holds just 2 entries — head (parent for
|
|
288
|
+
// next-slot production) and head.parent (proposer-boost-reorg fallback)
|
|
289
|
+
if (fork >= ForkSeq.gloas) {
|
|
290
|
+
callInNextEventLoop(() => {
|
|
291
|
+
const newHeadParent = this.forkChoice.getBlockHexDefaultStatus(newHead.parentRoot);
|
|
292
|
+
if (newHeadParent) {
|
|
293
|
+
this.seenPayloadEnvelopeInputCache.pruneBelowParent(newHeadParent);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
284
298
|
if (newHead.blockRoot !== oldHead.blockRoot) {
|
|
285
299
|
// Set head state as strong reference
|
|
286
300
|
this.regen.updateHeadState(newHead, postState);
|
|
@@ -61,10 +61,8 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
61
61
|
`Prev randao mismatch between bid and payload bid=${toHex(bid.prevRandao)} payload=${toHex(payload.prevRandao)}`
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
|
-
if (
|
|
65
|
-
throw new Error(
|
|
66
|
-
`Gas limit mismatch between payload and bid payload=${payload.gasLimit} bid=${Number(bid.gasLimit)}`
|
|
67
|
-
);
|
|
64
|
+
if (bid.gasLimit !== payload.gasLimit) {
|
|
65
|
+
throw new Error(`Gas limit mismatch between payload and bid payload=${payload.gasLimit} bid=${bid.gasLimit}`);
|
|
68
66
|
}
|
|
69
67
|
if (!byteArrayEquals(bid.blockHash, payload.blockHash)) {
|
|
70
68
|
throw new Error(
|
|
@@ -74,7 +72,7 @@ export function verifyExecutionPayloadEnvelope(
|
|
|
74
72
|
// Verify execution_requests_root matches bid commitment.
|
|
75
73
|
// Can be skipped if already verified during gossip validation.
|
|
76
74
|
if (verifyExecutionRequestsRoot) {
|
|
77
|
-
const requestsRoot = ssz.
|
|
75
|
+
const requestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
|
|
78
76
|
if (!byteArrayEquals(requestsRoot, bid.executionRequestsRoot)) {
|
|
79
77
|
throw new Error(
|
|
80
78
|
`Execution requests root mismatch envelope=${toRootHex(requestsRoot)} bid=${toRootHex(bid.executionRequestsRoot)}`
|
package/src/chain/chain.ts
CHANGED
|
@@ -40,7 +40,6 @@ import {
|
|
|
40
40
|
ValidatorIndex,
|
|
41
41
|
Wei,
|
|
42
42
|
deneb,
|
|
43
|
-
electra,
|
|
44
43
|
gloas,
|
|
45
44
|
isBlindedBeaconBlock,
|
|
46
45
|
phase0,
|
|
@@ -925,10 +924,10 @@ export class BeaconChain implements IBeaconChain {
|
|
|
925
924
|
async getParentExecutionRequests(
|
|
926
925
|
parentBlockSlot: Slot,
|
|
927
926
|
parentBlockRootHex: RootHex
|
|
928
|
-
): Promise<
|
|
927
|
+
): Promise<gloas.ExecutionRequests> {
|
|
929
928
|
// at the fork boundary, parent is pre-gloas
|
|
930
929
|
if (!isForkPostGloas(this.config.getForkName(parentBlockSlot))) {
|
|
931
|
-
return ssz.
|
|
930
|
+
return ssz.gloas.ExecutionRequests.defaultValue();
|
|
932
931
|
}
|
|
933
932
|
const envelope = await this.getExecutionPayloadEnvelope(parentBlockSlot, parentBlockRootHex);
|
|
934
933
|
if (envelope === null) {
|
|
@@ -3,6 +3,7 @@ import {GossipActionError} from "./gossipValidation.js";
|
|
|
3
3
|
|
|
4
4
|
export enum ExecutionPayloadBidErrorCode {
|
|
5
5
|
BUILDER_NOT_ELIGIBLE = "EXECUTION_PAYLOAD_BID_ERROR_BUILDER_NOT_ELIGIBLE",
|
|
6
|
+
INVALID_BUILDER_VERSION = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_BUILDER_VERSION",
|
|
6
7
|
NON_ZERO_EXECUTION_PAYMENT = "EXECUTION_PAYLOAD_BID_ERROR_NON_ZERO_EXECUTION_PAYMENT",
|
|
7
8
|
BID_ALREADY_KNOWN = "EXECUTION_PAYLOAD_BID_ERROR_BID_ALREADY_KNOWN",
|
|
8
9
|
BID_TOO_LOW = "EXECUTION_PAYLOAD_BID_ERROR_BID_TOO_LOW",
|
|
@@ -12,6 +13,7 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
12
13
|
UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
|
|
13
14
|
INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
|
|
14
15
|
NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
|
|
16
|
+
INVALID_PREV_RANDAO = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_PREV_RANDAO",
|
|
15
17
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
|
|
16
18
|
NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
|
|
17
19
|
PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
|
|
@@ -20,6 +22,12 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
20
22
|
|
|
21
23
|
export type ExecutionPayloadBidErrorType =
|
|
22
24
|
| {code: ExecutionPayloadBidErrorCode.BUILDER_NOT_ELIGIBLE; builderIndex: BuilderIndex}
|
|
25
|
+
| {
|
|
26
|
+
code: ExecutionPayloadBidErrorCode.INVALID_BUILDER_VERSION;
|
|
27
|
+
builderIndex: BuilderIndex;
|
|
28
|
+
version: number;
|
|
29
|
+
expectedVersion: number;
|
|
30
|
+
}
|
|
23
31
|
| {
|
|
24
32
|
code: ExecutionPayloadBidErrorCode.NON_ZERO_EXECUTION_PAYMENT;
|
|
25
33
|
builderIndex: BuilderIndex;
|
|
@@ -43,6 +51,12 @@ export type ExecutionPayloadBidErrorType =
|
|
|
43
51
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
|
|
44
52
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
|
|
45
53
|
| {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
54
|
+
| {
|
|
55
|
+
code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO;
|
|
56
|
+
builderIndex: BuilderIndex;
|
|
57
|
+
bidPrevRandao: string;
|
|
58
|
+
expectedPrevRandao: string;
|
|
59
|
+
}
|
|
46
60
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
|
|
47
61
|
| {
|
|
48
62
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
|
package/src/chain/interface.ts
CHANGED
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
altair,
|
|
19
19
|
capella,
|
|
20
20
|
deneb,
|
|
21
|
-
electra,
|
|
22
21
|
gloas,
|
|
23
22
|
phase0,
|
|
24
23
|
rewards,
|
|
@@ -236,7 +235,7 @@ export interface IBeaconChain {
|
|
|
236
235
|
blockSlot: Slot,
|
|
237
236
|
blockRootHex: string
|
|
238
237
|
): Promise<gloas.SignedExecutionPayloadEnvelope | null>;
|
|
239
|
-
getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<
|
|
238
|
+
getParentExecutionRequests(parentBlockSlot: Slot, parentBlockRootHex: RootHex): Promise<gloas.ExecutionRequests>;
|
|
240
239
|
|
|
241
240
|
produceCommonBlockBody(blockAttributes: BlockAttributes): Promise<CommonBlockBody>;
|
|
242
241
|
produceBlock(blockAttributes: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}): Promise<{
|
|
@@ -217,16 +217,6 @@ export class PrepareNextSlotScheduler {
|
|
|
217
217
|
});
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
221
|
-
// Cutoff = slot of the parent of the block we'll actually build on (post-reorg).
|
|
222
|
-
// Steady state: cache holds just 2 entries — head (parent for next-slot production)
|
|
223
|
-
// and head.parent (proposer-boost-reorg fallback). Anything older is evicted.
|
|
224
|
-
const updatedHeadParent = this.chain.forkChoice.getBlockHexDefaultStatus(updatedHead.parentRoot);
|
|
225
|
-
if (updatedHeadParent) {
|
|
226
|
-
this.chain.seenPayloadEnvelopeInputCache.pruneBelowParent(updatedHeadParent);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
220
|
this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
|
|
231
221
|
|
|
232
222
|
// If emitPayloadAttributes is true emit a SSE payloadAttributes event for
|
|
@@ -108,7 +108,7 @@ export type ProduceFullGloas = {
|
|
|
108
108
|
type: BlockType.Full;
|
|
109
109
|
fork: ForkPostGloas;
|
|
110
110
|
executionPayload: ExecutionPayload<ForkPostGloas>;
|
|
111
|
-
executionRequests:
|
|
111
|
+
executionRequests: gloas.ExecutionRequests;
|
|
112
112
|
blobsBundle: BlobsBundle<ForkPostGloas>;
|
|
113
113
|
cells: fulu.Cell[][];
|
|
114
114
|
parentBlockRoot: Root;
|
|
@@ -228,7 +228,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
228
228
|
);
|
|
229
229
|
const parentExecutionRequests = isExtendingPayload
|
|
230
230
|
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
231
|
-
: ssz.
|
|
231
|
+
: ssz.gloas.ExecutionRequests.defaultValue();
|
|
232
232
|
executionPayloadValue = BigInt(builderBid.message.value) * GWEI_TO_WEI;
|
|
233
233
|
|
|
234
234
|
const commonBlockBody = await commonBlockBodyPromise;
|
|
@@ -272,7 +272,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
272
272
|
|
|
273
273
|
// Get execution payload from EL
|
|
274
274
|
let parentBlockHash: Bytes32;
|
|
275
|
-
let parentExecutionRequests:
|
|
275
|
+
let parentExecutionRequests: gloas.ExecutionRequests;
|
|
276
276
|
// Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
|
|
277
277
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
|
|
278
278
|
// Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
|
|
@@ -285,7 +285,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
285
285
|
stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
|
|
286
286
|
} else {
|
|
287
287
|
parentBlockHash = currentState.latestExecutionPayloadBid.parentBlockHash;
|
|
288
|
-
parentExecutionRequests = ssz.
|
|
288
|
+
parentExecutionRequests = ssz.gloas.ExecutionRequests.defaultValue();
|
|
289
289
|
}
|
|
290
290
|
const prepareRes = await prepareExecutionPayload(
|
|
291
291
|
this,
|
|
@@ -344,13 +344,13 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
344
344
|
blockHash: executionPayload.blockHash,
|
|
345
345
|
prevRandao: currentState.getRandaoMix(currentState.epoch),
|
|
346
346
|
feeRecipient: executionPayload.feeRecipient,
|
|
347
|
-
gasLimit:
|
|
347
|
+
gasLimit: executionPayload.gasLimit,
|
|
348
348
|
builderIndex: BUILDER_INDEX_SELF_BUILD,
|
|
349
349
|
slot: blockSlot,
|
|
350
350
|
value: 0,
|
|
351
351
|
executionPayment: 0,
|
|
352
352
|
blobKzgCommitments: blobsBundle.commitments,
|
|
353
|
-
executionRequestsRoot: ssz.
|
|
353
|
+
executionRequestsRoot: ssz.gloas.ExecutionRequests.hashTreeRoot(executionRequests as gloas.ExecutionRequests),
|
|
354
354
|
};
|
|
355
355
|
const signedBid: gloas.SignedExecutionPayloadBid = {
|
|
356
356
|
message: bid,
|
|
@@ -375,7 +375,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
375
375
|
// Store execution payload data required to construct execution payload envelope later
|
|
376
376
|
const gloasResult = produceResult as ProduceFullGloas;
|
|
377
377
|
gloasResult.executionPayload = executionPayload as ExecutionPayload<ForkPostGloas>;
|
|
378
|
-
gloasResult.executionRequests = executionRequests;
|
|
378
|
+
gloasResult.executionRequests = executionRequests as gloas.ExecutionRequests;
|
|
379
379
|
gloasResult.blobsBundle = blobsBundle;
|
|
380
380
|
gloasResult.cells = cells;
|
|
381
381
|
gloasResult.parentBlockRoot = fromHex(parentBlock.blockRoot);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {ChainForkConfig} from "@lodestar/config";
|
|
2
|
-
import {CheckpointWithHex, IForkChoice, ProtoBlock} from "@lodestar/fork-choice";
|
|
2
|
+
import {CheckpointWithHex, IForkChoice, PayloadStatus, ProtoBlock} from "@lodestar/fork-choice";
|
|
3
3
|
import {computeStartSlotAtEpoch} from "@lodestar/state-transition";
|
|
4
4
|
import {RootHex} from "@lodestar/types";
|
|
5
5
|
import {Logger} from "@lodestar/utils";
|
|
@@ -154,9 +154,14 @@ export class SeenPayloadEnvelopeInput {
|
|
|
154
154
|
|
|
155
155
|
pruneBelowParent(parentBlock: ProtoBlock): void {
|
|
156
156
|
for (const block of this.forkChoice.getAllAncestorBlocks(parentBlock.blockRoot, parentBlock.payloadStatus)) {
|
|
157
|
-
|
|
157
|
+
// Only evict once the payload is FULL (revealed/imported) — on an EMPTY/PENDING branch we may
|
|
158
|
+
// still need to download the FULL envelope (see #9475), and evicting would make payload-by-root
|
|
159
|
+
// sync throw "Missing PayloadEnvelopeInput for known block".
|
|
160
|
+
if (block.slot < parentBlock.slot && block.payloadStatus === PayloadStatus.FULL) {
|
|
158
161
|
const input = this.payloadInputs.get(block.blockRoot);
|
|
159
|
-
|
|
162
|
+
// ...and don't evict while columns are still being gathered: writeDataColumnsToDb awaits the
|
|
163
|
+
// same hasComputedAllData() before persisting. Such entries are pruned by a later call.
|
|
164
|
+
if (input?.hasComputedAllData()) {
|
|
160
165
|
this.evictPayloadInput(input);
|
|
161
166
|
this.logger?.verbose("SeenPayloadEnvelopeInput.pruneBelowParent deleted", {
|
|
162
167
|
slot: block.slot,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
|
|
2
3
|
import {
|
|
3
4
|
computeEpochAtSlot,
|
|
4
5
|
createSingleSignatureSetFromComponents,
|
|
@@ -139,6 +140,17 @@ async function validateExecutionPayloadBid(
|
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
142
|
|
|
143
|
+
// [REJECT] The builder version is `PAYLOAD_BUILDER_VERSION` -- i.e.
|
|
144
|
+
// `state.builders[bid.builder_index].version == PAYLOAD_BUILDER_VERSION`.
|
|
145
|
+
if (builder.version !== PAYLOAD_BUILDER_VERSION) {
|
|
146
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
147
|
+
code: ExecutionPayloadBidErrorCode.INVALID_BUILDER_VERSION,
|
|
148
|
+
builderIndex: bid.builderIndex,
|
|
149
|
+
version: builder.version,
|
|
150
|
+
expectedVersion: PAYLOAD_BUILDER_VERSION,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
142
154
|
// [REJECT] `bid.execution_payment` is zero.
|
|
143
155
|
if (bid.executionPayment !== 0) {
|
|
144
156
|
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
@@ -175,7 +187,7 @@ async function validateExecutionPayloadBid(
|
|
|
175
187
|
// [IGNORE] `is_gas_limit_target_compatible(parent_gas_limit, bid.gas_limit, target_gas_limit)`,
|
|
176
188
|
// where `parent_gas_limit` is the `gas_limit` of the parent execution payload and
|
|
177
189
|
// `target_gas_limit` is `proposer_preferences.target_gas_limit`.
|
|
178
|
-
const bidGasLimit =
|
|
190
|
+
const bidGasLimit = bid.gasLimit;
|
|
179
191
|
const parentGasLimit = parentPayloadVariant.executionPayloadGasLimit;
|
|
180
192
|
const targetGasLimit = proposerPreferences.message.targetGasLimit;
|
|
181
193
|
if (!isGasLimitTargetCompatible(parentGasLimit, bidGasLimit, targetGasLimit)) {
|
|
@@ -232,6 +244,18 @@ async function validateExecutionPayloadBid(
|
|
|
232
244
|
});
|
|
233
245
|
}
|
|
234
246
|
|
|
247
|
+
// [REJECT] `bid.prev_randao` is the correct RANDAO mix -- i.e. validate that
|
|
248
|
+
// `bid.prev_randao == get_randao_mix(parent_state, get_current_epoch(parent_state))`.
|
|
249
|
+
const randaoMix = state.getRandaoMix(computeEpochAtSlot(state.slot));
|
|
250
|
+
if (!byteArrayEquals(bid.prevRandao, randaoMix)) {
|
|
251
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
252
|
+
code: ExecutionPayloadBidErrorCode.INVALID_PREV_RANDAO,
|
|
253
|
+
builderIndex: bid.builderIndex,
|
|
254
|
+
bidPrevRandao: toHex(bid.prevRandao),
|
|
255
|
+
expectedPrevRandao: toHex(randaoMix),
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
235
259
|
// [REJECT] `signed_execution_payload_bid.signature` is valid with respect to the `bid.builder_index`.
|
|
236
260
|
const signatureSet = createSingleSignatureSetFromComponents(
|
|
237
261
|
PublicKey.fromBytes(builder.pubkey),
|
|
@@ -106,7 +106,7 @@ async function validateExecutionPayloadEnvelope(
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
// [REJECT] `hash_tree_root(envelope.execution_requests) == bid.execution_requests_root`
|
|
109
|
-
const requestsRoot = ssz.
|
|
109
|
+
const requestsRoot = ssz.gloas.ExecutionRequests.hashTreeRoot(envelope.executionRequests);
|
|
110
110
|
if (!byteArrayEquals(requestsRoot, payloadInput.getBid().executionRequestsRoot)) {
|
|
111
111
|
throw new ExecutionPayloadEnvelopeError(GossipAction.REJECT, {
|
|
112
112
|
code: ExecutionPayloadEnvelopeErrorCode.EXECUTION_REQUESTS_ROOT_MISMATCH,
|
|
@@ -244,7 +244,7 @@ export class ExecutionEngineHttp implements IExecutionEngine {
|
|
|
244
244
|
if (executionRequests === undefined) {
|
|
245
245
|
throw Error(`executionRequests required in notifyNewPayload for fork=${fork}`);
|
|
246
246
|
}
|
|
247
|
-
const serializedExecutionRequests = serializeExecutionRequests(executionRequests);
|
|
247
|
+
const serializedExecutionRequests = serializeExecutionRequests(fork, executionRequests);
|
|
248
248
|
engineRequest = {
|
|
249
249
|
method: ForkSeq[fork] >= ForkSeq.gloas ? "engine_newPayloadV5" : "engine_newPayloadV4",
|
|
250
250
|
params: [
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {routes} from "@lodestar/api";
|
|
2
2
|
import {
|
|
3
|
+
BUILDER_DEPOSIT_REQUEST_TYPE,
|
|
4
|
+
BUILDER_EXIT_REQUEST_TYPE,
|
|
3
5
|
CONSOLIDATION_REQUEST_TYPE,
|
|
4
6
|
DEPOSIT_REQUEST_TYPE,
|
|
5
7
|
ForkName,
|
|
@@ -51,10 +53,18 @@ export enum ExecutionEngineState {
|
|
|
51
53
|
export type ExecutionRequestType =
|
|
52
54
|
| typeof DEPOSIT_REQUEST_TYPE
|
|
53
55
|
| typeof WITHDRAWAL_REQUEST_TYPE
|
|
54
|
-
| typeof CONSOLIDATION_REQUEST_TYPE
|
|
56
|
+
| typeof CONSOLIDATION_REQUEST_TYPE
|
|
57
|
+
| typeof BUILDER_DEPOSIT_REQUEST_TYPE
|
|
58
|
+
| typeof BUILDER_EXIT_REQUEST_TYPE;
|
|
55
59
|
|
|
56
60
|
export function isExecutionRequestType(type: number): type is ExecutionRequestType {
|
|
57
|
-
return
|
|
61
|
+
return (
|
|
62
|
+
type === DEPOSIT_REQUEST_TYPE ||
|
|
63
|
+
type === WITHDRAWAL_REQUEST_TYPE ||
|
|
64
|
+
type === CONSOLIDATION_REQUEST_TYPE ||
|
|
65
|
+
type === BUILDER_DEPOSIT_REQUEST_TYPE ||
|
|
66
|
+
type === BUILDER_EXIT_REQUEST_TYPE
|
|
67
|
+
);
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
export type ExecutePayloadResponse =
|
|
@@ -393,11 +393,12 @@ export class ExecutionEngineMockBackend implements JsonRpcBackend {
|
|
|
393
393
|
blobs,
|
|
394
394
|
proofs,
|
|
395
395
|
}),
|
|
396
|
-
executionRequests: serializeExecutionRequests(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
396
|
+
executionRequests: serializeExecutionRequests(
|
|
397
|
+
fork,
|
|
398
|
+
ForkSeq[fork] >= ForkSeq.gloas
|
|
399
|
+
? ssz.gloas.ExecutionRequests.defaultValue()
|
|
400
|
+
: ssz.electra.ExecutionRequests.defaultValue()
|
|
401
|
+
),
|
|
401
402
|
});
|
|
402
403
|
|
|
403
404
|
// IF the payload is deemed VALID and the build process has begun
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
+
BUILDER_DEPOSIT_REQUEST_TYPE,
|
|
3
|
+
BUILDER_EXIT_REQUEST_TYPE,
|
|
2
4
|
BYTES_PER_FIELD_ELEMENT,
|
|
3
5
|
BYTES_PER_LOGS_BLOOM,
|
|
4
6
|
CELLS_PER_EXT_BLOB,
|
|
@@ -205,16 +207,27 @@ export type WithdrawalRpc = {
|
|
|
205
207
|
};
|
|
206
208
|
|
|
207
209
|
/**
|
|
208
|
-
* ExecutionRequestsRpc
|
|
209
|
-
* -
|
|
210
|
-
* - ssz'ed
|
|
211
|
-
* - ssz'ed
|
|
210
|
+
* ExecutionRequestsRpc holds at most 5 elements and no repeated type, ordered by
|
|
211
|
+
* the single-byte request type prefix:
|
|
212
|
+
* - ssz'ed DepositRequests (0x00)
|
|
213
|
+
* - ssz'ed WithdrawalRequests (0x01)
|
|
214
|
+
* - ssz'ed ConsolidationRequests (0x02)
|
|
215
|
+
* - ssz'ed BuilderDepositRequests (0x03)
|
|
216
|
+
* - ssz'ed BuilderExitRequests (0x04)
|
|
212
217
|
*/
|
|
213
|
-
export type ExecutionRequestsRpc = (
|
|
218
|
+
export type ExecutionRequestsRpc = (
|
|
219
|
+
| DepositRequestsRpc
|
|
220
|
+
| WithdrawalRequestsRpc
|
|
221
|
+
| ConsolidationRequestsRpc
|
|
222
|
+
| BuilderDepositRequestsRpc
|
|
223
|
+
| BuilderExitRequestsRpc
|
|
224
|
+
)[];
|
|
214
225
|
|
|
215
226
|
export type DepositRequestsRpc = DATA;
|
|
216
227
|
export type WithdrawalRequestsRpc = DATA;
|
|
217
228
|
export type ConsolidationRequestsRpc = DATA;
|
|
229
|
+
export type BuilderDepositRequestsRpc = DATA;
|
|
230
|
+
export type BuilderExitRequestsRpc = DATA;
|
|
218
231
|
|
|
219
232
|
export type BlobAndProofRpc = {
|
|
220
233
|
blob: DATA;
|
|
@@ -339,7 +352,7 @@ export function parseExecutionPayload(
|
|
|
339
352
|
data = response.executionPayload;
|
|
340
353
|
blobsBundle = response.blobsBundle ? parseBlobsBundle(response.blobsBundle) : undefined;
|
|
341
354
|
executionRequests = response.executionRequests
|
|
342
|
-
? deserializeExecutionRequests(response.executionRequests)
|
|
355
|
+
? deserializeExecutionRequests(fork, response.executionRequests)
|
|
343
356
|
: undefined;
|
|
344
357
|
shouldOverrideBuilder = response.shouldOverrideBuilder ?? false;
|
|
345
358
|
} else {
|
|
@@ -526,13 +539,36 @@ function deserializeConsolidationRequests(serialized: ConsolidationRequestsRpc):
|
|
|
526
539
|
return ssz.electra.ConsolidationRequests.deserialize(dataToBytes(serialized, null));
|
|
527
540
|
}
|
|
528
541
|
|
|
542
|
+
function serializeBuilderDepositRequests(
|
|
543
|
+
builderDepositRequests: gloas.BuilderDepositRequests
|
|
544
|
+
): BuilderDepositRequestsRpc {
|
|
545
|
+
const requestsBytes = ssz.gloas.BuilderDepositRequests.serialize(builderDepositRequests);
|
|
546
|
+
return bytesToData(prefixRequests(requestsBytes, BUILDER_DEPOSIT_REQUEST_TYPE));
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function deserializeBuilderDepositRequests(serialized: BuilderDepositRequestsRpc): gloas.BuilderDepositRequests {
|
|
550
|
+
return ssz.gloas.BuilderDepositRequests.deserialize(dataToBytes(serialized, null));
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function serializeBuilderExitRequests(builderExitRequests: gloas.BuilderExitRequests): BuilderExitRequestsRpc {
|
|
554
|
+
const requestsBytes = ssz.gloas.BuilderExitRequests.serialize(builderExitRequests);
|
|
555
|
+
return bytesToData(prefixRequests(requestsBytes, BUILDER_EXIT_REQUEST_TYPE));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function deserializeBuilderExitRequests(serialized: BuilderExitRequestsRpc): gloas.BuilderExitRequests {
|
|
559
|
+
return ssz.gloas.BuilderExitRequests.deserialize(dataToBytes(serialized, null));
|
|
560
|
+
}
|
|
561
|
+
|
|
529
562
|
/**
|
|
530
563
|
* This is identical to get_execution_requests_list in
|
|
531
564
|
* https://github.com/ethereum/consensus-specs/blob/v1.5.0-alpha.8/specs/electra/beacon-chain.md#new-get_execution_requests_list
|
|
565
|
+
*
|
|
566
|
+
* Gloas extends the list with builder deposits (0x03) and builder exits (0x04) per
|
|
567
|
+
* https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.11/specs/gloas/beacon-chain.md#modified-get_execution_requests_list
|
|
532
568
|
*/
|
|
533
|
-
export function serializeExecutionRequests(executionRequests: ExecutionRequests): ExecutionRequestsRpc {
|
|
569
|
+
export function serializeExecutionRequests(fork: ForkName, executionRequests: ExecutionRequests): ExecutionRequestsRpc {
|
|
534
570
|
const {deposits, withdrawals, consolidations} = executionRequests;
|
|
535
|
-
const result = [];
|
|
571
|
+
const result: ExecutionRequestsRpc = [];
|
|
536
572
|
|
|
537
573
|
if (deposits.length !== 0) {
|
|
538
574
|
result.push(serializeDepositRequests(deposits));
|
|
@@ -546,15 +582,26 @@ export function serializeExecutionRequests(executionRequests: ExecutionRequests)
|
|
|
546
582
|
result.push(serializeConsolidationRequests(consolidations));
|
|
547
583
|
}
|
|
548
584
|
|
|
585
|
+
if (ForkSeq[fork] >= ForkSeq.gloas) {
|
|
586
|
+
const {builderDeposits, builderExits} = executionRequests as gloas.ExecutionRequests;
|
|
587
|
+
|
|
588
|
+
if (builderDeposits.length !== 0) {
|
|
589
|
+
result.push(serializeBuilderDepositRequests(builderDeposits));
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (builderExits.length !== 0) {
|
|
593
|
+
result.push(serializeBuilderExitRequests(builderExits));
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
549
597
|
return result;
|
|
550
598
|
}
|
|
551
599
|
|
|
552
|
-
export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc): ExecutionRequests {
|
|
553
|
-
const result: ExecutionRequests =
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
};
|
|
600
|
+
export function deserializeExecutionRequests(fork: ForkName, serialized: ExecutionRequestsRpc): ExecutionRequests {
|
|
601
|
+
const result: ExecutionRequests =
|
|
602
|
+
ForkSeq[fork] >= ForkSeq.gloas
|
|
603
|
+
? {deposits: [], withdrawals: [], consolidations: [], builderDeposits: [], builderExits: []}
|
|
604
|
+
: {deposits: [], withdrawals: [], consolidations: []};
|
|
558
605
|
|
|
559
606
|
if (serialized.length === 0) {
|
|
560
607
|
return result;
|
|
@@ -601,6 +648,20 @@ export function deserializeExecutionRequests(serialized: ExecutionRequestsRpc):
|
|
|
601
648
|
result.consolidations = deserializeConsolidationRequests(requests);
|
|
602
649
|
break;
|
|
603
650
|
}
|
|
651
|
+
case BUILDER_DEPOSIT_REQUEST_TYPE: {
|
|
652
|
+
if (ForkSeq[fork] < ForkSeq.gloas) {
|
|
653
|
+
throw Error(`Builder deposit request is not supported pre-gloas fork=${fork}`);
|
|
654
|
+
}
|
|
655
|
+
(result as gloas.ExecutionRequests).builderDeposits = deserializeBuilderDepositRequests(requests);
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
case BUILDER_EXIT_REQUEST_TYPE: {
|
|
659
|
+
if (ForkSeq[fork] < ForkSeq.gloas) {
|
|
660
|
+
throw Error(`Builder exit request is not supported pre-gloas fork=${fork}`);
|
|
661
|
+
}
|
|
662
|
+
(result as gloas.ExecutionRequests).builderExits = deserializeBuilderExitRequests(requests);
|
|
663
|
+
break;
|
|
664
|
+
}
|
|
604
665
|
}
|
|
605
666
|
prevRequestType = currentRequestType;
|
|
606
667
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {LogData} from "@lodestar/logger";
|
|
2
2
|
import {ForkSeq} from "@lodestar/params";
|
|
3
|
-
import {RespStatus, ResponseError} from "@lodestar/reqresp";
|
|
4
3
|
import {ColumnIndex, Slot} from "@lodestar/types";
|
|
5
4
|
import {prettyBytes, prettyPrintIndices, toRootHex} from "@lodestar/utils";
|
|
6
5
|
import {IBeaconChain} from "../../../chain/interface.js";
|
|
@@ -76,7 +75,7 @@ export async function handleColumnSidecarUnavailability({
|
|
|
76
75
|
|
|
77
76
|
export function validateRequestedDataColumns(chain: IBeaconChain, requestedColumns: ColumnIndex[]): ColumnIndex[] {
|
|
78
77
|
if (requestedColumns.length === 0) {
|
|
79
|
-
|
|
78
|
+
return [];
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
const {custodyColumns, custodyColumnsIndex} = chain.custodyConfig;
|
package/src/util/sszBytes.ts
CHANGED
|
@@ -706,7 +706,7 @@ export function getBlockRootFromPayloadAttestationMessageSerialized(data: Uint8A
|
|
|
706
706
|
* blockHash: Bytes32 (32 bytes)
|
|
707
707
|
* prevRandao: Bytes32 (32 bytes)
|
|
708
708
|
* feeRecipient: ExecutionAddress(20 bytes)
|
|
709
|
-
* gasLimit:
|
|
709
|
+
* gasLimit: UintNum64 (8 bytes)
|
|
710
710
|
* builderIndex: BuilderIndex (8 bytes)
|
|
711
711
|
* slot: Slot (8 bytes) ← absolute offset 264
|
|
712
712
|
*/
|