@lodestar/builder 1.47.0-dev.b2cbfa50c2 → 1.47.0-dev.cec7fd63e3
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/builder.d.ts +10 -1
- package/lib/builder.d.ts.map +1 -1
- package/lib/builder.js +31 -26
- package/lib/builder.js.map +1 -1
- package/lib/defaults.d.ts +1 -0
- package/lib/defaults.d.ts.map +1 -1
- package/lib/defaults.js +1 -0
- package/lib/defaults.js.map +1 -1
- package/lib/genesis.d.ts +1 -1
- package/lib/genesis.d.ts.map +1 -1
- package/lib/genesis.js.map +1 -1
- package/lib/identity.d.ts +10 -0
- package/lib/identity.d.ts.map +1 -0
- package/lib/identity.js +72 -0
- package/lib/identity.js.map +1 -0
- package/lib/readiness.d.ts +5 -0
- package/lib/readiness.d.ts.map +1 -0
- package/lib/readiness.js +55 -0
- package/lib/readiness.js.map +1 -0
- package/lib/services/builderStatusTracker.d.ts +21 -0
- package/lib/services/builderStatusTracker.d.ts.map +1 -0
- package/lib/services/builderStatusTracker.js +35 -0
- package/lib/services/builderStatusTracker.js.map +1 -0
- package/package.json +10 -10
- package/src/builder.ts +38 -34
- package/src/defaults.ts +1 -0
- package/src/genesis.ts +1 -1
- package/src/identity.ts +100 -0
- package/src/readiness.ts +66 -0
- package/src/services/builderStatusTracker.ts +42 -0
package/lib/builder.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { ApiClient } from "@lodestar/api";
|
|
2
2
|
import { ChainForkConfig } from "@lodestar/config";
|
|
3
3
|
import { ClockOptions, IClock } from "@lodestar/state-transition";
|
|
4
|
+
import { BuilderIndex, ExecutionAddress } from "@lodestar/types";
|
|
4
5
|
import { Logger } from "@lodestar/utils";
|
|
5
6
|
import { BuilderSigner, Keypair } from "./services/builderSigner.js";
|
|
7
|
+
import { BuilderStatusTracker } from "./services/builderStatusTracker.js";
|
|
6
8
|
export type BuilderModules = {
|
|
7
9
|
opts: BuilderOptions;
|
|
8
10
|
builderSigner: BuilderSigner;
|
|
11
|
+
builderStatusTracker: BuilderStatusTracker;
|
|
9
12
|
clock: IClock;
|
|
13
|
+
index: BuilderIndex;
|
|
10
14
|
};
|
|
11
15
|
export type BuilderOptions = {
|
|
12
16
|
logger: Logger;
|
|
@@ -15,15 +19,20 @@ export type BuilderOptions = {
|
|
|
15
19
|
abortController: AbortController;
|
|
16
20
|
api: ApiClient;
|
|
17
21
|
clock?: ClockOptions;
|
|
22
|
+
executionFeeRecipient: ExecutionAddress;
|
|
18
23
|
};
|
|
19
24
|
/**
|
|
20
25
|
* Main class for the Builder client.
|
|
21
26
|
*/
|
|
22
27
|
export declare class Builder {
|
|
23
28
|
readonly builderSigner: BuilderSigner;
|
|
29
|
+
private readonly builderStatusTracker;
|
|
24
30
|
private readonly controller;
|
|
25
31
|
private readonly clock;
|
|
26
|
-
|
|
32
|
+
private readonly index;
|
|
33
|
+
private readonly logger;
|
|
34
|
+
private readonly executionFeeRecipient;
|
|
35
|
+
constructor({ opts, builderSigner, builderStatusTracker, clock, index }: BuilderModules);
|
|
27
36
|
static init(opts: BuilderOptions): Promise<Builder>;
|
|
28
37
|
close(): Promise<void>;
|
|
29
38
|
}
|
package/lib/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,eAAe,CAAC;AACxC,OAAO,EAAC,eAAe,EAAwC,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAQ,YAAY,EAAE,MAAM,EAAC,MAAM,4BAA4B,CAAC;AACvE,OAAO,EAAC,YAAY,EAAE,gBAAgB,EAAC,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAC,MAAM,EAAmB,MAAM,iBAAiB,CAAC;AAIzD,OAAO,EAAC,aAAa,EAAE,OAAO,EAAC,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAC,oBAAoB,EAAC,MAAM,oCAAoC,CAAC;AAExE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,aAAa,EAAE,aAAa,CAAC;IAC7B,oBAAoB,EAAE,oBAAoB,CAAC;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,eAAe,CAAC;IACjC,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,qBAAqB,EAAE,gBAAgB,CAAC;CACzC,CAAC;AAEF;;GAEG;AACH,qBAAa,OAAO;IAClB,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAuB;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAmB;IAEzD,YAAY,EAAC,IAAI,EAAE,aAAa,EAAE,oBAAoB,EAAE,KAAK,EAAE,KAAK,EAAC,EAAE,cAAc,EAiBpF;IAED,OAAa,IAAI,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAwBxD;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;CACF"}
|
package/lib/builder.js
CHANGED
|
@@ -1,49 +1,54 @@
|
|
|
1
1
|
import { assertEqualParams, createBeaconConfig } from "@lodestar/config";
|
|
2
|
-
import { PAYLOAD_BUILDER_VERSION } from "@lodestar/params";
|
|
3
2
|
import { Clock } from "@lodestar/state-transition";
|
|
3
|
+
import { toHex, toRootHex } from "@lodestar/utils";
|
|
4
4
|
import { waitForGenesis } from "./genesis.js";
|
|
5
|
+
import { resolveBuilderIdentity } from "./identity.js";
|
|
6
|
+
import { logNodeVersion, waitForNodeReady } from "./readiness.js";
|
|
5
7
|
import { BuilderSigner } from "./services/builderSigner.js";
|
|
8
|
+
import { BuilderStatusTracker } from "./services/builderStatusTracker.js";
|
|
6
9
|
/**
|
|
7
10
|
* Main class for the Builder client.
|
|
8
11
|
*/
|
|
9
12
|
export class Builder {
|
|
10
13
|
builderSigner;
|
|
14
|
+
builderStatusTracker;
|
|
11
15
|
controller;
|
|
12
16
|
clock;
|
|
13
|
-
|
|
17
|
+
index;
|
|
18
|
+
logger;
|
|
19
|
+
executionFeeRecipient;
|
|
20
|
+
constructor({ opts, builderSigner, builderStatusTracker, clock, index }) {
|
|
14
21
|
this.builderSigner = builderSigner;
|
|
22
|
+
this.builderStatusTracker = builderStatusTracker;
|
|
15
23
|
this.clock = clock;
|
|
16
24
|
this.controller = opts.abortController;
|
|
25
|
+
this.logger = opts.logger;
|
|
26
|
+
this.index = index;
|
|
27
|
+
this.executionFeeRecipient = opts.executionFeeRecipient;
|
|
28
|
+
this.clock.runEveryEpoch(() => this.builderStatusTracker.poll());
|
|
17
29
|
this.clock.start(this.controller.signal);
|
|
30
|
+
this.logger.info("Builder client initialized", {
|
|
31
|
+
index: this.index,
|
|
32
|
+
executionFeeRecipient: toHex(this.executionFeeRecipient),
|
|
33
|
+
});
|
|
18
34
|
}
|
|
19
35
|
static async init(opts) {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
const { api, logger } = opts;
|
|
37
|
+
const genesis = await waitForGenesis(api, logger, opts.abortController.signal);
|
|
38
|
+
logger.info("Genesis fetched from the beacon node", {
|
|
39
|
+
genesisValidatorsRoot: toRootHex(genesis.genesisValidatorsRoot),
|
|
40
|
+
});
|
|
41
|
+
const specRes = await api.config.getSpec();
|
|
23
42
|
assertEqualParams(opts.config, specRes.value());
|
|
24
|
-
|
|
43
|
+
logger.info("Verified connected beacon node and builder have the same config");
|
|
25
44
|
const config = createBeaconConfig(opts.config, genesis.genesisValidatorsRoot);
|
|
26
45
|
const builderSigner = new BuilderSigner(config, opts.keypair);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
const builders = builderRes.value();
|
|
35
|
-
if (builders.length === 0) {
|
|
36
|
-
throw Error(`Builder not registered: ${builderSigner.getPubkeyHex()}`);
|
|
37
|
-
}
|
|
38
|
-
const builderStatus = builders[0];
|
|
39
|
-
if (builderStatus.status !== "active") {
|
|
40
|
-
throw Error(`Builder not active: ${builderStatus.status}`);
|
|
41
|
-
}
|
|
42
|
-
if (builderStatus.builder.version !== PAYLOAD_BUILDER_VERSION) {
|
|
43
|
-
throw Error(`Builder version mismatch: got ${builderStatus.builder.version}, expected ${PAYLOAD_BUILDER_VERSION}`);
|
|
44
|
-
}
|
|
45
|
-
const clock = new Clock(config, opts.logger, { genesisTime: Number(genesis.genesisTime), ...opts.clock });
|
|
46
|
-
return new Builder({ opts, builderSigner, clock });
|
|
46
|
+
await waitForNodeReady(api, logger, opts.abortController.signal);
|
|
47
|
+
await logNodeVersion(api, logger);
|
|
48
|
+
const index = await resolveBuilderIdentity(api, logger, builderSigner.getPubkeyHex(), opts.abortController.signal);
|
|
49
|
+
const clock = new Clock(config, logger, { genesisTime: Number(genesis.genesisTime), ...opts.clock });
|
|
50
|
+
const builderStatusTracker = new BuilderStatusTracker(api, logger, index);
|
|
51
|
+
return new Builder({ opts, builderSigner, builderStatusTracker, clock, index });
|
|
47
52
|
}
|
|
48
53
|
async close() {
|
|
49
54
|
this.controller.abort();
|
package/lib/builder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,iBAAiB,EAAE,kBAAkB,EAAC,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAC,
|
|
1
|
+
{"version":3,"file":"builder.js","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/lib/defaults.d.ts
CHANGED
package/lib/defaults.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;IAEzB,aAAa;
|
|
1
|
+
{"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc;IAEzB,aAAa;IACb,cAAc;CACf,CAAC"}
|
package/lib/defaults.js
CHANGED
package/lib/defaults.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,6CAA6C;IAC7C,aAAa,EAAE,uBAAuB;
|
|
1
|
+
{"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,6CAA6C;IAC7C,aAAa,EAAE,uBAAuB;IACtC,cAAc,EAAE,MAAM;CACvB,CAAC"}
|
package/lib/genesis.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiClient } from "@lodestar/api";
|
|
2
2
|
import { Genesis } from "@lodestar/types/phase0";
|
|
3
3
|
import { Logger } from "@lodestar/utils";
|
|
4
|
-
export declare function waitForGenesis(api: ApiClient, logger: Logger, signal
|
|
4
|
+
export declare function waitForGenesis(api: ApiClient, logger: Logger, signal: AbortSignal): Promise<Genesis>;
|
|
5
5
|
//# sourceMappingURL=genesis.d.ts.map
|
package/lib/genesis.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"genesis.d.ts","sourceRoot":"","sources":["../src/genesis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAA2B,MAAM,eAAe,CAAC;AAClE,OAAO,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAC,MAAM,EAAQ,MAAM,iBAAiB,CAAC;AAK9C,wBAAsB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"genesis.d.ts","sourceRoot":"","sources":["../src/genesis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAA2B,MAAM,eAAe,CAAC;AAClE,OAAO,EAAC,OAAO,EAAC,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAC,MAAM,EAAQ,MAAM,iBAAiB,CAAC;AAK9C,wBAAsB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAa1G"}
|
package/lib/genesis.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"genesis.js","sourceRoot":"","sources":["../src/genesis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,QAAQ,EAAE,cAAc,EAAC,MAAM,eAAe,CAAC;AAElE,OAAO,EAAS,KAAK,EAAC,MAAM,iBAAiB,CAAC;AAE9C,sDAAsD;AACtD,MAAM,2BAA2B,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAc,EAAE,MAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"genesis.js","sourceRoot":"","sources":["../src/genesis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,QAAQ,EAAE,cAAc,EAAC,MAAM,eAAe,CAAC;AAElE,OAAO,EAAS,KAAK,EAAC,MAAM,iBAAiB,CAAC;AAE9C,sDAAsD;AACtD,MAAM,2BAA2B,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAc,EAAE,MAAc,EAAE,MAAmB;IACtF,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACjD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,YAAY,QAAQ,IAAI,CAAC,CAAC,MAAM,KAAK,cAAc,CAAC,SAAS,EAAE,CAAC;gBACnE,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAC,CAAC,CAAC;YAC3D,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAC,OAAO,EAAG,CAAW,CAAC,OAAO,EAAC,CAAC,CAAC;YAC1E,CAAC;YACD,MAAM,KAAK,CAAC,2BAA2B,EAAE,MAAM,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ApiClient, routes } from "@lodestar/api";
|
|
2
|
+
import { BuilderIndex, BuilderStatus } from "@lodestar/types";
|
|
3
|
+
import { Logger } from "@lodestar/utils";
|
|
4
|
+
export declare const WAITING_FOR_BUILDER_POLL_MS: number;
|
|
5
|
+
export declare function resolveBuilderIdentity(api: ApiClient, logger: Logger, id: routes.beacon.BuilderId, signal: AbortSignal): Promise<BuilderIndex>;
|
|
6
|
+
export declare function getBuilderStatus(api: ApiClient, logger: Logger, id: routes.beacon.BuilderId): Promise<{
|
|
7
|
+
status: BuilderStatus;
|
|
8
|
+
balance: number;
|
|
9
|
+
} | null>;
|
|
10
|
+
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAC,YAAY,EAAE,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAC,MAAM,EAAe,MAAM,iBAAiB,CAAC;AAErD,eAAO,MAAM,2BAA2B,QAAY,CAAC;AAErD,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,EAC3B,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,YAAY,CAAC,CAevB;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,GAC1B,OAAO,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAC,GAAG,IAAI,CAAC,CAe1D"}
|
package/lib/identity.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { PAYLOAD_BUILDER_VERSION } from "@lodestar/params";
|
|
2
|
+
import { sleep, toHex } from "@lodestar/utils";
|
|
3
|
+
export const WAITING_FOR_BUILDER_POLL_MS = 10 * 1000;
|
|
4
|
+
export async function resolveBuilderIdentity(api, logger, id, signal) {
|
|
5
|
+
const builderEntry = await waitForBuilder(api, logger, id, signal);
|
|
6
|
+
if (builderEntry.builder.version !== PAYLOAD_BUILDER_VERSION) {
|
|
7
|
+
throw Error(`Builder version mismatch: got ${builderEntry.builder.version}, expected ${PAYLOAD_BUILDER_VERSION}`);
|
|
8
|
+
}
|
|
9
|
+
logger.info("Builder identity resolved", {
|
|
10
|
+
index: builderEntry.index,
|
|
11
|
+
status: builderEntry.status,
|
|
12
|
+
balanceGwei: builderEntry.builder.balance,
|
|
13
|
+
executionAddress: toHex(builderEntry.builder.executionAddress),
|
|
14
|
+
});
|
|
15
|
+
return builderEntry.index;
|
|
16
|
+
}
|
|
17
|
+
export async function getBuilderStatus(api, logger, id) {
|
|
18
|
+
try {
|
|
19
|
+
const builderEntry = await fetchBuilder(api, id);
|
|
20
|
+
if (builderEntry) {
|
|
21
|
+
return {
|
|
22
|
+
status: builderEntry.status,
|
|
23
|
+
balance: builderEntry.builder.balance,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
logger.warn("Builder status not available in beacon node");
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
logger.warn("Couldn't fetch the builder", {}, e);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function waitForBuilder(api, logger, id, signal) {
|
|
35
|
+
while (true) {
|
|
36
|
+
const builder = await fetchBuilder(api, id);
|
|
37
|
+
if (builder?.status === "active") {
|
|
38
|
+
return builder;
|
|
39
|
+
}
|
|
40
|
+
if (builder?.status === "exited") {
|
|
41
|
+
throw Error(`Builder exited: id=${id}`);
|
|
42
|
+
}
|
|
43
|
+
if (builder?.status === "pending") {
|
|
44
|
+
logger.info("Waiting for builder deposit to be finalized", { id });
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
logger.info("Waiting for builder to be known to the beacon node", { id });
|
|
48
|
+
}
|
|
49
|
+
await sleep(WAITING_FOR_BUILDER_POLL_MS, signal);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function fetchBuilder(api, id) {
|
|
53
|
+
const builderRes = await api.beacon.getStateBuilders({
|
|
54
|
+
stateId: "head",
|
|
55
|
+
builderIds: [id],
|
|
56
|
+
});
|
|
57
|
+
const builders = builderRes.value();
|
|
58
|
+
if (builders.length === 0) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const builder = builders[0];
|
|
62
|
+
if (typeof id === "number") {
|
|
63
|
+
if (id !== builder.index) {
|
|
64
|
+
throw Error(`Index mismatch: got=${builder.index} expected=${id}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (id !== toHex(builder.builder.pubkey)) {
|
|
68
|
+
throw Error(`Pubkey mismatch: got=${toHex(builder.builder.pubkey)} expected=${id}`);
|
|
69
|
+
}
|
|
70
|
+
return builder;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../src/identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ApiClient } from "@lodestar/api";
|
|
2
|
+
import { Logger } from "@lodestar/utils";
|
|
3
|
+
export declare function waitForNodeReady(api: ApiClient, logger: Logger, signal: AbortSignal): Promise<void>;
|
|
4
|
+
export declare function logNodeVersion(api: ApiClient, logger: Logger): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,eAAe,CAAC;AACxC,OAAO,EAAC,MAAM,EAAQ,MAAM,iBAAiB,CAAC;AAK9C,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzG;AA0CD,wBAAsB,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAalF"}
|
package/lib/readiness.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { sleep } from "@lodestar/utils";
|
|
2
|
+
/** The time between polls when waiting for BN to be ready */
|
|
3
|
+
const WAITING_FOR_NODE_READY_POLL_MS = 5 * 1000;
|
|
4
|
+
export async function waitForNodeReady(api, logger, signal) {
|
|
5
|
+
while (!(await isNodeReady(api, logger))) {
|
|
6
|
+
await sleep(WAITING_FOR_NODE_READY_POLL_MS, signal);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
async function isNodeReady(api, logger) {
|
|
10
|
+
try {
|
|
11
|
+
const syncingStatusRes = await api.node.getSyncingStatus();
|
|
12
|
+
if (!syncingStatusRes.ok) {
|
|
13
|
+
logger.warn("Cannot get node sync status", {
|
|
14
|
+
status: syncingStatusRes.status,
|
|
15
|
+
message: syncingStatusRes.error()?.message,
|
|
16
|
+
});
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const syncingStatus = syncingStatusRes.value();
|
|
20
|
+
if (syncingStatus.isSyncing || syncingStatus.elOffline) {
|
|
21
|
+
logger.info(syncingStatus.elOffline ? "Beacon node EL is offline, unable to submit bids" : "Beacon node is not ready yet", {
|
|
22
|
+
headSlot: syncingStatus.headSlot,
|
|
23
|
+
syncDistance: syncingStatus.syncDistance,
|
|
24
|
+
elOffline: syncingStatus.elOffline,
|
|
25
|
+
});
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (syncingStatus.isOptimistic) {
|
|
29
|
+
logger.warn("Beacon node head is optimistic, execution payloads are not yet verified - unable to submit bids");
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
logger.info("Beacon node is ready", { headSlot: syncingStatus.headSlot });
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
logger.warn("Cannot reach the beacon node", {}, e);
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function logNodeVersion(api, logger) {
|
|
41
|
+
try {
|
|
42
|
+
const versionRes = await api.node.getNodeVersionV2();
|
|
43
|
+
const version = versionRes.value();
|
|
44
|
+
logger.info("Connected node version", {
|
|
45
|
+
beaconNode: `${version.beaconNode.name}/${version.beaconNode.version}`,
|
|
46
|
+
executionClient: version.executionClient
|
|
47
|
+
? `${version.executionClient.name}/${version.executionClient.version}`
|
|
48
|
+
: "unknown",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
logger.warn("Failed to get node version", {}, e);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readiness.js","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,KAAK,EAAC,MAAM,iBAAiB,CAAC;AAE9C,6DAA6D;AAC7D,MAAM,8BAA8B,GAAG,CAAC,GAAG,IAAI,CAAC;AAEhD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAc,EAAE,MAAc,EAAE,MAAmB;IACxF,OAAO,CAAC,CAAC,MAAM,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;QACzC,MAAM,KAAK,CAAC,8BAA8B,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,GAAc,EAAE,MAAc;IACvD,IAAI,CAAC;QACH,MAAM,gBAAgB,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE3D,IAAI,CAAC,gBAAgB,CAAC,EAAE,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE;gBACzC,MAAM,EAAE,gBAAgB,CAAC,MAAM;gBAC/B,OAAO,EAAE,gBAAgB,CAAC,KAAK,EAAE,EAAE,OAAO;aAC3C,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,aAAa,GAAG,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAE/C,IAAI,aAAa,CAAC,SAAS,IAAI,aAAa,CAAC,SAAS,EAAE,CAAC;YACvD,MAAM,CAAC,IAAI,CACT,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,kDAAkD,CAAC,CAAC,CAAC,8BAA8B,EAC7G;gBACE,QAAQ,EAAE,aAAa,CAAC,QAAQ;gBAChC,YAAY,EAAE,aAAa,CAAC,YAAY;gBACxC,SAAS,EAAE,aAAa,CAAC,SAAS;aACnC,CACF,CAAC;YACF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,aAAa,CAAC,YAAY,EAAE,CAAC;YAC/B,MAAM,CAAC,IAAI,CAAC,iGAAiG,CAAC,CAAC;YAC/G,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,EAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,EAAC,CAAC,CAAC;QAExE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,EAAE,CAAU,CAAC,CAAC;QAC5D,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAc,EAAE,MAAc;IACjE,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACrD,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE;YACpC,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE;YACtE,eAAe,EAAE,OAAO,CAAC,eAAe;gBACtC,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,IAAI,IAAI,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE;gBACtE,CAAC,CAAC,SAAS;SACd,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,EAAE,CAAU,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ApiClient } from "@lodestar/api";
|
|
2
|
+
import { BuilderIndex, BuilderStatus } from "@lodestar/types";
|
|
3
|
+
import { Logger } from "@lodestar/utils";
|
|
4
|
+
/**
|
|
5
|
+
* Service for tracking builder status.
|
|
6
|
+
* Provides regular builder status and balance updates for operator diagnostics.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BuilderStatusTracker {
|
|
9
|
+
private readonly api;
|
|
10
|
+
private readonly logger;
|
|
11
|
+
private readonly index;
|
|
12
|
+
private status?;
|
|
13
|
+
private balanceGwei?;
|
|
14
|
+
constructor(api: ApiClient, logger: Logger, index: BuilderIndex);
|
|
15
|
+
poll(): Promise<void>;
|
|
16
|
+
getStatus(): {
|
|
17
|
+
status: BuilderStatus | undefined;
|
|
18
|
+
balance: number | undefined;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=builderStatusTracker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builderStatusTracker.d.ts","sourceRoot":"","sources":["../../src/services/builderStatusTracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,eAAe,CAAC;AACxC,OAAO,EAAC,YAAY,EAAE,aAAa,EAAC,MAAM,iBAAiB,CAAC;AAC5D,OAAO,EAAC,MAAM,EAAC,MAAM,iBAAiB,CAAC;AAGvC;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAY;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC,OAAO,CAAC,MAAM,CAAC,CAAgB;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAS;IAE7B,YAAY,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAI9D;IAEK,IAAI,kBAUT;IAED,SAAS,IAAI;QAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC;QAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAC,CAK5E;CACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getBuilderStatus } from "../identity.js";
|
|
2
|
+
/**
|
|
3
|
+
* Service for tracking builder status.
|
|
4
|
+
* Provides regular builder status and balance updates for operator diagnostics.
|
|
5
|
+
*/
|
|
6
|
+
export class BuilderStatusTracker {
|
|
7
|
+
api;
|
|
8
|
+
logger;
|
|
9
|
+
index;
|
|
10
|
+
status;
|
|
11
|
+
balanceGwei;
|
|
12
|
+
constructor(api, logger, index) {
|
|
13
|
+
this.api = api;
|
|
14
|
+
this.logger = logger;
|
|
15
|
+
this.index = index;
|
|
16
|
+
}
|
|
17
|
+
async poll() {
|
|
18
|
+
const builderStatus = await getBuilderStatus(this.api, this.logger, this.index);
|
|
19
|
+
if (builderStatus !== null) {
|
|
20
|
+
if (this.status !== undefined && this.status !== builderStatus.status) {
|
|
21
|
+
this.logger.info("Builder status changed", { from: this.status, to: builderStatus.status });
|
|
22
|
+
}
|
|
23
|
+
this.status = builderStatus.status;
|
|
24
|
+
this.balanceGwei = builderStatus.balance;
|
|
25
|
+
this.logger.info("Builder status", { status: builderStatus.status, balance: builderStatus.balance });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
getStatus() {
|
|
29
|
+
return {
|
|
30
|
+
status: this.status,
|
|
31
|
+
balance: this.balanceGwei,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=builderStatusTracker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builderStatusTracker.js","sourceRoot":"","sources":["../../src/services/builderStatusTracker.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,gBAAgB,EAAC,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,MAAM,OAAO,oBAAoB;IACd,GAAG,CAAY;IACf,MAAM,CAAS;IACf,KAAK,CAAe;IAE7B,MAAM,CAAiB;IACvB,WAAW,CAAU;IAE7B,YAAY,GAAc,EAAE,MAAc,EAAE,KAAmB;QAC7D,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,MAAM,aAAa,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAChF,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa,CAAC,MAAM,EAAE,CAAC;gBACtE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,aAAa,CAAC,MAAM,EAAC,CAAC,CAAC;YAC5F,CAAC;YACD,IAAI,CAAC,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC;YACnC,IAAI,CAAC,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC;YACzC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAC,MAAM,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,CAAC,OAAO,EAAC,CAAC,CAAC;QACrG,CAAC;IACH,CAAC;IAED,SAAS;QACP,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,WAAW;SAC1B,CAAC;IACJ,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lodestar/builder",
|
|
3
|
-
"version": "1.47.0-dev.
|
|
3
|
+
"version": "1.47.0-dev.cec7fd63e3",
|
|
4
4
|
"description": "A Typescript implementation of the builder client",
|
|
5
5
|
"author": "ChainSafe Systems",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@chainsafe/blst": "^2.2.0",
|
|
50
50
|
"@chainsafe/ssz": "^1.6.2",
|
|
51
|
-
"@lodestar/api": "^1.47.0-dev.
|
|
52
|
-
"@lodestar/config": "^1.47.0-dev.
|
|
53
|
-
"@lodestar/params": "^1.47.0-dev.
|
|
54
|
-
"@lodestar/state-transition": "^1.47.0-dev.
|
|
55
|
-
"@lodestar/types": "^1.47.0-dev.
|
|
56
|
-
"@lodestar/utils": "^1.47.0-dev.
|
|
51
|
+
"@lodestar/api": "^1.47.0-dev.cec7fd63e3",
|
|
52
|
+
"@lodestar/config": "^1.47.0-dev.cec7fd63e3",
|
|
53
|
+
"@lodestar/params": "^1.47.0-dev.cec7fd63e3",
|
|
54
|
+
"@lodestar/state-transition": "^1.47.0-dev.cec7fd63e3",
|
|
55
|
+
"@lodestar/types": "^1.47.0-dev.cec7fd63e3",
|
|
56
|
+
"@lodestar/utils": "^1.47.0-dev.cec7fd63e3"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"@lodestar/logger": "^1.47.0-dev.
|
|
60
|
-
"@lodestar/test-utils": "^1.47.0-dev.
|
|
59
|
+
"@lodestar/logger": "^1.47.0-dev.cec7fd63e3",
|
|
60
|
+
"@lodestar/test-utils": "^1.47.0-dev.cec7fd63e3"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "f2fc097b6c8f4baec5d104d07d2418e0643ea84f"
|
|
63
63
|
}
|
package/src/builder.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import {ApiClient
|
|
1
|
+
import {ApiClient} from "@lodestar/api";
|
|
2
2
|
import {ChainForkConfig, assertEqualParams, createBeaconConfig} from "@lodestar/config";
|
|
3
|
-
import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
|
|
4
3
|
import {Clock, ClockOptions, IClock} from "@lodestar/state-transition";
|
|
5
|
-
import {
|
|
4
|
+
import {BuilderIndex, ExecutionAddress} from "@lodestar/types";
|
|
5
|
+
import {Logger, toHex, toRootHex} from "@lodestar/utils";
|
|
6
6
|
import {waitForGenesis} from "./genesis.js";
|
|
7
|
+
import {resolveBuilderIdentity} from "./identity.js";
|
|
8
|
+
import {logNodeVersion, waitForNodeReady} from "./readiness.js";
|
|
7
9
|
import {BuilderSigner, Keypair} from "./services/builderSigner.js";
|
|
10
|
+
import {BuilderStatusTracker} from "./services/builderStatusTracker.js";
|
|
8
11
|
|
|
9
12
|
export type BuilderModules = {
|
|
10
13
|
opts: BuilderOptions;
|
|
11
14
|
builderSigner: BuilderSigner;
|
|
15
|
+
builderStatusTracker: BuilderStatusTracker;
|
|
12
16
|
clock: IClock;
|
|
17
|
+
index: BuilderIndex;
|
|
13
18
|
};
|
|
14
19
|
|
|
15
20
|
export type BuilderOptions = {
|
|
@@ -19,6 +24,7 @@ export type BuilderOptions = {
|
|
|
19
24
|
abortController: AbortController;
|
|
20
25
|
api: ApiClient;
|
|
21
26
|
clock?: ClockOptions;
|
|
27
|
+
executionFeeRecipient: ExecutionAddress;
|
|
22
28
|
};
|
|
23
29
|
|
|
24
30
|
/**
|
|
@@ -26,58 +32,56 @@ export type BuilderOptions = {
|
|
|
26
32
|
*/
|
|
27
33
|
export class Builder {
|
|
28
34
|
readonly builderSigner: BuilderSigner;
|
|
35
|
+
private readonly builderStatusTracker: BuilderStatusTracker;
|
|
29
36
|
private readonly controller: AbortController;
|
|
30
37
|
private readonly clock: IClock;
|
|
38
|
+
private readonly index: BuilderIndex;
|
|
39
|
+
private readonly logger: Logger;
|
|
40
|
+
private readonly executionFeeRecipient: ExecutionAddress;
|
|
31
41
|
|
|
32
|
-
constructor({opts, builderSigner, clock}: BuilderModules) {
|
|
42
|
+
constructor({opts, builderSigner, builderStatusTracker, clock, index}: BuilderModules) {
|
|
33
43
|
this.builderSigner = builderSigner;
|
|
44
|
+
this.builderStatusTracker = builderStatusTracker;
|
|
34
45
|
this.clock = clock;
|
|
35
46
|
this.controller = opts.abortController;
|
|
47
|
+
this.logger = opts.logger;
|
|
48
|
+
this.index = index;
|
|
36
49
|
|
|
50
|
+
this.executionFeeRecipient = opts.executionFeeRecipient;
|
|
51
|
+
|
|
52
|
+
this.clock.runEveryEpoch(() => this.builderStatusTracker.poll());
|
|
37
53
|
this.clock.start(this.controller.signal);
|
|
54
|
+
|
|
55
|
+
this.logger.info("Builder client initialized", {
|
|
56
|
+
index: this.index,
|
|
57
|
+
executionFeeRecipient: toHex(this.executionFeeRecipient),
|
|
58
|
+
});
|
|
38
59
|
}
|
|
39
60
|
|
|
40
61
|
static async init(opts: BuilderOptions): Promise<Builder> {
|
|
41
|
-
const
|
|
42
|
-
|
|
62
|
+
const {api, logger} = opts;
|
|
63
|
+
const genesis = await waitForGenesis(api, logger, opts.abortController.signal);
|
|
64
|
+
logger.info("Genesis fetched from the beacon node", {
|
|
65
|
+
genesisValidatorsRoot: toRootHex(genesis.genesisValidatorsRoot),
|
|
66
|
+
});
|
|
43
67
|
|
|
44
|
-
const specRes = await
|
|
68
|
+
const specRes = await api.config.getSpec();
|
|
45
69
|
assertEqualParams(opts.config, specRes.value());
|
|
46
|
-
|
|
70
|
+
logger.info("Verified connected beacon node and builder have the same config");
|
|
47
71
|
|
|
48
72
|
const config = createBeaconConfig(opts.config, genesis.genesisValidatorsRoot);
|
|
49
73
|
const builderSigner = new BuilderSigner(config, opts.keypair);
|
|
50
74
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
builderIds: [builderSigner.getPubkeyHex()],
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (!builderRes.ok) {
|
|
57
|
-
throw Error(`Getting state builders from BN failed: ${builderRes.status}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const builders = builderRes.value();
|
|
61
|
-
|
|
62
|
-
if (builders.length === 0) {
|
|
63
|
-
throw Error(`Builder not registered: ${builderSigner.getPubkeyHex()}`);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const builderStatus: routes.beacon.BuilderResponse = builders[0];
|
|
75
|
+
await waitForNodeReady(api, logger, opts.abortController.signal);
|
|
76
|
+
await logNodeVersion(api, logger);
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
throw Error(`Builder not active: ${builderStatus.status}`);
|
|
70
|
-
}
|
|
78
|
+
const index = await resolveBuilderIdentity(api, logger, builderSigner.getPubkeyHex(), opts.abortController.signal);
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
throw Error(
|
|
74
|
-
`Builder version mismatch: got ${builderStatus.builder.version}, expected ${PAYLOAD_BUILDER_VERSION}`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
80
|
+
const clock = new Clock(config, logger, {genesisTime: Number(genesis.genesisTime), ...opts.clock});
|
|
77
81
|
|
|
78
|
-
const
|
|
82
|
+
const builderStatusTracker = new BuilderStatusTracker(api, logger, index);
|
|
79
83
|
|
|
80
|
-
return new Builder({opts, builderSigner, clock});
|
|
84
|
+
return new Builder({opts, builderSigner, builderStatusTracker, clock, index});
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
async close(): Promise<void> {
|
package/src/defaults.ts
CHANGED
package/src/genesis.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {Logger, sleep} from "@lodestar/utils";
|
|
|
5
5
|
/** The time between polls when waiting for genesis */
|
|
6
6
|
const WAITING_FOR_GENESIS_POLL_MS = 12 * 1000;
|
|
7
7
|
|
|
8
|
-
export async function waitForGenesis(api: ApiClient, logger: Logger, signal
|
|
8
|
+
export async function waitForGenesis(api: ApiClient, logger: Logger, signal: AbortSignal): Promise<Genesis> {
|
|
9
9
|
while (true) {
|
|
10
10
|
try {
|
|
11
11
|
return (await api.beacon.getGenesis()).value();
|
package/src/identity.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {ApiClient, routes} from "@lodestar/api";
|
|
2
|
+
import {PAYLOAD_BUILDER_VERSION} from "@lodestar/params";
|
|
3
|
+
import {BuilderIndex, BuilderStatus} from "@lodestar/types";
|
|
4
|
+
import {Logger, sleep, toHex} from "@lodestar/utils";
|
|
5
|
+
|
|
6
|
+
export const WAITING_FOR_BUILDER_POLL_MS = 10 * 1000;
|
|
7
|
+
|
|
8
|
+
export async function resolveBuilderIdentity(
|
|
9
|
+
api: ApiClient,
|
|
10
|
+
logger: Logger,
|
|
11
|
+
id: routes.beacon.BuilderId,
|
|
12
|
+
signal: AbortSignal
|
|
13
|
+
): Promise<BuilderIndex> {
|
|
14
|
+
const builderEntry = await waitForBuilder(api, logger, id, signal);
|
|
15
|
+
|
|
16
|
+
if (builderEntry.builder.version !== PAYLOAD_BUILDER_VERSION) {
|
|
17
|
+
throw Error(`Builder version mismatch: got ${builderEntry.builder.version}, expected ${PAYLOAD_BUILDER_VERSION}`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
logger.info("Builder identity resolved", {
|
|
21
|
+
index: builderEntry.index,
|
|
22
|
+
status: builderEntry.status,
|
|
23
|
+
balanceGwei: builderEntry.builder.balance,
|
|
24
|
+
executionAddress: toHex(builderEntry.builder.executionAddress),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return builderEntry.index;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function getBuilderStatus(
|
|
31
|
+
api: ApiClient,
|
|
32
|
+
logger: Logger,
|
|
33
|
+
id: routes.beacon.BuilderId
|
|
34
|
+
): Promise<{status: BuilderStatus; balance: number} | null> {
|
|
35
|
+
try {
|
|
36
|
+
const builderEntry = await fetchBuilder(api, id);
|
|
37
|
+
if (builderEntry) {
|
|
38
|
+
return {
|
|
39
|
+
status: builderEntry.status,
|
|
40
|
+
balance: builderEntry.builder.balance,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
logger.warn("Builder status not available in beacon node");
|
|
44
|
+
return null;
|
|
45
|
+
} catch (e) {
|
|
46
|
+
logger.warn("Couldn't fetch the builder", {}, e as Error);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function waitForBuilder(
|
|
52
|
+
api: ApiClient,
|
|
53
|
+
logger: Logger,
|
|
54
|
+
id: routes.beacon.BuilderId,
|
|
55
|
+
signal: AbortSignal
|
|
56
|
+
): Promise<routes.beacon.BuilderResponse> {
|
|
57
|
+
while (true) {
|
|
58
|
+
const builder = await fetchBuilder(api, id);
|
|
59
|
+
if (builder?.status === "active") {
|
|
60
|
+
return builder;
|
|
61
|
+
}
|
|
62
|
+
if (builder?.status === "exited") {
|
|
63
|
+
throw Error(`Builder exited: id=${id}`);
|
|
64
|
+
}
|
|
65
|
+
if (builder?.status === "pending") {
|
|
66
|
+
logger.info("Waiting for builder deposit to be finalized", {id});
|
|
67
|
+
} else {
|
|
68
|
+
logger.info("Waiting for builder to be known to the beacon node", {id});
|
|
69
|
+
}
|
|
70
|
+
await sleep(WAITING_FOR_BUILDER_POLL_MS, signal);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function fetchBuilder(
|
|
75
|
+
api: ApiClient,
|
|
76
|
+
id: routes.beacon.BuilderId
|
|
77
|
+
): Promise<routes.beacon.BuilderResponse | null> {
|
|
78
|
+
const builderRes = await api.beacon.getStateBuilders({
|
|
79
|
+
stateId: "head",
|
|
80
|
+
builderIds: [id],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const builders = builderRes.value();
|
|
84
|
+
|
|
85
|
+
if (builders.length === 0) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const builder = builders[0];
|
|
90
|
+
|
|
91
|
+
if (typeof id === "number") {
|
|
92
|
+
if (id !== builder.index) {
|
|
93
|
+
throw Error(`Index mismatch: got=${builder.index} expected=${id}`);
|
|
94
|
+
}
|
|
95
|
+
} else if (id !== toHex(builder.builder.pubkey)) {
|
|
96
|
+
throw Error(`Pubkey mismatch: got=${toHex(builder.builder.pubkey)} expected=${id}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return builder;
|
|
100
|
+
}
|
package/src/readiness.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {ApiClient} from "@lodestar/api";
|
|
2
|
+
import {Logger, sleep} from "@lodestar/utils";
|
|
3
|
+
|
|
4
|
+
/** The time between polls when waiting for BN to be ready */
|
|
5
|
+
const WAITING_FOR_NODE_READY_POLL_MS = 5 * 1000;
|
|
6
|
+
|
|
7
|
+
export async function waitForNodeReady(api: ApiClient, logger: Logger, signal: AbortSignal): Promise<void> {
|
|
8
|
+
while (!(await isNodeReady(api, logger))) {
|
|
9
|
+
await sleep(WAITING_FOR_NODE_READY_POLL_MS, signal);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function isNodeReady(api: ApiClient, logger: Logger): Promise<boolean> {
|
|
14
|
+
try {
|
|
15
|
+
const syncingStatusRes = await api.node.getSyncingStatus();
|
|
16
|
+
|
|
17
|
+
if (!syncingStatusRes.ok) {
|
|
18
|
+
logger.warn("Cannot get node sync status", {
|
|
19
|
+
status: syncingStatusRes.status,
|
|
20
|
+
message: syncingStatusRes.error()?.message,
|
|
21
|
+
});
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const syncingStatus = syncingStatusRes.value();
|
|
26
|
+
|
|
27
|
+
if (syncingStatus.isSyncing || syncingStatus.elOffline) {
|
|
28
|
+
logger.info(
|
|
29
|
+
syncingStatus.elOffline ? "Beacon node EL is offline, unable to submit bids" : "Beacon node is not ready yet",
|
|
30
|
+
{
|
|
31
|
+
headSlot: syncingStatus.headSlot,
|
|
32
|
+
syncDistance: syncingStatus.syncDistance,
|
|
33
|
+
elOffline: syncingStatus.elOffline,
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (syncingStatus.isOptimistic) {
|
|
40
|
+
logger.warn("Beacon node head is optimistic, execution payloads are not yet verified - unable to submit bids");
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
logger.info("Beacon node is ready", {headSlot: syncingStatus.headSlot});
|
|
45
|
+
|
|
46
|
+
return true;
|
|
47
|
+
} catch (e) {
|
|
48
|
+
logger.warn("Cannot reach the beacon node", {}, e as Error);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function logNodeVersion(api: ApiClient, logger: Logger): Promise<void> {
|
|
54
|
+
try {
|
|
55
|
+
const versionRes = await api.node.getNodeVersionV2();
|
|
56
|
+
const version = versionRes.value();
|
|
57
|
+
logger.info("Connected node version", {
|
|
58
|
+
beaconNode: `${version.beaconNode.name}/${version.beaconNode.version}`,
|
|
59
|
+
executionClient: version.executionClient
|
|
60
|
+
? `${version.executionClient.name}/${version.executionClient.version}`
|
|
61
|
+
: "unknown",
|
|
62
|
+
});
|
|
63
|
+
} catch (e) {
|
|
64
|
+
logger.warn("Failed to get node version", {}, e as Error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {ApiClient} from "@lodestar/api";
|
|
2
|
+
import {BuilderIndex, BuilderStatus} from "@lodestar/types";
|
|
3
|
+
import {Logger} from "@lodestar/utils";
|
|
4
|
+
import {getBuilderStatus} from "../identity.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Service for tracking builder status.
|
|
8
|
+
* Provides regular builder status and balance updates for operator diagnostics.
|
|
9
|
+
*/
|
|
10
|
+
export class BuilderStatusTracker {
|
|
11
|
+
private readonly api: ApiClient;
|
|
12
|
+
private readonly logger: Logger;
|
|
13
|
+
private readonly index: BuilderIndex;
|
|
14
|
+
|
|
15
|
+
private status?: BuilderStatus;
|
|
16
|
+
private balanceGwei?: number;
|
|
17
|
+
|
|
18
|
+
constructor(api: ApiClient, logger: Logger, index: BuilderIndex) {
|
|
19
|
+
this.api = api;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
this.index = index;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async poll() {
|
|
25
|
+
const builderStatus = await getBuilderStatus(this.api, this.logger, this.index);
|
|
26
|
+
if (builderStatus !== null) {
|
|
27
|
+
if (this.status !== undefined && this.status !== builderStatus.status) {
|
|
28
|
+
this.logger.info("Builder status changed", {from: this.status, to: builderStatus.status});
|
|
29
|
+
}
|
|
30
|
+
this.status = builderStatus.status;
|
|
31
|
+
this.balanceGwei = builderStatus.balance;
|
|
32
|
+
this.logger.info("Builder status", {status: builderStatus.status, balance: builderStatus.balance});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getStatus(): {status: BuilderStatus | undefined; balance: number | undefined} {
|
|
37
|
+
return {
|
|
38
|
+
status: this.status,
|
|
39
|
+
balance: this.balanceGwei,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|