@aztec/aztec 0.75.0-commit.c03ba01a2a4122e43e90d5133ba017e54b90e9d2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +57 -0
  2. package/dest/bin/index.js +46 -0
  3. package/dest/cli/aztec_start_action.js +110 -0
  4. package/dest/cli/aztec_start_options.js +341 -0
  5. package/dest/cli/cli.js +33 -0
  6. package/dest/cli/cmds/start_archiver.js +35 -0
  7. package/dest/cli/cmds/start_bot.js +31 -0
  8. package/dest/cli/cmds/start_faucet.js +20 -0
  9. package/dest/cli/cmds/start_node.js +109 -0
  10. package/dest/cli/cmds/start_p2p_bootstrap.js +20 -0
  11. package/dest/cli/cmds/start_proof_verifier.js +12 -0
  12. package/dest/cli/cmds/start_prover_agent.js +32 -0
  13. package/dest/cli/cmds/start_prover_broker.js +22 -0
  14. package/dest/cli/cmds/start_prover_node.js +81 -0
  15. package/dest/cli/cmds/start_pxe.js +90 -0
  16. package/dest/cli/cmds/start_txe.js +11 -0
  17. package/dest/cli/index.js +1 -0
  18. package/dest/cli/util.js +154 -0
  19. package/dest/cli/validation.js +25 -0
  20. package/dest/examples/token.js +53 -0
  21. package/dest/examples/util.js +31 -0
  22. package/dest/index.js +1 -0
  23. package/dest/mnemonic.js +1 -0
  24. package/dest/sandbox.js +116 -0
  25. package/dest/splash.js +2 -0
  26. package/package.json +119 -0
  27. package/src/bin/index.ts +54 -0
  28. package/src/cli/aztec_start_action.ts +115 -0
  29. package/src/cli/aztec_start_options.ts +366 -0
  30. package/src/cli/cli.ts +48 -0
  31. package/src/cli/cmds/start_archiver.ts +47 -0
  32. package/src/cli/cmds/start_bot.ts +49 -0
  33. package/src/cli/cmds/start_faucet.ts +34 -0
  34. package/src/cli/cmds/start_node.ts +123 -0
  35. package/src/cli/cmds/start_p2p_bootstrap.ts +25 -0
  36. package/src/cli/cmds/start_proof_verifier.ts +18 -0
  37. package/src/cli/cmds/start_prover_agent.ts +65 -0
  38. package/src/cli/cmds/start_prover_broker.ts +37 -0
  39. package/src/cli/cmds/start_prover_node.ts +100 -0
  40. package/src/cli/cmds/start_pxe.ts +124 -0
  41. package/src/cli/cmds/start_txe.ts +15 -0
  42. package/src/cli/index.ts +1 -0
  43. package/src/cli/util.ts +216 -0
  44. package/src/cli/validation.ts +38 -0
  45. package/src/examples/token.ts +74 -0
  46. package/src/examples/util.ts +44 -0
  47. package/src/index.ts +1 -0
  48. package/src/mnemonic.ts +1 -0
  49. package/src/sandbox.ts +165 -0
  50. package/src/splash.ts +10 -0
@@ -0,0 +1,18 @@
1
+ import { type LogFn } from '@aztec/foundation/log';
2
+ import { ProofVerifier, proofVerifierConfigMappings } from '@aztec/proof-verifier';
3
+ import { initTelemetryClient, telemetryClientConfigMappings } from '@aztec/telemetry-client';
4
+
5
+ import { extractRelevantOptions } from '../util.js';
6
+
7
+ export async function startProofVerifier(options: any, signalHandlers: (() => Promise<void>)[], userLog: LogFn) {
8
+ const config = extractRelevantOptions(options, proofVerifierConfigMappings, 'proofVerifier');
9
+
10
+ const telemetryConfig = extractRelevantOptions(options, telemetryClientConfigMappings, 'tel');
11
+ const telemetry = initTelemetryClient(telemetryConfig);
12
+ const proofVerifier = await ProofVerifier.new(config, telemetry);
13
+
14
+ userLog('Starting proof verifier');
15
+ proofVerifier.start();
16
+
17
+ signalHandlers.push(() => proofVerifier.stop());
18
+ }
@@ -0,0 +1,65 @@
1
+ import { times } from '@aztec/foundation/collection';
2
+ import { type NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server';
3
+ import { type LogFn } from '@aztec/foundation/log';
4
+ import { buildServerCircuitProver } from '@aztec/prover-client';
5
+ import {
6
+ InlineProofStore,
7
+ type ProverAgentConfig,
8
+ ProvingAgent,
9
+ createProvingJobBrokerClient,
10
+ proverAgentConfigMappings,
11
+ } from '@aztec/prover-client/broker';
12
+ import { getProverNodeAgentConfigFromEnv } from '@aztec/prover-node';
13
+ import { initTelemetryClient, telemetryClientConfigMappings } from '@aztec/telemetry-client';
14
+
15
+ import { extractRelevantOptions } from '../util.js';
16
+
17
+ export async function startProverAgent(
18
+ options: any,
19
+ signalHandlers: (() => Promise<void>)[],
20
+ services: NamespacedApiHandlers,
21
+ userLog: LogFn,
22
+ ) {
23
+ if (options.node || options.sequencer || options.pxe || options.p2pBootstrap || options.txe) {
24
+ userLog(`Starting a prover agent with --node, --sequencer, --pxe, --p2p-bootstrap, or --txe is not supported.`);
25
+ process.exit(1);
26
+ }
27
+
28
+ const config = {
29
+ ...getProverNodeAgentConfigFromEnv(), // get default config from env
30
+ ...extractRelevantOptions<ProverAgentConfig>(options, proverAgentConfigMappings, 'proverAgent'), // override with command line options
31
+ };
32
+
33
+ if (config.realProofs && (!config.bbBinaryPath || !config.acvmBinaryPath)) {
34
+ process.exit(1);
35
+ }
36
+
37
+ if (!config.proverBrokerUrl) {
38
+ process.exit(1);
39
+ }
40
+
41
+ const broker = createProvingJobBrokerClient(config.proverBrokerUrl);
42
+
43
+ const telemetry = initTelemetryClient(extractRelevantOptions(options, telemetryClientConfigMappings, 'tel'));
44
+ const prover = await buildServerCircuitProver(config, telemetry);
45
+ const proofStore = new InlineProofStore();
46
+ const agents = times(
47
+ config.proverAgentCount,
48
+ () =>
49
+ new ProvingAgent(
50
+ broker,
51
+ proofStore,
52
+ prover,
53
+ config.proverAgentProofTypes,
54
+ config.proverAgentPollIntervalMs,
55
+ telemetry,
56
+ ),
57
+ );
58
+
59
+ await Promise.all(agents.map(agent => agent.start()));
60
+
61
+ signalHandlers.push(async () => {
62
+ await Promise.all(agents.map(agent => agent.stop()));
63
+ await telemetry.stop();
64
+ });
65
+ }
@@ -0,0 +1,37 @@
1
+ import { type ProvingJobBroker } from '@aztec/circuit-types';
2
+ import { type NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server';
3
+ import { type LogFn } from '@aztec/foundation/log';
4
+ import {
5
+ type ProverBrokerConfig,
6
+ ProvingJobBrokerSchema,
7
+ createAndStartProvingBroker,
8
+ proverBrokerConfigMappings,
9
+ } from '@aztec/prover-client/broker';
10
+ import { getProverNodeBrokerConfigFromEnv } from '@aztec/prover-node';
11
+ import { getConfigEnvVars as getTelemetryClientConfig, initTelemetryClient } from '@aztec/telemetry-client';
12
+
13
+ import { extractRelevantOptions } from '../util.js';
14
+
15
+ export async function startProverBroker(
16
+ options: any,
17
+ signalHandlers: (() => Promise<void>)[],
18
+ services: NamespacedApiHandlers,
19
+ userLog: LogFn,
20
+ ): Promise<ProvingJobBroker> {
21
+ if (options.node || options.sequencer || options.pxe || options.p2pBootstrap || options.txe) {
22
+ userLog(`Starting a prover broker with --node, --sequencer, --pxe, --p2p-bootstrap, or --txe is not supported.`);
23
+ process.exit(1);
24
+ }
25
+
26
+ const config: ProverBrokerConfig = {
27
+ ...getProverNodeBrokerConfigFromEnv(), // get default config from env
28
+ ...extractRelevantOptions<ProverBrokerConfig>(options, proverBrokerConfigMappings, 'proverBroker'), // override with command line options
29
+ };
30
+
31
+ const client = initTelemetryClient(getTelemetryClientConfig());
32
+ const broker = await createAndStartProvingBroker(config, client);
33
+ services.proverBroker = [broker, ProvingJobBrokerSchema];
34
+ signalHandlers.push(() => broker.stop());
35
+
36
+ return broker;
37
+ }
@@ -0,0 +1,100 @@
1
+ import { P2PApiSchema, ProverNodeApiSchema, type ProvingJobBroker, createAztecNodeClient } from '@aztec/circuit-types';
2
+ import { NULL_KEY } from '@aztec/ethereum';
3
+ import { type NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server';
4
+ import { type LogFn } from '@aztec/foundation/log';
5
+ import { ProvingJobConsumerSchema, createProvingJobBrokerClient } from '@aztec/prover-client/broker';
6
+ import {
7
+ type ProverNodeConfig,
8
+ createProverNode,
9
+ getProverNodeConfigFromEnv,
10
+ proverNodeConfigMappings,
11
+ } from '@aztec/prover-node';
12
+ import { initTelemetryClient, telemetryClientConfigMappings } from '@aztec/telemetry-client';
13
+
14
+ import { mnemonicToAccount } from 'viem/accounts';
15
+
16
+ import { extractRelevantOptions } from '../util.js';
17
+ import { validateL1Config } from '../validation.js';
18
+ import { startProverBroker } from './start_prover_broker.js';
19
+
20
+ export async function startProverNode(
21
+ options: any,
22
+ signalHandlers: (() => Promise<void>)[],
23
+ services: NamespacedApiHandlers,
24
+ userLog: LogFn,
25
+ ) {
26
+ if (options.node || options.sequencer || options.pxe || options.p2pBootstrap || options.txe) {
27
+ userLog(`Starting a prover-node with --node, --sequencer, --pxe, --p2p-bootstrap, or --txe is not supported.`);
28
+ process.exit(1);
29
+ }
30
+
31
+ const proverConfig = {
32
+ ...getProverNodeConfigFromEnv(), // get default config from env
33
+ ...extractRelevantOptions<ProverNodeConfig>(options, proverNodeConfigMappings, 'proverNode'), // override with command line options
34
+ };
35
+
36
+ if (!options.archiver && !proverConfig.archiverUrl) {
37
+ userLog('--archiver.archiverUrl is required to start a Prover Node without --archiver option');
38
+ process.exit(1);
39
+ }
40
+
41
+ if (!proverConfig.publisherPrivateKey || proverConfig.publisherPrivateKey === NULL_KEY) {
42
+ if (!options.l1Mnemonic) {
43
+ userLog(`--l1-mnemonic is required to start a Prover Node without --node.publisherPrivateKey`);
44
+ process.exit(1);
45
+ }
46
+ const hdAccount = mnemonicToAccount(options.l1Mnemonic);
47
+ const privKey = hdAccount.getHdKey().privateKey;
48
+ proverConfig.publisherPrivateKey = `0x${Buffer.from(privKey!).toString('hex')}`;
49
+ }
50
+
51
+ // TODO(palla/prover-node) L1 contract addresses should not silently default to zero,
52
+ // they should be undefined if not set and fail loudly.
53
+ // Load l1 contract addresses from aztec node if not set.
54
+ const isRollupAddressSet =
55
+ proverConfig.l1Contracts?.rollupAddress && !proverConfig.l1Contracts.rollupAddress.isZero();
56
+ const nodeUrl = proverConfig.nodeUrl ?? proverConfig.proverCoordinationNodeUrl;
57
+ if (nodeUrl && !isRollupAddressSet) {
58
+ userLog(`Loading L1 contract addresses from aztec node at ${nodeUrl}`);
59
+ proverConfig.l1Contracts = await createAztecNodeClient(nodeUrl).getL1ContractAddresses();
60
+ }
61
+
62
+ // If we create an archiver here, validate the L1 config
63
+ if (options.archiver) {
64
+ await validateL1Config(proverConfig);
65
+ }
66
+
67
+ const telemetry = initTelemetryClient(extractRelevantOptions(options, telemetryClientConfigMappings, 'tel'));
68
+
69
+ let broker: ProvingJobBroker;
70
+ if (proverConfig.proverBrokerUrl) {
71
+ broker = createProvingJobBrokerClient(proverConfig.proverBrokerUrl);
72
+ } else if (options.proverBroker) {
73
+ broker = await startProverBroker(options, signalHandlers, services, userLog);
74
+ } else {
75
+ userLog(`--prover-broker-url or --prover-broker is required to start a Prover Node`);
76
+ process.exit(1);
77
+ }
78
+
79
+ if (proverConfig.proverAgentCount === 0) {
80
+ userLog(
81
+ `Running prover node without local prover agent. Connect one or more prover agents to this node or pass --proverAgent.proverAgentCount`,
82
+ );
83
+ }
84
+
85
+ const proverNode = await createProverNode(proverConfig, { telemetry, broker });
86
+ services.proverNode = [proverNode, ProverNodeApiSchema];
87
+
88
+ const p2p = proverNode.getP2P();
89
+ if (p2p) {
90
+ services.p2p = [proverNode.getP2P(), P2PApiSchema];
91
+ }
92
+
93
+ if (!proverConfig.proverBrokerUrl) {
94
+ services.provingJobSource = [proverNode.getProver().getProvingJobSource(), ProvingJobConsumerSchema];
95
+ }
96
+
97
+ signalHandlers.push(proverNode.stop.bind(proverNode));
98
+
99
+ await proverNode.start();
100
+ }
@@ -0,0 +1,124 @@
1
+ import {
2
+ type ContractArtifact,
3
+ type ContractInstanceWithAddress,
4
+ Fr,
5
+ PublicKeys,
6
+ getContractClassFromArtifact,
7
+ } from '@aztec/aztec.js';
8
+ import { type AztecNode, PXESchema, createAztecNodeClient } from '@aztec/circuit-types';
9
+ import { getContractArtifact } from '@aztec/cli/cli-utils';
10
+ import { type NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server';
11
+ import { type LogFn } from '@aztec/foundation/log';
12
+ import {
13
+ AztecAddress,
14
+ type CliPXEOptions,
15
+ type PXEServiceConfig,
16
+ allPxeConfigMappings,
17
+ createPXEService,
18
+ } from '@aztec/pxe';
19
+ import { makeTracedFetch } from '@aztec/telemetry-client';
20
+ import { L2BasicContractsMap, Network } from '@aztec/types/network';
21
+
22
+ import { extractRelevantOptions } from '../util.js';
23
+
24
+ const contractAddressesUrl = 'http://static.aztec.network';
25
+
26
+ export async function startPXE(
27
+ options: any,
28
+ signalHandlers: (() => Promise<void>)[],
29
+ services: NamespacedApiHandlers,
30
+ userLog: LogFn,
31
+ ) {
32
+ await addPXE(options, signalHandlers, services, userLog, {});
33
+ return services;
34
+ }
35
+
36
+ function isValidNetwork(value: any): value is Network {
37
+ return Object.values(Network).includes(value);
38
+ }
39
+
40
+ async function fetchBasicContractAddresses(url: string) {
41
+ const response = await fetch(url);
42
+ if (!response.ok) {
43
+ throw new Error(`Failed to fetch basic contract addresses from ${url}`);
44
+ }
45
+ return response.json();
46
+ }
47
+
48
+ export async function addPXE(
49
+ options: any,
50
+ signalHandlers: (() => Promise<void>)[],
51
+ services: NamespacedApiHandlers,
52
+ userLog: LogFn,
53
+ deps: { node?: AztecNode } = {},
54
+ ) {
55
+ const pxeConfig = extractRelevantOptions<PXEServiceConfig & CliPXEOptions>(options, allPxeConfigMappings, 'pxe');
56
+
57
+ let nodeUrl;
58
+ if (pxeConfig.network) {
59
+ if (isValidNetwork(pxeConfig.network)) {
60
+ if (!pxeConfig.apiKey && !pxeConfig.nodeUrl) {
61
+ userLog(`API Key or Aztec Node URL is required to connect to ${pxeConfig.network}`);
62
+ process.exit(1);
63
+ } else if (pxeConfig.apiKey) {
64
+ nodeUrl = `https://api.aztec.network/${pxeConfig.network}/aztec-node-1/${pxeConfig.apiKey}`;
65
+ } else if (pxeConfig.nodeUrl) {
66
+ nodeUrl = pxeConfig.nodeUrl;
67
+ }
68
+ } else {
69
+ userLog(`Network ${pxeConfig.network} is not supported`);
70
+ process.exit(1);
71
+ }
72
+ } else {
73
+ nodeUrl = pxeConfig.nodeUrl;
74
+ }
75
+ if (!nodeUrl && !deps.node && !pxeConfig.network) {
76
+ userLog('Aztec Node URL (nodeUrl | AZTEC_NODE_URL) option is required to start PXE without --node option');
77
+ process.exit(1);
78
+ }
79
+
80
+ const node = deps.node ?? createAztecNodeClient(nodeUrl!, makeTracedFetch([1, 2, 3], true));
81
+ const pxe = await createPXEService(node, pxeConfig as PXEServiceConfig);
82
+
83
+ // register basic contracts
84
+ if (pxeConfig.network) {
85
+ userLog(`Registering basic contracts for ${pxeConfig.network}`);
86
+ const basicContractsInfo = await fetchBasicContractAddresses(
87
+ `${contractAddressesUrl}/${pxeConfig.network}/basic_contracts.json`,
88
+ );
89
+ const l2Contracts: Record<
90
+ string,
91
+ { name: string; address: AztecAddress; initHash: Fr; salt: Fr; artifact: ContractArtifact }
92
+ > = {};
93
+ for (const [key, artifactName] of Object.entries(L2BasicContractsMap[pxeConfig.network as Network])) {
94
+ l2Contracts[key] = {
95
+ name: key,
96
+ address: AztecAddress.fromString(basicContractsInfo[key].address),
97
+ initHash: Fr.fromHexString(basicContractsInfo[key].initHash),
98
+ salt: Fr.fromHexString(basicContractsInfo[key].salt),
99
+ artifact: await getContractArtifact(artifactName, userLog),
100
+ };
101
+ }
102
+
103
+ await Promise.all(
104
+ Object.values(l2Contracts).map(async ({ name, address, artifact, initHash, salt }) => {
105
+ const instance: ContractInstanceWithAddress = {
106
+ version: 1,
107
+ salt,
108
+ initializationHash: initHash,
109
+ address,
110
+ deployer: AztecAddress.ZERO,
111
+ contractClassId: (await getContractClassFromArtifact(artifact!)).id,
112
+ publicKeys: PublicKeys.default(),
113
+ };
114
+ userLog(`Registering ${name} at ${address.toString()}`);
115
+ await pxe.registerContract({ artifact, instance });
116
+ }),
117
+ );
118
+ }
119
+
120
+ // Add PXE to services list
121
+ services.pxe = [pxe, PXESchema];
122
+
123
+ return pxe;
124
+ }
@@ -0,0 +1,15 @@
1
+ import { startHttpRpcServer } from '@aztec/foundation/json-rpc/server';
2
+ import { type Logger } from '@aztec/foundation/log';
3
+ import { createTXERpcServer } from '@aztec/txe';
4
+
5
+ export async function startTXE(options: any, debugLogger: Logger) {
6
+ debugLogger.info(`Setting up TXE...`);
7
+
8
+ const txeServer = createTXERpcServer(debugLogger);
9
+ const { port } = await startHttpRpcServer(txeServer, {
10
+ port: options.port,
11
+ timeoutMs: 1e3 * 60 * 5,
12
+ });
13
+
14
+ debugLogger.info(`TXE listening on port ${port}`);
15
+ }
@@ -0,0 +1 @@
1
+ export * from './cli.js';
@@ -0,0 +1,216 @@
1
+ import { type AccountManager, type Fr } from '@aztec/aztec.js';
2
+ import { type ConfigMappingsType } from '@aztec/foundation/config';
3
+ import { type LogFn } from '@aztec/foundation/log';
4
+ import { type PXEService } from '@aztec/pxe';
5
+
6
+ import chalk from 'chalk';
7
+ import { type Command } from 'commander';
8
+
9
+ import { type AztecStartOption, aztecStartOptions } from './aztec_start_options.js';
10
+
11
+ export const installSignalHandlers = (logFn: LogFn, cb?: Array<() => Promise<void>>) => {
12
+ const shutdown = async () => {
13
+ logFn('Shutting down...');
14
+ if (cb) {
15
+ await Promise.all(cb);
16
+ }
17
+ process.exit(0);
18
+ };
19
+ process.removeAllListeners('SIGINT');
20
+ process.removeAllListeners('SIGTERM');
21
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
22
+ process.once('SIGINT', shutdown);
23
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
24
+ process.once('SIGTERM', shutdown);
25
+ };
26
+
27
+ /**
28
+ * Creates logs for the initial accounts
29
+ * @param accounts - The initial accounts
30
+ * @param pxe - A PXE instance to get the registered accounts
31
+ * @returns A string array containing the initial accounts details
32
+ */
33
+ export async function createAccountLogs(
34
+ accountsWithSecretKeys: {
35
+ /**
36
+ * The account object
37
+ */
38
+ account: AccountManager;
39
+ /**
40
+ * The secret key of the account
41
+ */
42
+ secretKey: Fr;
43
+ }[],
44
+ pxe: PXEService,
45
+ ) {
46
+ const registeredAccounts = await pxe.getRegisteredAccounts();
47
+ const accountLogStrings = [`Initial Accounts:\n\n`];
48
+ for (const accountWithSecretKey of accountsWithSecretKeys) {
49
+ const completeAddress = await accountWithSecretKey.account.getCompleteAddress();
50
+ if (registeredAccounts.find(a => a.equals(completeAddress))) {
51
+ accountLogStrings.push(` Address: ${completeAddress.address.toString()}\n`);
52
+ accountLogStrings.push(` Partial Address: ${completeAddress.partialAddress.toString()}\n`);
53
+ accountLogStrings.push(` Secret Key: ${accountWithSecretKey.secretKey.toString()}\n`);
54
+ accountLogStrings.push(
55
+ ` Master nullifier public key: ${completeAddress.publicKeys.masterNullifierPublicKey.toString()}\n`,
56
+ );
57
+ accountLogStrings.push(
58
+ ` Master incoming viewing public key: ${completeAddress.publicKeys.masterIncomingViewingPublicKey.toString()}\n\n`,
59
+ );
60
+ accountLogStrings.push(
61
+ ` Master outgoing viewing public key: ${completeAddress.publicKeys.masterOutgoingViewingPublicKey.toString()}\n\n`,
62
+ );
63
+ accountLogStrings.push(
64
+ ` Master tagging public key: ${completeAddress.publicKeys.masterTaggingPublicKey.toString()}\n\n`,
65
+ );
66
+ }
67
+ }
68
+ return accountLogStrings;
69
+ }
70
+
71
+ export function getMaxLengths(sections: { [key: string]: AztecStartOption[] }): [number, number] {
72
+ let maxFlagLength = 0;
73
+ let maxDefaultLength = 0;
74
+
75
+ Object.values(sections).forEach(options => {
76
+ options.forEach(option => {
77
+ if (option.flag.length > maxFlagLength) {
78
+ maxFlagLength = option.flag.length;
79
+ }
80
+ const defaultLength = option.defaultValue ? option.defaultValue.length : 0;
81
+ if (defaultLength > maxDefaultLength) {
82
+ maxDefaultLength = defaultLength;
83
+ }
84
+ });
85
+ });
86
+
87
+ return [maxFlagLength + 1, maxDefaultLength + 1];
88
+ }
89
+
90
+ export function formatHelpLine(
91
+ option: string,
92
+ defaultValue: string,
93
+ envVar: string,
94
+ maxOptionLength: number,
95
+ maxDefaultLength: number,
96
+ ): string {
97
+ const paddedOption = option.padEnd(maxOptionLength + 2, ' ');
98
+ const paddedDefault = defaultValue.padEnd(maxDefaultLength + 2, ' ');
99
+
100
+ return `${chalk.cyan(paddedOption)}${chalk.yellow(paddedDefault)}${chalk.green(envVar)}`;
101
+ }
102
+
103
+ const getDefaultOrEnvValue = (opt: AztecStartOption) => {
104
+ let val;
105
+ // if the option is set in the environment, use that & parse it
106
+ if (opt.envVar && process.env[opt.envVar]) {
107
+ val = process.env[opt.envVar];
108
+ if (val && opt.parseVal) {
109
+ return opt.parseVal(val);
110
+ }
111
+ // if no env variable, use the default value
112
+ } else if (opt.defaultValue) {
113
+ val = opt.defaultValue;
114
+ }
115
+
116
+ return val;
117
+ };
118
+
119
+ // Function to add options dynamically
120
+ export const addOptions = (cmd: Command, options: AztecStartOption[]) => {
121
+ options.forEach(opt => {
122
+ cmd.option(
123
+ opt.flag,
124
+ `${opt.description} (default: ${opt.defaultValue}) ($${opt.envVar})`,
125
+ opt.parseVal ? opt.parseVal : val => val,
126
+ getDefaultOrEnvValue(opt),
127
+ );
128
+ });
129
+ };
130
+
131
+ export const printAztecStartHelpText = () => {
132
+ const helpTextLines: string[] = [''];
133
+ const [maxFlagLength, maxDefaultLength] = getMaxLengths(aztecStartOptions);
134
+
135
+ Object.keys(aztecStartOptions).forEach(category => {
136
+ helpTextLines.push(chalk.bold.blue(` ${category}`));
137
+ helpTextLines.push('');
138
+
139
+ aztecStartOptions[category].forEach(opt => {
140
+ const defaultValueText = opt.defaultValue
141
+ ? `(default: ${opt.printDefault ? opt.printDefault(opt.defaultValue) : opt.defaultValue})`
142
+ : '';
143
+ const envVarText = opt.envVar ? `($${opt.envVar})` : '';
144
+ const flagText = `${opt.flag}`;
145
+
146
+ const paddedText = formatHelpLine(flagText, defaultValueText, envVarText, maxFlagLength, maxDefaultLength);
147
+
148
+ helpTextLines.push(` ${paddedText}`);
149
+ helpTextLines.push(` ${chalk.white(opt.description)}`);
150
+ helpTextLines.push('');
151
+ });
152
+ });
153
+
154
+ return helpTextLines.join('\n');
155
+ };
156
+
157
+ /**
158
+ * Extracts namespaced options from a key-value map.
159
+ * @param options - Key-value map of options.
160
+ * @param namespace - The namespace to extract.
161
+ * @returns Key-value map of namespaced options.
162
+ */
163
+ export const extractNamespacedOptions = (options: Record<string, any>, namespace: string) => {
164
+ const extract = `${namespace}.`;
165
+ const namespacedOptions: Record<string, any> = {};
166
+ for (const key in options) {
167
+ if (key.startsWith(extract)) {
168
+ namespacedOptions[key.replace(extract, '')] = options[key];
169
+ }
170
+ }
171
+ return namespacedOptions;
172
+ };
173
+
174
+ /**
175
+ * Extracts relevant options from a key-value map.
176
+ * @template T - The type of the relevant options.
177
+ * @param options - Key-value map of options.
178
+ * @param mappings - The mappings to extract.
179
+ * @param namespace - The namespace to extract for.
180
+ * @returns Key-value map of relevant options.
181
+ */
182
+ export const extractRelevantOptions = <T>(
183
+ options: Record<string, any>,
184
+ mappings: ConfigMappingsType<T>,
185
+ namespace: string,
186
+ ): T => {
187
+ const relevantOptions: T = {} as T;
188
+
189
+ // Iterate over each key in the options
190
+ Object.keys(options).forEach(optionKey => {
191
+ const keyParts = optionKey.split('.');
192
+ const optionNamespace = keyParts.length > 1 ? keyParts[0] : '';
193
+ const mainKey = keyParts.length > 1 ? keyParts[1] : keyParts[0];
194
+
195
+ // Check if the key exists in the mappings
196
+ if (mainKey in mappings) {
197
+ // Check for duplicates in the options
198
+ const duplicates = Object.keys(options).filter(optKey => {
199
+ const optKeyParts = optKey.split('.');
200
+ return optKeyParts[1] === mainKey || optKeyParts[0] === mainKey;
201
+ });
202
+
203
+ // If duplicates are found, use the namespace to differentiate
204
+ if (duplicates.length > 1) {
205
+ if (namespace === optionNamespace) {
206
+ relevantOptions[mainKey as keyof T] = options[optionKey];
207
+ }
208
+ } else {
209
+ // If no duplicates, extract the value without considering the namespace
210
+ relevantOptions[mainKey as keyof T] = options[optionKey];
211
+ }
212
+ }
213
+ });
214
+
215
+ return relevantOptions;
216
+ };
@@ -0,0 +1,38 @@
1
+ import {
2
+ type L1ContractAddresses,
3
+ type L1ContractsConfig,
4
+ getL1ContractsAddresses,
5
+ getL1ContractsConfig,
6
+ getPublicClient,
7
+ } from '@aztec/ethereum';
8
+
9
+ /**
10
+ * Connects to L1 using the provided L1 RPC URL and reads all addresses and settings from the governance
11
+ * contract. For each key, compares it against the provided config (if it is not empty) and throws on mismatches.
12
+ */
13
+ export async function validateL1Config(
14
+ config: L1ContractsConfig & { l1Contracts: L1ContractAddresses } & { l1ChainId: number; l1RpcUrl: string },
15
+ ) {
16
+ const publicClient = getPublicClient(config);
17
+ const actualAddresses = await getL1ContractsAddresses(publicClient, config.l1Contracts.governanceAddress);
18
+
19
+ for (const keyStr in actualAddresses) {
20
+ const key = keyStr as keyof Awaited<ReturnType<typeof getL1ContractsAddresses>>;
21
+ const actual = actualAddresses[key];
22
+ const expected = config.l1Contracts[key];
23
+
24
+ if (expected !== undefined && !expected.isZero() && !actual.equals(expected)) {
25
+ throw new Error(`Expected L1 contract address ${key} to be ${expected} but found ${actual}`);
26
+ }
27
+ }
28
+
29
+ const actualConfig = await getL1ContractsConfig(publicClient, actualAddresses);
30
+ for (const keyStr in actualConfig) {
31
+ const key = keyStr as keyof Awaited<ReturnType<typeof getL1ContractsConfig>> & keyof L1ContractsConfig;
32
+ const actual = actualConfig[key];
33
+ const expected = config[key];
34
+ if (expected !== undefined && actual !== expected) {
35
+ throw new Error(`Expected L1 setting ${key} to be ${expected} but found ${actual}`);
36
+ }
37
+ }
38
+ }