@lodestar/beacon-node 1.44.0-dev.985999b30c → 1.44.0-dev.edb7c53fbc
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 +77 -37
- 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.d.ts.map +1 -1
- package/lib/chain/blocks/importExecutionPayload.js +4 -2
- 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/prepareNextSlot.d.ts.map +1 -1
- package/lib/chain/prepareNextSlot.js +2 -1
- package/lib/chain/prepareNextSlot.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 +59 -16
- package/lib/chain/produceBlock/produceBlockBody.js.map +1 -1
- package/lib/chain/regen/interface.d.ts +1 -0
- package/lib/chain/regen/interface.d.ts.map +1 -1
- package/lib/chain/regen/interface.js +1 -0
- package/lib/chain/regen/interface.js.map +1 -1
- package/lib/chain/validation/executionPayloadBid.js +12 -2
- package/lib/chain/validation/executionPayloadBid.js.map +1 -1
- package/lib/metrics/metrics/lodestar.d.ts +1 -1
- package/lib/metrics/metrics/lodestar.d.ts.map +1 -1
- package/lib/metrics/metrics/lodestar.js +4 -3
- package/lib/metrics/metrics/lodestar.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 +10 -3
- 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 +2 -1
- package/src/api/impl/validator/index.ts +90 -38
- package/src/chain/blocks/importBlock.ts +9 -2
- package/src/chain/blocks/importExecutionPayload.ts +7 -2
- 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/prepareNextSlot.ts +2 -1
- package/src/chain/produceBlock/produceBlockBody.ts +83 -18
- package/src/chain/regen/interface.ts +1 -0
- package/src/chain/validation/executionPayloadBid.ts +13 -2
- package/src/metrics/metrics/lodestar.ts +4 -3
- package/src/network/interface.ts +1 -0
- package/src/network/network.ts +11 -0
- package/src/network/processor/gossipHandlers.ts +13 -3
|
@@ -116,13 +116,19 @@ export async function importBlock(
|
|
|
116
116
|
}
|
|
117
117
|
executionStatus = parentBlock.executionStatus;
|
|
118
118
|
}
|
|
119
|
+
|
|
120
|
+
// getBeaconProposerOrNull will return null if head state is more than one epoch away
|
|
121
|
+
// from block slot. We skip proposer boost canonical check as we cannot determine the canonical proposer
|
|
122
|
+
const expectedProposerIndex: number | null = this.getHeadState().getBeaconProposerOrNull(blockSlot);
|
|
123
|
+
|
|
119
124
|
const blockSummary = this.forkChoice.onBlock(
|
|
120
125
|
block.message,
|
|
121
126
|
postState,
|
|
122
127
|
blockDelaySec,
|
|
123
128
|
currentSlot,
|
|
124
129
|
executionStatus,
|
|
125
|
-
dataAvailabilityStatus
|
|
130
|
+
dataAvailabilityStatus,
|
|
131
|
+
expectedProposerIndex
|
|
126
132
|
);
|
|
127
133
|
|
|
128
134
|
// This adds the state necessary to process the next block
|
|
@@ -258,7 +264,8 @@ export async function importBlock(
|
|
|
258
264
|
this.forkChoice.notifyPtcMessages(
|
|
259
265
|
toRootHex(payloadAttestation.data.beaconBlockRoot),
|
|
260
266
|
ptcIndices,
|
|
261
|
-
payloadAttestation.data.payloadPresent
|
|
267
|
+
payloadAttestation.data.payloadPresent,
|
|
268
|
+
payloadAttestation.data.blobDataAvailable
|
|
262
269
|
);
|
|
263
270
|
}
|
|
264
271
|
} catch (e) {
|
|
@@ -129,7 +129,7 @@ export async function importExecutionPayload(
|
|
|
129
129
|
|
|
130
130
|
// 3. Regenerate state for envelope verification
|
|
131
131
|
const blockState = await this.regen
|
|
132
|
-
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.
|
|
132
|
+
.getBlockSlotState(protoBlock, protoBlock.slot, {dontTransferCache: true}, RegenCaller.importExecutionPayload)
|
|
133
133
|
.catch(() =>
|
|
134
134
|
// only happen at the 1st batch of skipped slot checkpoint sync
|
|
135
135
|
this.regen.getClosestHeadState(protoBlock)
|
|
@@ -255,7 +255,11 @@ export async function importExecutionPayload(
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
// 8. Record metrics for payload envelope and column sources
|
|
258
|
-
this.
|
|
258
|
+
const delaySec = this.clock.secFromSlot(slot);
|
|
259
|
+
this.metrics?.importPayload.elapsedTimeTillImported.observe(
|
|
260
|
+
{source: payloadInput.getPayloadEnvelopeSource().source},
|
|
261
|
+
delaySec
|
|
262
|
+
);
|
|
259
263
|
for (const {source} of payloadInput.getSampledColumnsWithSource()) {
|
|
260
264
|
this.metrics?.importPayload.columnsBySource.inc({source});
|
|
261
265
|
}
|
|
@@ -276,6 +280,7 @@ export async function importExecutionPayload(
|
|
|
276
280
|
builderIndex: envelope.builderIndex,
|
|
277
281
|
blockRoot: blockRootHex,
|
|
278
282
|
blockHash: blockHashHex,
|
|
283
|
+
delaySec,
|
|
279
284
|
});
|
|
280
285
|
}
|
|
281
286
|
|
package/src/chain/chain.ts
CHANGED
|
@@ -1049,6 +1049,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1049
1049
|
feeRecipient,
|
|
1050
1050
|
commonBlockBodyPromise,
|
|
1051
1051
|
parentBlock,
|
|
1052
|
+
builderBid,
|
|
1052
1053
|
}: BlockAttributes & {commonBlockBodyPromise: Promise<CommonBlockBody>}
|
|
1053
1054
|
): Promise<{
|
|
1054
1055
|
block: AssembledBlockType<T>;
|
|
@@ -1078,6 +1079,7 @@ export class BeaconChain implements IBeaconChain {
|
|
|
1078
1079
|
proposerIndex,
|
|
1079
1080
|
proposerPubKey,
|
|
1080
1081
|
commonBlockBodyPromise,
|
|
1082
|
+
builderBid,
|
|
1081
1083
|
}
|
|
1082
1084
|
);
|
|
1083
1085
|
|
|
@@ -11,6 +11,7 @@ export enum ExecutionPayloadBidErrorCode {
|
|
|
11
11
|
UNKNOWN_BLOCK_ROOT = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_BLOCK_ROOT",
|
|
12
12
|
UNKNOWN_PARENT_BLOCK_HASH = "EXECUTION_PAYLOAD_BID_ERROR_UNKNOWN_PARENT_BLOCK_HASH",
|
|
13
13
|
INVALID_SLOT = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SLOT",
|
|
14
|
+
NOT_LATER_THAN_PARENT = "EXECUTION_PAYLOAD_BID_ERROR_NOT_LATER_THAN_PARENT",
|
|
14
15
|
INVALID_SIGNATURE = "EXECUTION_PAYLOAD_BID_ERROR_INVALID_SIGNATURE",
|
|
15
16
|
NO_MATCHING_PROPOSER_PREFERENCES = "EXECUTION_PAYLOAD_BID_ERROR_NO_MATCHING_PROPOSER_PREFERENCES",
|
|
16
17
|
PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH = "EXECUTION_PAYLOAD_BID_ERROR_PROPOSER_PREFERENCES_FEE_RECIPIENT_MISMATCH",
|
|
@@ -41,6 +42,7 @@ export type ExecutionPayloadBidErrorType =
|
|
|
41
42
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_BLOCK_ROOT; parentBlockRoot: RootHex}
|
|
42
43
|
| {code: ExecutionPayloadBidErrorCode.UNKNOWN_PARENT_BLOCK_HASH; parentBlockHash: RootHex}
|
|
43
44
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SLOT; builderIndex: BuilderIndex; slot: Slot}
|
|
45
|
+
| {code: ExecutionPayloadBidErrorCode.NOT_LATER_THAN_PARENT; parentSlot: Slot; slot: Slot}
|
|
44
46
|
| {code: ExecutionPayloadBidErrorCode.INVALID_SIGNATURE; builderIndex: BuilderIndex; slot: Slot}
|
|
45
47
|
| {
|
|
46
48
|
code: ExecutionPayloadBidErrorCode.NO_MATCHING_PROPOSER_PREFERENCES;
|
|
@@ -12,13 +12,13 @@ type BlockRootHex = string;
|
|
|
12
12
|
type BlockHashHex = string;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Store the best execution payload bid per slot / (parent block root, parent block hash).
|
|
15
|
+
* Store the best signed execution payload bid per slot / (parent block root, parent block hash).
|
|
16
16
|
*/
|
|
17
17
|
export class ExecutionPayloadBidPool {
|
|
18
18
|
private readonly bidByParentHashByParentRootBySlot = new MapDef<
|
|
19
19
|
Slot,
|
|
20
|
-
MapDef<BlockRootHex, Map<BlockHashHex, gloas.
|
|
21
|
-
>(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.
|
|
20
|
+
MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>
|
|
21
|
+
>(() => new MapDef<BlockRootHex, Map<BlockHashHex, gloas.SignedExecutionPayloadBid>>(() => new Map()));
|
|
22
22
|
private lowestPermissibleSlot = 0;
|
|
23
23
|
|
|
24
24
|
get size(): number {
|
|
@@ -31,8 +31,8 @@ export class ExecutionPayloadBidPool {
|
|
|
31
31
|
return count;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
add(bid: gloas.
|
|
35
|
-
const {slot, parentBlockRoot, parentBlockHash, value} = bid;
|
|
34
|
+
add(bid: gloas.SignedExecutionPayloadBid): InsertOutcome {
|
|
35
|
+
const {slot, parentBlockRoot, parentBlockHash, value} = bid.message;
|
|
36
36
|
const lowestPermissibleSlot = this.lowestPermissibleSlot;
|
|
37
37
|
|
|
38
38
|
if (slot < lowestPermissibleSlot) {
|
|
@@ -45,7 +45,7 @@ export class ExecutionPayloadBidPool {
|
|
|
45
45
|
const existing = bidByParentHash.get(parentHashHex);
|
|
46
46
|
|
|
47
47
|
if (existing) {
|
|
48
|
-
const existingValue = existing.value;
|
|
48
|
+
const existingValue = existing.message.value;
|
|
49
49
|
const newValue = value;
|
|
50
50
|
if (newValue > existingValue) {
|
|
51
51
|
bidByParentHash.set(parentHashHex, bid);
|
|
@@ -59,14 +59,15 @@ export class ExecutionPayloadBidPool {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Return the highest-value bid matching slot, parent block hash, and parent block root.
|
|
62
|
+
* Return the highest-value signed bid matching slot, parent block hash, and parent block root.
|
|
63
63
|
* Used for gossip validation and block production.
|
|
64
64
|
*/
|
|
65
65
|
getBestBid(
|
|
66
66
|
slot: Slot,
|
|
67
|
-
parentBlockHash: BlockHashHex,
|
|
67
|
+
parentBlockHash: BlockHashHex | null,
|
|
68
68
|
parentBlockRoot: BlockRootHex
|
|
69
|
-
): gloas.
|
|
69
|
+
): gloas.SignedExecutionPayloadBid | null {
|
|
70
|
+
if (parentBlockHash === null) return null;
|
|
70
71
|
const bidByParentHash = this.bidByParentHashByParentRootBySlot.get(slot)?.get(parentBlockRoot);
|
|
71
72
|
return bidByParentHash?.get(parentBlockHash) ?? null;
|
|
72
73
|
}
|
|
@@ -169,7 +169,8 @@ export class PrepareNextSlotScheduler {
|
|
|
169
169
|
// Apply parent payload once here as it's reused by EL prep and SSE emit below
|
|
170
170
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = updatedPrepareState;
|
|
171
171
|
if (isStatePostGloas(updatedPrepareState)) {
|
|
172
|
-
|
|
172
|
+
// Spec: should_build_on_full(store, head) — see produceBlockBody.ts for context.
|
|
173
|
+
if (this.chain.forkChoice.shouldBuildOnFull(updatedHead)) {
|
|
173
174
|
parentBlockHash = updatedPrepareState.latestExecutionPayloadBid.blockHash;
|
|
174
175
|
// Skip applying parent payload unless we're proposing the next slot or have to emit payload_attributes events
|
|
175
176
|
if (feeRecipient !== undefined || this.chain.opts.emitPayloadAttributes === true) {
|
|
@@ -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,19 +270,16 @@ 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;
|
|
221
276
|
// Apply parent payload once here as it's reused by EL prep and voluntary exit filtering below
|
|
222
277
|
let stateAfterParentPayload: IBeaconStateViewBellatrix = currentState;
|
|
223
|
-
|
|
224
|
-
|
|
278
|
+
// Spec: should_build_on_full(store, head). `parentBlock` is the proposer's head
|
|
279
|
+
// (set by chain.getProposerHead(slot)). Returns false when the PTC majority
|
|
280
|
+
// signalled the blob data is not available, forcing a build on EMPTY (reorg).
|
|
281
|
+
const isBuildingOnFull = this.forkChoice.shouldBuildOnFull(parentBlock);
|
|
282
|
+
if (isBuildingOnFull) {
|
|
225
283
|
parentBlockHash = currentState.latestExecutionPayloadBid.blockHash;
|
|
226
284
|
parentExecutionRequests = await this.getParentExecutionRequests(parentBlock.slot, parentBlock.blockRoot);
|
|
227
285
|
stateAfterParentPayload = currentState.withParentPayloadApplied(parentExecutionRequests);
|
|
@@ -244,6 +302,16 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
244
302
|
const {prepType, payloadId} = prepareRes;
|
|
245
303
|
Object.assign(logMeta, {executionPayloadPrepType: prepType});
|
|
246
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
|
+
|
|
247
315
|
if (prepType !== PayloadPreparationType.Cached) {
|
|
248
316
|
await sleep(PAYLOAD_GENERATION_TIME_MS);
|
|
249
317
|
}
|
|
@@ -297,14 +365,11 @@ export async function produceBlockBody<T extends BlockType>(
|
|
|
297
365
|
blockSlot - 1
|
|
298
366
|
);
|
|
299
367
|
gloasBody.parentExecutionRequests = parentExecutionRequests;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
stateAfterParentPayload.isValidVoluntaryExit(signedVoluntaryExit, false)
|
|
306
|
-
);
|
|
307
|
-
}
|
|
368
|
+
gloasBody.voluntaryExits = maybeFilterInvalidatedVoluntaryExits(
|
|
369
|
+
commonBlockBody,
|
|
370
|
+
isBuildingOnFull,
|
|
371
|
+
() => stateAfterParentPayload
|
|
372
|
+
);
|
|
308
373
|
blockBody = gloasBody as AssembledBodyType<T>;
|
|
309
374
|
|
|
310
375
|
// Store execution payload data required to construct execution payload envelope later
|
|
@@ -17,6 +17,7 @@ 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",
|
|
@@ -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 --
|
|
@@ -987,10 +987,11 @@ export function createLodestarMetrics(
|
|
|
987
987
|
}),
|
|
988
988
|
},
|
|
989
989
|
importPayload: {
|
|
990
|
-
|
|
991
|
-
name: "
|
|
992
|
-
help: "
|
|
990
|
+
elapsedTimeTillImported: register.histogram<{source: PayloadEnvelopeInputSource}>({
|
|
991
|
+
name: "lodestar_import_payload_elapsed_time_till_imported_seconds",
|
|
992
|
+
help: "Time elapsed between slot time and the time execution payload envelope is imported (added to fork choice)",
|
|
993
993
|
labelNames: ["source"],
|
|
994
|
+
buckets: [1, 2, 3, 6, 9, 12],
|
|
994
995
|
}),
|
|
995
996
|
columnsBySource: register.gauge<{source: PayloadEnvelopeInputSource}>({
|
|
996
997
|
name: "lodestar_import_payload_columns_by_source_total",
|
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);
|
|
@@ -1117,7 +1117,16 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1117
1117
|
}
|
|
1118
1118
|
|
|
1119
1119
|
const slot = envelope.payload.slotNumber;
|
|
1120
|
-
const delaySec =
|
|
1120
|
+
const delaySec = chain.clock.secFromSlot(slot, seenTimestampSec);
|
|
1121
|
+
|
|
1122
|
+
logger.debug("Received gossip payload envelope", {
|
|
1123
|
+
currentSlot: chain.clock.currentSlot,
|
|
1124
|
+
peerId: peerIdStr,
|
|
1125
|
+
slot,
|
|
1126
|
+
blockRoot: toRootHex(envelope.beaconBlockRoot),
|
|
1127
|
+
delaySec,
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1121
1130
|
metrics?.gossipExecutionPayloadEnvelope.elapsedTimeTillReceived.observe({source: OpSource.gossip}, delaySec);
|
|
1122
1131
|
chain.validatorMonitor?.registerExecutionPayloadEnvelope(OpSource.gossip, delaySec, signedEnvelope);
|
|
1123
1132
|
|
|
@@ -1206,7 +1215,8 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1206
1215
|
chain.forkChoice.notifyPtcMessages(
|
|
1207
1216
|
toRootHex(payloadAttestationMessage.data.beaconBlockRoot),
|
|
1208
1217
|
validationResult.validatorCommitteeIndices,
|
|
1209
|
-
payloadAttestationMessage.data.payloadPresent
|
|
1218
|
+
payloadAttestationMessage.data.payloadPresent,
|
|
1219
|
+
payloadAttestationMessage.data.blobDataAvailable
|
|
1210
1220
|
);
|
|
1211
1221
|
},
|
|
1212
1222
|
[GossipType.execution_payload_bid]: async ({
|
|
@@ -1219,7 +1229,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
|
|
|
1219
1229
|
|
|
1220
1230
|
// Handle valid payload bid by storing in a bid pool
|
|
1221
1231
|
try {
|
|
1222
|
-
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid
|
|
1232
|
+
const insertOutcome = chain.executionPayloadBidPool.add(executionPayloadBid);
|
|
1223
1233
|
metrics?.opPool.executionPayloadBidPool.gossipInsertOutcome.inc({insertOutcome});
|
|
1224
1234
|
} catch (e) {
|
|
1225
1235
|
logger.error("Error adding to executionPayloadBid pool", {}, e as Error);
|