@lodestar/api 1.45.0-dev.51a1c44b27 → 1.45.0-dev.6568180f96

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.
Files changed (54) hide show
  1. package/lib/beacon/routes/beacon/block.d.ts +10 -1
  2. package/lib/beacon/routes/beacon/block.d.ts.map +1 -1
  3. package/lib/beacon/routes/beacon/block.js +34 -12
  4. package/lib/beacon/routes/beacon/block.js.map +1 -1
  5. package/lib/beacon/routes/beacon/index.d.ts +1 -1
  6. package/lib/beacon/routes/beacon/index.d.ts.map +1 -1
  7. package/lib/beacon/routes/beacon/index.js.map +1 -1
  8. package/lib/beacon/routes/beacon/pool.d.ts +1 -35
  9. package/lib/beacon/routes/beacon/pool.d.ts.map +1 -1
  10. package/lib/beacon/routes/beacon/pool.js +1 -44
  11. package/lib/beacon/routes/beacon/pool.js.map +1 -1
  12. package/lib/beacon/routes/beacon/state.d.ts +54 -2
  13. package/lib/beacon/routes/beacon/state.d.ts.map +1 -1
  14. package/lib/beacon/routes/beacon/state.js +34 -1
  15. package/lib/beacon/routes/beacon/state.js.map +1 -1
  16. package/lib/beacon/routes/events.d.ts +118 -3
  17. package/lib/beacon/routes/events.d.ts.map +1 -1
  18. package/lib/beacon/routes/events.js +4 -0
  19. package/lib/beacon/routes/events.js.map +1 -1
  20. package/lib/beacon/routes/node.d.ts +2 -0
  21. package/lib/beacon/routes/node.d.ts.map +1 -1
  22. package/lib/beacon/routes/node.js +2 -0
  23. package/lib/beacon/routes/node.js.map +1 -1
  24. package/lib/beacon/routes/validator.d.ts +45 -5
  25. package/lib/beacon/routes/validator.d.ts.map +1 -1
  26. package/lib/beacon/routes/validator.js +50 -9
  27. package/lib/beacon/routes/validator.js.map +1 -1
  28. package/lib/utils/metadata.d.ts +2 -0
  29. package/lib/utils/metadata.d.ts.map +1 -1
  30. package/lib/utils/metadata.js +2 -0
  31. package/lib/utils/metadata.js.map +1 -1
  32. package/lib/utils/schema.d.ts +2 -1
  33. package/lib/utils/schema.d.ts.map +1 -1
  34. package/lib/utils/schema.js +3 -0
  35. package/lib/utils/schema.js.map +1 -1
  36. package/lib/utils/serdes.d.ts +2 -0
  37. package/lib/utils/serdes.d.ts.map +1 -1
  38. package/lib/utils/serdes.js +2 -0
  39. package/lib/utils/serdes.js.map +1 -1
  40. package/lib/utils/server/handler.d.ts.map +1 -1
  41. package/lib/utils/server/handler.js +4 -0
  42. package/lib/utils/server/handler.js.map +1 -1
  43. package/package.json +10 -10
  44. package/src/beacon/routes/beacon/block.ts +51 -14
  45. package/src/beacon/routes/beacon/index.ts +3 -0
  46. package/src/beacon/routes/beacon/pool.ts +0 -72
  47. package/src/beacon/routes/beacon/state.ts +63 -2
  48. package/src/beacon/routes/events.ts +5 -0
  49. package/src/beacon/routes/node.ts +3 -1
  50. package/src/beacon/routes/validator.ts +95 -11
  51. package/src/utils/metadata.ts +2 -0
  52. package/src/utils/schema.ts +3 -0
  53. package/src/utils/serdes.ts +3 -0
  54. package/src/utils/server/handler.ts +5 -0
@@ -18,6 +18,7 @@ import {
18
18
  Slot,
19
19
  deneb,
20
20
  gloas,
21
+ isSignedExecutionPayloadEnvelopeContents,
21
22
  ssz,
22
23
  sszTypesFor,
23
24
  } from "@lodestar/types";
@@ -32,6 +33,7 @@ import {
32
33
  ExecutionOptimisticFinalizedAndVersionMeta,
33
34
  MetaHeader,
34
35
  } from "../../../utils/metadata.js";
36
+ import {toBoolean} from "../../../utils/serdes.js";
35
37
  import {WireFormat} from "../../../utils/wireFormat.js";
36
38
 
37
39
  export const BlockHeaderResponseType = new ContainerType({
@@ -187,11 +189,22 @@ export type Endpoints = {
187
189
  * Instructs the beacon node to broadcast a signed execution payload envelope to the network,
188
190
  * to be gossiped for payload validation. A success response (20x) indicates that the envelope
189
191
  * passed gossip validation and was successfully broadcast onto the network.
192
+ *
193
+ * The request body is either a `SignedExecutionPayloadEnvelopeContents` (envelope with blobs
194
+ * and KZG proofs, stateless flow) or a `SignedExecutionPayloadEnvelope` (stateful flow,
195
+ * the beacon node attaches blobs and KZG proofs from its block production cache).
190
196
  */
191
197
  publishExecutionPayloadEnvelope: Endpoint<
192
198
  "POST",
193
- {signedExecutionPayloadEnvelope: gloas.SignedExecutionPayloadEnvelope},
194
- {body: unknown; headers: {[MetaHeader.Version]: string}},
199
+ {
200
+ signedEnvelopeOrContents: gloas.SignedExecutionPayloadEnvelopeContents | gloas.SignedExecutionPayloadEnvelope;
201
+ broadcastValidation?: BroadcastValidation;
202
+ },
203
+ {
204
+ body: unknown;
205
+ headers: {[MetaHeader.Version]: string; [MetaHeader.BlobDataIncluded]: string};
206
+ query: {broadcast_validation?: string};
207
+ },
195
208
  EmptyResponseData,
196
209
  EmptyMeta
197
210
  >;
@@ -453,40 +466,64 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
453
466
  url: "/eth/v1/beacon/execution_payload_envelopes",
454
467
  method: "POST",
455
468
  req: {
456
- writeReqJson: ({signedExecutionPayloadEnvelope}) => {
457
- const fork = config.getForkName(signedExecutionPayloadEnvelope.message.payload.slotNumber);
469
+ writeReqJson: ({signedEnvelopeOrContents, broadcastValidation}) => {
470
+ const blobDataIncluded = isSignedExecutionPayloadEnvelopeContents(signedEnvelopeOrContents);
471
+ const fork = config.getForkName(
472
+ (blobDataIncluded ? signedEnvelopeOrContents.signedExecutionPayloadEnvelope : signedEnvelopeOrContents)
473
+ .message.payload.slotNumber
474
+ );
458
475
  return {
459
- body: getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.toJson(signedExecutionPayloadEnvelope),
476
+ body: blobDataIncluded
477
+ ? getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelopeContents.toJson(signedEnvelopeOrContents)
478
+ : getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.toJson(signedEnvelopeOrContents),
460
479
  headers: {
461
480
  [MetaHeader.Version]: fork,
481
+ [MetaHeader.BlobDataIncluded]: blobDataIncluded.toString(),
462
482
  },
483
+ query: {broadcast_validation: broadcastValidation},
463
484
  };
464
485
  },
465
- parseReqJson: ({body, headers}) => {
486
+ parseReqJson: ({body, headers, query}) => {
466
487
  const fork = toForkName(fromHeaders(headers, MetaHeader.Version));
488
+ const blobDataIncluded = toBoolean(fromHeaders(headers, MetaHeader.BlobDataIncluded));
467
489
  return {
468
- signedExecutionPayloadEnvelope: getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.fromJson(body),
490
+ signedEnvelopeOrContents: blobDataIncluded
491
+ ? getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelopeContents.fromJson(body)
492
+ : getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.fromJson(body),
493
+ broadcastValidation: query.broadcast_validation as BroadcastValidation,
469
494
  };
470
495
  },
471
- writeReqSsz: ({signedExecutionPayloadEnvelope}) => {
472
- const fork = config.getForkName(signedExecutionPayloadEnvelope.message.payload.slotNumber);
496
+ writeReqSsz: ({signedEnvelopeOrContents, broadcastValidation}) => {
497
+ const blobDataIncluded = isSignedExecutionPayloadEnvelopeContents(signedEnvelopeOrContents);
498
+ const fork = config.getForkName(
499
+ (blobDataIncluded ? signedEnvelopeOrContents.signedExecutionPayloadEnvelope : signedEnvelopeOrContents)
500
+ .message.payload.slotNumber
501
+ );
473
502
  return {
474
- body: getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.serialize(signedExecutionPayloadEnvelope),
503
+ body: blobDataIncluded
504
+ ? getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelopeContents.serialize(signedEnvelopeOrContents)
505
+ : getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.serialize(signedEnvelopeOrContents),
475
506
  headers: {
476
507
  [MetaHeader.Version]: fork,
508
+ [MetaHeader.BlobDataIncluded]: blobDataIncluded.toString(),
477
509
  },
510
+ query: {broadcast_validation: broadcastValidation},
478
511
  };
479
512
  },
480
- parseReqSsz: ({body, headers}) => {
513
+ parseReqSsz: ({body, headers, query}) => {
481
514
  const fork = toForkName(fromHeaders(headers, MetaHeader.Version));
515
+ const blobDataIncluded = toBoolean(fromHeaders(headers, MetaHeader.BlobDataIncluded));
482
516
  return {
483
- signedExecutionPayloadEnvelope:
484
- getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.deserialize(body),
517
+ signedEnvelopeOrContents: blobDataIncluded
518
+ ? getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelopeContents.deserialize(body)
519
+ : getPostGloasForkTypes(fork).SignedExecutionPayloadEnvelope.deserialize(body),
520
+ broadcastValidation: query.broadcast_validation as BroadcastValidation,
485
521
  };
486
522
  },
487
523
  schema: {
488
524
  body: Schema.Object,
489
- headers: {[MetaHeader.Version]: Schema.String},
525
+ query: {broadcast_validation: Schema.String},
526
+ headers: {[MetaHeader.Version]: Schema.String, [MetaHeader.BlobDataIncluded]: Schema.String},
490
527
  },
491
528
  },
492
529
  resp: EmptyResponseCodec,
@@ -15,6 +15,9 @@ export type {BlockHeaderResponse, BlockId} from "./block.js";
15
15
  export {BroadcastValidation} from "./block.js";
16
16
  // TODO: Review if re-exporting all these types is necessary
17
17
  export type {
18
+ BuilderId,
19
+ BuilderResponse,
20
+ BuilderStatus,
18
21
  EpochCommitteeResponse,
19
22
  EpochSyncCommitteeResponse,
20
23
  FinalityCheckpoints,
@@ -6,7 +6,6 @@ import {
6
6
  ForkPreElectra,
7
7
  MAX_PAYLOAD_ATTESTATIONS,
8
8
  PTC_SIZE,
9
- SLOTS_PER_EPOCH,
10
9
  isForkPostElectra,
11
10
  } from "@lodestar/params";
12
11
  import {
@@ -47,12 +46,6 @@ const SignedBLSToExecutionChangeListType = ArrayOf(ssz.capella.SignedBLSToExecut
47
46
  const SyncCommitteeMessageListType = ArrayOf(ssz.altair.SyncCommitteeMessage);
48
47
  const PayloadAttestationListType = ArrayOf(ssz.gloas.PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS);
49
48
  const PayloadAttestationMessageListType = ArrayOf(ssz.gloas.PayloadAttestationMessage, PTC_SIZE);
50
- // 2 * SLOTS_PER_EPOCH max normally; getAll() may return up to 3 * SLOTS_PER_EPOCH during non-finality
51
- const MAX_PROPOSER_PREFERENCES_PER_REQUEST = 3 * SLOTS_PER_EPOCH;
52
- const SignedProposerPreferencesListType = ArrayOf(
53
- ssz.gloas.SignedProposerPreferences,
54
- MAX_PROPOSER_PREFERENCES_PER_REQUEST
55
- );
56
49
 
57
50
  type AttestationListPhase0 = ValueOf<typeof AttestationListTypePhase0>;
58
51
  type AttestationListElectra = ValueOf<typeof AttestationListTypeElectra>;
@@ -68,7 +61,6 @@ type SignedBLSToExecutionChangeList = ValueOf<typeof SignedBLSToExecutionChangeL
68
61
  type SyncCommitteeMessageList = ValueOf<typeof SyncCommitteeMessageListType>;
69
62
  type PayloadAttestationList = ValueOf<typeof PayloadAttestationListType>;
70
63
  type PayloadAttestationMessageList = ValueOf<typeof PayloadAttestationMessageListType>;
71
- type SignedProposerPreferencesList = ValueOf<typeof SignedProposerPreferencesListType>;
72
64
 
73
65
  export type Endpoints = {
74
66
  /**
@@ -95,18 +87,6 @@ export type Endpoints = {
95
87
  VersionMeta
96
88
  >;
97
89
 
98
- /**
99
- * Get signed proposer preferences from operations pool
100
- * Retrieves proposer preferences known by the node but not necessarily incorporated into any block.
101
- */
102
- getPoolProposerPreferences: Endpoint<
103
- "GET",
104
- {slot?: Slot},
105
- {query: {slot?: number}},
106
- SignedProposerPreferencesList,
107
- VersionMeta
108
- >;
109
-
110
90
  /**
111
91
  * Get AttesterSlashings from operations pool
112
92
  * Retrieves attester slashings known by the node but not necessarily incorporated into any block
@@ -246,18 +226,6 @@ export type Endpoints = {
246
226
  EmptyResponseData,
247
227
  EmptyMeta
248
228
  >;
249
-
250
- /**
251
- * Submit signed proposer preferences
252
- * Submits signed proposer preferences to the beacon node.
253
- */
254
- submitSignedProposerPreferences: Endpoint<
255
- "POST",
256
- {signedProposerPreferences: SignedProposerPreferencesList},
257
- {body: unknown; headers: {[MetaHeader.Version]: string}},
258
- EmptyResponseData,
259
- EmptyMeta
260
- >;
261
229
  };
262
230
 
263
231
  export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> {
@@ -288,19 +256,6 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
288
256
  meta: VersionCodec,
289
257
  },
290
258
  },
291
- getPoolProposerPreferences: {
292
- url: "/eth/v1/beacon/pool/proposer_preferences",
293
- method: "GET",
294
- req: {
295
- writeReq: ({slot}) => ({query: {slot}}),
296
- parseReq: ({query}) => ({slot: query.slot}),
297
- schema: {query: {slot: Schema.Uint}},
298
- },
299
- resp: {
300
- data: SignedProposerPreferencesListType,
301
- meta: VersionCodec,
302
- },
303
- },
304
259
  getPoolAttesterSlashingsV2: {
305
260
  url: "/eth/v2/beacon/pool/attester_slashings",
306
261
  method: "GET",
@@ -516,32 +471,5 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
516
471
  },
517
472
  resp: EmptyResponseCodec,
518
473
  },
519
- submitSignedProposerPreferences: {
520
- url: "/eth/v1/beacon/pool/proposer_preferences",
521
- method: "POST",
522
- req: {
523
- writeReqJson: ({signedProposerPreferences}) => ({
524
- body: SignedProposerPreferencesListType.toJson(signedProposerPreferences),
525
- headers: {[MetaHeader.Version]: ForkName.gloas},
526
- }),
527
- parseReqJson: ({body, headers}) => {
528
- toForkName(fromHeaders(headers, MetaHeader.Version));
529
- return {signedProposerPreferences: SignedProposerPreferencesListType.fromJson(body)};
530
- },
531
- writeReqSsz: ({signedProposerPreferences}) => ({
532
- body: SignedProposerPreferencesListType.serialize(signedProposerPreferences),
533
- headers: {[MetaHeader.Version]: ForkName.gloas},
534
- }),
535
- parseReqSsz: ({body, headers}) => {
536
- toForkName(fromHeaders(headers, MetaHeader.Version));
537
- return {signedProposerPreferences: SignedProposerPreferencesListType.deserialize(body)};
538
- },
539
- schema: {
540
- body: Schema.ObjectArray,
541
- headers: {[MetaHeader.Version]: Schema.String},
542
- },
543
- },
544
- resp: EmptyResponseCodec,
545
- },
546
474
  };
547
475
  }
@@ -3,6 +3,7 @@ import {ChainForkConfig} from "@lodestar/config";
3
3
  import {MAX_VALIDATORS_PER_COMMITTEE} from "@lodestar/params";
4
4
  import {
5
5
  ArrayOf,
6
+ BuilderStatus,
6
7
  CommitteeIndex,
7
8
  Epoch,
8
9
  RootHex,
@@ -22,7 +23,7 @@ import {
22
23
  ExecutionOptimisticFinalizedAndVersionCodec,
23
24
  ExecutionOptimisticFinalizedAndVersionMeta,
24
25
  } from "../../../utils/metadata.js";
25
- import {fromValidatorIdsStr, toValidatorIdsStr} from "../../../utils/serdes.js";
26
+ import {fromBuilderIdsStr, fromValidatorIdsStr, toBuilderIdsStr, toValidatorIdsStr} from "../../../utils/serdes.js";
26
27
  import {WireFormat} from "../../../utils/wireFormat.js";
27
28
  import {RootResponse, RootResponseType} from "./block.js";
28
29
 
@@ -37,8 +38,9 @@ export type StateArgs = {
37
38
  };
38
39
 
39
40
  export type ValidatorId = string | number;
41
+ export type BuilderId = string | number;
40
42
 
41
- export type {ValidatorStatus};
43
+ export type {BuilderStatus, ValidatorStatus};
42
44
 
43
45
  export const RandaoResponseType = new ContainerType({
44
46
  randao: ssz.Root,
@@ -57,6 +59,11 @@ export const ValidatorResponseType = new ContainerType({
57
59
  status: new StringType<ValidatorStatus>(),
58
60
  validator: ssz.phase0.Validator,
59
61
  });
62
+ export const BuilderResponseType = new ContainerType({
63
+ index: ssz.BuilderIndex,
64
+ status: new StringType<BuilderStatus>(),
65
+ builder: ssz.gloas.Builder,
66
+ });
60
67
  export const ValidatorIdentityType = new ContainerType(
61
68
  {
62
69
  index: ssz.ValidatorIndex,
@@ -84,6 +91,7 @@ export const EpochSyncCommitteeResponseType = new ContainerType(
84
91
  {jsonCase: "eth2"}
85
92
  );
86
93
  export const ValidatorResponseListType = ArrayOf(ValidatorResponseType);
94
+ export const BuilderResponseListType = ArrayOf(BuilderResponseType);
87
95
  export const ValidatorIdentitiesType = ArrayOf(ValidatorIdentityType);
88
96
  export const EpochCommitteeResponseListType = ArrayOf(EpochCommitteeResponseType);
89
97
  export const ValidatorBalanceListType = ArrayOf(ValidatorBalanceType);
@@ -91,11 +99,13 @@ export const ValidatorBalanceListType = ArrayOf(ValidatorBalanceType);
91
99
  export type RandaoResponse = ValueOf<typeof RandaoResponseType>;
92
100
  export type FinalityCheckpoints = ValueOf<typeof FinalityCheckpointsType>;
93
101
  export type ValidatorResponse = ValueOf<typeof ValidatorResponseType>;
102
+ export type BuilderResponse = ValueOf<typeof BuilderResponseType>;
94
103
  export type EpochCommitteeResponse = ValueOf<typeof EpochCommitteeResponseType>;
95
104
  export type ValidatorBalance = ValueOf<typeof ValidatorBalanceType>;
96
105
  export type EpochSyncCommitteeResponse = ValueOf<typeof EpochSyncCommitteeResponseType>;
97
106
 
98
107
  export type ValidatorResponseList = ValueOf<typeof ValidatorResponseListType>;
108
+ export type BuilderResponseList = ValueOf<typeof BuilderResponseListType>;
99
109
  export type ValidatorIdentities = ValueOf<typeof ValidatorIdentitiesType>;
100
110
  export type EpochCommitteeResponseList = ValueOf<typeof EpochCommitteeResponseListType>;
101
111
  export type ValidatorBalanceList = ValueOf<typeof ValidatorBalanceListType>;
@@ -204,6 +214,30 @@ export type Endpoints = {
204
214
  ExecutionOptimisticAndFinalizedMeta
205
215
  >;
206
216
 
217
+ /**
218
+ * Get builders from state
219
+ *
220
+ * Returns filterable list of builders with their status and index.
221
+ *
222
+ * Information will be returned for all indices or public keys that match known builders. If an index or public key does not
223
+ * match any known builder, no information will be returned but this will not cause an error. There are no guarantees for the
224
+ * returned data in terms of ordering; both the index and public key are returned for each builder, and can be used to confirm
225
+ * for which inputs a response has been returned.
226
+ *
227
+ * Returns 400 if the requested state is prior to Gloas.
228
+ */
229
+ getStateBuilders: Endpoint<
230
+ "POST",
231
+ StateArgs & {
232
+ /** Either hex encoded public key (any bytes48 with 0x prefix) or builder index */
233
+ builderIds?: BuilderId[];
234
+ statuses?: BuilderStatus[];
235
+ },
236
+ {params: {state_id: string}; body: {ids?: string[]; statuses?: BuilderStatus[]}},
237
+ BuilderResponseList,
238
+ ExecutionOptimisticAndFinalizedMeta
239
+ >;
240
+
207
241
  /**
208
242
  * Get validator identities from state
209
243
  *
@@ -489,6 +523,33 @@ export function getDefinitions(_config: ChainForkConfig): RouteDefinitions<Endpo
489
523
  meta: ExecutionOptimisticAndFinalizedCodec,
490
524
  },
491
525
  },
526
+ getStateBuilders: {
527
+ url: "/eth/v1/beacon/states/{state_id}/builders",
528
+ method: "POST",
529
+ req: JsonOnlyReq({
530
+ writeReqJson: ({stateId, builderIds, statuses}) => ({
531
+ params: {state_id: stateId.toString()},
532
+ body: {
533
+ ids: toBuilderIdsStr(builderIds),
534
+ statuses,
535
+ },
536
+ }),
537
+ parseReqJson: ({params, body = {}}) => ({
538
+ stateId: params.state_id,
539
+ builderIds: fromBuilderIdsStr(body.ids),
540
+ statuses: body.statuses ?? undefined,
541
+ }),
542
+ schema: {
543
+ params: {state_id: Schema.StringRequired},
544
+ body: Schema.Object,
545
+ },
546
+ }),
547
+ resp: {
548
+ onlySupport: WireFormat.json,
549
+ data: BuilderResponseListType,
550
+ meta: ExecutionOptimisticAndFinalizedCodec,
551
+ },
552
+ },
492
553
  postStateValidatorIdentities: {
493
554
  url: "/eth/v1/beacon/states/{state_id}/validator_identities",
494
555
  method: "POST",
@@ -110,6 +110,8 @@ export enum EventType {
110
110
  executionPayloadBid = "execution_payload_bid",
111
111
  /** The node has received a `SignedProposerPreferences` (from P2P or API) that passes gossip validation on the `proposer_preferences` topic */
112
112
  proposerPreferences = "proposer_preferences",
113
+ /** The node has received a `PayloadAttestationMessage` (from P2P or API) that passes validation rules of the `payload_attestation_message` topic */
114
+ payloadAttestationMessage = "payload_attestation_message",
113
115
  /** The node has executed the Fast Confirmation Rule and produced a confirmed beacon block */
114
116
  fastConfirmation = "fast_confirmation",
115
117
  }
@@ -137,6 +139,7 @@ export const eventTypes: {[K in EventType]: K} = {
137
139
  [EventType.executionPayloadAvailable]: EventType.executionPayloadAvailable,
138
140
  [EventType.executionPayloadBid]: EventType.executionPayloadBid,
139
141
  [EventType.proposerPreferences]: EventType.proposerPreferences,
142
+ [EventType.payloadAttestationMessage]: EventType.payloadAttestationMessage,
140
143
  [EventType.fastConfirmation]: EventType.fastConfirmation,
141
144
  };
142
145
 
@@ -206,6 +209,7 @@ export type EventData = {
206
209
  };
207
210
  [EventType.executionPayloadBid]: {version: ForkName; data: gloas.SignedExecutionPayloadBid};
208
211
  [EventType.proposerPreferences]: {version: ForkName; data: gloas.SignedProposerPreferences};
212
+ [EventType.payloadAttestationMessage]: {version: ForkName; data: gloas.PayloadAttestationMessage};
209
213
  [EventType.fastConfirmation]: {
210
214
  block: RootHex;
211
215
  slot: Slot;
@@ -408,6 +412,7 @@ export function getTypeByEvent(config: ChainForkConfig): {[K in EventType]: Type
408
412
  ),
409
413
  [EventType.executionPayloadBid]: WithVersion((fork) => getPostGloasForkTypes(fork).SignedExecutionPayloadBid),
410
414
  [EventType.proposerPreferences]: WithVersion((fork) => getPostGloasForkTypes(fork).SignedProposerPreferences),
415
+ [EventType.payloadAttestationMessage]: WithVersion((fork) => getPostGloasForkTypes(fork).PayloadAttestationMessage),
411
416
  [EventType.fastConfirmation]: new ContainerType(
412
417
  {
413
418
  block: stringType,
@@ -106,6 +106,7 @@ export enum NodeHealth {
106
106
  */
107
107
  export enum ClientCode {
108
108
  BU = "BU", // besu
109
+ CN = "CN", // caplin
109
110
  EJ = "EJ", // ethereumJS
110
111
  EG = "EG", // erigon
111
112
  EX = "EX", // ethrex
@@ -114,7 +115,8 @@ export enum ClientCode {
114
115
  LH = "LH", // lighthouse
115
116
  LS = "LS", // lodestar
116
117
  NM = "NM", // nethermind
117
- NB = "NB", // nimbus
118
+ NB = "NB", // nimbus CL
119
+ NE = "NE", // nimbus EL
118
120
  TE = "TE", // trin-execution
119
121
  TK = "TK", // teku
120
122
  PM = "PM", // prysm
@@ -4,6 +4,8 @@ import {
4
4
  ForkPostDeneb,
5
5
  ForkPostGloas,
6
6
  ForkPreDeneb,
7
+ MIN_SEED_LOOKAHEAD,
8
+ SLOTS_PER_EPOCH,
7
9
  VALIDATOR_REGISTRY_LIMIT,
8
10
  isForkPostDeneb,
9
11
  isForkPostElectra,
@@ -36,6 +38,7 @@ import {
36
38
  EmptyResponseCodec,
37
39
  EmptyResponseData,
38
40
  JsonOnlyReq,
41
+ WithMeta,
39
42
  WithVersion,
40
43
  } from "../../utils/codecs.js";
41
44
  import {getPostBellatrixForkTypes, getPostGloasForkTypes, toForkName} from "../../utils/fork.js";
@@ -96,6 +99,10 @@ export const ProduceBlockV4MetaType = new ContainerType(
96
99
  ...VersionType.fields,
97
100
  /** Consensus rewards paid to the proposer for this block, in Wei */
98
101
  consensusBlockValue: ssz.UintBn64,
102
+ /** Local execution payload value when self-building, or builder bid value when committing to a bid, in Wei */
103
+ executionPayloadValue: ssz.UintBn64,
104
+ /** Specifies whether the response contains full block contents or only the beacon block */
105
+ executionPayloadIncluded: ssz.Boolean,
99
106
  },
100
107
  {jsonCase: "eth2"}
101
108
  );
@@ -246,6 +253,10 @@ export const SignedValidatorRegistrationV1ListType = ArrayOf(
246
253
  ssz.bellatrix.SignedValidatorRegistrationV1,
247
254
  VALIDATOR_REGISTRY_LIMIT
248
255
  );
256
+ export const SignedProposerPreferencesListType = ArrayOf(
257
+ ssz.gloas.SignedProposerPreferences,
258
+ (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
259
+ );
249
260
 
250
261
  export type ValidatorIndices = ValueOf<typeof ValidatorIndicesType>;
251
262
  export type AttesterDuty = ValueOf<typeof AttesterDutyType>;
@@ -273,6 +284,13 @@ export type SyncCommitteeSelectionList = ValueOf<typeof SyncCommitteeSelectionLi
273
284
  export type LivenessResponseData = ValueOf<typeof LivenessResponseDataType>;
274
285
  export type LivenessResponseDataList = ValueOf<typeof LivenessResponseDataListType>;
275
286
  export type SignedValidatorRegistrationV1List = ValueOf<typeof SignedValidatorRegistrationV1ListType>;
287
+ export type SignedProposerPreferencesList = ValueOf<typeof SignedProposerPreferencesListType>;
288
+
289
+ // The beacon node does not return any data if there is no canonical block at the requested slot (missed slot).
290
+ // In this case, we receive a success response (204) which is not handled as an error. The generic response
291
+ // handler already checks the status code and will not attempt to parse the body, but it will return no value.
292
+ // It is important that this type indicates that there might be no value to ensure it is properly handled downstream.
293
+ export type MaybePayloadAttestationData = gloas.PayloadAttestationData | undefined;
276
294
 
277
295
  export type Endpoints = {
278
296
  /**
@@ -408,6 +426,12 @@ export type Endpoints = {
408
426
  * Post-Gloas, proposers submit execution payload bids rather than full execution payloads,
409
427
  * so there is no longer a concept of blinded or unblinded blocks. Builders release the payload later.
410
428
  * This endpoint is specific to the post-Gloas forks and is not backwards compatible with previous forks.
429
+ *
430
+ * When self-building and `includePayload` is true, the response contains the full `BlockContents`
431
+ * (block, execution payload envelope, KZG proofs and blobs) which enables stateless envelope
432
+ * publishing via any beacon node. When `includePayload` is false, only the `BeaconBlock` is
433
+ * returned and the beacon node caches the envelope and blobs internally.
434
+ * When committing to a builder bid, only the `BeaconBlock` is returned in either case.
411
435
  */
412
436
  produceBlockV4: Endpoint<
413
437
  "GET",
@@ -420,6 +444,8 @@ export type Endpoints = {
420
444
  graffiti?: string;
421
445
  skipRandaoVerification?: boolean;
422
446
  builderBoostFactor?: UintBn64;
447
+ /** Include execution payload envelope and blobs in the response when self-building */
448
+ includePayload: boolean;
423
449
  } & Omit<ExtraProduceBlockOpts, "blindedLocal">,
424
450
  {
425
451
  params: {slot: number};
@@ -431,16 +457,18 @@ export type Endpoints = {
431
457
  builder_selection?: string;
432
458
  builder_boost_factor?: string;
433
459
  strict_fee_recipient_check?: boolean;
460
+ include_payload: boolean;
434
461
  };
435
462
  },
436
- BeaconBlock<ForkPostGloas>,
463
+ BeaconBlock<ForkPostGloas> | BlockContents<ForkPostGloas>,
437
464
  ProduceBlockV4Meta
438
465
  >;
439
466
 
440
467
  /**
441
468
  * Get execution payload envelope.
442
- * Retrieves execution payload envelope for a given slot and beacon block root.
443
- * The envelope contains the full execution payload along with associated metadata.
469
+ * Retrieves the cached execution payload envelope for a given slot and beacon block root,
470
+ * to be signed and published via `publishExecutionPayloadEnvelope`.
471
+ * Used in the stateful (`includePayload=false`) local build flow.
444
472
  */
445
473
  getExecutionPayloadEnvelope: Endpoint<
446
474
  "GET",
@@ -482,8 +510,8 @@ export type Endpoints = {
482
510
  /** The slot for which payload attestation data should be created */
483
511
  slot: Slot;
484
512
  },
485
- {params: {slot: Slot}},
486
- gloas.PayloadAttestationData,
513
+ {query: {slot: Slot}},
514
+ MaybePayloadAttestationData,
487
515
  VersionMeta
488
516
  >;
489
517
 
@@ -646,6 +674,20 @@ export type Endpoints = {
646
674
  EmptyResponseData,
647
675
  EmptyMeta
648
676
  >;
677
+
678
+ /**
679
+ * Submit signed proposer preferences
680
+ *
681
+ * Verifies given signed proposer preferences and publishes them on the `proposer_preferences`
682
+ * gossipsub topic. Supersedes `prepareBeaconProposer` and `registerValidator` from Gloas onwards.
683
+ */
684
+ submitProposerPreferences: Endpoint<
685
+ "POST",
686
+ {signedProposerPreferences: SignedProposerPreferencesList},
687
+ {body: unknown; headers: {[MetaHeader.Version]: string}},
688
+ EmptyResponseData,
689
+ EmptyMeta
690
+ >;
649
691
  };
650
692
 
651
693
  export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoints> {
@@ -879,6 +921,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
879
921
  builderSelection,
880
922
  builderBoostFactor,
881
923
  strictFeeRecipientCheck,
924
+ includePayload,
882
925
  }) => ({
883
926
  params: {slot},
884
927
  query: {
@@ -889,6 +932,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
889
932
  builder_selection: builderSelection,
890
933
  builder_boost_factor: builderBoostFactor?.toString(),
891
934
  strict_fee_recipient_check: strictFeeRecipientCheck,
935
+ include_payload: includePayload,
892
936
  },
893
937
  }),
894
938
  parseReq: ({params, query}) => ({
@@ -900,6 +944,7 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
900
944
  builderSelection: query.builder_selection as BuilderSelection,
901
945
  builderBoostFactor: parseBuilderBoostFactor(query.builder_boost_factor),
902
946
  strictFeeRecipientCheck: query.strict_fee_recipient_check,
947
+ includePayload: query.include_payload,
903
948
  }),
904
949
  schema: {
905
950
  params: {slot: Schema.UintRequired},
@@ -911,21 +956,33 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
911
956
  builder_selection: Schema.String,
912
957
  builder_boost_factor: Schema.String,
913
958
  strict_fee_recipient_check: Schema.Boolean,
959
+ include_payload: Schema.BooleanRequired,
914
960
  },
915
961
  },
916
962
  },
917
963
  resp: {
918
- data: WithVersion((fork) => getPostGloasForkTypes(fork).BeaconBlock),
964
+ data: WithMeta(
965
+ ({version, executionPayloadIncluded}) =>
966
+ (executionPayloadIncluded
967
+ ? getPostGloasForkTypes(version).BlockContents
968
+ : getPostGloasForkTypes(version).BeaconBlock) as Type<
969
+ BeaconBlock<ForkPostGloas> | BlockContents<ForkPostGloas>
970
+ >
971
+ ),
919
972
  meta: {
920
973
  toJson: (meta) => ProduceBlockV4MetaType.toJson(meta),
921
974
  fromJson: (val) => ProduceBlockV4MetaType.fromJson(val),
922
975
  toHeadersObject: (meta) => ({
923
976
  [MetaHeader.Version]: meta.version,
924
977
  [MetaHeader.ConsensusBlockValue]: meta.consensusBlockValue.toString(),
978
+ [MetaHeader.ExecutionPayloadValue]: meta.executionPayloadValue.toString(),
979
+ [MetaHeader.ExecutionPayloadIncluded]: meta.executionPayloadIncluded.toString(),
925
980
  }),
926
981
  fromHeaders: (headers) => ({
927
982
  version: toForkName(headers.getRequired(MetaHeader.Version)),
928
983
  consensusBlockValue: BigInt(headers.getRequired(MetaHeader.ConsensusBlockValue)),
984
+ executionPayloadValue: BigInt(headers.getRequired(MetaHeader.ExecutionPayloadValue)),
985
+ executionPayloadIncluded: toBoolean(headers.getRequired(MetaHeader.ExecutionPayloadIncluded)),
929
986
  }),
930
987
  },
931
988
  },
@@ -972,17 +1029,17 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
972
1029
  },
973
1030
  },
974
1031
  producePayloadAttestationData: {
975
- url: "/eth/v1/validator/payload_attestation_data/{slot}",
1032
+ url: "/eth/v1/validator/payload_attestation_data",
976
1033
  method: "GET",
977
1034
  req: {
978
- writeReq: ({slot}) => ({params: {slot}}),
979
- parseReq: ({params}) => ({slot: params.slot}),
1035
+ writeReq: ({slot}) => ({query: {slot}}),
1036
+ parseReq: ({query}) => ({slot: query.slot}),
980
1037
  schema: {
981
- params: {slot: Schema.UintRequired},
1038
+ query: {slot: Schema.UintRequired},
982
1039
  },
983
1040
  },
984
1041
  resp: {
985
- data: ssz.gloas.PayloadAttestationData,
1042
+ data: WithVersion<MaybePayloadAttestationData, VersionMeta>(() => ssz.gloas.PayloadAttestationData),
986
1043
  meta: VersionCodec,
987
1044
  },
988
1045
  },
@@ -1210,6 +1267,33 @@ export function getDefinitions(config: ChainForkConfig): RouteDefinitions<Endpoi
1210
1267
  requestWireFormat: WireFormat.ssz,
1211
1268
  },
1212
1269
  },
1270
+ submitProposerPreferences: {
1271
+ url: "/eth/v1/validator/proposer_preferences",
1272
+ method: "POST",
1273
+ req: {
1274
+ writeReqJson: ({signedProposerPreferences}) => ({
1275
+ body: SignedProposerPreferencesListType.toJson(signedProposerPreferences),
1276
+ headers: {[MetaHeader.Version]: config.getForkName(signedProposerPreferences[0]?.message.proposalSlot ?? 0)},
1277
+ }),
1278
+ parseReqJson: ({body, headers}) => {
1279
+ toForkName(fromHeaders(headers, MetaHeader.Version));
1280
+ return {signedProposerPreferences: SignedProposerPreferencesListType.fromJson(body)};
1281
+ },
1282
+ writeReqSsz: ({signedProposerPreferences}) => ({
1283
+ body: SignedProposerPreferencesListType.serialize(signedProposerPreferences),
1284
+ headers: {[MetaHeader.Version]: config.getForkName(signedProposerPreferences[0]?.message.proposalSlot ?? 0)},
1285
+ }),
1286
+ parseReqSsz: ({body, headers}) => {
1287
+ toForkName(fromHeaders(headers, MetaHeader.Version));
1288
+ return {signedProposerPreferences: SignedProposerPreferencesListType.deserialize(body)};
1289
+ },
1290
+ schema: {
1291
+ body: Schema.ObjectArray,
1292
+ headers: {[MetaHeader.Version]: Schema.String},
1293
+ },
1294
+ },
1295
+ resp: EmptyResponseCodec,
1296
+ },
1213
1297
  };
1214
1298
  }
1215
1299
 
@@ -74,8 +74,10 @@ export type ExecutionOptimisticAndDependentRootMeta = ValueOf<typeof ExecutionOp
74
74
 
75
75
  export enum MetaHeader {
76
76
  Version = "Eth-Consensus-Version",
77
+ BlobDataIncluded = "Eth-Blob-Data-Included",
77
78
  ConsensusBlockValue = "Eth-Consensus-Block-Value",
78
79
  ExecutionPayloadBlinded = "Eth-Execution-Payload-Blinded",
80
+ ExecutionPayloadIncluded = "Eth-Execution-Payload-Included",
79
81
  ExecutionPayloadValue = "Eth-Execution-Payload-Value",
80
82
 
81
83
  /* Lodestar-specific (non-standardized) headers */