@lodestar/validator 1.47.0 → 1.48.0-dev.042c96183c
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/services/block.js +3 -3
- package/lib/services/block.js.map +1 -1
- package/lib/services/builderPreferences.js +1 -1
- package/lib/services/builderPreferences.js.map +1 -1
- package/lib/services/prepareBeaconProposer.d.ts.map +1 -1
- package/lib/services/prepareBeaconProposer.js +5 -2
- package/lib/services/prepareBeaconProposer.js.map +1 -1
- package/lib/services/validatorStore.d.ts +5 -1
- package/lib/services/validatorStore.d.ts.map +1 -1
- package/lib/services/validatorStore.js +25 -21
- package/lib/services/validatorStore.js.map +1 -1
- package/lib/slashingProtection/attestation/attestationByTargetRepository.d.ts +2 -0
- package/lib/slashingProtection/attestation/attestationByTargetRepository.d.ts.map +1 -1
- package/lib/slashingProtection/attestation/attestationByTargetRepository.js +11 -0
- package/lib/slashingProtection/attestation/attestationByTargetRepository.js.map +1 -1
- package/lib/slashingProtection/attestation/errors.d.ts +10 -1
- package/lib/slashingProtection/attestation/errors.d.ts.map +1 -1
- package/lib/slashingProtection/attestation/errors.js +5 -0
- package/lib/slashingProtection/attestation/errors.js.map +1 -1
- package/lib/slashingProtection/attestation/index.d.ts.map +1 -1
- package/lib/slashingProtection/attestation/index.js +41 -2
- package/lib/slashingProtection/attestation/index.js.map +1 -1
- package/lib/slashingProtection/minMaxSurround/minMaxSurround.d.ts +3 -1
- package/lib/slashingProtection/minMaxSurround/minMaxSurround.d.ts.map +1 -1
- package/lib/slashingProtection/minMaxSurround/minMaxSurround.js +7 -2
- package/lib/slashingProtection/minMaxSurround/minMaxSurround.js.map +1 -1
- package/lib/util/externalSignerClient.d.ts +2 -2
- package/lib/util/externalSignerClient.d.ts.map +1 -1
- package/lib/util/externalSignerClient.js +38 -10
- package/lib/util/externalSignerClient.js.map +1 -1
- package/lib/validator.d.ts.map +1 -1
- package/lib/validator.js +14 -3
- package/lib/validator.js.map +1 -1
- package/package.json +13 -13
- package/src/services/block.ts +3 -3
- package/src/services/builderPreferences.ts +1 -1
- package/src/services/prepareBeaconProposer.ts +5 -2
- package/src/services/validatorStore.ts +34 -25
- package/src/slashingProtection/attestation/attestationByTargetRepository.ts +12 -0
- package/src/slashingProtection/attestation/errors.ts +10 -0
- package/src/slashingProtection/attestation/index.ts +43 -2
- package/src/slashingProtection/minMaxSurround/minMaxSurround.ts +9 -3
- package/src/util/externalSignerClient.ts +40 -12
- package/src/validator.ts +28 -3
|
@@ -173,6 +173,32 @@ export const defaultOptions = {
|
|
|
173
173
|
|
|
174
174
|
export const MAX_BUILDER_BOOST_FACTOR = 2n ** 64n - 1n;
|
|
175
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
|
+
|
|
176
202
|
/**
|
|
177
203
|
* Service that sets up and handles validator attester duties.
|
|
178
204
|
*/
|
|
@@ -309,12 +335,12 @@ export class ValidatorStore {
|
|
|
309
335
|
|
|
310
336
|
getBuilderSelectionParams(
|
|
311
337
|
pubkeyHex: PubkeyHex,
|
|
312
|
-
slot
|
|
338
|
+
slot: Slot
|
|
313
339
|
): {selection: routes.validator.BuilderSelection; boostFactor: bigint} {
|
|
314
340
|
// Builder bids post-gloas are in-protocol, so the default strategy uses them regardless of
|
|
315
341
|
// whether they are received over p2p or through a builder API. Pre-gloas there is no
|
|
316
342
|
// in-protocol builder, so the default remains local-only (executiononly).
|
|
317
|
-
const isPostGloas =
|
|
343
|
+
const isPostGloas = this.config.getForkSeq(slot) >= ForkSeq.gloas;
|
|
318
344
|
return this.resolveBuilderSelectionParams(pubkeyHex, isPostGloas);
|
|
319
345
|
}
|
|
320
346
|
|
|
@@ -323,7 +349,7 @@ export class ValidatorStore {
|
|
|
323
349
|
isPostGloas: boolean
|
|
324
350
|
): {selection: routes.validator.BuilderSelection; boostFactor: bigint} {
|
|
325
351
|
const validatorBuilder = this.validators.get(pubkeyHex)?.builder;
|
|
326
|
-
const defaultSelection = isPostGloas
|
|
352
|
+
const defaultSelection = getDefaultBuilderSelection(isPostGloas);
|
|
327
353
|
let selection = validatorBuilder?.selection ?? this.defaultProposerConfig.builder.selection ?? defaultSelection;
|
|
328
354
|
|
|
329
355
|
// The standard per-key builder config directly controls the post-Gloas boost. It takes
|
|
@@ -343,27 +369,10 @@ export class ValidatorStore {
|
|
|
343
369
|
}
|
|
344
370
|
}
|
|
345
371
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
// The people have spoken and so it shall be https://x.com/lodestar_eth/status/1772679499928191044
|
|
351
|
-
boostFactor = BigInt(90);
|
|
352
|
-
break;
|
|
353
|
-
|
|
354
|
-
case routes.validator.BuilderSelection.MaxProfit:
|
|
355
|
-
boostFactor = validatorBuilder?.boostFactor ?? this.defaultProposerConfig.builder.boostFactor;
|
|
356
|
-
break;
|
|
357
|
-
|
|
358
|
-
case routes.validator.BuilderSelection.BuilderAlways:
|
|
359
|
-
case routes.validator.BuilderSelection.BuilderOnly:
|
|
360
|
-
boostFactor = MAX_BUILDER_BOOST_FACTOR;
|
|
361
|
-
break;
|
|
362
|
-
|
|
363
|
-
case routes.validator.BuilderSelection.ExecutionAlways:
|
|
364
|
-
case routes.validator.BuilderSelection.ExecutionOnly:
|
|
365
|
-
boostFactor = BigInt(0);
|
|
366
|
-
}
|
|
372
|
+
const boostFactor = getBuilderBoostFactor(
|
|
373
|
+
selection,
|
|
374
|
+
validatorBuilder?.boostFactor ?? this.defaultProposerConfig.builder.boostFactor
|
|
375
|
+
);
|
|
367
376
|
|
|
368
377
|
return {selection, boostFactor};
|
|
369
378
|
}
|
|
@@ -900,7 +909,7 @@ export class ValidatorStore {
|
|
|
900
909
|
});
|
|
901
910
|
|
|
902
911
|
const signableMessage: SignableMessage = {
|
|
903
|
-
type: SignableMessageType.
|
|
912
|
+
type: SignableMessageType.PAYLOAD_ATTESTATION_MESSAGE,
|
|
904
913
|
data,
|
|
905
914
|
};
|
|
906
915
|
|
|
@@ -41,6 +41,18 @@ export class AttestationByTargetRepository {
|
|
|
41
41
|
return attestations.map((attestation) => this.type.deserialize(attestation));
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** Attestation with the highest target epoch recorded for `pubkey`, or null if none */
|
|
45
|
+
async getLatest(pubkey: BLSPubkey): Promise<SlashingProtectionAttestation | null> {
|
|
46
|
+
const [att] = await this.db.values({
|
|
47
|
+
gte: this.encodeKey(pubkey, 0),
|
|
48
|
+
lt: this.encodeKey(pubkey, Number.MAX_SAFE_INTEGER),
|
|
49
|
+
reverse: true,
|
|
50
|
+
limit: 1,
|
|
51
|
+
bucketId: this.bucketId,
|
|
52
|
+
});
|
|
53
|
+
return att ? this.type.deserialize(att) : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
44
56
|
async get(pubkey: BLSPubkey, targetEpoch: Epoch): Promise<SlashingProtectionAttestation | null> {
|
|
45
57
|
const att = await this.db.get(this.encodeKey(pubkey, targetEpoch), this.dbReqOpts);
|
|
46
58
|
return att && this.type.deserialize(att);
|
|
@@ -29,6 +29,11 @@ export enum InvalidAttestationErrorCode {
|
|
|
29
29
|
* bound on target epochs for this validator.
|
|
30
30
|
*/
|
|
31
31
|
TARGET_LESS_THAN_OR_EQ_LOWER_BOUND = "ERR_INVALID_ATTESTATION_TARGET_LESS_THAN_OR_EQ_LOWER_BOUND",
|
|
32
|
+
/**
|
|
33
|
+
* The attestation is invalid because its source epoch is below the min-max span lookback window of the
|
|
34
|
+
* latest attestation of this validator, where a surround vote could not be detected.
|
|
35
|
+
*/
|
|
36
|
+
SOURCE_BELOW_MIN_SPAN_LOOKBACK = "ERR_INVALID_ATTESTATION_SOURCE_BELOW_MIN_SPAN_LOOKBACK",
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
type InvalidAttestationErrorType =
|
|
@@ -61,6 +66,11 @@ type InvalidAttestationErrorType =
|
|
|
61
66
|
code: InvalidAttestationErrorCode.TARGET_LESS_THAN_OR_EQ_LOWER_BOUND;
|
|
62
67
|
targetEpoch: Epoch;
|
|
63
68
|
minTargetEpoch: Epoch;
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
code: InvalidAttestationErrorCode.SOURCE_BELOW_MIN_SPAN_LOOKBACK;
|
|
72
|
+
sourceEpoch: Epoch;
|
|
73
|
+
minSourceEpoch: Epoch;
|
|
64
74
|
};
|
|
65
75
|
|
|
66
76
|
export class InvalidAttestationError extends LodestarError<InvalidAttestationErrorType> {}
|
|
@@ -56,9 +56,16 @@ export class SlashingProtectionAttestationService {
|
|
|
56
56
|
throw new InvalidAttestationError({code: InvalidAttestationErrorCode.SOURCE_EXCEEDS_TARGET});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
const latestAtt = await this.attestationByTarget.getLatest(pubKey);
|
|
60
|
+
|
|
59
61
|
// Check for a double vote. Namely, an existing attestation with the same target epoch,
|
|
60
|
-
// and a different signing root.
|
|
61
|
-
|
|
62
|
+
// and a different signing root. No db read needed if the latest attestation has a lower target epoch.
|
|
63
|
+
let sameTargetAtt: SlashingProtectionAttestation | null = null;
|
|
64
|
+
if (latestAtt && latestAtt.targetEpoch === attestation.targetEpoch) {
|
|
65
|
+
sameTargetAtt = latestAtt;
|
|
66
|
+
} else if (latestAtt && latestAtt.targetEpoch > attestation.targetEpoch) {
|
|
67
|
+
sameTargetAtt = await this.attestationByTarget.get(pubKey, attestation.targetEpoch);
|
|
68
|
+
}
|
|
62
69
|
if (sameTargetAtt) {
|
|
63
70
|
// Interchange format allows for attestations without signing_root, then assume root is equal
|
|
64
71
|
if (isEqualNonZeroRoot(sameTargetAtt.signingRoot, attestation.signingRoot)) {
|
|
@@ -71,6 +78,20 @@ export class SlashingProtectionAttestationService {
|
|
|
71
78
|
});
|
|
72
79
|
}
|
|
73
80
|
|
|
81
|
+
// Min-span entries only exist within the lookback window below each recorded source epoch, a surround vote
|
|
82
|
+
// with an older source epoch is undetectable by min-max surround and must be rejected outright. Recorded
|
|
83
|
+
// attestations are surround-free, so the latest one has the highest source epoch and the widest window.
|
|
84
|
+
if (latestAtt) {
|
|
85
|
+
const minSourceEpoch = this.minMaxSurround.minSpanCoverageStart(latestAtt.sourceEpoch);
|
|
86
|
+
if (attestation.sourceEpoch < minSourceEpoch) {
|
|
87
|
+
throw new InvalidAttestationError({
|
|
88
|
+
code: InvalidAttestationErrorCode.SOURCE_BELOW_MIN_SPAN_LOOKBACK,
|
|
89
|
+
sourceEpoch: attestation.sourceEpoch,
|
|
90
|
+
minSourceEpoch,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
74
95
|
// Check for a surround vote
|
|
75
96
|
try {
|
|
76
97
|
await this.minMaxSurround.assertNoSurround(pubKey, attestation);
|
|
@@ -143,6 +164,26 @@ export class SlashingProtectionAttestationService {
|
|
|
143
164
|
* Interchange import / export functionality
|
|
144
165
|
*/
|
|
145
166
|
async importAttestations(pubkey: BLSPubkey, attestations: SlashingProtectionAttestation[]): Promise<void> {
|
|
167
|
+
// Min-max surround misses a surround vote beyond its lookback, require the highest target attestation to
|
|
168
|
+
// have the highest source epoch as `checkAttestation` relies on it
|
|
169
|
+
let latestAtt = await this.attestationByTarget.getLatest(pubkey);
|
|
170
|
+
let maxSourceAtt = latestAtt;
|
|
171
|
+
for (const attestation of attestations) {
|
|
172
|
+
if (latestAtt === null || attestation.targetEpoch >= latestAtt.targetEpoch) {
|
|
173
|
+
latestAtt = attestation;
|
|
174
|
+
}
|
|
175
|
+
if (maxSourceAtt === null || attestation.sourceEpoch > maxSourceAtt.sourceEpoch) {
|
|
176
|
+
maxSourceAtt = attestation;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (latestAtt && maxSourceAtt && latestAtt.sourceEpoch < maxSourceAtt.sourceEpoch) {
|
|
180
|
+
throw new InvalidAttestationError({
|
|
181
|
+
code: InvalidAttestationErrorCode.NEW_SURROUNDS_PREV,
|
|
182
|
+
attestation: latestAtt,
|
|
183
|
+
prev: maxSourceAtt,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
146
187
|
await this.attestationByTarget.set(pubkey, attestations);
|
|
147
188
|
|
|
148
189
|
// Pre-compute spans for all attestations
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {BLSPubkey} from "@lodestar/types";
|
|
1
|
+
import {BLSPubkey, Epoch} from "@lodestar/types";
|
|
2
2
|
import {SurroundAttestationError, SurroundAttestationErrorCode} from "./errors.js";
|
|
3
3
|
import {DistanceEntry, IDistanceStore, IMinMaxSurround, MinMaxSurroundAttestation} from "./interface.js";
|
|
4
4
|
|
|
@@ -9,7 +9,8 @@ import {DistanceEntry, IDistanceStore, IMinMaxSurround, MinMaxSurroundAttestatio
|
|
|
9
9
|
* Number of epochs in the past to check for surrounding attestations.
|
|
10
10
|
*
|
|
11
11
|
* This value can be limited to a reasonable high amount as Lodestar does not solely rely on this strategy but also
|
|
12
|
-
*
|
|
12
|
+
* rejects any source epoch below `minSpanCoverageStart` of the latest recorded attestation, which is the minimal
|
|
13
|
+
* strategy (formally proven safe, https://github.com/michaelsproul/slashing-proofs) relaxed by this lookback.
|
|
13
14
|
*
|
|
14
15
|
* Limiting this value is required due to practical reasons as otherwise there would be a min-span DB read and write
|
|
15
16
|
* for each validator from current epoch until genesis which massively increases DB size and causes I/O lag, resulting in
|
|
@@ -30,6 +31,11 @@ export class MinMaxSurround implements IMinMaxSurround {
|
|
|
30
31
|
this.maxEpochLookback = options?.maxEpochLookback ?? DEFAULT_MAX_EPOCH_LOOKBACK;
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
/** Lowest epoch with a min-span entry after inserting an attestation with `sourceEpoch` */
|
|
35
|
+
minSpanCoverageStart(sourceEpoch: Epoch): Epoch {
|
|
36
|
+
return Math.max(0, sourceEpoch - 1 - this.maxEpochLookback);
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
async assertNoSurround(pubKey: BLSPubkey, attestation: MinMaxSurroundAttestation): Promise<void> {
|
|
34
40
|
await this.assertNotSurrounding(pubKey, attestation);
|
|
35
41
|
await this.assertNotSurrounded(pubKey, attestation);
|
|
@@ -45,7 +51,7 @@ export class MinMaxSurround implements IMinMaxSurround {
|
|
|
45
51
|
private async updateMinSpan(pubKey: BLSPubkey, attestation: MinMaxSurroundAttestation): Promise<void> {
|
|
46
52
|
await this.assertNotSurrounding(pubKey, attestation);
|
|
47
53
|
|
|
48
|
-
const untilEpoch =
|
|
54
|
+
const untilEpoch = this.minSpanCoverageStart(attestation.sourceEpoch);
|
|
49
55
|
|
|
50
56
|
const values: DistanceEntry[] = [];
|
|
51
57
|
for (let epoch = attestation.sourceEpoch - 1; epoch >= untilEpoch; epoch--) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {ContainerType, ValueOf} from "@chainsafe/ssz";
|
|
2
2
|
import {BeaconConfig} from "@lodestar/config";
|
|
3
|
-
import {ForkName, ForkPreBellatrix, ForkSeq, isForkPostDeneb} from "@lodestar/params";
|
|
3
|
+
import {ForkName, ForkPostGloas, ForkPreBellatrix, ForkSeq, isForkPostDeneb} from "@lodestar/params";
|
|
4
4
|
import {blindedOrFullBlockToHeader, computeEpochAtSlot} from "@lodestar/state-transition";
|
|
5
5
|
import {
|
|
6
6
|
AggregateAndProof,
|
|
@@ -34,7 +34,7 @@ export enum SignableMessageType {
|
|
|
34
34
|
SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF = "SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF",
|
|
35
35
|
VALIDATOR_REGISTRATION = "VALIDATOR_REGISTRATION",
|
|
36
36
|
EXECUTION_PAYLOAD_ENVELOPE = "EXECUTION_PAYLOAD_ENVELOPE",
|
|
37
|
-
|
|
37
|
+
PAYLOAD_ATTESTATION_MESSAGE = "PAYLOAD_ATTESTATION_MESSAGE",
|
|
38
38
|
PROPOSER_PREFERENCES = "PROPOSER_PREFERENCES",
|
|
39
39
|
BUILDER_REQUEST_AUTH = "BUILDER_REQUEST_AUTH",
|
|
40
40
|
}
|
|
@@ -87,7 +87,7 @@ export type SignableMessage =
|
|
|
87
87
|
| {type: SignableMessageType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF; data: altair.ContributionAndProof}
|
|
88
88
|
| {type: SignableMessageType.VALIDATOR_REGISTRATION; data: ValidatorRegistrationV1}
|
|
89
89
|
| {type: SignableMessageType.EXECUTION_PAYLOAD_ENVELOPE; data: gloas.ExecutionPayloadEnvelope}
|
|
90
|
-
| {type: SignableMessageType.
|
|
90
|
+
| {type: SignableMessageType.PAYLOAD_ATTESTATION_MESSAGE; data: gloas.PayloadAttestationData}
|
|
91
91
|
| {type: SignableMessageType.PROPOSER_PREFERENCES; data: gloas.ProposerPreferences}
|
|
92
92
|
| {type: SignableMessageType.BUILDER_REQUEST_AUTH; data: gloas.BuilderRequestAuth};
|
|
93
93
|
|
|
@@ -105,7 +105,7 @@ const requiresForkInfo: Record<SignableMessageType, boolean> = {
|
|
|
105
105
|
[SignableMessageType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF]: true,
|
|
106
106
|
[SignableMessageType.VALIDATOR_REGISTRATION]: false,
|
|
107
107
|
[SignableMessageType.EXECUTION_PAYLOAD_ENVELOPE]: true,
|
|
108
|
-
[SignableMessageType.
|
|
108
|
+
[SignableMessageType.PAYLOAD_ATTESTATION_MESSAGE]: true,
|
|
109
109
|
[SignableMessageType.PROPOSER_PREFERENCES]: true,
|
|
110
110
|
[SignableMessageType.BUILDER_REQUEST_AUTH]: false,
|
|
111
111
|
};
|
|
@@ -280,17 +280,45 @@ function serializerSignableMessagePayload(config: BeaconConfig, payload: Signabl
|
|
|
280
280
|
case SignableMessageType.VALIDATOR_REGISTRATION:
|
|
281
281
|
return {validator_registration: ssz.bellatrix.ValidatorRegistrationV1.toJson(payload.data)};
|
|
282
282
|
|
|
283
|
-
case SignableMessageType.EXECUTION_PAYLOAD_ENVELOPE:
|
|
284
|
-
|
|
283
|
+
case SignableMessageType.EXECUTION_PAYLOAD_ENVELOPE: {
|
|
284
|
+
const slot = payload.data.payload.slotNumber;
|
|
285
|
+
return {
|
|
286
|
+
execution_payload_envelope: {
|
|
287
|
+
version: config.getForkName(slot).toUpperCase(),
|
|
288
|
+
data: config.getForkTypes<ForkPostGloas>(slot).ExecutionPayloadEnvelope.toJson(payload.data),
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
285
292
|
|
|
286
|
-
case SignableMessageType.
|
|
287
|
-
|
|
293
|
+
case SignableMessageType.PAYLOAD_ATTESTATION_MESSAGE: {
|
|
294
|
+
const slot = payload.data.slot;
|
|
295
|
+
return {
|
|
296
|
+
payload_attestation_message: {
|
|
297
|
+
version: config.getForkName(slot).toUpperCase(),
|
|
298
|
+
data: config.getForkTypes<ForkPostGloas>(slot).PayloadAttestationData.toJson(payload.data),
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
}
|
|
288
302
|
|
|
289
|
-
case SignableMessageType.PROPOSER_PREFERENCES:
|
|
290
|
-
|
|
303
|
+
case SignableMessageType.PROPOSER_PREFERENCES: {
|
|
304
|
+
const slot = payload.data.proposalSlot;
|
|
305
|
+
return {
|
|
306
|
+
proposer_preferences: {
|
|
307
|
+
version: config.getForkName(slot).toUpperCase(),
|
|
308
|
+
data: config.getForkTypes<ForkPostGloas>(slot).ProposerPreferences.toJson(payload.data),
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
}
|
|
291
312
|
|
|
292
|
-
case SignableMessageType.BUILDER_REQUEST_AUTH:
|
|
293
|
-
|
|
313
|
+
case SignableMessageType.BUILDER_REQUEST_AUTH: {
|
|
314
|
+
const slot = payload.data.slot;
|
|
315
|
+
return {
|
|
316
|
+
builder_request_auth: {
|
|
317
|
+
version: config.getForkName(slot).toUpperCase(),
|
|
318
|
+
data: config.getForkTypes<ForkPostGloas>(slot).BuilderRequestAuth.toJson(payload.data),
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
294
322
|
}
|
|
295
323
|
}
|
|
296
324
|
|
package/src/validator.ts
CHANGED
|
@@ -9,7 +9,7 @@ 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";
|
|
@@ -27,7 +27,14 @@ import {ProposerPreferencesService} from "./services/proposerPreferences.js";
|
|
|
27
27
|
import {PtcService} from "./services/ptc.js";
|
|
28
28
|
import {SyncCommitteeService} from "./services/syncCommittee.js";
|
|
29
29
|
import {SyncingStatusTracker} from "./services/syncingStatusTracker.js";
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
Signer,
|
|
32
|
+
ValidatorProposerConfig,
|
|
33
|
+
ValidatorStore,
|
|
34
|
+
defaultOptions,
|
|
35
|
+
getBuilderBoostFactor,
|
|
36
|
+
getDefaultBuilderSelection,
|
|
37
|
+
} from "./services/validatorStore.js";
|
|
31
38
|
import {ISlashingProtection, Interchange, InterchangeFormatVersion} from "./slashingProtection/index.js";
|
|
32
39
|
import {LodestarValidatorDatabaseController, ProcessShutdownCallback, PubkeyHex} from "./types.js";
|
|
33
40
|
import {getLoggerVc} from "./util/index.js";
|
|
@@ -369,8 +376,9 @@ export class Validator {
|
|
|
369
376
|
logger.info("Verified connected beacon node and validator have the same genesisValidatorRoot");
|
|
370
377
|
|
|
371
378
|
const {broadcastValidation = defaultOptions.broadcastValidation, valProposerConfig} = opts;
|
|
379
|
+
const gloasScheduled = config.GLOAS_FORK_EPOCH !== Infinity;
|
|
372
380
|
const defaultBuilderSelection =
|
|
373
|
-
valProposerConfig?.defaultConfig.builder?.selection ??
|
|
381
|
+
valProposerConfig?.defaultConfig.builder?.selection ?? getDefaultBuilderSelection(gloasScheduled);
|
|
374
382
|
const strictFeeRecipientCheck = valProposerConfig?.defaultConfig.strictFeeRecipientCheck ?? false;
|
|
375
383
|
const suggestedFeeRecipient = valProposerConfig?.defaultConfig.feeRecipient ?? defaultOptions.suggestedFeeRecipient;
|
|
376
384
|
|
|
@@ -383,6 +391,23 @@ export class Validator {
|
|
|
383
391
|
|
|
384
392
|
metrics?.defaultConfiguration.set({builderSelection: defaultBuilderSelection, broadcastValidation}, 1);
|
|
385
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
|
+
|
|
386
411
|
// Instantiates block and attestation services and runs them once the chain has been started.
|
|
387
412
|
return Validator.init(opts, genesis, metrics);
|
|
388
413
|
}
|