@lodestar/beacon-node 1.44.0-dev.b506aab66d → 1.44.0-dev.c8732e0f70
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 +30 -0
- 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 +1 -1
- package/lib/api/impl/beacon/pool/index.js.map +1 -1
- package/lib/api/impl/validator/index.d.ts.map +1 -1
- package/lib/api/impl/validator/index.js +82 -44
- 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 +5 -2
- package/lib/chain/blocks/importBlock.js.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +1 -1
- package/lib/chain/blocks/importExecutionPayload.js.map +1 -1
- package/lib/chain/chain.d.ts +1 -1
- package/lib/chain/chain.d.ts.map +1 -1
- package/lib/chain/chain.js +2 -1
- package/lib/chain/chain.js.map +1 -1
- package/lib/chain/errors/executionPayloadBid.d.ts +5 -0
- package/lib/chain/errors/executionPayloadBid.d.ts.map +1 -1
- package/lib/chain/errors/executionPayloadBid.js +1 -0
- package/lib/chain/errors/executionPayloadBid.js.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.d.ts +4 -4
- package/lib/chain/opPools/executionPayloadBidPool.d.ts.map +1 -1
- package/lib/chain/opPools/executionPayloadBidPool.js +6 -4
- package/lib/chain/opPools/executionPayloadBidPool.js.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts +3 -1
- package/lib/chain/produceBlock/produceBlockBody.d.ts.map +1 -1
- package/lib/chain/produceBlock/produceBlockBody.js +54 -14
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +2 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +2 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +12 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.d.ts.map +1 -1
- package/lib/chain/validation/payloadAttestationMessage.js +15 -5
- package/lib/chain/validation/payloadAttestationMessage.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 +2 -2
- package/lib/network/processor/gossipHandlers.js.map +1 -1
- package/package.json +14 -14
- package/src/api/impl/beacon/blocks/index.ts +36 -0
- package/src/api/impl/beacon/pool/index.ts +1 -0
- package/src/api/impl/validator/index.ts +95 -45
- package/src/chain/blocks/importBlock.ts +8 -1
- package/src/chain/blocks/importExecutionPayload.ts +1 -1
- package/src/chain/chain.ts +2 -0
- package/src/chain/errors/executionPayloadBid.ts +2 -0
- package/src/chain/opPools/executionPayloadBidPool.ts +10 -9
- package/src/chain/produceBlock/produceBlockBody.ts +78 -16
- package/src/chain/regen/interface.ts +2 -0
- package/src/chain/validation/executionPayloadBid.ts +13 -2
- package/src/chain/validation/payloadAttestationMessage.ts +17 -6
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +2 -1
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
gloas,
|
|
51
51
|
ssz,
|
|
52
52
|
} from "@lodestar/types";
|
|
53
|
-
import {Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
53
|
+
import {GWEI_TO_WEI, Logger, byteArrayEquals, fromHex, sleep, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
|
|
54
54
|
import {ZERO_HASH_HEX} from "../../constants/index.js";
|
|
55
55
|
import {numToQuantity} from "../../execution/engine/utils.js";
|
|
56
56
|
import {IExecutionBuilder, IExecutionEngine, PayloadAttributes, PayloadId} from "../../execution/index.js";
|
|
@@ -91,6 +91,8 @@ export type BlockAttributes = {
|
|
|
91
91
|
slot: Slot;
|
|
92
92
|
parentBlock: ProtoBlock;
|
|
93
93
|
feeRecipient?: string;
|
|
94
|
+
/** When provided, build block with this builder bid instead of a self-build bid */
|
|
95
|
+
builderBid?: gloas.SignedExecutionPayloadBid;
|
|
94
96
|
};
|
|
95
97
|
|
|
96
98
|
export enum BlockType {
|
|
@@ -150,6 +152,28 @@ export type ProduceResult =
|
|
|
150
152
|
| ProduceFullPhase0
|
|
151
153
|
| ProduceBlinded;
|
|
152
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Drop voluntary exits that `parent_execution_requests` have invalidated (e.g. a withdrawal
|
|
157
|
+
* request initiating an exit on the same validator). Op pool selected against the unapplied
|
|
158
|
+
* state, so re-validate against the post-apply state to avoid producing an invalid block.
|
|
159
|
+
*
|
|
160
|
+
* `getStateAfterParentPayload` is a thunk so the post-apply state is only materialized when
|
|
161
|
+
* actually needed (i.e. when extending the parent payload and there are exits to filter).
|
|
162
|
+
*/
|
|
163
|
+
function maybeFilterInvalidatedVoluntaryExits(
|
|
164
|
+
commonBlockBody: CommonBlockBody,
|
|
165
|
+
isExtendingPayload: boolean,
|
|
166
|
+
getStateAfterParentPayload: () => IBeaconStateViewBellatrix
|
|
167
|
+
): CommonBlockBody["voluntaryExits"] {
|
|
168
|
+
if (!isExtendingPayload || commonBlockBody.voluntaryExits.length === 0) {
|
|
169
|
+
return commonBlockBody.voluntaryExits;
|
|
170
|
+
}
|
|
171
|
+
const state = getStateAfterParentPayload();
|
|
172
|
+
return commonBlockBody.voluntaryExits.filter((signedVoluntaryExit) =>
|
|
173
|
+
state.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
153
177
|
export async function produceBlockBody<T extends BlockType>(
|
|
154
178
|
this: BeaconChain,
|
|
155
179
|
blockType: T,
|
|
@@ -172,6 +196,7 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
172
196
|
proposerIndex,
|
|
173
197
|
proposerPubKey,
|
|
174
198
|
commonBlockBodyPromise,
|
|
199
|
+
builderBid,
|
|
175
200
|
} = blockAttr;
|
|
176
201
|
let executionPayloadValue: Wei;
|
|
177
202
|
let blockBody: AssembledBodyType<T>;
|
|
@@ -192,7 +217,43 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
192
217
|
};
|
|
193
218
|
this.logger.verbose("Producing beacon block body", logMeta);
|
|
194
219
|
|
|
195
|
-
if (
|
|
220
|
+
if (builderBid !== undefined) {
|
|
221
|
+
if (!isStatePostGloas(currentState)) {
|
|
222
|
+
throw new Error("Expected Gloas state for builder bid block production");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const isExtendingPayload = byteArrayEquals(
|
|
226
|
+
builderBid.message.parentBlockHash,
|
|
227
|
+
currentState.latestExecutionPayloadBid.blockHash
|
|
228
|
+
);
|
|
229
|
+
const parentExecutionRequests = isExtendingPayload
|
|
230
|
+
? await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot)
|
|
231
|
+
: ssz.electra.ExecutionRequests.defaultValue();
|
|
232
|
+
executionPayloadValue = BigInt(builderBid.message.value) * GWEI_TO_WEI;
|
|
233
|
+
|
|
234
|
+
const commonBlockBody = await commonBlockBodyPromise;
|
|
235
|
+
const gloasBody = Object.assign({}, commonBlockBody) as gloas.BeaconBlockBody;
|
|
236
|
+
gloasBody.signedExecutionPayloadBid = builderBid;
|
|
237
|
+
gloasBody.payloadAttestations = this.payloadAttestationPool.getPayloadAttestationsForBlock(
|
|
238
|
+
parentBlock.blockRoot,
|
|
239
|
+
blockSlot - 1
|
|
240
|
+
);
|
|
241
|
+
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
242
|
+
gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(commonBlockBody, isExtendingPayload, () =>
|
|
243
|
+
currentState.withParentPayloadApplied(parentExecutionRequests)
|
|
244
|
+
);
|
|
245
|
+
blockBody = gloasBody as AssembledBodyType<T>;
|
|
246
|
+
|
|
247
|
+
this.logger.verbose("Produced block with builder bid", {
|
|
248
|
+
slot: blockSlot,
|
|
249
|
+
builderIndex: builderBid.message.builderIndex,
|
|
250
|
+
bidValue: builderBid.message.value,
|
|
251
|
+
parentBlockHash: toRootHex(builderBid.message.parentBlockHash),
|
|
252
|
+
parentBlockRoot: toRootHex(builderBid.message.parentBlockRoot),
|
|
253
|
+
blockHash: toRootHex(builderBid.message.blockHash),
|
|
254
|
+
isExtendingPayload,
|
|
255
|
+
});
|
|
256
|
+
} else if (isForkPostGloas(fork)) {
|
|
196
257
|
if (!isStatePostGloas(currentState)) {
|
|
197
258
|
throw new Error("Expected Gloas state for Gloas block production");
|
|
198
259
|
}
|
|
@@ -209,12 +270,6 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
209
270
|
|
|
210
271
|
const endExecutionPayload = this.metrics?.executionBlockProductionTimeSteps.startTimer();
|
|
211
272
|
|
|
212
|
-
this.logger.verbose("Preparing execution payload from engine", {
|
|
213
|
-
slot: blockSlot,
|
|
214
|
-
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
215
|
-
feeRecipient,
|
|
216
|
-
});
|
|
217
|
-
|
|
218
273
|
// Get execution payload from EL
|
|
219
274
|
let parentBlockHash: Bytes32;
|
|
220
275
|
let parentExecutionRequests: electra.ExecutionRequests;
|
|
@@ -247,6 +302,16 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
247
302
|
const {prepType, payloadId} = prepareRes;
|
|
248
303
|
Object.assign(logMeta, {executionPayloadPrepType: prepType});
|
|
249
304
|
|
|
305
|
+
this.logger.verbose("Prepared execution payload from engine", {
|
|
306
|
+
slot: blockSlot,
|
|
307
|
+
parentBlockRoot: toRootHex(parentBlockRoot),
|
|
308
|
+
parentBlockHash: toRootHex(parentBlockHash),
|
|
309
|
+
feeRecipient,
|
|
310
|
+
prepType,
|
|
311
|
+
payloadId,
|
|
312
|
+
isBuildingOnFull,
|
|
313
|
+
});
|
|
314
|
+
|
|
250
315
|
if (prepType !== PayloadPreparationType.Cached) {
|
|
251
316
|
await sleep(PAYLOAD_GENERATION_TIME_MS);
|
|
252
317
|
}
|
|
@@ -300,14 +365,11 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
300
365
|
blockSlot - 1
|
|
301
366
|
);
|
|
302
367
|
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
309
|
-
);
|
|
310
|
-
}
|
|
368
|
+
gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(
|
|
369
|
+
commonBlockBody,
|
|
370
|
+
isBuildingOnFull,
|
|
371
|
+
() => stateAfterParentPayload
|
|
372
|
+
);
|
|
311
373
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
312
374
|
|
|
313
375
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -17,10 +17,12 @@ export enum RegenCaller {
|
|
|
17
17
|
predictProposerHead = "predictProposerHead",
|
|
18
18
|
produceAttestationData = "produceAttestationData",
|
|
19
19
|
processBlocksInEpoch = "processBlocksInEpoch",
|
|
20
|
+
importExecutionPayload = "importExecutionPayload",
|
|
20
21
|
validateGossipAggregateAndProof = "validateGossipAggregateAndProof",
|
|
21
22
|
validateGossipAttestation = "validateGossipAttestation",
|
|
22
23
|
validateGossipVoluntaryExit = "validateGossipVoluntaryExit",
|
|
23
24
|
validateGossipExecutionPayloadBid = "validateGossipExecutionPayloadBid",
|
|
25
|
+
validateGossipPayloadAttestationMessage = "validateGossipPayloadAttestationMessage",
|
|
24
26
|
validateGossipProposerPreferences = "validateGossipProposerPreferences",
|
|
25
27
|
onForkChoiceFinalized = "onForkChoiceFinalized",
|
|
26
28
|
restApi = "restApi",
|
|
@@ -61,6 +61,17 @@ async function validateExecutionPayloadBid(
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// [REJECT] The bid is for a higher slot than its parent block -- i.e.
|
|
65
|
+
// validate that `bid.slot` is greater than the slot of the block with root
|
|
66
|
+
// `bid.parent_block_root`.
|
|
67
|
+
if (bid.slot <= parentBlock.slot) {
|
|
68
|
+
throw new ExecutionPayloadBidError(GossipAction.REJECT, {
|
|
69
|
+
code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT,
|
|
70
|
+
parentSlot: parentBlock.slot,
|
|
71
|
+
slot: bid.slot,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
// [IGNORE] A `SignedProposerPreferences` matching `bid.slot` and the bid's branch has been
|
|
65
76
|
// seen — i.e. `proposal_slot == bid.slot` AND `dependent_root ==
|
|
66
77
|
// get_proposer_dependent_root(parent_state, compute_epoch_at_slot(bid.slot))`.
|
|
@@ -186,11 +197,11 @@ async function validateExecutionPayloadBid(
|
|
|
186
197
|
// [IGNORE] this bid is the highest value bid seen for the tuple
|
|
187
198
|
// `(bid.slot, bid.parent_block_hash, bid.parent_block_root)`.
|
|
188
199
|
const bestBid = chain.executionPayloadBidPool.getBestBid(bid.slot, parentBlockHashHex, parentBlockRootHex);
|
|
189
|
-
if (bestBid !== null && bestBid.value >= bid.value) {
|
|
200
|
+
if (bestBid !== null && bestBid.message.value >= bid.value) {
|
|
190
201
|
throw new ExecutionPayloadBidError(GossipAction.IGNORE, {
|
|
191
202
|
code: ExecutionPayloadBidErrorCode.BID_TOO_LOW,
|
|
192
203
|
bidValue: bid.value,
|
|
193
|
-
currentHighestBid: bestBid.value,
|
|
204
|
+
currentHighestBid: bestBid.message.value,
|
|
194
205
|
});
|
|
195
206
|
}
|
|
196
207
|
// [IGNORE] `bid.value` is less or equal than the builder's excess balance --
|
|
@@ -8,6 +8,7 @@ import {RootHex, gloas, ssz} from "@lodestar/types";
|
|
|
8
8
|
import {toRootHex} from "@lodestar/utils";
|
|
9
9
|
import {GossipAction, PayloadAttestationError, PayloadAttestationErrorCode} from "../errors/index.js";
|
|
10
10
|
import {IBeaconChain} from "../index.js";
|
|
11
|
+
import {RegenCaller} from "../regen/index.js";
|
|
11
12
|
|
|
12
13
|
export type PayloadAttestationValidationResult = {
|
|
13
14
|
attDataRootHex: RootHex;
|
|
@@ -61,22 +62,32 @@ async function validatePayloadAttestationMessage(
|
|
|
61
62
|
// [IGNORE] The message's block `data.beacon_block_root` has been seen (via
|
|
62
63
|
// gossip or non-gossip sources) (a client MAY queue attestation for processing
|
|
63
64
|
// once the block is retrieved. Note a client might want to request payload after).
|
|
64
|
-
|
|
65
|
+
const block = chain.forkChoice.getBlockDefaultStatus(data.beaconBlockRoot);
|
|
66
|
+
if (!block) {
|
|
65
67
|
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
66
68
|
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
67
69
|
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
68
70
|
});
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
const state = chain.getHeadState();
|
|
72
|
-
if (!isStatePostGloas(state)) {
|
|
73
|
-
throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
73
|
// [REJECT] The message's block `data.beacon_block_root` passes validation.
|
|
77
74
|
// TODO GLOAS: implement this. Technically if we cannot get proto block from fork choice,
|
|
78
75
|
// it is possible that the block didn't pass the validation
|
|
79
76
|
|
|
77
|
+
// Use the referenced block's branch state for the PTC committee check
|
|
78
|
+
const state = await chain.regen
|
|
79
|
+
.getBlockSlotState(block, data.slot, {dontTransferCache: true}, RegenCaller.validateGossipPayloadAttestationMessage)
|
|
80
|
+
.catch(() => {
|
|
81
|
+
throw new PayloadAttestationError(GossipAction.IGNORE, {
|
|
82
|
+
code: PayloadAttestationErrorCode.UNKNOWN_BLOCK_ROOT,
|
|
83
|
+
blockRoot: toRootHex(data.beaconBlockRoot),
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (!isStatePostGloas(state)) {
|
|
88
|
+
throw new Error(`Expected gloas+ state for payload attestation validation, got fork=${state.forkName}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
// [REJECT] The message's validator index is within the payload committee in
|
|
81
92
|
// `get_ptc(state, data.slot)`. The `state` is the head state corresponding to
|
|
82
93
|
// processing the block up to the current slot as determined by the fork choice.
|
package/src/network/interface.ts
CHANGED
|
@@ -113,6 +113,7 @@ export interface INetwork extends INetworkCorePublic {
|
|
|
113
113
|
publishLightClientFinalityUpdate(update: LightClientFinalityUpdate): Promise<number>;
|
|
114
114
|
publishLightClientOptimisticUpdate(update: LightClientOptimisticUpdate): Promise<number>;
|
|
115
115
|
publishSignedExecutionPayloadEnvelope(signedEnvelope: gloas.SignedExecutionPayloadEnvelope): Promise<number>;
|
|
116
|
+
publishSignedExecutionPayloadBid(signedBid: gloas.SignedExecutionPayloadBid): Promise<number>;
|
|
116
117
|
publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number>;
|
|
117
118
|
publishProposerPreferences(signedProposerPreferences: gloas.SignedProposerPreferences): Promise<number>;
|
|
118
119
|
|
package/src/network/network.ts
CHANGED
|
@@ -515,6 +515,17 @@ export class Network implements INetwork {
|
|
|
515
515
|
);
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
async publishSignedExecutionPayloadBid(signedBid: gloas.SignedExecutionPayloadBid): Promise<number> {
|
|
519
|
+
const epoch = computeEpochAtSlot(signedBid.message.slot);
|
|
520
|
+
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
521
|
+
|
|
522
|
+
return this.publishGossip<GossipType.execution_payload_bid>(
|
|
523
|
+
{type: GossipType.execution_payload_bid, boundary},
|
|
524
|
+
signedBid,
|
|
525
|
+
{ignoreDuplicatePublishError: true}
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
518
529
|
async publishPayloadAttestationMessage(payloadAttestationMessage: gloas.PayloadAttestationMessage): Promise<number> {
|
|
519
530
|
const epoch = computeEpochAtSlot(payloadAttestationMessage.data.slot);
|
|
520
531
|
const boundary = this.config.getForkBoundaryAtEpoch(epoch);
|
|
@@ -1214,6 +1214,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1214
1214
|
}
|
|
1215
1215
|
chain.forkChoice.notifyPtcMessages(
|
|
1216
1216
|
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1217
|
+
payloadAttestationMessage.data.slot,
|
|
1217
1218
|
validationResult.validatorCommitteeIndices,
|
|
1218
1219
|
payloadAttestationMessage.data.payloadPresent,
|
|
1219
1220
|
payloadAttestationMessage.data.blobDataAvailable
|
|
@@ -1229,7 +1230,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1229
1230
|
|
|
1230
1231
|
// Handle valid payload bid by storing in a bid pool
|
|
1231
1232
|
try {
|
|
1232
|
-
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid
|
|
1233
|
+
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
|
|
1233
1234
|
metrics?.opPool.executionPayloadBidPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1234
1235
|
} catch (e) {
|
|
1235
1236
|
logger.error("Error adding to executionPayloadBid pool", {}, e as Error);
|