@chainsafe/lodestar 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.
- package/.git-data.json +1 -1
- package/lib/cmds/beacon/handler.d.ts.map +1 -1
- package/lib/cmds/beacon/handler.js +10 -4
- package/lib/cmds/beacon/handler.js.map +1 -1
- package/lib/cmds/builder/loadKeypair.js +1 -1
- package/lib/cmds/builder/loadKeypair.js.map +1 -1
- package/lib/cmds/dev/options.d.ts +4 -0
- package/lib/cmds/dev/options.d.ts.map +1 -1
- package/lib/cmds/validator/blsToExecutionChange.js +1 -1
- package/lib/cmds/validator/blsToExecutionChange.js.map +1 -1
- package/lib/cmds/validator/handler.d.ts.map +1 -1
- package/lib/cmds/validator/handler.js +15 -2
- package/lib/cmds/validator/handler.js.map +1 -1
- package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.js +1 -1
- package/lib/cmds/validator/keymanager/decryptKeystoreDefinitions.js.map +1 -1
- package/lib/cmds/validator/keymanager/impl.d.ts +13 -2
- package/lib/cmds/validator/keymanager/impl.d.ts.map +1 -1
- package/lib/cmds/validator/keymanager/impl.js +41 -4
- package/lib/cmds/validator/keymanager/impl.js.map +1 -1
- package/lib/cmds/validator/keymanager/keystoreCache.js +1 -1
- package/lib/cmds/validator/keymanager/keystoreCache.js.map +1 -1
- package/lib/cmds/validator/keymanager/persistedKeys.d.ts.map +1 -1
- package/lib/cmds/validator/keymanager/persistedKeys.js +3 -1
- package/lib/cmds/validator/keymanager/persistedKeys.js.map +1 -1
- package/lib/cmds/validator/options.d.ts +4 -0
- package/lib/cmds/validator/options.d.ts.map +1 -1
- package/lib/cmds/validator/options.js +24 -0
- package/lib/cmds/validator/options.js.map +1 -1
- package/lib/cmds/validator/signers/index.js +1 -1
- package/lib/cmds/validator/signers/index.js.map +1 -1
- package/lib/cmds/validator/voluntaryExit.js +1 -1
- package/lib/cmds/validator/voluntaryExit.js.map +1 -1
- package/lib/networks/chiado.d.ts.map +1 -1
- package/lib/networks/chiado.js +4 -10
- package/lib/networks/chiado.js.map +1 -1
- package/lib/options/beaconNodeOptions/builder.d.ts.map +1 -1
- package/lib/options/beaconNodeOptions/builder.js +2 -2
- package/lib/options/beaconNodeOptions/builder.js.map +1 -1
- package/lib/options/beaconNodeOptions/chain.d.ts.map +1 -1
- package/lib/options/beaconNodeOptions/chain.js +1 -1
- package/lib/options/beaconNodeOptions/chain.js.map +1 -1
- package/lib/util/format.js +1 -1
- package/lib/util/format.js.map +1 -1
- package/lib/util/proposerConfig.d.ts +16 -11
- package/lib/util/proposerConfig.d.ts.map +1 -1
- package/lib/util/proposerConfig.js +128 -11
- package/lib/util/proposerConfig.js.map +1 -1
- package/package.json +21 -21
- package/src/cmds/beacon/handler.ts +10 -4
- package/src/cmds/builder/loadKeypair.ts +1 -1
- package/src/cmds/validator/blsToExecutionChange.ts +1 -1
- package/src/cmds/validator/handler.ts +29 -2
- package/src/cmds/validator/keymanager/decryptKeystoreDefinitions.ts +1 -1
- package/src/cmds/validator/keymanager/impl.ts +56 -4
- package/src/cmds/validator/keymanager/keystoreCache.ts +1 -1
- package/src/cmds/validator/keymanager/persistedKeys.ts +5 -1
- package/src/cmds/validator/options.ts +36 -0
- package/src/cmds/validator/signers/index.ts +1 -1
- package/src/cmds/validator/voluntaryExit.ts +1 -1
- package/src/networks/chiado.ts +4 -10
- package/src/options/beaconNodeOptions/builder.ts +4 -2
- package/src/options/beaconNodeOptions/chain.ts +2 -1
- package/src/util/format.ts +1 -1
- package/src/util/proposerConfig.ts +152 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chainsafe/lodestar",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.48.0-dev.67344272c0",
|
|
4
4
|
"description": "Command line interface for lodestar",
|
|
5
5
|
"author": "ChainSafe Systems",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -59,26 +59,26 @@
|
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@chainsafe/bls-keygen": "^0.4.0",
|
|
61
61
|
"@chainsafe/bls-keystore": "^3.1.0",
|
|
62
|
-
"@chainsafe/blst": "^2.2.0",
|
|
63
62
|
"@chainsafe/discv5": "^12.0.2",
|
|
64
63
|
"@chainsafe/enr": "^6.0.2",
|
|
65
|
-
"@chainsafe/
|
|
66
|
-
"@chainsafe/
|
|
64
|
+
"@chainsafe/lodestar-z": "catalog:",
|
|
65
|
+
"@chainsafe/persistent-merkle-tree": "^1.3.1",
|
|
66
|
+
"@chainsafe/ssz": "^1.6.3",
|
|
67
67
|
"@chainsafe/threads": "^1.11.3",
|
|
68
|
-
"@libp2p/crypto": "^5.1.
|
|
69
|
-
"@libp2p/interface": "^3.
|
|
70
|
-
"@libp2p/peer-id": "^6.0.
|
|
71
|
-
"@lodestar/api": "^1.
|
|
72
|
-
"@lodestar/beacon-node": "^1.
|
|
73
|
-
"@lodestar/builder": "^1.
|
|
74
|
-
"@lodestar/config": "^1.
|
|
75
|
-
"@lodestar/db": "^1.
|
|
76
|
-
"@lodestar/logger": "^1.
|
|
77
|
-
"@lodestar/params": "^1.
|
|
78
|
-
"@lodestar/state-transition": "^1.
|
|
79
|
-
"@lodestar/types": "^1.
|
|
80
|
-
"@lodestar/utils": "^1.
|
|
81
|
-
"@lodestar/validator": "^1.
|
|
68
|
+
"@libp2p/crypto": "^5.1.23",
|
|
69
|
+
"@libp2p/interface": "^3.3.0",
|
|
70
|
+
"@libp2p/peer-id": "^6.0.15",
|
|
71
|
+
"@lodestar/api": "^1.48.0-dev.67344272c0",
|
|
72
|
+
"@lodestar/beacon-node": "^1.48.0-dev.67344272c0",
|
|
73
|
+
"@lodestar/builder": "^1.48.0-dev.67344272c0",
|
|
74
|
+
"@lodestar/config": "^1.48.0-dev.67344272c0",
|
|
75
|
+
"@lodestar/db": "^1.48.0-dev.67344272c0",
|
|
76
|
+
"@lodestar/logger": "^1.48.0-dev.67344272c0",
|
|
77
|
+
"@lodestar/params": "^1.48.0-dev.67344272c0",
|
|
78
|
+
"@lodestar/state-transition": "^1.48.0-dev.67344272c0",
|
|
79
|
+
"@lodestar/types": "^1.48.0-dev.67344272c0",
|
|
80
|
+
"@lodestar/utils": "^1.48.0-dev.67344272c0",
|
|
81
|
+
"@lodestar/validator": "^1.48.0-dev.67344272c0",
|
|
82
82
|
"@multiformats/multiaddr": "^13.0.1",
|
|
83
83
|
"deepmerge": "^4.3.1",
|
|
84
84
|
"ethers": "^6.7.0",
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
"yargs": "^17.7.1"
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@chainsafe/as-sha256": "^1.2.
|
|
95
|
+
"@chainsafe/as-sha256": "^1.2.5",
|
|
96
96
|
"@ethereumjs/rlp": "^4.0.1",
|
|
97
|
-
"@lodestar/test-utils": "^1.
|
|
97
|
+
"@lodestar/test-utils": "^1.48.0-dev.67344272c0",
|
|
98
98
|
"@types/debug": "^4.1.7",
|
|
99
99
|
"@types/inquirer": "^9.0.3",
|
|
100
100
|
"@types/js-yaml": "^4.0.5",
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
"web3": "^4.0.3",
|
|
109
109
|
"web3-eth-accounts": "^4.0.3"
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "11114921165ea242fa2d33b68ac8813cdb75a4ee"
|
|
112
112
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import {getHeapStatistics} from "node:v8";
|
|
3
3
|
import {SignableENR} from "@chainsafe/enr";
|
|
4
|
+
import {pubkeyCache} from "@chainsafe/lodestar-z/pubkeys";
|
|
4
5
|
import {hasher} from "@chainsafe/persistent-merkle-tree";
|
|
5
6
|
import {BeaconDb, BeaconNode} from "@lodestar/beacon-node";
|
|
6
7
|
import {ChainForkConfig, createBeaconConfig} from "@lodestar/config";
|
|
7
8
|
import {LevelDbController} from "@lodestar/db/controller/level";
|
|
8
9
|
import {LoggerNode, getNodeLogger} from "@lodestar/logger/node";
|
|
9
|
-
import {ACTIVE_PRESET, PresetName} from "@lodestar/params";
|
|
10
|
-
import {createBeaconStateView
|
|
10
|
+
import {ACTIVE_PRESET, MAX_PENDING_DEPOSITS_PER_EPOCH, PresetName, SLOTS_PER_EPOCH} from "@lodestar/params";
|
|
11
|
+
import {createBeaconStateView} from "@lodestar/state-transition";
|
|
11
12
|
import {ErrorAborted, bytesToInt, formatBytes} from "@lodestar/utils";
|
|
12
13
|
import {ProcessShutdownCallback} from "@lodestar/validator";
|
|
13
14
|
import {BeaconNodeOptions, getBeaconConfigFromArgs} from "../../config/index.js";
|
|
@@ -78,8 +79,13 @@ export async function beaconHandler(args: BeaconArgs & GlobalArgs): Promise<void
|
|
|
78
79
|
wsCheckpoint,
|
|
79
80
|
} = await initBeaconState(args, beaconPaths.dataDir, config, db, logger);
|
|
80
81
|
const beaconConfig = createBeaconConfig(config, anchorState.genesisValidatorsRoot);
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
// Reserve 3 months of worst-case registry growth (MAX_PENDING_DEPOSITS_PER_EPOCH per epoch),
|
|
83
|
+
// over a year at organic rates, to avoid routine cache reallocations. Cache growth is protected
|
|
84
|
+
// by its native lock; if this headroom is exceeded, it grows by the same fixed step.
|
|
85
|
+
const headroomEpochs = (90 * 24 * 60 * 60) / (config.SECONDS_PER_SLOT * SLOTS_PER_EPOCH);
|
|
86
|
+
const pubkeyCacheHeadroom = MAX_PENDING_DEPOSITS_PER_EPOCH * Math.ceil(headroomEpochs);
|
|
87
|
+
pubkeyCache.ensureCapacity(anchorState.validators.length + pubkeyCacheHeadroom);
|
|
88
|
+
pubkeyCache.syncPubkeys(anchorState.validators.getAllReadonlyValues());
|
|
83
89
|
const anchorStateView = args["chain.nativeStateView"]
|
|
84
90
|
? createBeaconStateView({useNative: true, stateBytes: anchorStateBytes})
|
|
85
91
|
: createBeaconStateView({useNative: false, anchorState, config: beaconConfig, pubkeyCache});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import {Keystore} from "@chainsafe/bls-keystore";
|
|
3
|
-
import {SecretKey} from "@chainsafe/blst";
|
|
3
|
+
import {SecretKey} from "@chainsafe/lodestar-z/blst";
|
|
4
4
|
import {Keypair} from "@lodestar/builder";
|
|
5
5
|
import {Logger} from "@lodestar/utils";
|
|
6
6
|
import {ensure0xPrefix} from "../../util/format.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {SecretKey} from "@chainsafe/blst";
|
|
1
|
+
import {SecretKey} from "@chainsafe/lodestar-z/blst";
|
|
2
2
|
import {getClient} from "@lodestar/api";
|
|
3
3
|
import {createBeaconConfig} from "@lodestar/config";
|
|
4
4
|
import {DOMAIN_BLS_TO_EXECUTION_CHANGE, ForkName} from "@lodestar/params";
|
|
@@ -28,7 +28,13 @@ import {
|
|
|
28
28
|
parseLoggerArgs,
|
|
29
29
|
parseProposerConfig,
|
|
30
30
|
} from "../../util/index.js";
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
parseBuilderBoostFactor,
|
|
33
|
+
parseBuilderGweiAmount,
|
|
34
|
+
parseBuilderMinBid,
|
|
35
|
+
parseBuilderSelection,
|
|
36
|
+
parseBuilderUrls,
|
|
37
|
+
} from "../../util/proposerConfig.js";
|
|
32
38
|
import {getVersionData} from "../../util/version.js";
|
|
33
39
|
import {KeymanagerApi} from "./keymanager/impl.js";
|
|
34
40
|
import {IPersistedKeysBackend} from "./keymanager/interface.js";
|
|
@@ -212,7 +218,8 @@ export async function validatorHandler(args: IValidatorCliArgs & GlobalArgs): Pr
|
|
|
212
218
|
validator,
|
|
213
219
|
persistedKeysBackend,
|
|
214
220
|
abortController.signal,
|
|
215
|
-
proposerConfigWriteDisabled
|
|
221
|
+
proposerConfigWriteDisabled,
|
|
222
|
+
args.allowDangerousTrustedPayments
|
|
216
223
|
);
|
|
217
224
|
const keymanagerServer = new KeymanagerRestApiServer(
|
|
218
225
|
{
|
|
@@ -250,6 +257,9 @@ function getProposerConfigFromArgs(
|
|
|
250
257
|
args["builder.selection"] ?? (args.builder ? defaultOptions.builderAliasSelection : undefined)
|
|
251
258
|
),
|
|
252
259
|
boostFactor: parseBuilderBoostFactor(args["builder.boostFactor"]),
|
|
260
|
+
minBid: parseBuilderMinBid(args["builder.minBid"]),
|
|
261
|
+
maxExecutionPayment: parseBuilderGweiAmount(args["builder.maxExecutionPayment"]),
|
|
262
|
+
builders: parseBuilderUrls(args["builder.urls"]),
|
|
253
263
|
},
|
|
254
264
|
};
|
|
255
265
|
|
|
@@ -273,6 +283,23 @@ function getProposerConfigFromArgs(
|
|
|
273
283
|
valProposerConfig = {defaultConfig} as ValidatorProposerConfig;
|
|
274
284
|
}
|
|
275
285
|
}
|
|
286
|
+
|
|
287
|
+
// Trusted execution payments are only backed by the builder's promise to pay, require an
|
|
288
|
+
// explicit opt-in before any configuration source can set a max execution payment above 0
|
|
289
|
+
if (args.allowDangerousTrustedPayments !== true) {
|
|
290
|
+
const configs = [valProposerConfig.defaultConfig, ...Object.values(valProposerConfig.proposerConfig ?? {})];
|
|
291
|
+
const hasTrustedPayment = configs.some(
|
|
292
|
+
(config) =>
|
|
293
|
+
(config.builder?.maxExecutionPayment ?? 0n) > 0n ||
|
|
294
|
+
(config.builder?.builders ?? []).some((entry) => (entry.maxExecutionPayment ?? 0n) > 0n)
|
|
295
|
+
);
|
|
296
|
+
if (hasTrustedPayment) {
|
|
297
|
+
throw new YargsError(
|
|
298
|
+
"Configuring a builder max execution payment above 0 requires --allowDangerousTrustedPayments"
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
276
303
|
return valProposerConfig;
|
|
277
304
|
}
|
|
278
305
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {Keystore} from "@chainsafe/bls-keystore";
|
|
4
|
-
import {SecretKey} from "@chainsafe/blst";
|
|
4
|
+
import {SecretKey} from "@chainsafe/lodestar-z/blst";
|
|
5
5
|
import {LogLevel, Logger} from "@lodestar/utils";
|
|
6
6
|
import {SignerLocal, SignerType} from "@lodestar/validator";
|
|
7
7
|
import {lockFilepath, unlockFilepath} from "../../../util/lockfile.js";
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {Keystore} from "@chainsafe/bls-keystore";
|
|
2
|
-
import {SecretKey} from "@chainsafe/blst";
|
|
2
|
+
import {SecretKey} from "@chainsafe/lodestar-z/blst";
|
|
3
3
|
import {
|
|
4
4
|
BuilderBoostFactorData,
|
|
5
|
+
BuilderConfigData,
|
|
5
6
|
DeleteRemoteKeyStatus,
|
|
6
7
|
DeletionStatus,
|
|
7
8
|
FeeRecipientData,
|
|
@@ -32,12 +33,13 @@ export class KeymanagerApi implements Api {
|
|
|
32
33
|
private readonly validator: Validator,
|
|
33
34
|
private readonly persistedKeysBackend: IPersistedKeysBackend,
|
|
34
35
|
private readonly signal: AbortSignal,
|
|
35
|
-
private readonly proposerConfigWriteDisabled?: boolean
|
|
36
|
+
private readonly proposerConfigWriteDisabled?: boolean,
|
|
37
|
+
private readonly allowDangerousTrustedPayments?: boolean
|
|
36
38
|
) {}
|
|
37
39
|
|
|
38
40
|
private checkIfProposerWriteEnabled(): void {
|
|
39
41
|
if (this.proposerConfigWriteDisabled === true) {
|
|
40
|
-
throw
|
|
42
|
+
throw new ApiError(403, "proposerSettingsFile option activated");
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
|
|
@@ -379,6 +381,48 @@ export class KeymanagerApi implements Api {
|
|
|
379
381
|
return {status: 204};
|
|
380
382
|
}
|
|
381
383
|
|
|
384
|
+
async getBuilderConfig({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["getBuilderConfig"]> {
|
|
385
|
+
this.assertValidKnownPubkey(pubkey);
|
|
386
|
+
return {data: this.validator.validatorStore.getBuilderConfig(pubkey)};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async setBuilderConfig({
|
|
390
|
+
pubkey,
|
|
391
|
+
builderConfig,
|
|
392
|
+
}: {
|
|
393
|
+
pubkey: PubkeyHex;
|
|
394
|
+
builderConfig: BuilderConfigData;
|
|
395
|
+
}): ReturnType<Api["setBuilderConfig"]> {
|
|
396
|
+
this.checkIfProposerWriteEnabled();
|
|
397
|
+
this.assertValidKnownPubkey(pubkey);
|
|
398
|
+
|
|
399
|
+
if (
|
|
400
|
+
this.allowDangerousTrustedPayments !== true &&
|
|
401
|
+
builderConfig.builders?.some((entry) => (entry.maxExecutionPayment ?? 0n) > 0n)
|
|
402
|
+
) {
|
|
403
|
+
throw new ApiError(
|
|
404
|
+
400,
|
|
405
|
+
"Configuring a builder max execution payment above 0 requires --allowDangerousTrustedPayments"
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
this.validator.validatorStore.setBuilderConfig(pubkey, builderConfig);
|
|
411
|
+
} catch (e) {
|
|
412
|
+
throw new ApiError(400, (e as Error).message);
|
|
413
|
+
}
|
|
414
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
415
|
+
return {status: 202};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async deleteBuilderConfig({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["deleteBuilderConfig"]> {
|
|
419
|
+
this.checkIfProposerWriteEnabled();
|
|
420
|
+
this.assertValidKnownPubkey(pubkey);
|
|
421
|
+
this.validator.validatorStore.deleteBuilderConfig(pubkey);
|
|
422
|
+
this.persistedKeysBackend.writeProposerConfig(pubkey, this.validator.validatorStore.getProposerConfig(pubkey));
|
|
423
|
+
return {status: 204};
|
|
424
|
+
}
|
|
425
|
+
|
|
382
426
|
async getProposerConfig({pubkey}: {pubkey: PubkeyHex}): ReturnType<Api["getProposerConfig"]> {
|
|
383
427
|
this.assertValidKnownPubkey(pubkey);
|
|
384
428
|
|
|
@@ -390,7 +434,15 @@ export class KeymanagerApi implements Api {
|
|
|
390
434
|
? {
|
|
391
435
|
...config.builder,
|
|
392
436
|
// Default JSON serialization can't handle BigInt
|
|
393
|
-
boostFactor: config.builder.boostFactor
|
|
437
|
+
boostFactor: config.builder.boostFactor?.toString(),
|
|
438
|
+
minBid: config.builder.minBid?.toString(),
|
|
439
|
+
maxExecutionPayment: config.builder.maxExecutionPayment?.toString(),
|
|
440
|
+
builders: config.builder.builders?.map((entry) => ({
|
|
441
|
+
...entry,
|
|
442
|
+
maxExecutionPayment: entry.maxExecutionPayment?.toString(),
|
|
443
|
+
minBid: entry.minBid?.toString(),
|
|
444
|
+
builderBoostFactor: entry.builderBoostFactor?.toString(),
|
|
445
|
+
})),
|
|
394
446
|
}
|
|
395
447
|
: undefined,
|
|
396
448
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {Keystore} from "@chainsafe/bls-keystore";
|
|
4
|
-
import {SecretKey} from "@chainsafe/blst";
|
|
4
|
+
import {SecretKey} from "@chainsafe/lodestar-z/blst";
|
|
5
5
|
import {fromHex, toHex, toPubkeyHex} from "@lodestar/utils";
|
|
6
6
|
import {SignerLocal, SignerType} from "@lodestar/validator";
|
|
7
7
|
import {writeFile600Perm} from "../../../util/file.js";
|
|
@@ -53,7 +53,11 @@ export class PersistedKeysBackend implements IPersistedKeysBackend {
|
|
|
53
53
|
if (proposerConfig !== null) {
|
|
54
54
|
// if proposerConfig is not empty write or update the json to file
|
|
55
55
|
const {proposerDirPath} = this.getValidatorPaths(pubkeyHex);
|
|
56
|
-
writeFile600Perm(
|
|
56
|
+
writeFile600Perm(
|
|
57
|
+
proposerDirPath,
|
|
58
|
+
// Default JSON serialization can't handle BigInt
|
|
59
|
+
JSON.stringify(proposerConfig, (_key, value) => (typeof value === "bigint" ? value.toString() : value))
|
|
60
|
+
);
|
|
57
61
|
} else {
|
|
58
62
|
this.deleteProposerConfig(pubkeyHex);
|
|
59
63
|
}
|
|
@@ -48,6 +48,10 @@ export type IValidatorCliArgs = AccountValidatorArgs &
|
|
|
48
48
|
builder?: boolean;
|
|
49
49
|
"builder.selection"?: string;
|
|
50
50
|
"builder.boostFactor"?: string;
|
|
51
|
+
"builder.minBid"?: string;
|
|
52
|
+
"builder.urls"?: string[];
|
|
53
|
+
"builder.maxExecutionPayment"?: string;
|
|
54
|
+
allowDangerousTrustedPayments?: boolean;
|
|
51
55
|
|
|
52
56
|
/** @deprecated */
|
|
53
57
|
useProduceBlockV3?: boolean;
|
|
@@ -276,6 +280,38 @@ export const validatorOptions: CliCommandOptions<IValidatorCliArgs> = {
|
|
|
276
280
|
group: "builder",
|
|
277
281
|
},
|
|
278
282
|
|
|
283
|
+
"builder.minBid": {
|
|
284
|
+
type: "string",
|
|
285
|
+
description:
|
|
286
|
+
"Minimum counted total payment in Gwei accepted from a builder bid. The total is the bid value plus its execution payment up to the configured cap. Only used post-Gloas",
|
|
287
|
+
defaultDescription: `${defaultOptions.builderMinBid}`,
|
|
288
|
+
group: "builder",
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
"builder.urls": {
|
|
292
|
+
description:
|
|
293
|
+
"URL(s) of external builders to request execution payload bids from. Auth data agreed with a builder may be appended as a hex fragment, e.g. https://builder.example.com#0x0123, otherwise the UTF-8 bytes of the URL are used. Only used post-Gloas",
|
|
294
|
+
type: "array",
|
|
295
|
+
string: true,
|
|
296
|
+
coerce: (urls: string[]): string[] => urls.flatMap((url) => url.split(",")),
|
|
297
|
+
group: "builder",
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
"builder.maxExecutionPayment": {
|
|
301
|
+
type: "string",
|
|
302
|
+
description:
|
|
303
|
+
"Maximum execution layer payment in Gwei counted toward a builder bid. A value of 0 means only trustless payments via the builder's staked collateral count toward the bid. Values above 0 require `--allowDangerousTrustedPayments`. Only used post-Gloas",
|
|
304
|
+
defaultDescription: `${defaultOptions.builderMaxExecutionPayment}`,
|
|
305
|
+
group: "builder",
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
allowDangerousTrustedPayments: {
|
|
309
|
+
type: "boolean",
|
|
310
|
+
description:
|
|
311
|
+
"Allow configuring a builder max execution payment above 0. Trusted execution layer payments are only backed by the builder's promise to pay, not by its staked collateral, a builder that fails to pay cannot be penalized in protocol",
|
|
312
|
+
group: "builder",
|
|
313
|
+
},
|
|
314
|
+
|
|
279
315
|
useProduceBlockV3: {
|
|
280
316
|
hidden: true,
|
|
281
317
|
deprecated: true,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import {deriveEth2ValidatorKeys, deriveKeyFromMnemonic} from "@chainsafe/bls-keygen";
|
|
3
|
-
import {SecretKey} from "@chainsafe/blst";
|
|
3
|
+
import {SecretKey} from "@chainsafe/lodestar-z/blst";
|
|
4
4
|
import {interopSecretKey} from "@lodestar/state-transition";
|
|
5
5
|
import {LogLevel, Logger, isValidHttpUrl} from "@lodestar/utils";
|
|
6
6
|
import {Signer, SignerType, externalSignerGetKeys} from "@lodestar/validator";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import inquirer from "inquirer";
|
|
4
|
-
import {Signature} from "@chainsafe/blst";
|
|
4
|
+
import {Signature} from "@chainsafe/lodestar-z/blst";
|
|
5
5
|
import {ApiClient, getClient} from "@lodestar/api";
|
|
6
6
|
import {BeaconConfig, createBeaconConfig} from "@lodestar/config";
|
|
7
7
|
import {
|
package/src/networks/chiado.ts
CHANGED
|
@@ -5,14 +5,8 @@ export const genesisStateRoot = "0xa48419160f8f146ecaa53d12a5d6e1e6af414a328afdc
|
|
|
5
5
|
export const bootnodesFileUrl = "https://raw.githubusercontent.com/gnosischain/configs/main/chiado/bootnodes.yaml";
|
|
6
6
|
|
|
7
7
|
export const bootEnrs = [
|
|
8
|
-
"enr:-
|
|
9
|
-
"enr:-
|
|
10
|
-
"enr:-
|
|
11
|
-
"enr:-
|
|
12
|
-
"enr:-Ly4QLYLNqrjvSxD3lpAPBUNlxa6cIbe79JqLZLFcZZjWoCjZcw-85agLUErHiygG2weRSCLnd5V460qTbLbwJQsfZkoh2F0dG5ldHOI__________-EZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhKq7mu-Jc2VjcDI1NmsxoQP900YAYa9kdvzlSKGjVo-F3XVzATjOYp3BsjLjSophO4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA",
|
|
13
|
-
"enr:-Ly4QCGeYvTCNOGKi0mKRUd45rLj96b4pH98qG7B9TCUGXGpHZALtaL2-XfjASQyhbCqENccI4PGXVqYTIehNT9KJMQgh2F0dG5ldHOI__________-EZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhIuQrVSJc2VjcDI1NmsxoQP9iDchx2PGl3JyJ29B9fhLCvVMN6n23pPAIIeFV-sHOIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA",
|
|
14
|
-
"enr:-Ly4QAtr21x5Ps7HYhdZkIBRBgcBkvlIfEel1YNjtFWf4cV3au2LgBGICz9PtEs9-p2HUl_eME8m1WImxTxSB3AkCMwBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhANHhOeJc2VjcDI1NmsxoQNLp1QPV8-pyMCohOtj6xGtSBM_GtVTqzlbvNsCF4ezkYhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA",
|
|
15
|
-
"enr:-Ly4QLgn8Bx6faigkKUGZQvd1HDToV2FAxZIiENK-lczruzQb90qJK-4E65ADly0s4__dQOW7IkLMW7ZAyJy2vtiLy8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhANFIw2Jc2VjcDI1NmsxoQMa-fWEy9UJHfOl_lix3wdY5qust78sHAqZnWwEiyqKgYhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA",
|
|
16
|
-
"enr:-KG4QF7z4LUdMfgwvh-fS-MDv_1hPSUCqGfyOWGLNJuoBHKFAMSHz8geQn8v3qDDbuSQKud3WIAjKqR4gqJoLBUEJ08ZhGV0aDKQDc1ElgAAAG___________4JpZIJ2NIJpcIQjzq5ciXNlY3AyNTZrMaECt7YO363pV54d3QdgnluL5kxzhCR_k0yM9C-G6bqMGoKDdGNwgiMog3VkcIIjKA",
|
|
17
|
-
"enr:-LK4QCUTEmZrT1AgCKdyVgwnHL5J0VSoxsyjruAtwo-owBTBVEOyAnQRVNXlcW5aL-ycntk5oHDrKCR-DXZAlUAKpjEBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpCdM7Z1BAAAb___________gmlkgnY0gmlwhCPSfheJc2VjcDI1NmsxoQNpdf8U9pzsU9m6Hzgd1rmTI-On-QImJnkZBGqDp4org4N0Y3CCIyiDdWRwgiMo",
|
|
8
|
+
"enr:-IS4QC9xcSNOKeUBrjOX1-qwcPDGOvRK-Aql_YnRfuYsuXElQawANMLTOz1YfrZTMI07B7DAl9hmk3ic5G7ZHQbq4HsUgmlkgnY0gmlwhDNL9gKJc2VjcDI1NmsxoQOzB8HdKe07eQBOxu5nexAWECV47zxlau23oEYqoToy9YN1ZHCCIyg",
|
|
9
|
+
"enr:-IS4QMpCWNqXmqbtXg7vQyYBwV2xTDpgTNpgAz060TY5cpJ3JdjuwAkLG01Vf9Vlfb0jtRhJEpj0CfteG-3K_V36bmECgmlkgnY0gmlwhDmBRt6Jc2VjcDI1NmsxoQNkoifSsk1ijP3-OR9pcL2VWCSfD5_syapyrXA83GcAvYN1ZHCCIyg",
|
|
10
|
+
"enr:-IS4QBa3hl_BcjrY4z_OvhRBx7qUMBFGkjkoedGNMXgC_fzLRBsg161SipeHWAkUnSuvBg_7529-Qfxcc7CAbokn4fMCgmlkgnY0gmlwhDmA_HuJc2VjcDI1NmsxoQL8xX-V_pyU_l3AIfVokHKMN1OMUG02vcy4AMOxdyi5jIN1ZHCCIyg",
|
|
11
|
+
"enr:-IS4QEuUku9zL0V3hFHt41fqmEdvphEvrOg0D4K_TgiiaePCDH2IPzt6B3O6NLHtgJNXk3UW68DL6KVBD4rjoW-nW9ACgmlkgnY0gmlwhDYnlGKJc2VjcDI1NmsxoQP3W66lisn2YOJF4B0QDEnURZ6tqzpi5gBy9rbMfpC70YN1ZHCCIyg",
|
|
18
12
|
];
|
|
@@ -31,7 +31,8 @@ export function parseArgs(args: ExecutionBuilderArgs): IBeaconNodeOptions["execu
|
|
|
31
31
|
|
|
32
32
|
export const options: CliCommandOptions<ExecutionBuilderArgs> = {
|
|
33
33
|
builder: {
|
|
34
|
-
description:
|
|
34
|
+
description:
|
|
35
|
+
"Enable external builder. Pre-Gloas only, deprecated post-Gloas where builders are configured on the validator client",
|
|
35
36
|
type: "boolean",
|
|
36
37
|
default: defaultExecutionBuilderHttpOpts.enabled,
|
|
37
38
|
group: "builder",
|
|
@@ -39,7 +40,8 @@ export const options: CliCommandOptions<ExecutionBuilderArgs> = {
|
|
|
39
40
|
|
|
40
41
|
"builder.url": {
|
|
41
42
|
alias: ["builder.urls"],
|
|
42
|
-
description:
|
|
43
|
+
description:
|
|
44
|
+
"Url hosting the builder API. Pre-Gloas only, deprecated post-Gloas where builders are configured on the validator client",
|
|
43
45
|
defaultDescription: defaultExecutionBuilderHttpOpts.url,
|
|
44
46
|
type: "string",
|
|
45
47
|
group: "builder",
|
|
@@ -343,7 +343,8 @@ Will double processing times. Use only for debugging purposes.",
|
|
|
343
343
|
|
|
344
344
|
"chain.maxCPStateEpochsOnDisk": {
|
|
345
345
|
hidden: true,
|
|
346
|
-
description:
|
|
346
|
+
description:
|
|
347
|
+
"Max number of checkpoint state epochs to keep on disk. Default (Infinity) uses tiered pruning to bound disk usage during long non-finality; set a finite N to keep only the last N epochs instead (previous behavior)",
|
|
347
348
|
type: "number",
|
|
348
349
|
default: defaultOptions.chain.maxCPStateEpochsOnDisk,
|
|
349
350
|
group: "chain",
|
package/src/util/format.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {routes} from "@lodestar/api";
|
|
4
|
+
import {BuilderEntryConfig, builderConfigDataFromJson} from "@lodestar/api/keymanager";
|
|
5
|
+
import {MAX_BUILDER_AUTH_DATA_SIZE, MAX_BUILDER_ENTRIES, MAX_BUILDER_URL_SIZE} from "@lodestar/params";
|
|
6
|
+
import {fromHex, isValidAsciiHttpUrl, toHex} from "@lodestar/utils";
|
|
4
7
|
import {ValidatorProposerConfig} from "@lodestar/validator";
|
|
5
8
|
import {parseFeeRecipient} from "./feeRecipient.js";
|
|
6
9
|
import {readFile} from "./file.js";
|
|
7
10
|
|
|
11
|
+
const UINT64_MAX = 2n ** 64n - 1n;
|
|
12
|
+
const BUILDER_AUTH_DATA_PATTERN = new RegExp(`^0x(?:[a-fA-F0-9]{2}){1,${MAX_BUILDER_AUTH_DATA_SIZE}}$`);
|
|
13
|
+
|
|
8
14
|
type ProposerConfig = ValidatorProposerConfig["defaultConfig"];
|
|
9
15
|
|
|
10
16
|
type ProposerConfigFileSection = {
|
|
@@ -17,6 +23,9 @@ type ProposerConfigFileSection = {
|
|
|
17
23
|
gas_limit?: number;
|
|
18
24
|
selection?: routes.validator.BuilderSelection;
|
|
19
25
|
boost_factor?: bigint;
|
|
26
|
+
min_bid?: bigint;
|
|
27
|
+
max_execution_payment?: bigint;
|
|
28
|
+
builders?: unknown;
|
|
20
29
|
};
|
|
21
30
|
};
|
|
22
31
|
|
|
@@ -54,7 +63,14 @@ function parseProposerConfigSection(
|
|
|
54
63
|
overrideConfig?: ProposerConfig
|
|
55
64
|
): ProposerConfig {
|
|
56
65
|
const {graffiti, strict_fee_recipient_check, fee_recipient, builder} = proposerFileSection;
|
|
57
|
-
const {
|
|
66
|
+
const {
|
|
67
|
+
gas_limit,
|
|
68
|
+
selection: builderSelection,
|
|
69
|
+
boost_factor,
|
|
70
|
+
min_bid,
|
|
71
|
+
max_execution_payment,
|
|
72
|
+
builders,
|
|
73
|
+
} = builder || {};
|
|
58
74
|
|
|
59
75
|
if (graffiti !== undefined && typeof graffiti !== "string") {
|
|
60
76
|
throw Error("graffiti is not 'string");
|
|
@@ -79,6 +95,29 @@ function parseProposerConfigSection(
|
|
|
79
95
|
if (boost_factor !== undefined && typeof boost_factor !== "string") {
|
|
80
96
|
throw Error("boost_factor is not 'string");
|
|
81
97
|
}
|
|
98
|
+
if (min_bid !== undefined && typeof min_bid !== "string") {
|
|
99
|
+
throw Error("min_bid is not 'string");
|
|
100
|
+
}
|
|
101
|
+
if (max_execution_payment !== undefined && typeof max_execution_payment !== "string") {
|
|
102
|
+
throw Error("max_execution_payment is not 'string");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const parsedBuilder =
|
|
106
|
+
overrideConfig?.builder || builder
|
|
107
|
+
? {
|
|
108
|
+
gasLimit: overrideConfig?.builder?.gasLimit ?? (gas_limit !== undefined ? Number(gas_limit) : undefined),
|
|
109
|
+
selection: overrideConfig?.builder?.selection ?? parseBuilderSelection(builderSelection),
|
|
110
|
+
boostFactor: overrideConfig?.builder?.boostFactor ?? parseBuilderBoostFactor(boost_factor),
|
|
111
|
+
minBid: overrideConfig?.builder?.minBid ?? parseBuilderMinBid(min_bid),
|
|
112
|
+
maxExecutionPayment:
|
|
113
|
+
overrideConfig?.builder?.maxExecutionPayment ?? parseBuilderGweiAmount(max_execution_payment),
|
|
114
|
+
builders: overrideConfig?.builder?.builders ?? parseBuilderEntries(builders),
|
|
115
|
+
}
|
|
116
|
+
: undefined;
|
|
117
|
+
|
|
118
|
+
if (overrideConfig?.builder?.builders !== undefined && builders !== undefined) {
|
|
119
|
+
throw Error("Cannot configure both --builder.urls and builders in the proposer settings file");
|
|
120
|
+
}
|
|
82
121
|
|
|
83
122
|
return {
|
|
84
123
|
graffiti: overrideConfig?.graffiti ?? graffiti,
|
|
@@ -86,21 +125,27 @@ function parseProposerConfigSection(
|
|
|
86
125
|
overrideConfig?.strictFeeRecipientCheck ??
|
|
87
126
|
(strict_fee_recipient_check ? stringtoBool(strict_fee_recipient_check) : undefined),
|
|
88
127
|
feeRecipient: overrideConfig?.feeRecipient ?? (fee_recipient ? parseFeeRecipient(fee_recipient) : undefined),
|
|
89
|
-
builder:
|
|
90
|
-
overrideConfig?.builder || builder
|
|
91
|
-
? {
|
|
92
|
-
gasLimit: overrideConfig?.builder?.gasLimit ?? (gas_limit !== undefined ? Number(gas_limit) : undefined),
|
|
93
|
-
selection: overrideConfig?.builder?.selection ?? parseBuilderSelection(builderSelection),
|
|
94
|
-
boostFactor: overrideConfig?.builder?.boostFactor ?? parseBuilderBoostFactor(boost_factor),
|
|
95
|
-
}
|
|
96
|
-
: undefined,
|
|
128
|
+
builder: parsedBuilder,
|
|
97
129
|
};
|
|
98
130
|
}
|
|
99
131
|
|
|
100
|
-
export function readProposerConfigDir(filepath: string, filename: string):
|
|
132
|
+
export function readProposerConfigDir(filepath: string, filename: string): ProposerConfig {
|
|
101
133
|
const proposerConfigStr = fs.readFileSync(path.join(filepath, filename), "utf8");
|
|
102
|
-
|
|
103
|
-
|
|
134
|
+
// Persisted via `writeProposerConfig` with BigInt values serialized as strings
|
|
135
|
+
const persisted = JSON.parse(proposerConfigStr) as ProposerConfig;
|
|
136
|
+
if (persisted.builder) {
|
|
137
|
+
const {boostFactor, minBid, maxExecutionPayment, builders} = persisted.builder;
|
|
138
|
+
persisted.builder.boostFactor = boostFactor !== undefined ? BigInt(boostFactor) : undefined;
|
|
139
|
+
persisted.builder.minBid = minBid !== undefined ? BigInt(minBid) : undefined;
|
|
140
|
+
persisted.builder.maxExecutionPayment = maxExecutionPayment !== undefined ? BigInt(maxExecutionPayment) : undefined;
|
|
141
|
+
persisted.builder.builders = builders?.map((entry) => ({
|
|
142
|
+
...entry,
|
|
143
|
+
maxExecutionPayment: entry.maxExecutionPayment !== undefined ? BigInt(entry.maxExecutionPayment) : undefined,
|
|
144
|
+
minBid: entry.minBid !== undefined ? BigInt(entry.minBid) : undefined,
|
|
145
|
+
builderBoostFactor: entry.builderBoostFactor !== undefined ? BigInt(entry.builderBoostFactor) : undefined,
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
return persisted;
|
|
104
149
|
}
|
|
105
150
|
|
|
106
151
|
export function parseBuilderSelection(builderSelection?: string): routes.validator.BuilderSelection | undefined {
|
|
@@ -131,6 +176,100 @@ export function parseBuilderBoostFactor(boostFactor?: string): bigint | undefine
|
|
|
131
176
|
if (!/^\d+$/.test(boostFactor)) {
|
|
132
177
|
throw Error("Invalid input for builder boost factor, must be a valid number without decimals");
|
|
133
178
|
}
|
|
179
|
+
const parsed = BigInt(boostFactor);
|
|
180
|
+
if (parsed > UINT64_MAX) {
|
|
181
|
+
throw Error("Invalid input for builder boost factor, must not exceed 2**64 - 1");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return parsed;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function parseBuilderMinBid(minBid?: string | bigint): bigint | undefined {
|
|
188
|
+
if (minBid === undefined) return;
|
|
189
|
+
|
|
190
|
+
if (!/^\d+$/.test(minBid.toString())) {
|
|
191
|
+
throw Error("Invalid input for builder min bid, must be a valid number without decimals");
|
|
192
|
+
}
|
|
193
|
+
const parsed = BigInt(minBid);
|
|
194
|
+
if (parsed > UINT64_MAX) {
|
|
195
|
+
throw Error("Invalid input for builder min bid, must not exceed 2**64 - 1");
|
|
196
|
+
}
|
|
134
197
|
|
|
135
|
-
return
|
|
198
|
+
return parsed;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function parseBuilderGweiAmount(amount?: string | bigint): bigint | undefined {
|
|
202
|
+
if (amount === undefined) return;
|
|
203
|
+
|
|
204
|
+
if (!/^\d+$/.test(amount.toString())) {
|
|
205
|
+
throw Error("Invalid input for builder Gwei amount, must be a valid number without decimals");
|
|
206
|
+
}
|
|
207
|
+
const parsed = BigInt(amount);
|
|
208
|
+
if (parsed > UINT64_MAX) {
|
|
209
|
+
throw Error("Invalid input for builder Gwei amount, must not exceed 2**64 - 1");
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return parsed;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Parse per-builder entries from the proposer settings file, same shape and validation as the
|
|
217
|
+
* keymanager builders api. No two entries may share both their url and their auth data, an
|
|
218
|
+
* omitted auth data is compared as the value derived from the entry url.
|
|
219
|
+
*/
|
|
220
|
+
export function parseBuilderEntries(builders?: unknown): BuilderEntryConfig[] | undefined {
|
|
221
|
+
if (builders === undefined) return undefined;
|
|
222
|
+
|
|
223
|
+
const {builders: entries} = builderConfigDataFromJson({builders});
|
|
224
|
+
const seenEntries = new Set<string>();
|
|
225
|
+
for (const entry of entries ?? []) {
|
|
226
|
+
if (!isValidAsciiHttpUrl(entry.url)) {
|
|
227
|
+
throw Error(`Invalid builder url: ${entry.url}`);
|
|
228
|
+
}
|
|
229
|
+
const authData = entry.authData !== undefined ? toHex(fromHex(entry.authData)) : toHex(Buffer.from(entry.url));
|
|
230
|
+
const entryKey = `${entry.url}|${authData}`;
|
|
231
|
+
if (seenEntries.has(entryKey)) {
|
|
232
|
+
throw Error(`Duplicate builder entry url=${entry.url}`);
|
|
233
|
+
}
|
|
234
|
+
seenEntries.add(entryKey);
|
|
235
|
+
}
|
|
236
|
+
return entries;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Parse builder urls into builder entries. Auth data agreed with a builder out of band may be
|
|
241
|
+
* appended as a hex fragment (`https://builder.example.com#0x0123`), it is stripped from the url
|
|
242
|
+
* and never sent on the wire. Without a fragment the auth data derives from the url.
|
|
243
|
+
*/
|
|
244
|
+
export function parseBuilderUrls(urls?: string[]): BuilderEntryConfig[] | undefined {
|
|
245
|
+
if (urls === undefined) return undefined;
|
|
246
|
+
|
|
247
|
+
const entries: BuilderEntryConfig[] = [];
|
|
248
|
+
const seenEntries = new Set<string>();
|
|
249
|
+
for (const value of urls) {
|
|
250
|
+
const fragmentIndex = value.indexOf("#");
|
|
251
|
+
const url = fragmentIndex === -1 ? value : value.slice(0, fragmentIndex);
|
|
252
|
+
const authData = fragmentIndex === -1 ? undefined : value.slice(fragmentIndex + 1);
|
|
253
|
+
if (!isValidAsciiHttpUrl(url)) {
|
|
254
|
+
throw Error(`Invalid builder url: ${url}`);
|
|
255
|
+
}
|
|
256
|
+
if (Buffer.byteLength(url, "utf8") > MAX_BUILDER_URL_SIZE) {
|
|
257
|
+
throw Error(`Invalid builder url, must not exceed ${MAX_BUILDER_URL_SIZE} bytes: ${url}`);
|
|
258
|
+
}
|
|
259
|
+
if (authData !== undefined && !BUILDER_AUTH_DATA_PATTERN.test(authData)) {
|
|
260
|
+
throw Error(
|
|
261
|
+
`Invalid builder url auth data, must be a 0x-prefixed hex string of 1 to ${MAX_BUILDER_AUTH_DATA_SIZE} bytes: ${url}`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
const entryKey = `${url}|${authData !== undefined ? toHex(fromHex(authData)) : toHex(Buffer.from(url))}`;
|
|
265
|
+
if (seenEntries.has(entryKey)) {
|
|
266
|
+
throw Error(`Duplicate builder url: ${url}`);
|
|
267
|
+
}
|
|
268
|
+
seenEntries.add(entryKey);
|
|
269
|
+
entries.push({url, authData});
|
|
270
|
+
}
|
|
271
|
+
if (entries.length > MAX_BUILDER_ENTRIES) {
|
|
272
|
+
throw Error(`Number of builder urls must not exceed ${MAX_BUILDER_ENTRIES}, got ${entries.length}`);
|
|
273
|
+
}
|
|
274
|
+
return entries;
|
|
136
275
|
}
|