@lodestar/validator 1.47.0-dev.f591cb177e → 1.48.0-dev.67344272c0

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 (34) hide show
  1. package/lib/services/block.d.ts.map +1 -1
  2. package/lib/services/block.js +33 -3
  3. package/lib/services/block.js.map +1 -1
  4. package/lib/services/builderPreferences.d.ts +27 -0
  5. package/lib/services/builderPreferences.d.ts.map +1 -0
  6. package/lib/services/builderPreferences.js +127 -0
  7. package/lib/services/builderPreferences.js.map +1 -0
  8. package/lib/services/externalSignerSync.js +1 -1
  9. package/lib/services/externalSignerSync.js.map +1 -1
  10. package/lib/services/prepareBeaconProposer.d.ts.map +1 -1
  11. package/lib/services/prepareBeaconProposer.js +5 -2
  12. package/lib/services/prepareBeaconProposer.js.map +1 -1
  13. package/lib/services/validatorStore.d.ts +44 -2
  14. package/lib/services/validatorStore.d.ts.map +1 -1
  15. package/lib/services/validatorStore.js +196 -28
  16. package/lib/services/validatorStore.js.map +1 -1
  17. package/lib/types.d.ts +1 -1
  18. package/lib/types.d.ts.map +1 -1
  19. package/lib/util/externalSignerClient.d.ts +5 -1
  20. package/lib/util/externalSignerClient.d.ts.map +1 -1
  21. package/lib/util/externalSignerClient.js +4 -0
  22. package/lib/util/externalSignerClient.js.map +1 -1
  23. package/lib/validator.d.ts.map +1 -1
  24. package/lib/validator.js +16 -3
  25. package/lib/validator.js.map +1 -1
  26. package/package.json +14 -14
  27. package/src/services/block.ts +37 -3
  28. package/src/services/builderPreferences.ts +142 -0
  29. package/src/services/externalSignerSync.ts +1 -1
  30. package/src/services/prepareBeaconProposer.ts +5 -2
  31. package/src/services/validatorStore.ts +268 -33
  32. package/src/types.ts +1 -1
  33. package/src/util/externalSignerClient.ts +7 -1
  34. package/src/validator.ts +30 -3
@@ -1,6 +1,7 @@
1
- import {SecretKey} from "@chainsafe/blst";
1
+ import {SecretKey} from "@chainsafe/lodestar-z/blst";
2
2
  import {BitArray} from "@chainsafe/ssz";
3
3
  import {routes} from "@lodestar/api";
4
+ import {BuilderConfigData, BuilderEntryConfig} from "@lodestar/api/keymanager";
4
5
  import {BeaconConfig} from "@lodestar/config";
5
6
  import {
6
7
  DOMAIN_AGGREGATE_AND_PROOF,
@@ -8,6 +9,7 @@ import {
8
9
  DOMAIN_BEACON_ATTESTER,
9
10
  DOMAIN_BEACON_BUILDER,
10
11
  DOMAIN_BEACON_PROPOSER,
12
+ DOMAIN_BUILDER_REQUEST_AUTH,
11
13
  DOMAIN_CONTRIBUTION_AND_PROOF,
12
14
  DOMAIN_PROPOSER_PREFERENCES,
13
15
  DOMAIN_PTC_ATTESTER,
@@ -16,6 +18,7 @@ import {
16
18
  DOMAIN_SYNC_COMMITTEE,
17
19
  DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF,
18
20
  ForkSeq,
21
+ MAX_BUILDER_AUTH_DATA_SIZE,
19
22
  } from "@lodestar/params";
20
23
  import {
21
24
  ZERO_HASH,
@@ -46,7 +49,7 @@ import {
46
49
  phase0,
47
50
  ssz,
48
51
  } from "@lodestar/types";
49
- import {fromHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
52
+ import {fromHex, isValidAsciiHttpUrl, toHex, toPubkeyHex, toRootHex} from "@lodestar/utils";
50
53
  import {Metrics} from "../metrics.js";
51
54
  import {ISlashingProtection} from "../slashingProtection/index.js";
52
55
  import {PubkeyHex} from "../types.js";
@@ -84,6 +87,9 @@ type DefaultProposerConfig = {
84
87
  gasLimit?: number;
85
88
  selection?: routes.validator.BuilderSelection;
86
89
  boostFactor: bigint;
90
+ minBid: bigint;
91
+ maxExecutionPayment: bigint;
92
+ builders?: BuilderEntryConfig[];
87
93
  };
88
94
  };
89
95
 
@@ -95,9 +101,23 @@ export type ProposerConfig = {
95
101
  gasLimit?: number;
96
102
  selection?: routes.validator.BuilderSelection;
97
103
  boostFactor?: bigint;
104
+ minBid?: bigint;
105
+ maxExecutionPayment?: bigint;
106
+ /** Per-key builder entries, replacing the validator client's builders */
107
+ builders?: BuilderEntryConfig[];
98
108
  };
99
109
  };
100
110
 
111
+ /** A builder entry with every omitted value resolved against the key and validator client defaults */
112
+ export type ResolvedBuilderEntry = {
113
+ url: string;
114
+ authData: Uint8Array;
115
+ builderPubkeys: Uint8Array[];
116
+ maxExecutionPayment: bigint;
117
+ minBid: bigint;
118
+ builderBoostFactor: bigint;
119
+ };
120
+
101
121
  export type ValidatorProposerConfig = {
102
122
  proposerConfig: {[index: PubkeyHex]: ProposerConfig};
103
123
  defaultConfig: ProposerConfig;
@@ -132,6 +152,8 @@ export type Signer = SignerLocal | SignerRemote;
132
152
  type ValidatorData = ProposerConfig & {
133
153
  signer: Signer;
134
154
  builderData?: BuilderData;
155
+ /** Pre-signed builder request auths keyed by proposal slot and auth data, pruned by proposal slot */
156
+ builderRequestAuths?: Map<string, gloas.SignedBuilderRequestAuth>;
135
157
  };
136
158
 
137
159
  export const defaultOptions = {
@@ -139,7 +161,10 @@ export const defaultOptions = {
139
161
  defaultGasLimit: 60_000_000,
140
162
  builderSelection: routes.validator.BuilderSelection.ExecutionOnly,
141
163
  builderAliasSelection: routes.validator.BuilderSelection.Default,
142
- builderBoostFactor: BigInt(100),
164
+ builderBoostFactor: 100n,
165
+ builderMinBid: 0n,
166
+ // Only trustless payments via the builder's staked collateral are counted by default
167
+ builderMaxExecutionPayment: 0n,
143
168
  // spec asks for gossip validation by default
144
169
  broadcastValidation: routes.beacon.BroadcastValidation.gossip,
145
170
  // should request fetching the locally produced block in blinded format
@@ -148,6 +173,32 @@ export const defaultOptions = {
148
173
 
149
174
  export const MAX_BUILDER_BOOST_FACTOR = 2n ** 64n - 1n;
150
175
 
176
+ /** Pre-Gloas there is no in-protocol builder so the default is local-only, post-Gloas bids are used */
177
+ export function getDefaultBuilderSelection(isPostGloas: boolean): routes.validator.BuilderSelection {
178
+ return isPostGloas ? defaultOptions.builderAliasSelection : defaultOptions.builderSelection;
179
+ }
180
+
181
+ /** Boost factor implied by a builder selection, `configuredBoostFactor` only applies to `maxprofit` */
182
+ export function getBuilderBoostFactor(
183
+ selection: routes.validator.BuilderSelection,
184
+ configuredBoostFactor: bigint
185
+ ): bigint {
186
+ switch (selection) {
187
+ case routes.validator.BuilderSelection.Default:
188
+ // Default value slightly favors local block to improve censorship resistance of Ethereum
189
+ // The people have spoken and so it shall be https://x.com/lodestar_eth/status/1772679499928191044
190
+ return BigInt(90);
191
+ case routes.validator.BuilderSelection.MaxProfit:
192
+ return configuredBoostFactor;
193
+ case routes.validator.BuilderSelection.BuilderAlways:
194
+ case routes.validator.BuilderSelection.BuilderOnly:
195
+ return MAX_BUILDER_BOOST_FACTOR;
196
+ case routes.validator.BuilderSelection.ExecutionAlways:
197
+ case routes.validator.BuilderSelection.ExecutionOnly:
198
+ return BigInt(0);
199
+ }
200
+ }
201
+
151
202
  /**
152
203
  * Service that sets up and handles validator attester duties.
153
204
  */
@@ -185,6 +236,9 @@ export class ValidatorStore {
185
236
  gasLimit: defaultConfig.builder?.gasLimit,
186
237
  selection: defaultConfig.builder?.selection,
187
238
  boostFactor: builderBoostFactor,
239
+ minBid: defaultConfig.builder?.minBid ?? defaultOptions.builderMinBid,
240
+ maxExecutionPayment: defaultConfig.builder?.maxExecutionPayment ?? defaultOptions.builderMaxExecutionPayment,
241
+ builders: defaultConfig.builder?.builders,
188
242
  },
189
243
  };
190
244
 
@@ -281,17 +335,28 @@ export class ValidatorStore {
281
335
 
282
336
  getBuilderSelectionParams(
283
337
  pubkeyHex: PubkeyHex,
284
- slot?: Slot
338
+ slot: Slot
285
339
  ): {selection: routes.validator.BuilderSelection; boostFactor: bigint} {
286
340
  // Builder bids post-gloas are in-protocol, so the default strategy uses them regardless of
287
341
  // whether they are received over p2p or through a builder API. Pre-gloas there is no
288
342
  // in-protocol builder, so the default remains local-only (executiononly).
289
- const isPostGloas = slot !== undefined && this.config.getForkSeq(slot) >= ForkSeq.gloas;
290
- const defaultSelection = isPostGloas ? defaultOptions.builderAliasSelection : defaultOptions.builderSelection;
291
- let selection =
292
- this.validators.get(pubkeyHex)?.builder?.selection ??
293
- this.defaultProposerConfig.builder.selection ??
294
- defaultSelection;
343
+ const isPostGloas = this.config.getForkSeq(slot) >= ForkSeq.gloas;
344
+ return this.resolveBuilderSelectionParams(pubkeyHex, isPostGloas);
345
+ }
346
+
347
+ private resolveBuilderSelectionParams(
348
+ pubkeyHex: PubkeyHex,
349
+ isPostGloas: boolean
350
+ ): {selection: routes.validator.BuilderSelection; boostFactor: bigint} {
351
+ const validatorBuilder = this.validators.get(pubkeyHex)?.builder;
352
+ const defaultSelection = getDefaultBuilderSelection(isPostGloas);
353
+ let selection = validatorBuilder?.selection ?? this.defaultProposerConfig.builder.selection ?? defaultSelection;
354
+
355
+ // The standard per-key builder config directly controls the post-Gloas boost. It takes
356
+ // precedence over the legacy selection aliases when explicitly configured.
357
+ if (isPostGloas && validatorBuilder?.boostFactor !== undefined) {
358
+ return {selection: routes.validator.BuilderSelection.MaxProfit, boostFactor: validatorBuilder.boostFactor};
359
+ }
295
360
 
296
361
  // Post-Gloas block production uses standard builder boost factor. Need to normalize the
297
362
  // gloas-deprecated "builderonly" and "executiononly" to the gloas fallback "builderalways"
@@ -304,28 +369,10 @@ export class ValidatorStore {
304
369
  }
305
370
  }
306
371
 
307
- let boostFactor: bigint;
308
- switch (selection) {
309
- case routes.validator.BuilderSelection.Default:
310
- // Default value slightly favors local block to improve censorship resistance of Ethereum
311
- // The people have spoken and so it shall be https://x.com/lodestar_eth/status/1772679499928191044
312
- boostFactor = BigInt(90);
313
- break;
314
-
315
- case routes.validator.BuilderSelection.MaxProfit:
316
- boostFactor =
317
- this.validators.get(pubkeyHex)?.builder?.boostFactor ?? this.defaultProposerConfig.builder.boostFactor;
318
- break;
319
-
320
- case routes.validator.BuilderSelection.BuilderAlways:
321
- case routes.validator.BuilderSelection.BuilderOnly:
322
- boostFactor = MAX_BUILDER_BOOST_FACTOR;
323
- break;
324
-
325
- case routes.validator.BuilderSelection.ExecutionAlways:
326
- case routes.validator.BuilderSelection.ExecutionOnly:
327
- boostFactor = BigInt(0);
328
- }
372
+ const boostFactor = getBuilderBoostFactor(
373
+ selection,
374
+ validatorBuilder?.boostFactor ?? this.defaultProposerConfig.builder.boostFactor
375
+ );
329
376
 
330
377
  return {selection, boostFactor};
331
378
  }
@@ -409,6 +456,126 @@ export class ValidatorStore {
409
456
  delete validatorData.builder?.boostFactor;
410
457
  }
411
458
 
459
+ getBuilderMinBid(pubkeyHex: PubkeyHex): bigint {
460
+ const validatorData = this.validators.get(pubkeyHex);
461
+ if (validatorData === undefined) {
462
+ throw Error(`Validator pubkey ${pubkeyHex} not known`);
463
+ }
464
+ return validatorData?.builder?.minBid ?? this.defaultProposerConfig.builder.minBid;
465
+ }
466
+
467
+ getBuilderMaxExecutionPayment(pubkeyHex: PubkeyHex): bigint {
468
+ const validatorData = this.validators.get(pubkeyHex);
469
+ if (validatorData === undefined) {
470
+ throw Error(`Validator pubkey ${pubkeyHex} not known`);
471
+ }
472
+ return validatorData?.builder?.maxExecutionPayment ?? this.defaultProposerConfig.builder.maxExecutionPayment;
473
+ }
474
+
475
+ /**
476
+ * Resolve the builder entries for this key. Per-key entries replace the validator client's
477
+ * builders. A value omitted on an entry takes this key's default, then the validator
478
+ * client's configuration, while omitted auth data is derived from the entry url instead.
479
+ */
480
+ getResolvedBuilderEntries(pubkeyHex: PubkeyHex, boostFactor?: bigint): ResolvedBuilderEntry[] {
481
+ const validatorData = this.validators.get(pubkeyHex);
482
+ if (validatorData === undefined) {
483
+ throw Error(`Validator pubkey ${pubkeyHex} not known`);
484
+ }
485
+
486
+ const keyMinBid = validatorData.builder?.minBid ?? this.defaultProposerConfig.builder.minBid;
487
+ const keyBoostFactor =
488
+ boostFactor ?? validatorData.builder?.boostFactor ?? this.defaultProposerConfig.builder.boostFactor;
489
+ const keyMaxExecutionPayment =
490
+ validatorData.builder?.maxExecutionPayment ?? this.defaultProposerConfig.builder.maxExecutionPayment;
491
+
492
+ // The key's defaults apply to the validator client's own builders all the same
493
+ const builders = validatorData.builder?.builders ?? this.defaultProposerConfig.builder.builders ?? [];
494
+ return builders.map((entry) => ({
495
+ url: entry.url,
496
+ authData: entry.authData !== undefined ? fromHex(entry.authData) : new TextEncoder().encode(entry.url),
497
+ builderPubkeys: (entry.builderPubkeys ?? []).map(fromHex),
498
+ maxExecutionPayment: entry.maxExecutionPayment ?? keyMaxExecutionPayment,
499
+ minBid: entry.minBid ?? keyMinBid,
500
+ builderBoostFactor: entry.builderBoostFactor ?? keyBoostFactor,
501
+ }));
502
+ }
503
+
504
+ /** Return the builder configuration in effect for this key, with omitted values resolved */
505
+ getBuilderConfig(pubkeyHex: PubkeyHex): BuilderConfigData {
506
+ const validatorData = this.validators.get(pubkeyHex);
507
+ if (validatorData === undefined) {
508
+ throw Error(`Validator pubkey ${pubkeyHex} not known`);
509
+ }
510
+ const {boostFactor} = this.resolveBuilderSelectionParams(pubkeyHex, true);
511
+
512
+ return {
513
+ minBid: validatorData.builder?.minBid ?? this.defaultProposerConfig.builder.minBid,
514
+ builderBoostFactor: boostFactor,
515
+ builders: this.getResolvedBuilderEntries(pubkeyHex, boostFactor).map((entry) => ({
516
+ url: entry.url,
517
+ authData: toHex(entry.authData),
518
+ builderPubkeys: entry.builderPubkeys.map(toPubkeyHex),
519
+ maxExecutionPayment: entry.maxExecutionPayment,
520
+ minBid: entry.minBid,
521
+ builderBoostFactor: entry.builderBoostFactor,
522
+ })),
523
+ };
524
+ }
525
+
526
+ /** Set the builder configuration for this key, replacing any stored configuration in full */
527
+ setBuilderConfig(pubkeyHex: PubkeyHex, config: BuilderConfigData): void {
528
+ const validatorData = this.validators.get(pubkeyHex);
529
+ if (validatorData === undefined) {
530
+ throw Error(`Validator pubkey ${pubkeyHex} not known`);
531
+ }
532
+
533
+ for (const value of [config.minBid, config.builderBoostFactor]) {
534
+ if (value !== undefined && value > MAX_BUILDER_BOOST_FACTOR) {
535
+ throw Error(`Invalid builder config value=${value} exceeds uint64`);
536
+ }
537
+ }
538
+
539
+ // No two entries may share both their url and their auth data, an omitted auth data is
540
+ // compared as the value derived from the entry url
541
+ const seenEntries = new Set<string>();
542
+ for (const entry of config.builders ?? []) {
543
+ if (!isValidAsciiHttpUrl(entry.url)) {
544
+ throw Error(`Invalid builder url: ${entry.url}`);
545
+ }
546
+ const authData =
547
+ entry.authData !== undefined ? toHex(fromHex(entry.authData)) : toHex(new TextEncoder().encode(entry.url));
548
+ const entryKey = `${entry.url}|${authData}`;
549
+ if (seenEntries.has(entryKey)) {
550
+ throw Error(`Duplicate builder entry url=${entry.url} authData=${authData}`);
551
+ }
552
+ seenEntries.add(entryKey);
553
+ for (const value of [entry.maxExecutionPayment, entry.minBid, entry.builderBoostFactor]) {
554
+ if (value !== undefined && value > MAX_BUILDER_BOOST_FACTOR) {
555
+ throw Error(`Invalid builder entry value=${value} exceeds uint64`);
556
+ }
557
+ }
558
+ }
559
+
560
+ validatorData.builder = {
561
+ ...validatorData.builder,
562
+ minBid: config.minBid,
563
+ boostFactor: config.builderBoostFactor,
564
+ builders: config.builders,
565
+ };
566
+ }
567
+
568
+ /** Remove the builder configuration for this key, and revert to the validator client configuration */
569
+ deleteBuilderConfig(pubkeyHex: PubkeyHex): void {
570
+ const validatorData = this.validators.get(pubkeyHex);
571
+ if (validatorData === undefined) {
572
+ throw Error(`Validator pubkey ${pubkeyHex} not known`);
573
+ }
574
+ delete validatorData.builder?.minBid;
575
+ delete validatorData.builder?.boostFactor;
576
+ delete validatorData.builder?.builders;
577
+ }
578
+
412
579
  /** Return true if `index` is active part of this validator client */
413
580
  hasValidatorIndex(index: ValidatorIndex): boolean {
414
581
  return this.indicesService.index2pubkey.has(index);
@@ -430,7 +597,10 @@ export class ValidatorStore {
430
597
  feeRecipient !== undefined ||
431
598
  builder?.gasLimit !== undefined ||
432
599
  builder?.selection !== undefined ||
433
- builder?.boostFactor !== undefined
600
+ builder?.boostFactor !== undefined ||
601
+ builder?.minBid !== undefined ||
602
+ builder?.maxExecutionPayment !== undefined ||
603
+ builder?.builders !== undefined
434
604
  ) {
435
605
  proposerConfig = {graffiti, strictFeeRecipientCheck, feeRecipient, builder};
436
606
  }
@@ -879,6 +1049,71 @@ export class ValidatorStore {
879
1049
  };
880
1050
  }
881
1051
 
1052
+ async signBuilderRequestAuth(
1053
+ pubkeyMaybeHex: BLSPubkeyMaybeHex,
1054
+ data: Uint8Array,
1055
+ proposalSlot: Slot
1056
+ ): Promise<gloas.SignedBuilderRequestAuth> {
1057
+ if (data.length === 0 || data.length > MAX_BUILDER_AUTH_DATA_SIZE) {
1058
+ throw Error(
1059
+ `Invalid builder request auth data length=${data.length}, must be within 1 and ${MAX_BUILDER_AUTH_DATA_SIZE} bytes`
1060
+ );
1061
+ }
1062
+
1063
+ const message: gloas.BuilderRequestAuth = {data, slot: proposalSlot};
1064
+
1065
+ const signingSlot = 0;
1066
+ const domain = computeDomain(DOMAIN_BUILDER_REQUEST_AUTH, this.config.GENESIS_FORK_VERSION, ZERO_HASH);
1067
+ const signingRoot = computeSigningRoot(ssz.gloas.BuilderRequestAuth, message, domain);
1068
+
1069
+ const signableMessage: SignableMessage = {
1070
+ type: SignableMessageType.BUILDER_REQUEST_AUTH,
1071
+ data: message,
1072
+ };
1073
+
1074
+ return {
1075
+ message,
1076
+ signature: await this.getSignature(pubkeyMaybeHex, signingRoot, signingSlot, signableMessage),
1077
+ };
1078
+ }
1079
+
1080
+ /**
1081
+ * Return a pre-signed builder request auth for the auth data and proposal slot, or sign and cache a new
1082
+ * one. Signing happens off the block proposal hot path when preferences are submitted ahead of
1083
+ * time, cached auths are then used just-in-time when requesting bids at proposal time.
1084
+ */
1085
+ async getBuilderRequestAuth(
1086
+ pubkeyMaybeHex: BLSPubkeyMaybeHex,
1087
+ data: Uint8Array,
1088
+ proposalSlot: Slot,
1089
+ currentSlot: Slot
1090
+ ): Promise<gloas.SignedBuilderRequestAuth> {
1091
+ const pubkeyHex = typeof pubkeyMaybeHex === "string" ? pubkeyMaybeHex : toPubkeyHex(pubkeyMaybeHex);
1092
+ const authKey = `${proposalSlot}-${toHex(data)}`;
1093
+ const validatorData = this.validators.get(pubkeyHex);
1094
+ const cached = validatorData?.builderRequestAuths?.get(authKey);
1095
+ if (cached !== undefined) {
1096
+ return cached;
1097
+ }
1098
+
1099
+ const signedRequestAuth = await this.signBuilderRequestAuth(pubkeyMaybeHex, data, proposalSlot);
1100
+
1101
+ if (validatorData !== undefined) {
1102
+ const builderRequestAuths =
1103
+ validatorData.builderRequestAuths ?? new Map<string, gloas.SignedBuilderRequestAuth>();
1104
+ // Prune auths for proposal slots that are already in the past
1105
+ for (const key of builderRequestAuths.keys()) {
1106
+ if (Number(key.slice(0, key.indexOf("-"))) < currentSlot) {
1107
+ builderRequestAuths.delete(key);
1108
+ }
1109
+ }
1110
+ builderRequestAuths.set(authKey, signedRequestAuth);
1111
+ validatorData.builderRequestAuths = builderRequestAuths;
1112
+ }
1113
+
1114
+ return signedRequestAuth;
1115
+ }
1116
+
882
1117
  async getValidatorRegistration(
883
1118
  pubkeyMaybeHex: BLSPubkeyMaybeHex,
884
1119
  regAttributes: {feeRecipient: ExecutionAddress; gasLimit: number},
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {SecretKey} from "@chainsafe/blst";
1
+ import {SecretKey} from "@chainsafe/lodestar-z/blst";
2
2
  import {DatabaseController} from "@lodestar/db";
3
3
  import {BLSPubkey} from "@lodestar/types";
4
4
 
@@ -36,6 +36,7 @@ export enum SignableMessageType {
36
36
  EXECUTION_PAYLOAD_ENVELOPE = "EXECUTION_PAYLOAD_ENVELOPE",
37
37
  PAYLOAD_ATTESTATION = "PAYLOAD_ATTESTATION",
38
38
  PROPOSER_PREFERENCES = "PROPOSER_PREFERENCES",
39
+ BUILDER_REQUEST_AUTH = "BUILDER_REQUEST_AUTH",
39
40
  }
40
41
 
41
42
  const AggregationSlotType = new ContainerType({
@@ -87,7 +88,8 @@ export type SignableMessage =
87
88
  | {type: SignableMessageType.VALIDATOR_REGISTRATION; data: ValidatorRegistrationV1}
88
89
  | {type: SignableMessageType.EXECUTION_PAYLOAD_ENVELOPE; data: gloas.ExecutionPayloadEnvelope}
89
90
  | {type: SignableMessageType.PAYLOAD_ATTESTATION; data: gloas.PayloadAttestationData}
90
- | {type: SignableMessageType.PROPOSER_PREFERENCES; data: gloas.ProposerPreferences};
91
+ | {type: SignableMessageType.PROPOSER_PREFERENCES; data: gloas.ProposerPreferences}
92
+ | {type: SignableMessageType.BUILDER_REQUEST_AUTH; data: gloas.BuilderRequestAuth};
91
93
 
92
94
  const requiresForkInfo: Record<SignableMessageType, boolean> = {
93
95
  [SignableMessageType.AGGREGATION_SLOT]: true,
@@ -105,6 +107,7 @@ const requiresForkInfo: Record<SignableMessageType, boolean> = {
105
107
  [SignableMessageType.EXECUTION_PAYLOAD_ENVELOPE]: true,
106
108
  [SignableMessageType.PAYLOAD_ATTESTATION]: true,
107
109
  [SignableMessageType.PROPOSER_PREFERENCES]: true,
110
+ [SignableMessageType.BUILDER_REQUEST_AUTH]: false,
108
111
  };
109
112
 
110
113
  type Web3SignerSerializedRequest = {
@@ -285,6 +288,9 @@ function serializerSignableMessagePayload(config: BeaconConfig, payload: Signabl
285
288
 
286
289
  case SignableMessageType.PROPOSER_PREFERENCES:
287
290
  return {proposer_preferences: ssz.gloas.ProposerPreferences.toJson(payload.data)};
291
+
292
+ case SignableMessageType.BUILDER_REQUEST_AUTH:
293
+ return {builder_request_auth: ssz.gloas.BuilderRequestAuth.toJson(payload.data)};
288
294
  }
289
295
  }
290
296
 
package/src/validator.ts CHANGED
@@ -9,13 +9,14 @@ import {
9
9
  import {Clock, ClockOptions, IClock, computeEpochAtSlot, getCurrentSlot} from "@lodestar/state-transition";
10
10
  import {BLSPubkey, phase0, ssz} from "@lodestar/types";
11
11
  import {Genesis} from "@lodestar/types/phase0";
12
- import {Logger, toPrintableUrl, toRootHex} from "@lodestar/utils";
12
+ import {Logger, prettyGweiToEth, toPrintableUrl, toRootHex} from "@lodestar/utils";
13
13
  import {waitForGenesis} from "./genesis.js";
14
14
  import {Metrics} from "./metrics.js";
15
15
  import {MetaDataRepository} from "./repositories/metaDataRepository.js";
16
16
  import {AttestationService} from "./services/attestation.js";
17
17
  import {BlockProposingService} from "./services/block.js";
18
18
  import {BlockDutiesService} from "./services/blockDuties.js";
19
+ import {BuilderPreferencesService} from "./services/builderPreferences.js";
19
20
  import {ChainHeaderTracker} from "./services/chainHeaderTracker.js";
20
21
  import {DoppelgangerService} from "./services/doppelgangerService.js";
21
22
  import {ValidatorEventEmitter} from "./services/emitter.js";
@@ -26,7 +27,14 @@ import {ProposerPreferencesService} from "./services/proposerPreferences.js";
26
27
  import {PtcService} from "./services/ptc.js";
27
28
  import {SyncCommitteeService} from "./services/syncCommittee.js";
28
29
  import {SyncingStatusTracker} from "./services/syncingStatusTracker.js";
29
- import {Signer, ValidatorProposerConfig, ValidatorStore, defaultOptions} from "./services/validatorStore.js";
30
+ import {
31
+ Signer,
32
+ ValidatorProposerConfig,
33
+ ValidatorStore,
34
+ defaultOptions,
35
+ getBuilderBoostFactor,
36
+ getDefaultBuilderSelection,
37
+ } from "./services/validatorStore.js";
30
38
  import {ISlashingProtection, Interchange, InterchangeFormatVersion} from "./slashingProtection/index.js";
31
39
  import {LodestarValidatorDatabaseController, ProcessShutdownCallback, PubkeyHex} from "./types.js";
32
40
  import {getLoggerVc} from "./util/index.js";
@@ -313,6 +321,7 @@ export class Validator {
313
321
  );
314
322
 
315
323
  new ProposerPreferencesService(config, loggerVc, api, clock, validatorStore, blockDutiesService, metrics);
324
+ new BuilderPreferencesService(config, loggerVc, api, clock, validatorStore, blockDutiesService, metrics);
316
325
 
317
326
  return new Validator({
318
327
  opts,
@@ -367,8 +376,9 @@ export class Validator {
367
376
  logger.info("Verified connected beacon node and validator have the same genesisValidatorRoot");
368
377
 
369
378
  const {broadcastValidation = defaultOptions.broadcastValidation, valProposerConfig} = opts;
379
+ const gloasScheduled = config.GLOAS_FORK_EPOCH !== Infinity;
370
380
  const defaultBuilderSelection =
371
- valProposerConfig?.defaultConfig.builder?.selection ?? defaultOptions.builderSelection;
381
+ valProposerConfig?.defaultConfig.builder?.selection ?? getDefaultBuilderSelection(gloasScheduled);
372
382
  const strictFeeRecipientCheck = valProposerConfig?.defaultConfig.strictFeeRecipientCheck ?? false;
373
383
  const suggestedFeeRecipient = valProposerConfig?.defaultConfig.feeRecipient ?? defaultOptions.suggestedFeeRecipient;
374
384
 
@@ -381,6 +391,23 @@ export class Validator {
381
391
 
382
392
  metrics?.defaultConfiguration.set({builderSelection: defaultBuilderSelection, broadcastValidation}, 1);
383
393
 
394
+ if (gloasScheduled) {
395
+ const defaultBuilderConfig = valProposerConfig?.defaultConfig.builder;
396
+ const defaultBuilders = defaultBuilderConfig?.builders ?? [];
397
+
398
+ logger.info("Builder config", {
399
+ builders: defaultBuilders.map((entry) => toPrintableUrl(entry.url)).join(",") || "p2p only",
400
+ boostFactor: getBuilderBoostFactor(
401
+ defaultBuilderSelection,
402
+ defaultBuilderConfig?.boostFactor ?? defaultOptions.builderBoostFactor
403
+ ),
404
+ minBid: prettyGweiToEth(defaultBuilderConfig?.minBid ?? defaultOptions.builderMinBid),
405
+ maxExecutionPayment: prettyGweiToEth(
406
+ defaultBuilderConfig?.maxExecutionPayment ?? defaultOptions.builderMaxExecutionPayment
407
+ ),
408
+ });
409
+ }
410
+
384
411
  // Instantiates block and attestation services and runs them once the chain has been started.
385
412
  return Validator.init(opts, genesis, metrics);
386
413
  }