@lodestar/validator 1.35.0-dev.8ea34e52ba → 1.35.0-dev.91dadf81de
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/buckets.d.ts.map +1 -0
- package/lib/defaults.d.ts.map +1 -0
- package/lib/genesis.d.ts.map +1 -0
- package/lib/index.d.ts +7 -7
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +5 -5
- package/lib/index.js.map +1 -1
- package/lib/metrics.d.ts.map +1 -0
- package/lib/metrics.js +14 -14
- package/lib/metrics.js.map +1 -1
- package/lib/repositories/index.d.ts.map +1 -0
- package/lib/repositories/metaDataRepository.d.ts.map +1 -0
- package/lib/repositories/metaDataRepository.js.map +1 -1
- package/lib/services/attestation.d.ts.map +1 -0
- package/lib/services/attestation.js +35 -27
- package/lib/services/attestation.js.map +1 -1
- package/lib/services/attestationDuties.d.ts.map +1 -0
- package/lib/services/attestationDuties.js +1 -0
- package/lib/services/attestationDuties.js.map +1 -1
- package/lib/services/block.d.ts.map +1 -0
- package/lib/services/blockDuties.d.ts +2 -2
- package/lib/services/blockDuties.d.ts.map +1 -0
- package/lib/services/blockDuties.js +4 -3
- package/lib/services/blockDuties.js.map +1 -1
- package/lib/services/chainHeaderTracker.d.ts.map +1 -0
- package/lib/services/doppelgangerService.d.ts.map +1 -0
- package/lib/services/emitter.d.ts +1 -1
- package/lib/services/emitter.d.ts.map +1 -0
- package/lib/services/externalSignerSync.d.ts.map +1 -0
- package/lib/services/externalSignerSync.js.map +1 -1
- package/lib/services/indices.d.ts.map +1 -0
- package/lib/services/prepareBeaconProposer.d.ts.map +1 -0
- package/lib/services/prepareBeaconProposer.js.map +1 -1
- package/lib/services/syncCommittee.d.ts.map +1 -0
- package/lib/services/syncCommittee.js +30 -22
- package/lib/services/syncCommittee.js.map +1 -1
- package/lib/services/syncCommitteeDuties.d.ts.map +1 -0
- package/lib/services/syncingStatusTracker.d.ts.map +1 -0
- package/lib/services/utils.d.ts.map +1 -0
- package/lib/services/validatorStore.d.ts.map +1 -0
- package/lib/slashingProtection/attestation/attestationByTargetRepository.d.ts.map +1 -0
- package/lib/slashingProtection/attestation/attestationLowerBoundRepository.d.ts.map +1 -0
- package/lib/slashingProtection/attestation/errors.d.ts.map +1 -0
- package/lib/slashingProtection/attestation/index.d.ts.map +1 -0
- package/lib/slashingProtection/block/blockBySlotRepository.d.ts.map +1 -0
- package/lib/slashingProtection/block/errors.d.ts.map +1 -0
- package/lib/slashingProtection/block/index.d.ts.map +1 -0
- package/lib/slashingProtection/index.d.ts +1 -1
- package/lib/slashingProtection/index.d.ts.map +1 -0
- package/lib/slashingProtection/index.js.map +1 -1
- package/lib/slashingProtection/interchange/errors.d.ts.map +1 -0
- package/lib/slashingProtection/interchange/formats/completeV4.d.ts.map +1 -0
- package/lib/slashingProtection/interchange/formats/index.d.ts.map +1 -0
- package/lib/slashingProtection/interchange/formats/v5.d.ts.map +1 -0
- package/lib/slashingProtection/interchange/index.d.ts.map +1 -0
- package/lib/slashingProtection/interchange/parseInterchange.d.ts.map +1 -0
- package/lib/slashingProtection/interchange/serializeInterchange.d.ts.map +1 -0
- package/lib/slashingProtection/interchange/types.d.ts.map +1 -0
- package/lib/slashingProtection/interface.d.ts.map +1 -0
- package/lib/slashingProtection/minMaxSurround/distanceStoreRepository.d.ts.map +1 -0
- package/lib/slashingProtection/minMaxSurround/errors.d.ts.map +1 -0
- package/lib/slashingProtection/minMaxSurround/index.d.ts.map +1 -0
- package/lib/slashingProtection/minMaxSurround/interface.d.ts.map +1 -0
- package/lib/slashingProtection/minMaxSurround/minMaxSurround.d.ts.map +1 -0
- package/lib/slashingProtection/types.d.ts.map +1 -0
- package/lib/slashingProtection/utils.d.ts.map +1 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/util/batch.d.ts.map +1 -0
- package/lib/util/clock.d.ts +3 -0
- package/lib/util/clock.d.ts.map +1 -0
- package/lib/util/clock.js +4 -0
- package/lib/util/clock.js.map +1 -1
- package/lib/util/difference.d.ts.map +1 -0
- package/lib/util/externalSignerClient.d.ts.map +1 -0
- package/lib/util/format.d.ts.map +1 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/logger.d.ts.map +1 -0
- package/lib/util/params.d.ts.map +1 -0
- package/lib/util/params.js +17 -1
- package/lib/util/params.js.map +1 -1
- package/lib/util/url.d.ts.map +1 -0
- package/lib/validator.d.ts.map +1 -0
- package/package.json +19 -16
- package/src/buckets.ts +30 -0
- package/src/defaults.ts +8 -0
- package/src/genesis.ts +19 -0
- package/src/index.ts +22 -0
- package/src/metrics.ts +417 -0
- package/src/repositories/index.ts +1 -0
- package/src/repositories/metaDataRepository.ts +42 -0
- package/src/services/attestation.ts +362 -0
- package/src/services/attestationDuties.ts +406 -0
- package/src/services/block.ts +261 -0
- package/src/services/blockDuties.ts +217 -0
- package/src/services/chainHeaderTracker.ts +89 -0
- package/src/services/doppelgangerService.ts +286 -0
- package/src/services/emitter.ts +43 -0
- package/src/services/externalSignerSync.ts +81 -0
- package/src/services/indices.ts +165 -0
- package/src/services/prepareBeaconProposer.ts +119 -0
- package/src/services/syncCommittee.ts +338 -0
- package/src/services/syncCommitteeDuties.ts +337 -0
- package/src/services/syncingStatusTracker.ts +74 -0
- package/src/services/utils.ts +58 -0
- package/src/services/validatorStore.ts +830 -0
- package/src/slashingProtection/attestation/attestationByTargetRepository.ts +77 -0
- package/src/slashingProtection/attestation/attestationLowerBoundRepository.ts +44 -0
- package/src/slashingProtection/attestation/errors.ts +66 -0
- package/src/slashingProtection/attestation/index.ts +171 -0
- package/src/slashingProtection/block/blockBySlotRepository.ts +78 -0
- package/src/slashingProtection/block/errors.ts +28 -0
- package/src/slashingProtection/block/index.ts +94 -0
- package/src/slashingProtection/index.ts +95 -0
- package/src/slashingProtection/interchange/errors.ts +15 -0
- package/src/slashingProtection/interchange/formats/completeV4.ts +125 -0
- package/src/slashingProtection/interchange/formats/index.ts +7 -0
- package/src/slashingProtection/interchange/formats/v5.ts +120 -0
- package/src/slashingProtection/interchange/index.ts +5 -0
- package/src/slashingProtection/interchange/parseInterchange.ts +55 -0
- package/src/slashingProtection/interchange/serializeInterchange.ts +35 -0
- package/src/slashingProtection/interchange/types.ts +18 -0
- package/src/slashingProtection/interface.ts +28 -0
- package/src/slashingProtection/minMaxSurround/distanceStoreRepository.ts +57 -0
- package/src/slashingProtection/minMaxSurround/errors.ts +27 -0
- package/src/slashingProtection/minMaxSurround/index.ts +4 -0
- package/src/slashingProtection/minMaxSurround/interface.ts +23 -0
- package/src/slashingProtection/minMaxSurround/minMaxSurround.ts +104 -0
- package/src/slashingProtection/types.ts +12 -0
- package/src/slashingProtection/utils.ts +42 -0
- package/src/types.ts +31 -0
- package/src/util/batch.ts +15 -0
- package/src/util/clock.ts +170 -0
- package/src/util/difference.ts +10 -0
- package/src/util/externalSignerClient.ts +277 -0
- package/src/util/format.ts +3 -0
- package/src/util/index.ts +6 -0
- package/src/util/logger.ts +51 -0
- package/src/util/params.ts +320 -0
- package/src/util/url.ts +16 -0
- package/src/validator.ts +418 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {PublicKey} from "@chainsafe/blst";
|
|
2
|
+
import {ChainForkConfig} from "@lodestar/config";
|
|
3
|
+
import {SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
4
|
+
import {fromHex, toPrintableUrl} from "@lodestar/utils";
|
|
5
|
+
import {externalSignerGetKeys} from "../util/externalSignerClient.js";
|
|
6
|
+
import {LoggerVc} from "../util/index.js";
|
|
7
|
+
import {SignerType, ValidatorStore} from "./validatorStore.js";
|
|
8
|
+
|
|
9
|
+
export type ExternalSignerOptions = {
|
|
10
|
+
url?: string;
|
|
11
|
+
fetch?: boolean;
|
|
12
|
+
fetchInterval?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* This service is responsible for keeping the keys managed by the connected
|
|
17
|
+
* external signer and the validator client in sync by adding newly discovered keys
|
|
18
|
+
* and removing no longer present keys on external signer from the validator store.
|
|
19
|
+
*/
|
|
20
|
+
export function pollExternalSignerPubkeys(
|
|
21
|
+
config: ChainForkConfig,
|
|
22
|
+
logger: LoggerVc,
|
|
23
|
+
signal: AbortSignal,
|
|
24
|
+
validatorStore: ValidatorStore,
|
|
25
|
+
opts?: ExternalSignerOptions
|
|
26
|
+
): void {
|
|
27
|
+
const externalSigner = opts ?? {};
|
|
28
|
+
|
|
29
|
+
if (!externalSigner.url || !externalSigner.fetch) {
|
|
30
|
+
return; // Disabled
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function fetchExternalSignerPubkeys(): Promise<void> {
|
|
34
|
+
// External signer URL is already validated earlier
|
|
35
|
+
const externalSignerUrl = externalSigner.url as string;
|
|
36
|
+
const printableUrl = toPrintableUrl(externalSignerUrl);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
logger.debug("Fetching public keys from external signer", {url: printableUrl});
|
|
40
|
+
const externalPubkeys = await externalSignerGetKeys(externalSignerUrl);
|
|
41
|
+
assertValidPubkeysHex(externalPubkeys);
|
|
42
|
+
logger.debug("Received public keys from external signer", {url: printableUrl, count: externalPubkeys.length});
|
|
43
|
+
|
|
44
|
+
const localPubkeys = validatorStore.getRemoteSignerPubkeys(externalSignerUrl);
|
|
45
|
+
logger.debug("Local public keys stored for external signer", {url: printableUrl, count: localPubkeys.length});
|
|
46
|
+
|
|
47
|
+
const localPubkeysSet = new Set(localPubkeys);
|
|
48
|
+
for (const pubkey of externalPubkeys) {
|
|
49
|
+
if (!localPubkeysSet.has(pubkey)) {
|
|
50
|
+
await validatorStore.addSigner({type: SignerType.Remote, pubkey, url: externalSignerUrl});
|
|
51
|
+
logger.info("Added remote signer", {pubkey, url: printableUrl});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const externalPubkeysSet = new Set(externalPubkeys);
|
|
56
|
+
for (const pubkey of localPubkeys) {
|
|
57
|
+
if (!externalPubkeysSet.has(pubkey)) {
|
|
58
|
+
validatorStore.removeSigner(pubkey);
|
|
59
|
+
logger.info("Removed remote signer", {pubkey, url: printableUrl});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch (e) {
|
|
63
|
+
logger.error("Failed to fetch public keys from external signer", {url: printableUrl}, e as Error);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const interval = setInterval(
|
|
68
|
+
fetchExternalSignerPubkeys,
|
|
69
|
+
externalSigner.fetchInterval ??
|
|
70
|
+
// Once per epoch by default
|
|
71
|
+
SLOTS_PER_EPOCH * config.SECONDS_PER_SLOT * 1000
|
|
72
|
+
);
|
|
73
|
+
signal.addEventListener("abort", () => clearInterval(interval), {once: true});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function assertValidPubkeysHex(pubkeysHex: string[]): void {
|
|
77
|
+
for (const pubkeyHex of pubkeysHex) {
|
|
78
|
+
const pubkeyBytes = fromHex(pubkeyHex);
|
|
79
|
+
PublicKey.fromBytes(pubkeyBytes, true);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {ApiClient, routes} from "@lodestar/api";
|
|
2
|
+
import {ValidatorIndex} from "@lodestar/types";
|
|
3
|
+
import {Logger, MapDef, toPubkeyHex} from "@lodestar/utils";
|
|
4
|
+
import {Metrics} from "../metrics.js";
|
|
5
|
+
import {batchItems} from "../util/index.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This is to prevent the "Request body is too large" issue for http post.
|
|
9
|
+
* Typical servers accept up to 1MB (2 ** 20 bytes) of request body, for example fastify and nginx.
|
|
10
|
+
* A hex encoded public key with "0x"-prefix has a size of 98 bytes + 2 bytes to account for commas
|
|
11
|
+
* and other JSON padding. `Math.floor(2 ** 20 / 100) == 10485`, we can send up to ~10k keys per request.
|
|
12
|
+
*/
|
|
13
|
+
const PUBKEYS_PER_REQUEST = 10_000;
|
|
14
|
+
|
|
15
|
+
// To assist with readability
|
|
16
|
+
type PubkeyHex = string;
|
|
17
|
+
|
|
18
|
+
// To assist with logging statuses, we only log the statuses that are not active_exiting or withdrawal_possible
|
|
19
|
+
type SimpleValidatorStatus = "pending" | "active" | "exited" | "withdrawn";
|
|
20
|
+
|
|
21
|
+
const statusToSimpleStatusMapping = (status: routes.beacon.ValidatorStatus): SimpleValidatorStatus => {
|
|
22
|
+
switch (status) {
|
|
23
|
+
case "active_exiting":
|
|
24
|
+
case "active_slashed":
|
|
25
|
+
case "active_ongoing":
|
|
26
|
+
return "active";
|
|
27
|
+
|
|
28
|
+
case "withdrawal_possible":
|
|
29
|
+
case "exited_slashed":
|
|
30
|
+
case "exited_unslashed":
|
|
31
|
+
return "exited";
|
|
32
|
+
|
|
33
|
+
case "pending_initialized":
|
|
34
|
+
case "pending_queued":
|
|
35
|
+
return "pending";
|
|
36
|
+
|
|
37
|
+
case "withdrawal_done":
|
|
38
|
+
return "withdrawn";
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export class IndicesService {
|
|
43
|
+
readonly index2pubkey = new Map<ValidatorIndex, PubkeyHex>();
|
|
44
|
+
/** Indexed by pubkey in hex 0x prefixed */
|
|
45
|
+
readonly pubkey2index = new Map<PubkeyHex, ValidatorIndex>();
|
|
46
|
+
// Request indices once
|
|
47
|
+
private pollValidatorIndicesPromise: Promise<ValidatorIndex[]> | null = null;
|
|
48
|
+
|
|
49
|
+
constructor(
|
|
50
|
+
private readonly logger: Logger,
|
|
51
|
+
private readonly api: ApiClient,
|
|
52
|
+
private readonly metrics: Metrics | null
|
|
53
|
+
) {
|
|
54
|
+
if (metrics) {
|
|
55
|
+
metrics.indices.addCollect(() => metrics.indices.set(this.index2pubkey.size));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get indexCount(): number {
|
|
60
|
+
return this.index2pubkey.size;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Returns the validator index for a given validator pubkey */
|
|
64
|
+
getValidatorIndex(pubKey: PubkeyHex): ValidatorIndex | undefined {
|
|
65
|
+
return this.pubkey2index.get(pubKey);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Return all known indices from the validatorStore pubkeys */
|
|
69
|
+
getAllLocalIndices(): ValidatorIndex[] {
|
|
70
|
+
return Array.from(this.index2pubkey.keys());
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Return true if `index` is active part of this validator client */
|
|
74
|
+
hasValidatorIndex(index: ValidatorIndex): boolean {
|
|
75
|
+
return this.index2pubkey.has(index);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async pollValidatorIndices(pubkeysHex: PubkeyHex[]): Promise<ValidatorIndex[]> {
|
|
79
|
+
// Ensures pollValidatorIndicesInternal() is not called more than once at the same time.
|
|
80
|
+
// AttestationDutiesService, SyncCommitteeDutiesService and DoppelgangerService will call this function at the same time, so this will
|
|
81
|
+
// cache the promise and return it to the second caller, preventing calling the API twice for the same data.
|
|
82
|
+
if (this.pollValidatorIndicesPromise) {
|
|
83
|
+
return this.pollValidatorIndicesPromise;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.pollValidatorIndicesPromise = this.pollValidatorIndicesInternal(pubkeysHex).finally(() => {
|
|
87
|
+
// Once the pollValidatorIndicesInternal() resolves or rejects null the cached promise so it can be called again.
|
|
88
|
+
this.pollValidatorIndicesPromise = null;
|
|
89
|
+
});
|
|
90
|
+
return this.pollValidatorIndicesPromise;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
removeForKey(pubkey: PubkeyHex): boolean {
|
|
94
|
+
for (const [index, value] of this.index2pubkey) {
|
|
95
|
+
if (value === pubkey) {
|
|
96
|
+
this.index2pubkey.delete(index);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return this.pubkey2index.delete(pubkey);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Iterate through all the voting pubkeys in the `ValidatorStore` and attempt to learn any unknown
|
|
103
|
+
validator indices. Returns the new discovered indexes */
|
|
104
|
+
private async pollValidatorIndicesInternal(pubkeysHex: PubkeyHex[]): Promise<ValidatorIndex[]> {
|
|
105
|
+
const pubkeysHexToDiscover = pubkeysHex.filter((pubkey) => !this.pubkey2index.has(pubkey));
|
|
106
|
+
|
|
107
|
+
if (pubkeysHexToDiscover.length === 0) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Query the remote BN to resolve a pubkey to a validator index.
|
|
112
|
+
// support up to 10k pubkeys per poll
|
|
113
|
+
const pubkeysHexBatches = batchItems(pubkeysHexToDiscover, {batchSize: PUBKEYS_PER_REQUEST});
|
|
114
|
+
|
|
115
|
+
const newIndices: number[] = [];
|
|
116
|
+
for (const pubkeysHexBatch of pubkeysHexBatches) {
|
|
117
|
+
const validatorIndicesArr = await this.fetchValidatorIndices(pubkeysHexBatch);
|
|
118
|
+
newIndices.push(...validatorIndicesArr);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.metrics?.discoveredIndices.inc(newIndices.length);
|
|
122
|
+
|
|
123
|
+
return newIndices;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private async fetchValidatorIndices(pubkeysHex: string[]): Promise<ValidatorIndex[]> {
|
|
127
|
+
const validators = (await this.api.beacon.postStateValidators({stateId: "head", validatorIds: pubkeysHex})).value();
|
|
128
|
+
|
|
129
|
+
const newIndices = [];
|
|
130
|
+
|
|
131
|
+
const allValidatorStatuses = new MapDef<SimpleValidatorStatus, number>(() => 0);
|
|
132
|
+
|
|
133
|
+
for (const validator of validators) {
|
|
134
|
+
// Group all validators by status
|
|
135
|
+
const status = statusToSimpleStatusMapping(validator.status);
|
|
136
|
+
allValidatorStatuses.set(status, allValidatorStatuses.getOrDefault(status) + 1);
|
|
137
|
+
|
|
138
|
+
const pubkeyHex = toPubkeyHex(validator.validator.pubkey);
|
|
139
|
+
if (!this.pubkey2index.has(pubkeyHex)) {
|
|
140
|
+
this.logger.info("Validator seen on beacon chain", {
|
|
141
|
+
validatorIndex: validator.index,
|
|
142
|
+
pubKey: pubkeyHex,
|
|
143
|
+
});
|
|
144
|
+
this.pubkey2index.set(pubkeyHex, validator.index);
|
|
145
|
+
this.index2pubkey.set(validator.index, pubkeyHex);
|
|
146
|
+
newIndices.push(validator.index);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// The number of validators that are not in the beacon chain
|
|
151
|
+
const pendingCount = pubkeysHex.length - validators.length;
|
|
152
|
+
|
|
153
|
+
allValidatorStatuses.set("pending", allValidatorStatuses.getOrDefault("pending") + pendingCount);
|
|
154
|
+
|
|
155
|
+
// Retrieve the number of validators for each status
|
|
156
|
+
const statuses = Object.fromEntries(Array.from(allValidatorStatuses.entries()).filter((entry) => entry[1] > 0));
|
|
157
|
+
|
|
158
|
+
// The total number of validators
|
|
159
|
+
const total = pubkeysHex.length;
|
|
160
|
+
|
|
161
|
+
this.logger.info("Validator statuses", {...statuses, total});
|
|
162
|
+
|
|
163
|
+
return newIndices;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {ApiClient, routes} from "@lodestar/api";
|
|
2
|
+
import {BeaconConfig} from "@lodestar/config";
|
|
3
|
+
import {GENESIS_EPOCH, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
4
|
+
import {Epoch, bellatrix} from "@lodestar/types";
|
|
5
|
+
import {Metrics} from "../metrics.js";
|
|
6
|
+
import {IClock, LoggerVc, batchItems} from "../util/index.js";
|
|
7
|
+
import {ValidatorStore} from "./validatorStore.js";
|
|
8
|
+
|
|
9
|
+
const REGISTRATION_CHUNK_SIZE = 512;
|
|
10
|
+
/**
|
|
11
|
+
* This service is responsible for registering validators to beacon node with the
|
|
12
|
+
* proposer data (currently `feeRecipient`) so that it can issue advance fcUs to
|
|
13
|
+
* the engine for building execution payload with transactions.
|
|
14
|
+
*
|
|
15
|
+
* This needs to be done every epoch because the BN will cache it at most for
|
|
16
|
+
* two epochs.
|
|
17
|
+
*/
|
|
18
|
+
export function pollPrepareBeaconProposer(
|
|
19
|
+
config: BeaconConfig,
|
|
20
|
+
logger: LoggerVc,
|
|
21
|
+
api: ApiClient,
|
|
22
|
+
clock: IClock,
|
|
23
|
+
validatorStore: ValidatorStore,
|
|
24
|
+
_metrics: Metrics | null
|
|
25
|
+
): void {
|
|
26
|
+
async function prepareBeaconProposer(epoch: Epoch): Promise<void> {
|
|
27
|
+
// Before bellatrix we don't need to update this data on bn/builder
|
|
28
|
+
if (epoch < config.BELLATRIX_FORK_EPOCH - 1) return;
|
|
29
|
+
|
|
30
|
+
// prepareBeaconProposer is not as time sensitive as attesting.
|
|
31
|
+
// Poll indices first, then call api.validator.prepareBeaconProposer once
|
|
32
|
+
await validatorStore.pollValidatorIndices().catch((e: Error) => {
|
|
33
|
+
logger.error("Error on pollValidatorIndices for prepareBeaconProposer", {epoch}, e);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const indicesChunks = batchItems(validatorStore.getAllLocalIndices(), {batchSize: REGISTRATION_CHUNK_SIZE});
|
|
37
|
+
|
|
38
|
+
for (const indices of indicesChunks) {
|
|
39
|
+
try {
|
|
40
|
+
const proposers = indices.map(
|
|
41
|
+
(index): routes.validator.ProposerPreparationData => ({
|
|
42
|
+
validatorIndex: index,
|
|
43
|
+
feeRecipient: validatorStore.getFeeRecipientByIndex(index),
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
(await api.validator.prepareBeaconProposer({proposers})).assertOk();
|
|
47
|
+
logger.debug("Registered proposers with beacon node", {epoch, count: proposers.length});
|
|
48
|
+
} catch (e) {
|
|
49
|
+
logger.error("Failed to register proposers with beacon node", {epoch}, e as Error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
clock.runEveryEpoch(prepareBeaconProposer);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* This service is responsible for registering validators with the mev builder as they
|
|
59
|
+
* might prepare and keep ready the execution payloads of just registered validators.
|
|
60
|
+
*
|
|
61
|
+
* This needs to be done every epoch because the builder(s) will cache it at most for
|
|
62
|
+
* two epochs.
|
|
63
|
+
*/
|
|
64
|
+
export function pollBuilderValidatorRegistration(
|
|
65
|
+
config: BeaconConfig,
|
|
66
|
+
logger: LoggerVc,
|
|
67
|
+
api: ApiClient,
|
|
68
|
+
clock: IClock,
|
|
69
|
+
validatorStore: ValidatorStore,
|
|
70
|
+
_metrics: Metrics | null
|
|
71
|
+
): void {
|
|
72
|
+
async function registerValidator(epoch: Epoch): Promise<void> {
|
|
73
|
+
// Don't send validator registrations pre-genesis as mev-boost-relay will reject
|
|
74
|
+
// those registrations anyways if timestamp is before genesis time and we wanna
|
|
75
|
+
// avoid caching and re-sending them in subsequent requests
|
|
76
|
+
if (epoch < GENESIS_EPOCH) return;
|
|
77
|
+
|
|
78
|
+
// Before bellatrix we don't need to update this data on bn/builder
|
|
79
|
+
if (epoch < config.BELLATRIX_FORK_EPOCH - 1) return;
|
|
80
|
+
const slot = epoch * SLOTS_PER_EPOCH;
|
|
81
|
+
|
|
82
|
+
// registerValidator is not as time sensitive as attesting.
|
|
83
|
+
// Poll indices first, then call api.validator.registerValidator once
|
|
84
|
+
await validatorStore.pollValidatorIndices().catch((e: Error) => {
|
|
85
|
+
logger.error("Error on pollValidatorIndices for registerValidator", {epoch}, e);
|
|
86
|
+
});
|
|
87
|
+
const pubkeyHexes = validatorStore
|
|
88
|
+
.getAllLocalIndices()
|
|
89
|
+
.map((index) => validatorStore.getPubkeyOfIndex(index))
|
|
90
|
+
.filter(
|
|
91
|
+
(pubkeyHex): pubkeyHex is string =>
|
|
92
|
+
pubkeyHex !== undefined &&
|
|
93
|
+
validatorStore.getBuilderSelectionParams(pubkeyHex).selection !==
|
|
94
|
+
routes.validator.BuilderSelection.ExecutionOnly
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
if (pubkeyHexes.length > 0) {
|
|
98
|
+
const pubkeyHexesChunks = batchItems(pubkeyHexes, {batchSize: REGISTRATION_CHUNK_SIZE});
|
|
99
|
+
|
|
100
|
+
for (const pubkeyHexes of pubkeyHexesChunks) {
|
|
101
|
+
try {
|
|
102
|
+
const registrations = await Promise.all(
|
|
103
|
+
pubkeyHexes.map((pubkeyHex): Promise<bellatrix.SignedValidatorRegistrationV1> => {
|
|
104
|
+
const feeRecipient = validatorStore.getFeeRecipient(pubkeyHex);
|
|
105
|
+
const gasLimit = validatorStore.getGasLimit(pubkeyHex);
|
|
106
|
+
return validatorStore.getValidatorRegistration(pubkeyHex, {feeRecipient, gasLimit}, slot);
|
|
107
|
+
})
|
|
108
|
+
);
|
|
109
|
+
(await api.validator.registerValidator({registrations})).assertOk();
|
|
110
|
+
logger.info("Published validator registrations to builder", {epoch, count: registrations.length});
|
|
111
|
+
} catch (e) {
|
|
112
|
+
logger.error("Failed to publish validator registrations to builder", {epoch}, e as Error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
clock.runEveryEpoch(registerValidator);
|
|
119
|
+
}
|