diff --git a/yarn-project/archiver/src/config.ts b/yarn-project/archiver/src/config.ts index 947906524b03..ba70b1995381 100644 --- a/yarn-project/archiver/src/config.ts +++ b/yarn-project/archiver/src/config.ts @@ -4,8 +4,8 @@ import { type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum/l1- import { type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, numberConfigHelper, optionalNumberConfigHelper, } from '@aztec/foundation/config'; @@ -94,7 +94,7 @@ export const archiverConfigMappings: ConfigMappingsType = compos * @returns The archiver configuration. */ export function getArchiverConfigFromEnv(): ArchiverConfig { - return getConfigFromMappings(archiverConfigMappings); + return buildConfigFromEnv(archiverConfigMappings); } /** Extracts the archiver-specific configuration from the full ArchiverConfig */ diff --git a/yarn-project/aztec-node/src/aztec-node/config.ts b/yarn-project/aztec-node/src/aztec-node/config.ts index c243199f8def..d783bd155e78 100644 --- a/yarn-project/aztec-node/src/aztec-node/config.ts +++ b/yarn-project/aztec-node/src/aztec-node/config.ts @@ -3,8 +3,8 @@ import type { GenesisStateConfig } from '@aztec/ethereum/config'; import { type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; import { @@ -97,7 +97,7 @@ export const aztecNodeConfigMappings: ConfigMappingsType = comp * @returns A valid aztec node config. */ export function getConfigEnvVars(): AztecNodeConfig { - return getConfigFromMappings(aztecNodeConfigMappings); + return buildConfigFromEnv(aztecNodeConfigMappings); } type ConfigRequiredToBuildKeyStore = SequencerClientConfig & SharedNodeConfig & ValidatorClientConfig; diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index 108e35adf1d8..c450b52fa0f4 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -67,6 +67,7 @@ "commander": "^12.1.0", "koa": "^2.16.1", "koa-router": "^13.1.1", + "lodash.kebabcase": "^4.1.1", "viem": "npm:@aztec/viem@2.38.2" }, "files": [ @@ -80,6 +81,7 @@ "@jest/globals": "^30.0.0", "@types/jest": "^30.0.0", "@types/koa": "^2.15.0", + "@types/lodash.kebabcase": "^4", "@typescript/native-preview": "7.0.0-dev.20260113.1", "jest": "^30.0.0", "ts-node": "^10.9.1", diff --git a/yarn-project/aztec/src/bin/index.ts b/yarn-project/aztec/src/bin/index.ts index a47a27bf2f34..870b3ca4e05f 100644 --- a/yarn-project/aztec/src/bin/index.ts +++ b/yarn-project/aztec/src/bin/index.ts @@ -2,14 +2,11 @@ // import { injectCommands as injectBuilderCommands } from '@aztec/builder'; import { injectCommands as injectAztecNodeCommands } from '@aztec/cli/aztec_node'; -import { enrichEnvironmentWithChainName } from '@aztec/cli/config/chain'; -import { enrichEnvironmentWithNetworkConfig } from '@aztec/cli/config/network'; import { injectCommands as injectContractCommands } from '@aztec/cli/contracts'; import { injectCommands as injectInfrastructureCommands } from '@aztec/cli/infrastructure'; import { injectCommands as injectL1Commands } from '@aztec/cli/l1'; import { injectCommands as injectMiscCommands } from '@aztec/cli/misc'; import { injectCommands as injectValidatorKeysCommands } from '@aztec/cli/validator_keys'; -import { getActiveNetworkName } from '@aztec/foundation/config'; import { createConsoleLogger, createLogger } from '@aztec/foundation/log'; import { getPackageVersion } from '@aztec/stdlib/update-checker'; @@ -20,8 +17,6 @@ import { injectMigrateCommand } from '../cli/cmds/migrate_ha_db.js'; import { injectProfileCommand } from '../cli/cmds/profile.js'; import { injectAztecCommands } from '../cli/index.js'; -const NETWORK_FLAG = 'network'; - const userLog = createConsoleLogger(); const debugLogger = createLogger('cli'); @@ -33,20 +28,6 @@ async function main() { process.once('SIGINT', shutdown); process.once('SIGTERM', shutdown); - // Intercept the setting of a network and enrich the environment with defaults for that network - let networkValue: string | undefined; - - const args = process.argv.slice(2); - const networkIndex = args.findIndex(arg => arg.startsWith(`--${NETWORK_FLAG}=`) || arg === `--${NETWORK_FLAG}`); - - if (networkIndex !== -1) { - networkValue = args[networkIndex].split('=')[1] || args[networkIndex + 1]; - } - - const networkName = getActiveNetworkName(networkValue); - await enrichEnvironmentWithNetworkConfig(networkName); - enrichEnvironmentWithChainName(networkName); - const cliVersion = getPackageVersion(); let program = new Command('aztec'); program.description('Aztec command line interface').version(cliVersion).enablePositionalOptions(); diff --git a/yarn-project/aztec/src/cli/api_config.ts b/yarn-project/aztec/src/cli/api_config.ts new file mode 100644 index 000000000000..afc3e1059678 --- /dev/null +++ b/yarn-project/aztec/src/cli/api_config.ts @@ -0,0 +1,64 @@ +import { + type ConfigMappingsType, + booleanConfigHelper, + composeConfigMappings, + numberConfigHelper, +} from '@aztec/foundation/config'; +import { type NodeRPCConfig, nodeRpcConfigMappings } from '@aztec/stdlib/config'; + +type OwnApiConfig = { + port: number; + adminPort: number; + adminApiKeyHash: string | undefined; + disableAdminApiKey: boolean; + resetAdminApiKey: boolean; + nodeDebug: boolean; + apiPrefix: string; +}; + +export type ApiConfig = OwnApiConfig & NodeRPCConfig; + +const ownApiConfigMappings: ConfigMappingsType = { + port: { + env: 'AZTEC_PORT', + description: 'Port to run the Aztec Services on', + ...numberConfigHelper(8080), + }, + adminPort: { + env: 'AZTEC_ADMIN_PORT', + description: 'Port to run admin APIs of Aztec Services on', + ...numberConfigHelper(8880), + }, + adminApiKeyHash: { + env: 'AZTEC_ADMIN_API_KEY_HASH', + description: + 'SHA-256 hex hash of a pre-generated admin API key. When set, the node uses this hash for authentication instead of auto-generating a key.', + }, + disableAdminApiKey: { + env: 'AZTEC_DISABLE_ADMIN_API_KEY', + description: + 'Disable API key authentication on the admin RPC endpoint. By default, a key is auto-generated, displayed once, and its hash is persisted.', + ...booleanConfigHelper(false), + }, + resetAdminApiKey: { + env: 'AZTEC_RESET_ADMIN_API_KEY', + description: + 'Force-generate a new admin API key, replacing any previously persisted key hash. The new key is displayed once at startup.', + ...booleanConfigHelper(false), + }, + nodeDebug: { + env: 'AZTEC_NODE_DEBUG', + description: 'Expose debug endpoints (e.g. mineBlock) on the main RPC port', + ...booleanConfigHelper(false), + }, + apiPrefix: { + env: 'API_PREFIX', + description: 'Prefix for API routes on any service that is started', + defaultValue: '', + }, +}; + +export const apiConfigMappings: ConfigMappingsType = composeConfigMappings( + ownApiConfigMappings, + nodeRpcConfigMappings, +); diff --git a/yarn-project/aztec/src/cli/aztec_start_action.ts b/yarn-project/aztec/src/cli/aztec_start_action.ts index 2a0800d7a954..0a5d742091bc 100644 --- a/yarn-project/aztec/src/cli/aztec_start_action.ts +++ b/yarn-project/aztec/src/cli/aztec_start_action.ts @@ -1,3 +1,4 @@ +import { getChainConfigLayer, getNetworkConfig } from '@aztec/cli/config'; import { getActiveNetworkName } from '@aztec/foundation/config'; import { type NamespacedApiHandlers, @@ -8,14 +9,16 @@ import { import type { LogFn, Logger } from '@aztec/foundation/log'; import type { ChainConfig } from '@aztec/stdlib/config'; import { AztecNodeAdminApiSchema, AztecNodeApiSchema, AztecNodeDebugApiSchema } from '@aztec/stdlib/interfaces/client'; +import { dataConfigMappings } from '@aztec/stdlib/kv-store'; import { getPackageVersion } from '@aztec/stdlib/update-checker'; import { getVersioningMiddleware } from '@aztec/stdlib/versioning'; import { getOtelJsonRpcPropagationMiddleware } from '@aztec/telemetry-client'; -import { createLocalNetwork } from '../local-network/index.js'; +import { type LocalNetworkConfig, createLocalNetwork, localNetworkConfigMappings } from '../local-network/index.js'; import { github, splash } from '../splash.js'; import { resolveAdminApiKey } from './admin_api_key_store.js'; -import { extractNamespacedOptions, installSignalHandlers } from './util.js'; +import { apiConfigMappings } from './api_config.js'; +import { createConfigResolver, installSignalHandlers } from './util.js'; import { getVersions } from './versioning.js'; export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logger) { @@ -26,25 +29,43 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg const packageVersion = getPackageVersion(); let config: ChainConfig | undefined = undefined; + // Resolve network name from CLI flag or env, then fetch remote config and chain defaults. + const networkName = getActiveNetworkName(options.network); + // For caching the network config fetch, use whichever data directory is explicitly available + // before full resolution. The full resolver isn't built yet (it needs the network config). + const earlyDataDir = options.dataDirectory ?? process.env.DATA_DIRECTORY; + const cacheDir = earlyDataDir ? `${earlyDataDir}/cache` : undefined; + const remoteNetworkConfig = networkName !== 'local' ? await getNetworkConfig(networkName, cacheDir) : undefined; + const chainConfigLayer = getChainConfigLayer(networkName); + const resolveConfig = createConfigResolver(options, remoteNetworkConfig, chainConfigLayer); + const apiConfig = resolveConfig(apiConfigMappings); + const { dataDirectory: resolvedDataDirectory } = resolveConfig(dataConfigMappings); + if (options.localNetwork) { - const localNetwork = extractNamespacedOptions(options, 'localNetwork'); userLog(`${splash}\n${github}\n\n`); userLog(`Setting up Aztec local network ${packageVersion}, please stand by...`); - const { node, stop } = await createLocalNetwork( - { - l1Mnemonic: localNetwork.l1Mnemonic, - l1RpcUrls: options.l1RpcUrls, - testAccounts: localNetwork.testAccounts, - realProofs: false, - // Setting the epoch duration to 2 by default for local network. This allows the epoch to be "proven" faster, so - // the users can consume out hash without having to wait for a long time. - // Note: We are not proving anything in the local network (realProofs == false). But in `createLocalNetwork`, - // the EpochTestSettler will set the out hash to the outbox when an epoch is complete. - aztecEpochDuration: 2, - }, - userLog, - ); + // testAccounts lives on the genesis state mapping (default: false). For local network the default + // should be true, so override the mapping default unless the user explicitly opted in or out via + // env or CLI. + const testAccountsExplicit = + options['localNetwork.testAccounts'] !== undefined || + (process.env.TEST_ACCOUNTS !== undefined && process.env.TEST_ACCOUNTS !== ''); + + const baseLocalConfig = resolveConfig(localNetworkConfigMappings, 'localNetwork'); + const localNetworkConfig: LocalNetworkConfig = { + ...baseLocalConfig, + testAccounts: testAccountsExplicit ? baseLocalConfig.testAccounts : true, + // Local network always runs without real proofs. + realProofs: false, + // Setting the epoch duration to 2 by default for local network. This allows the epoch to be "proven" faster, so + // the users can consume out hash without having to wait for a long time. + // Note: We are not proving anything in the local network (realProofs == false). But in `createLocalNetwork`, + // the EpochTestSettler will set the out hash to the outbox when an epoch is complete. + aztecEpochDuration: 2, + }; + + const { node, stop } = await createLocalNetwork(localNetworkConfig, userLog); // Start Node and PXE JSON-RPC server signalHandlers.push(stop); @@ -66,23 +87,30 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg if (options.node) { const { startNode } = await import('./cmds/start_node.js'); - const networkName = getActiveNetworkName(options.network); - ({ config } = await startNode(options, signalHandlers, services, adminServices, userLog, networkName)); - if (options.nodeDebug && services.node) { + ({ config } = await startNode( + options, + signalHandlers, + services, + adminServices, + userLog, + networkName, + resolveConfig, + )); + if (apiConfig.nodeDebug && services.node) { services.nodeDebug = [services.node[0], AztecNodeDebugApiSchema]; } } else if (options.bot) { const { startBot } = await import('./cmds/start_bot.js'); - await startBot(options, signalHandlers, services, userLog); + await startBot(options, signalHandlers, services, userLog, resolveConfig); } else if (options.p2pBootstrap) { const { startP2PBootstrap } = await import('./cmds/start_p2p_bootstrap.js'); - ({ config } = await startP2PBootstrap(options, signalHandlers, services, userLog)); + ({ config } = await startP2PBootstrap(signalHandlers, services, userLog, resolveConfig)); } else if (options.proverAgent) { const { startProverAgent } = await import('./cmds/start_prover_agent.js'); - await startProverAgent(options, signalHandlers, services, userLog); + await startProverAgent(options, signalHandlers, services, userLog, resolveConfig); } else if (options.proverBroker) { const { startProverBroker } = await import('./cmds/start_prover_broker.js'); - await startProverBroker(options, signalHandlers, services, userLog); + await startProverBroker(options, signalHandlers, services, userLog, resolveConfig); } else if (options.txe) { const { startTXE } = await import('./cmds/start_txe.js'); await startTXE(options, signalHandlers, debugLogger); @@ -105,10 +133,10 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg http200OnError: false, log: debugLogger, middlewares: [getOtelJsonRpcPropagationMiddleware(), getVersioningMiddleware(versions, versioningOpts)], - maxBatchSize: options.rpcMaxBatchSize, - maxBodySizeBytes: options.rpcMaxBodySize, + maxBatchSize: apiConfig.rpcMaxBatchSize, + maxBodySizeBytes: apiConfig.rpcMaxBodySize, }); - const { port } = await startHttpRpcServer(rpcServer, { port: options.port }); + const { port } = await startHttpRpcServer(rpcServer, { apiPrefix: apiConfig.apiPrefix, port: apiConfig.port }); debugLogger.info(`Aztec Server listening on port ${port}`, versions); } @@ -119,10 +147,10 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg // Resolve the admin API key (auto-generated and persisted, or opt-out) const apiKeyResolution = await resolveAdminApiKey( { - adminApiKeyHash: options.adminApiKeyHash, - disableAdminApiKey: options.disableAdminApiKey, - resetAdminApiKey: options.resetAdminApiKey, - dataDirectory: options.dataDirectory, + adminApiKeyHash: apiConfig.adminApiKeyHash, + disableAdminApiKey: apiConfig.disableAdminApiKey, + resetAdminApiKey: apiConfig.resetAdminApiKey, + dataDirectory: resolvedDataDirectory, }, debugLogger, ); @@ -136,10 +164,10 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg http200OnError: false, log: debugLogger, middlewares: adminMiddlewares, - maxBatchSize: options.rpcMaxBatchSize, - maxBodySizeBytes: options.rpcMaxBodySize, + maxBatchSize: apiConfig.rpcMaxBatchSize, + maxBodySizeBytes: apiConfig.rpcMaxBodySize, }); - const { port } = await startHttpRpcServer(rpcServer, { port: options.adminPort }); + const { port } = await startHttpRpcServer(rpcServer, { apiPrefix: apiConfig.apiPrefix, port: apiConfig.adminPort }); debugLogger.info(`Aztec Server admin API listening on port ${port}`, versions); // Display the API key after the server has started diff --git a/yarn-project/aztec/src/cli/aztec_start_options.test.ts b/yarn-project/aztec/src/cli/aztec_start_options.test.ts index befe8bedf91a..a935d3e5f33f 100644 --- a/yarn-project/aztec/src/cli/aztec_start_options.test.ts +++ b/yarn-project/aztec/src/cli/aztec_start_options.test.ts @@ -35,15 +35,6 @@ describe('aztec_start_options commander integration', () => { expect(opts.port).toBe(1234); }); - it('reads from the env', () => { - process.env.NETWORK = 'testnet'; - const cmd = buildCommandWith(['API', 'MISC']); - cmd.parse(['node', 'cli', '--port', '1234']); - const opts = cmd.opts(); - expect(opts.network).toBe('testnet'); - expect(opts.port).toBe(1234); - }); - it('maps namespaced flags to dotted keys', () => { const cmd = buildCommandWith(['P2P SUBSYSTEM']); cmd.parse(['node', 'cli', '--p2p.listenAddress', '1.2.3.4']); @@ -58,68 +49,52 @@ describe('aztec_start_options commander integration', () => { expect(opts.l1RpcUrls).toEqual(['http://a', 'http://b']); }); - it('parses SecretValue arrays from env for ETHEREUM consensus keys', () => { - process.env.L1_CONSENSUS_HOST_API_KEYS = 'k1, k2'; + it('parses SecretValue arrays from CLI for ETHEREUM consensus keys', () => { const cmd = buildCommandWith(['ETHEREUM']); - cmd.parse(['node', 'cli']); + cmd.parse(['node', 'cli', '--l1-consensus-host-api-keys', 'k1, k2']); const opts = cmd.opts(); const keys = opts.l1ConsensusHostApiKeys as SecretValue[]; expect(Array.isArray(keys)).toBe(true); expect(keys.map(k => k.getValue())).toEqual(['k1', 'k2']); }); - it('does not set l1RpcUrls and l1ChainId by default', () => { - const cmd = buildCommandWith(['ETHEREUM']); - cmd.parse(['node', 'cli']); - const opts = cmd.opts(); - expect(opts.l1ChainId).toBeUndefined(); - expect(opts.l1RpcUrls).toBeUndefined(); - }); - - it('uses defaults when flags are missing (numeric defaults)', () => { - const cmd = buildCommandWith(['API']); - cmd.parse(['node', 'cli']); - const opts = cmd.opts(); - expect(opts.port).toBe(8080); - expect(opts.adminPort).toBe(8880); - }); - - it('uses environment variables when flags are missing', () => { - process.env.AZTEC_PORT = '9090'; - const cmd = buildCommandWith(['API']); - cmd.parse(['node', 'cli']); + it('captures local-network flags passed explicitly on CLI', () => { + const cmd = buildCommandWith(['LOCAL_NETWORK']); + cmd.parse(['node', 'cli', '--local-network.testAccounts', 'false']); const opts = cmd.opts(); - // Parsed through parseVal into a number - expect(opts.port).toBe(9090); - expect(typeof opts.port).toBe('number'); + expect(opts['localNetwork.testAccounts']).toBe(false); }); - it('respects TEST_ACCOUNTS env var for local network', () => { - process.env.TEST_ACCOUNTS = 'false'; + it('parses --local-network.l1Mnemonic into a SecretValue', () => { const cmd = buildCommandWith(['LOCAL_NETWORK']); - cmd.parse(['node', 'cli']); + cmd.parse(['node', 'cli', '--local-network.l1Mnemonic', 'word1 word2 word3']); const opts = cmd.opts(); - expect(opts['localNetwork.testAccounts']).toBe(false); + const mnemonic = opts['localNetwork.l1Mnemonic'] as SecretValue; + expect(mnemonic).toBeInstanceOf(SecretValue); + expect(mnemonic.getValue()).toBe('word1 word2 word3'); }); - it('defaults testAccounts to true for local network', () => { + it('auto-generates --local-network.* flags for genesis state config', () => { const cmd = buildCommandWith(['LOCAL_NETWORK']); - cmd.parse(['node', 'cli']); + // sponsoredFPC and prefundAddresses come from genesisStateConfigMappings and are now + // auto-exposed under --local-network.* since they only matter for fresh-chain init. + cmd.parse(['node', 'cli', '--local-network.sponsoredFPC', 'true', '--local-network.prefundAddresses', '0x1,0x2']); const opts = cmd.opts(); - expect(opts['localNetwork.testAccounts']).toBe(true); + expect(opts['localNetwork.sponsoredFPC']).toBe(true); + expect(opts['localNetwork.prefundAddresses']).toEqual(['0x1', '0x2']); }); it('parses optional boolean flag values', () => { - const cmd = buildCommandWith(['P2P SUBSYSTEM']); - - expect(cmd.parse(['node', 'cli', '--p2p-enabled']).opts().p2pEnabled).toBe(true); - expect(cmd.parse(['node', 'cli', '--p2p-enabled', 'false']).opts().p2pEnabled).toBe(false); - expect(cmd.parse(['node', 'cli']).opts().p2pEnabled).toBe(false); + expect(buildCommandWith(['P2P SUBSYSTEM']).parse(['node', 'cli', '--p2p-enabled']).opts().p2pEnabled).toBe(true); + expect(buildCommandWith(['P2P SUBSYSTEM']).parse(['node', 'cli', '--p2p-enabled', 'false']).opts().p2pEnabled).toBe( + false, + ); + expect(buildCommandWith(['P2P SUBSYSTEM']).parse(['node', 'cli']).opts().p2pEnabled).toBeUndefined(); }); - it('uses numeric defaults from P2P mappings', () => { + it('captures explicit numeric P2P port flag', () => { const cmd = buildCommandWith(['P2P SUBSYSTEM']); - cmd.parse(['node', 'cli']); + cmd.parse(['node', 'cli', '--p2p.p2pPort', String(DEFAULT_P2P_PORT)]); const opts = cmd.opts(); expect(opts['p2p.p2pPort']).toBe(DEFAULT_P2P_PORT); expect(typeof opts['p2p.p2pPort']).toBe('number'); diff --git a/yarn-project/aztec/src/cli/aztec_start_options.ts b/yarn-project/aztec/src/cli/aztec_start_options.ts index 5130041389c8..cd66a7987bcd 100644 --- a/yarn-project/aztec/src/cli/aztec_start_options.ts +++ b/yarn-project/aztec/src/cli/aztec_start_options.ts @@ -5,25 +5,22 @@ import { l1ContractsConfigMappings } from '@aztec/ethereum/config'; import { l1ContractAddressesMapping } from '@aztec/ethereum/l1-contract-addresses'; import { l1ReaderConfigMappings } from '@aztec/ethereum/l1-reader'; import { getKeys } from '@aztec/foundation/collection'; -import { - type ConfigMapping, - type EnvVar, - booleanConfigHelper, - isBooleanConfigValue, - omitConfigMappings, -} from '@aztec/foundation/config'; +import { type ConfigMapping, type EnvVar, booleanConfigHelper, omitConfigMappings } from '@aztec/foundation/config'; import { sharedNodeConfigMappings } from '@aztec/node-lib/config'; import { bootnodeConfigMappings, p2pConfigMappings } from '@aztec/p2p/config'; import { proverAgentConfigMappings, proverBrokerConfigMappings } from '@aztec/prover-client/broker/config'; import { proverNodeConfigMappings } from '@aztec/prover-node/config'; import { allPxeConfigMappings } from '@aztec/pxe/config'; import { sequencerClientConfigMappings } from '@aztec/sequencer-client/config'; -import { chainConfigMappings, nodeRpcConfigMappings } from '@aztec/stdlib/config'; +import { chainConfigMappings } from '@aztec/stdlib/config'; import { dataConfigMappings } from '@aztec/stdlib/kv-store'; import { telemetryClientConfigMappings } from '@aztec/telemetry-client/config'; import { worldStateConfigMappings } from '@aztec/world-state/config'; -import { DefaultMnemonic } from '../mnemonic.js'; +import kebabCase from 'lodash.kebabcase'; + +import { localNetworkCliConfigMappings } from '../local-network/config.js'; +import { apiConfigMappings } from './api_config.js'; // Define an interface for options export interface AztecStartOption { @@ -38,15 +35,25 @@ export interface AztecStartOption { export const getOptions = (namespace: string, configMappings: Record>) => { const options: AztecStartOption[] = []; - for (const [key, { env, defaultValue: def, parseEnv, description, printDefault, fallback }] of Object.entries( - configMappings, - )) { + for (const [ + key, + { env, defaultValue: def, parseEnv, description, printDefault, fallback, isBoolean }, + ] of Object.entries(configMappings)) { if (universalOptions.includes(key)) { continue; } - const isBoolean = isBooleanConfigValue(configMappings, key as keyof typeof configMappings); + // const flagPrefix = namespace ? `${namespace}.` : ''; + let flagPrefix: string; + let flag: string; + if (!namespace) { + flagPrefix = ''; + flag = kebabCase(key); + } else { + flagPrefix = `${namespace}.`; + flag = key; + } options.push({ - flag: `--${namespace}.${key}${isBoolean ? '' : ' '}`, + flag: `--${flagPrefix}${flag}${isBoolean ? ' [value]' : ' '}`, description, defaultValue: def, printDefault, @@ -123,75 +130,9 @@ export const aztecStartOptions: { [key: string]: AztecStartOption[] } = { defaultValue: undefined, env: undefined, }, - { - flag: '--local-network.l1Mnemonic ', - description: - 'Mnemonic for L1 accounts. Will be used to generate private keys for the sequencer/validator accounts', - defaultValue: DefaultMnemonic, - env: 'MNEMONIC', - }, - { - flag: '--local-network.testAccounts', - description: 'Deploy test accounts on local network start', - env: 'TEST_ACCOUNTS', - ...booleanConfigHelper(true), - }, - ], - API: [ - { - flag: '--port ', - description: 'Port to run the Aztec Services on', - defaultValue: 8080, - env: 'AZTEC_PORT', - parseVal: val => parseInt(val, 10), - }, - { - flag: '--admin-port ', - description: 'Port to run admin APIs of Aztec Services on', - defaultValue: 8880, - env: 'AZTEC_ADMIN_PORT', - parseVal: val => parseInt(val, 10), - }, - { - flag: '--admin-api-key-hash ', - description: - 'SHA-256 hex hash of a pre-generated admin API key. When set, the node uses this hash for authentication instead of auto-generating a key.', - defaultValue: undefined, - env: 'AZTEC_ADMIN_API_KEY_HASH', - }, - { - flag: '--disable-admin-api-key', - description: - 'Disable API key authentication on the admin RPC endpoint. By default, a key is auto-generated, displayed once, and its hash is persisted.', - defaultValue: false, - env: 'AZTEC_DISABLE_ADMIN_API_KEY', - // undefined means the flag was passed without a value (boolean toggle), treat as true. - parseVal: val => val === undefined || val === 'true' || val === '1', - }, - { - flag: '--reset-admin-api-key', - description: - 'Force-generate a new admin API key, replacing any previously persisted key hash. The new key is displayed once at startup.', - defaultValue: false, - env: 'AZTEC_RESET_ADMIN_API_KEY', - parseVal: val => val === 'true' || val === '1', - }, - { - flag: '--node-debug', - description: 'Expose debug endpoints (e.g. mineBlock) on the main RPC port', - defaultValue: false, - env: 'AZTEC_NODE_DEBUG', - parseVal: val => val === undefined || val === 'true' || val === '1', - }, - { - flag: '--api-prefix ', - description: 'Prefix for API routes on any service that is started', - defaultValue: '', - env: 'API_PREFIX', - }, - configToFlag('--rpcMaxBatchSize', nodeRpcConfigMappings.rpcMaxBatchSize), - configToFlag('--rpcMaxBodySize', nodeRpcConfigMappings.rpcMaxBodySize), + ...getOptions('local-network', localNetworkCliConfigMappings), ], + API: [...getOptions('', apiConfigMappings)], ETHEREUM: [ // Do not set defaults for CLI: keep undefined unless provided via flag or env configToFlag('--l1-chain-id', { ...l1ReaderConfigMappings.l1ChainId, defaultValue: undefined }), diff --git a/yarn-project/aztec/src/cli/cmds/start_bot.ts b/yarn-project/aztec/src/cli/cmds/start_bot.ts index 2484041c1775..8bc0f96b2ed1 100644 --- a/yarn-project/aztec/src/cli/cmds/start_bot.ts +++ b/yarn-project/aztec/src/cli/cmds/start_bot.ts @@ -5,14 +5,10 @@ import { createStore, openTmpStore } from '@aztec/kv-store/lmdb-v2'; import { type CliPXEOptions, type PXEConfig, allPxeConfigMappings } from '@aztec/pxe/config'; import { type AztecNode, type AztecNodeAdmin, createAztecNodeClient } from '@aztec/stdlib/interfaces/client'; import type { TelemetryClient } from '@aztec/telemetry-client'; -import { - getConfigEnvVars as getTelemetryClientConfig, - initTelemetryClient, - makeTracedFetch, -} from '@aztec/telemetry-client'; +import { initTelemetryClient, makeTracedFetch, telemetryClientConfigMappings } from '@aztec/telemetry-client'; import { EmbeddedWallet } from '@aztec/wallets/embedded'; -import { extractRelevantOptions, stringifyConfig } from '../util.js'; +import { type ConfigResolverFn, stringifyConfig } from '../util.js'; import { getVersions } from '../versioning.js'; export async function startBot( @@ -20,6 +16,7 @@ export async function startBot( signalHandlers: (() => Promise)[], services: NamespacedApiHandlers, userLog: LogFn, + resolveConfig: ConfigResolverFn, ) { const { proverNode, sequencer, p2pBootstrap, txe, prover } = options; if (proverNode || sequencer || p2pBootstrap || txe || prover) { @@ -28,32 +25,32 @@ export async function startBot( } const fetch = makeTracedFetch([1, 2, 3], true); - const config = extractRelevantOptions(options, botConfigMappings, 'bot'); + const config = resolveConfig(botConfigMappings, 'bot'); if (!config.nodeUrl) { throw new Error('The bot requires access to a Node'); } const aztecNode = createAztecNodeClient(config.nodeUrl, getVersions(), fetch); - const pxeConfig = extractRelevantOptions(options, allPxeConfigMappings, 'pxe'); + const pxeConfig = resolveConfig(allPxeConfigMappings, 'pxe'); userLog(`Creating bot wallet with config ${stringifyConfig(pxeConfig)}`); const wallet = await EmbeddedWallet.create(aztecNode, { pxeConfig }); - const telemetry = await initTelemetryClient(getTelemetryClientConfig()); - await addBot(options, signalHandlers, services, wallet, aztecNode, telemetry, undefined, userLog); + const telemetry = await initTelemetryClient(resolveConfig(telemetryClientConfigMappings, 'tel')); + await addBot(signalHandlers, services, wallet, aztecNode, telemetry, resolveConfig, undefined, userLog); } export async function addBot( - options: any, signalHandlers: (() => Promise)[], services: NamespacedApiHandlers, wallet: EmbeddedWallet, aztecNode: AztecNode, telemetry: TelemetryClient, + resolveConfig: ConfigResolverFn, aztecNodeAdmin?: AztecNodeAdmin, userLog?: LogFn, ) { - const config = extractRelevantOptions(options, botConfigMappings, 'bot'); + const config = resolveConfig(botConfigMappings, 'bot'); userLog?.(`Starting bot with config ${stringifyConfig(config)}`); const db = await (config.dataDirectory diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts index 02a1b5bf0c2a..31201b339694 100644 --- a/yarn-project/aztec/src/cli/cmds/start_node.ts +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -1,4 +1,4 @@ -import { type AztecNodeConfig, aztecNodeConfigMappings, getConfigEnvVars } from '@aztec/aztec-node'; +import { type AztecNodeConfig, AztecNodeService, aztecNodeConfigMappings } from '@aztec/aztec-node'; import { Fr } from '@aztec/aztec.js/fields'; import { getL1Config } from '@aztec/cli/config'; import { getPublicClient } from '@aztec/ethereum/client'; @@ -25,13 +25,7 @@ import { } from '@aztec/telemetry-client'; import { EmbeddedWallet } from '@aztec/wallets/embedded'; -import { createAztecNode } from '../../local-network/index.js'; -import { - extractNamespacedOptions, - extractRelevantOptions, - preloadCrsDataForVerifying, - setupVersionChecker, -} from '../util.js'; +import { type ConfigResolverFn, preloadCrsDataForVerifying, setupVersionChecker } from '../util.js'; import { getVersions } from '../versioning.js'; import { computeExpectedGenesisRoot, waitForCompatibleRollup } from './standby.js'; import { startProverBroker } from './start_prover_broker.js'; @@ -43,18 +37,9 @@ export async function startNode( adminServices: NamespacedApiHandlers, userLog: LogFn, networkName: NetworkNames, + resolveConfig: ConfigResolverFn, ): Promise<{ config: AztecNodeConfig }> { - // All options set from environment variables - const configFromEnvVars = getConfigEnvVars(); - - // Extract relevant options from command line arguments - const relevantOptions = extractRelevantOptions(options, aztecNodeConfigMappings, 'node'); - - // All options that are relevant to the Aztec Node - let nodeConfig: AztecNodeConfig = { - ...configFromEnvVars, - ...relevantOptions, - }; + let nodeConfig: AztecNodeConfig = resolveConfig(aztecNodeConfigMappings, 'node'); // Prover node configuration and broker setup // REFACTOR: Move the broker setup out of here and into the prover-node factory @@ -73,7 +58,7 @@ export async function startNode( const fetch = makeTracedFetch(proverBrokerBackoff, false, makeUndiciFetch(new Agent({ connections: 100 }))); broker = createProvingJobBrokerClient(nodeConfig.proverBrokerUrl, getVersions(nodeConfig), fetch); } else if (options.proverBroker) { - ({ broker } = await startProverBroker(options, signalHandlers, services, userLog)); + ({ broker } = await startProverBroker(options, signalHandlers, services, userLog, resolveConfig)); } else { userLog(`--prover-broker-url or --prover-broker is required to start a Prover Node`); process.exit(1); @@ -101,7 +86,7 @@ export async function startNode( userLog, ); - const { addresses, config } = await getL1Config( + const { addresses, config: l1config } = await getL1Config( nodeConfig.registryAddress, nodeConfig.l1RpcUrls, nodeConfig.l1ChainId, @@ -110,37 +95,34 @@ export async function startNode( process.env.ROLLUP_CONTRACT_ADDRESS ??= addresses.rollupAddress.toString(); - if (!Fr.fromHexString(config.genesisArchiveTreeRoot).equals(genesisArchiveRoot)) { + if (!Fr.fromHexString(l1config.genesisArchiveTreeRoot).equals(genesisArchiveRoot)) { throw new Error( - `The computed genesis archive tree root ${genesisArchiveRoot} does not match the expected genesis archive tree root ${config.genesisArchiveTreeRoot} for the rollup deployed at ${addresses.rollupAddress}`, + `The computed genesis archive tree root ${genesisArchiveRoot} does not match the expected genesis archive tree root ${l1config.genesisArchiveTreeRoot} for the rollup deployed at ${addresses.rollupAddress}`, ); } + // L1-authoritative merge: contract addresses and rollup constants read directly from the L1 + // registry override anything the user supplied via CLI/ENV/network. This is intentional — the + // node must follow the on-chain rollup or it will desync. Kept outside `resolveConfig` because + // L1 is not a user-intent source like the other layers. + // TODO(A-1000): once L1-derived fields are removed from `aztecNodeConfigMappings`, this merge + // becomes a straight assembly step and the silent overwrite of any user values for these keys + // goes away. nodeConfig = { ...nodeConfig, ...addresses, - ...config, + ...l1config, }; if (!options.sequencer && !nodeConfig.fishermanMode) { nodeConfig.disableValidator = true; } else { - const sequencerConfig = { - ...configFromEnvVars, - ...extractNamespacedOptions(options, 'sequencer'), - }; // If no publisher private keys have been given, use the first validator key - if ( - sequencerConfig.sequencerPublisherPrivateKeys === undefined || - !sequencerConfig.sequencerPublisherPrivateKeys.length - ) { - if (sequencerConfig.validatorPrivateKeys?.getValue().length) { - sequencerConfig.sequencerPublisherPrivateKeys = [ - new SecretValue(sequencerConfig.validatorPrivateKeys.getValue()[0]), - ]; + if (nodeConfig.sequencerPublisherPrivateKeys === undefined || !nodeConfig.sequencerPublisherPrivateKeys.length) { + if (nodeConfig.validatorPrivateKeys?.getValue().length) { + nodeConfig.sequencerPublisherPrivateKeys = [new SecretValue(nodeConfig.validatorPrivateKeys.getValue()[0])]; } } - nodeConfig.sequencerPublisherPrivateKeys = sequencerConfig.sequencerPublisherPrivateKeys; } if (nodeConfig.p2pEnabled) { @@ -150,11 +132,11 @@ export async function startNode( } } - const telemetryConfig = extractRelevantOptions(options, telemetryClientConfigMappings, 'tel'); + const telemetryConfig = resolveConfig(telemetryClientConfigMappings, 'tel'); const telemetry = await initTelemetryClient(telemetryConfig); // Create and start Aztec Node - const node = await createAztecNode(nodeConfig, { telemetry, proverBroker: broker }, { genesis }); + const node = await AztecNodeService.createAndSync(nodeConfig, { telemetry, proverNodeDeps: { broker } }, { genesis }); // Add node and p2p to services list services.node = [node, AztecNodeApiSchema]; @@ -177,14 +159,14 @@ export async function startNode( if (options.bot) { const { addBot } = await import('./start_bot.js'); - const pxeConfig = extractRelevantOptions(options, allPxeConfigMappings, 'pxe'); + const pxeConfig = resolveConfig(allPxeConfigMappings, 'pxe'); const wallet = await EmbeddedWallet.create(node, { pxeConfig }); - await addBot(options, signalHandlers, services, wallet, node, telemetry, undefined); + await addBot(signalHandlers, services, wallet, node, telemetry, resolveConfig, undefined); } if (nodeConfig.enableVersionCheck && networkName !== 'local') { - const cacheDir = process.env.DATA_DIRECTORY ? `${process.env.DATA_DIRECTORY}/cache` : undefined; + const cacheDir = nodeConfig.dataDirectory ? `${nodeConfig.dataDirectory}/cache` : undefined; try { await setupVersionChecker( networkName, diff --git a/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts b/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts index bc1875f69203..4adc788ced7e 100644 --- a/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts +++ b/yarn-project/aztec/src/cli/cmds/start_p2p_bootstrap.ts @@ -5,27 +5,21 @@ import { createStore } from '@aztec/kv-store/lmdb-v2'; import { type BootnodeConfig, BootstrapNode, bootnodeConfigMappings } from '@aztec/p2p'; import { emptyChainConfig } from '@aztec/stdlib/config'; import { P2PBootstrapApiSchema } from '@aztec/stdlib/interfaces/server'; -import { - type TelemetryClientConfig, - initTelemetryClient, - telemetryClientConfigMappings, -} from '@aztec/telemetry-client'; +import { initTelemetryClient, telemetryClientConfigMappings } from '@aztec/telemetry-client'; -import { extractRelevantOptions } from '../util.js'; +import type { ConfigResolverFn } from '../util.js'; export async function startP2PBootstrap( - options: any, signalHandlers: (() => Promise)[], services: NamespacedApiHandlers, userLog: LogFn, + resolveConfig: ConfigResolverFn, ) { - // Start a P2P bootstrap node. - const config = extractRelevantOptions(options, bootnodeConfigMappings, 'p2p'); + const config = resolveConfig(bootnodeConfigMappings, 'p2pBootstrap'); const safeConfig = { ...config, peerIdPrivateKey: '' }; userLog(`Starting P2P bootstrap node with config: ${jsonStringify(safeConfig)}`); - const telemetryConfig = extractRelevantOptions(options, telemetryClientConfigMappings, 'tel'); - const telemetryClient = await initTelemetryClient(telemetryConfig); + const telemetryClient = await initTelemetryClient(resolveConfig(telemetryClientConfigMappings, 'tel')); const store = await createStore('p2p-bootstrap', 1, config); const node = new BootstrapNode(store, telemetryClient); diff --git a/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts b/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts index 738bb7ba81db..574ea5f62bc1 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover_agent.ts @@ -1,38 +1,38 @@ import { times } from '@aztec/foundation/collection'; +import { composeConfigMappings } from '@aztec/foundation/config'; import type { NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server'; import { Agent, makeUndiciFetch } from '@aztec/foundation/json-rpc/undici'; import type { LogFn } from '@aztec/foundation/log'; import { buildServerCircuitProver } from '@aztec/prover-client'; import { - type ProverAgentConfig, ProvingAgent, createProofStore, createProvingJobBrokerClient, proverAgentConfigMappings, proverBrokerBackoff, } from '@aztec/prover-client/broker'; -import { getProverNodeAgentConfigFromEnv } from '@aztec/prover-node'; +import { bbConfigMappings } from '@aztec/prover-client/config'; import { ProverAgentApiSchema } from '@aztec/stdlib/interfaces/server'; import { initTelemetryClient, makeTracedFetch, telemetryClientConfigMappings } from '@aztec/telemetry-client'; -import { extractRelevantOptions, preloadCrsDataForServerSideProving } from '../util.js'; +import { type ConfigResolverFn, preloadCrsDataForServerSideProving } from '../util.js'; import { getVersions } from '../versioning.js'; +const proverAgentFullConfigMappings = composeConfigMappings(proverAgentConfigMappings, bbConfigMappings); + export async function startProverAgent( options: any, signalHandlers: (() => Promise)[], services: NamespacedApiHandlers, userLog: LogFn, + resolveConfig: ConfigResolverFn, ) { if (options.node || options.sequencer || options.p2pBootstrap || options.txe) { userLog(`Starting a prover agent with --node, --sequencer, --p2p-bootstrap, or --txe is not supported.`); process.exit(1); } - const config = { - ...getProverNodeAgentConfigFromEnv(), // get default config from env - ...extractRelevantOptions(options, proverAgentConfigMappings, 'proverAgent'), // override with command line options - }; + const config = resolveConfig(proverAgentFullConfigMappings, 'proverAgent'); if (config.realProofs && (!config.bbBinaryPath || !config.acvmBinaryPath)) { userLog(`Requested real proving but no path to bb or acvm binaries provided`); @@ -50,7 +50,7 @@ export async function startProverAgent( const fetch = makeTracedFetch(proverBrokerBackoff, false, makeUndiciFetch(new Agent({ connections: 10 }))); const broker = createProvingJobBrokerClient(config.proverBrokerUrl, getVersions(), fetch); - const telemetry = await initTelemetryClient(extractRelevantOptions(options, telemetryClientConfigMappings, 'tel')); + const telemetry = await initTelemetryClient(resolveConfig(telemetryClientConfigMappings, 'tel')); const prover = await buildServerCircuitProver(config, telemetry); const proofStore = await createProofStore(config.proofStore); const agents = times( diff --git a/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts b/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts index bab990e9992a..d13e686714fd 100644 --- a/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts +++ b/yarn-project/aztec/src/cli/cmds/start_prover_broker.ts @@ -11,11 +11,10 @@ import { createAndStartProvingBroker, proverBrokerConfigMappings, } from '@aztec/prover-client/broker'; -import { getProverNodeBrokerConfigFromEnv } from '@aztec/prover-node'; import type { ProvingJobBroker } from '@aztec/stdlib/interfaces/server'; -import { getConfigEnvVars as getTelemetryClientConfig, initTelemetryClient } from '@aztec/telemetry-client'; +import { initTelemetryClient, telemetryClientConfigMappings } from '@aztec/telemetry-client'; -import { extractRelevantOptions } from '../util.js'; +import type { ConfigResolverFn } from '../util.js'; import { computeExpectedGenesisRoot, waitForCompatibleRollup } from './standby.js'; export async function startProverBroker( @@ -23,16 +22,14 @@ export async function startProverBroker( signalHandlers: (() => Promise)[], services: NamespacedApiHandlers, userLog: LogFn, + resolveConfig: ConfigResolverFn, ): Promise<{ broker: ProvingJobBroker; config: ProverBrokerConfig }> { if (options.node || options.sequencer || options.p2pBootstrap || options.txe) { userLog(`Starting a prover broker with --node, --sequencer, --p2p-bootstrap, or --txe is not supported.`); process.exit(1); } - const baseConfig: ProverBrokerConfig = { - ...getProverNodeBrokerConfigFromEnv(), // get default config from env - ...extractRelevantOptions(options, proverBrokerConfigMappings, 'proverBroker'), // override with command line options - }; + const baseConfig = resolveConfig(proverBrokerConfigMappings, 'proverBroker'); if (!baseConfig.registryAddress || baseConfig.registryAddress.isZero()) { throw new Error('L1 registry address is required to start Aztec Node without --deploy-aztec-contracts option'); @@ -56,7 +53,7 @@ export async function startProverBroker( const config: ProverBrokerConfig = { ...baseConfig, ...addresses, rollupVersion: rollupConfig.rollupVersion }; - const client = await initTelemetryClient(getTelemetryClientConfig()); + const client = await initTelemetryClient(resolveConfig(telemetryClientConfigMappings, 'tel')); const broker = await createAndStartProvingBroker(config, client); services.proverBroker = [ diff --git a/yarn-project/aztec/src/cli/config_resolver.test.ts b/yarn-project/aztec/src/cli/config_resolver.test.ts new file mode 100644 index 000000000000..0fcb081a5f20 --- /dev/null +++ b/yarn-project/aztec/src/cli/config_resolver.test.ts @@ -0,0 +1,94 @@ +import { type ConfigMappingsType, booleanConfigHelper, numberConfigHelper } from '@aztec/foundation/config'; + +import { afterEach, beforeEach, describe, expect, it } from '@jest/globals'; + +import { apiConfigMappings } from './api_config.js'; +import { createConfigResolver } from './util.js'; + +interface TestConfig { + port: number; + enabled: boolean; + dataDirectory: string; +} + +// Use real registered env var names to satisfy the EnvVar branded type +const testMappings: ConfigMappingsType = { + port: { + env: 'AZTEC_PORT', + description: 'aztec port', + ...numberConfigHelper(8080), + }, + enabled: { + env: 'P2P_ENABLED', + description: 'is enabled', + ...booleanConfigHelper(false), + }, + dataDirectory: { + env: 'DATA_DIRECTORY', + description: 'data directory', + defaultValue: '/tmp/aztec', + }, +}; + +describe('createConfigResolver', () => { + let savedEnv: NodeJS.ProcessEnv; + + beforeEach(() => { + savedEnv = process.env; + process.env = {}; + }); + + afterEach(() => { + process.env = savedEnv; + }); + + it('applies mapping defaults when no layers provide a value', () => { + const resolve = createConfigResolver({}, undefined, {}); + const config = resolve(testMappings); + expect(config.port).toBe(8080); + expect(config.enabled).toBe(false); + expect(config.dataDirectory).toBe('/tmp/aztec'); + }); + + it('ENV beats mapping defaults', () => { + process.env.AZTEC_PORT = '9090'; + const resolve = createConfigResolver({}, undefined, {}); + const config = resolve(testMappings); + expect(config.port).toBe(9090); + }); + + it('CLI beats ENV', () => { + process.env.AZTEC_PORT = '9090'; + const resolve = createConfigResolver({ port: 1234 }, undefined, {}); + const config = resolve(testMappings); + expect(config.port).toBe(1234); + }); + + it('chain config layer sits between ENV and mapping defaults', () => { + const chainLayer = { AZTEC_PORT: '7777' }; + + const resolveWithEnv = createConfigResolver({}, undefined, chainLayer); + process.env.AZTEC_PORT = '9090'; + expect(resolveWithEnv(testMappings).port).toBe(9090); + + delete process.env.AZTEC_PORT; + const resolveWithoutEnv = createConfigResolver({}, undefined, chainLayer); + expect(resolveWithoutEnv(testMappings).port).toBe(7777); + }); + + it('CLI namespace scoping ignores options from other namespaces', () => { + const resolve = createConfigResolver({ 'api.port': 4444, 'other.port': 9999 }, undefined, {}); + const config = resolve(testMappings, 'api'); + expect(config.port).toBe(4444); + }); + + it('resolves API defaults through config mappings', () => { + const resolve = createConfigResolver({}, undefined, {}); + const config = resolve(apiConfigMappings); + expect(config.port).toBe(8080); + expect(config.adminPort).toBe(8880); + expect(config.disableAdminApiKey).toBe(false); + expect(config.resetAdminApiKey).toBe(false); + expect(config.nodeDebug).toBe(false); + }); +}); diff --git a/yarn-project/aztec/src/cli/util.ts b/yarn-project/aztec/src/cli/util.ts index 06d91a711ae2..e7e80d3f9a68 100644 --- a/yarn-project/aztec/src/cli/util.ts +++ b/yarn-project/aztec/src/cli/util.ts @@ -3,7 +3,17 @@ import type { AccountManager } from '@aztec/aztec.js/wallet'; import { getNetworkConfig } from '@aztec/cli/config'; import { RegistryContract } from '@aztec/ethereum/contracts'; import type { ViemClient } from '@aztec/ethereum/types'; -import type { ConfigMappingsType, NetworkNames } from '@aztec/foundation/config'; +import { + ConfigLayerName, + type ConfigMappingsType, + type NetworkConfig, + type NetworkNames, + cliToTyped, + envToTyped, + networkConfigToTyped, + resolveConfig, + resolvedToConfig, +} from '@aztec/foundation/config'; import { jsonStringify } from '@aztec/foundation/json-rpc'; import { type LogFn, createLogger } from '@aztec/foundation/log'; import type { ProverConfig } from '@aztec/stdlib/interfaces/server'; @@ -131,44 +141,14 @@ export function formatHelpLine( return `${chalk.cyan(paddedOption)}${chalk.yellow(paddedDefault)}${chalk.green(envVar)}`; } -const getDefaultOrEnvValue = (opt: AztecStartOption) => { - let val; - - // if the option is set in the environment, use that - if (opt.env) { - val = process.env[opt.env]; - } - - // if we have fallback env vars, check those - if (!val && opt.fallback && opt.fallback.length > 0) { - for (const fallback of opt.fallback) { - val = process.env[fallback]; - if (val) { - break; - } - } - } - - // if we have a value, optionally parse it and return - if (val) { - if (opt.parseVal) { - return opt.parseVal(val); - } - return val; - } else if (opt.defaultValue !== undefined) { - return opt.defaultValue; - } - return undefined; -}; - -// Function to add options dynamically +// Function to add options dynamically. Only the parseVal coercion is wired into Commander export const addOptions = (cmd: Command, options: AztecStartOption[]) => { options.forEach(opt => { cmd.option( opt.flag, `${opt.description} (default: ${opt.defaultValue}) ($${opt.env})`, opt.parseVal ? opt.parseVal : val => val, - getDefaultOrEnvValue(opt), + undefined, ); }); }; @@ -217,6 +197,42 @@ export const extractNamespacedOptions = (options: Record, namespace return namespacedOptions; }; +/** A function that resolves typed config from CLI, env, network, and default layers. */ +export type ConfigResolverFn = (mappings: ConfigMappingsType, namespace?: string) => T; + +/** + * Creates a config resolver that merges four sources in order of decreasing precedence: + * 1. CLI flags (parsed by Commander, filtered through `cliToTyped`) + * 2. Explicit env vars (`envToTyped` against `process.env`) + * 3. Network layer: remote network JSON + generated chain defaults (via `envToTyped` with stringified map) + * 4. Mapping `defaultValue` fields (DEFAULT layer inside `resolveConfig`) + * + * @param options - Raw Commander `opts()` object. + * @param remoteNetworkConfig - Fetched remote network config, or undefined for local. + * @param chainConfigLayer - Stringified spartan-generated network defaults keyed by env var name. + */ +export function createConfigResolver( + options: Record, + remoteNetworkConfig: NetworkConfig | undefined, + chainConfigLayer: Record, +): ConfigResolverFn { + return function resolveComponentConfig(mappings: ConfigMappingsType, namespace?: string): T { + const networkLayerValues: Partial = { + ...envToTyped(mappings, chainConfigLayer), + ...(remoteNetworkConfig ? networkConfigToTyped(mappings, remoteNetworkConfig) : {}), + }; + + return resolvedToConfig( + // NOTE: resolved object will be used for logging later + resolveConfig(mappings, [ + { name: ConfigLayerName.CLI, values: cliToTyped(mappings, options, namespace) }, + { name: ConfigLayerName.ENV, values: envToTyped(mappings) }, + { name: ConfigLayerName.NETWORK, values: networkLayerValues }, + ]), + ); + }; +} + /** * Extracts relevant options from a key-value map. * @template T - The type of the relevant options. diff --git a/yarn-project/aztec/src/index.ts b/yarn-project/aztec/src/index.ts index d129a5fb25c5..5ec04536447b 100644 --- a/yarn-project/aztec/src/index.ts +++ b/yarn-project/aztec/src/index.ts @@ -1,6 +1,9 @@ export { + type LocalNetworkConfig, createLocalNetwork, + localNetworkConfigMappings, registerDeployedBananaCoinInWalletAndGetAddress, registerDeployedBananaFPCInWalletAndGetAddress, registerDeployedSponsoredFPCInWalletAndGetAddress, } from './local-network/index.js'; +export { DefaultMnemonic } from './mnemonic.js'; diff --git a/yarn-project/aztec/src/local-network/config.ts b/yarn-project/aztec/src/local-network/config.ts new file mode 100644 index 000000000000..4346325b1230 --- /dev/null +++ b/yarn-project/aztec/src/local-network/config.ts @@ -0,0 +1,47 @@ +import { type AztecNodeConfig, aztecNodeConfigMappings } from '@aztec/aztec-node/config'; +import { type GenesisStateConfig, genesisStateConfigMappings } from '@aztec/ethereum/config'; +import { + type ConfigMappingsType, + type SecretValue, + composeConfigMappings, + secretStringConfigHelper, +} from '@aztec/foundation/config'; + +import { DefaultMnemonic } from '../mnemonic.js'; + +type OwnLocalNetworkConfig = { + /** Mnemonic used to derive the L1 deployer private key. */ + l1Mnemonic: SecretValue; +}; + +const ownLocalNetworkConfigMappings: ConfigMappingsType = { + l1Mnemonic: { + env: 'MNEMONIC', + description: 'Mnemonic used to derive the L1 deployer private key.', + ...secretStringConfigHelper(DefaultMnemonic), + }, +}; + +/** + * Local network settings. `GenesisStateConfig` keys (`testAccounts`, `sponsoredFPC`, + * `prefundAddresses`) live on the genesis state mapping shared with the regular node config; the + * local-network-specific default for `testAccounts` (true) is applied by callers, since the + * generic node default is false. + */ +export type LocalNetworkConfig = AztecNodeConfig & OwnLocalNetworkConfig; + +export const localNetworkConfigMappings: ConfigMappingsType = composeConfigMappings( + ownLocalNetworkConfigMappings, + aztecNodeConfigMappings, +); + +/** + * Narrowed mapping used to auto-generate `--local-network.*` CLI flags. Includes the + * local-network-only fields plus genesis state config — those settings only matter when + * initialising a fresh chain, which is exclusively a `--local-network` concern. + * + * Resolution at startup uses the full `localNetworkConfigMappings`; this subset is for CLI + * registration only. + */ +export const localNetworkCliConfigMappings: ConfigMappingsType = + composeConfigMappings(ownLocalNetworkConfigMappings, genesisStateConfigMappings); diff --git a/yarn-project/aztec/src/local-network/local-network.ts b/yarn-project/aztec/src/local-network/local-network.ts index 6293df2653c5..b040d9041b0d 100644 --- a/yarn-project/aztec/src/local-network/local-network.ts +++ b/yarn-project/aztec/src/local-network/local-network.ts @@ -1,10 +1,9 @@ #!/usr/bin/env -S node --no-warnings import { getInitialTestAccountsData } from '@aztec/accounts/testing'; import { AztecNodeService } from '@aztec/aztec-node'; -import { type AztecNodeConfig, getConfigEnvVars } from '@aztec/aztec-node/config'; +import type { AztecNodeConfig } from '@aztec/aztec-node/config'; import { Fr } from '@aztec/aztec.js/fields'; import { createLogger } from '@aztec/aztec.js/log'; -import { type BlobClientInterface, createBlobClient } from '@aztec/blob-client/client'; import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants'; import { createEthereumChain } from '@aztec/ethereum/chain'; import { waitForPublicClient } from '@aztec/ethereum/client'; @@ -16,18 +15,12 @@ import { EthCheatCodes } from '@aztec/ethereum/test'; import { SecretValue } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; import type { LogFn } from '@aztec/foundation/log'; -import { DateProvider, TestDateProvider } from '@aztec/foundation/timer'; +import { TestDateProvider } from '@aztec/foundation/timer'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree'; import { protocolContractsHash } from '@aztec/protocol-contracts'; import { SequencerState } from '@aztec/sequencer-client'; import { AztecAddress } from '@aztec/stdlib/aztec-address'; -import type { ProvingJobBroker } from '@aztec/stdlib/interfaces/server'; -import type { GenesisData } from '@aztec/stdlib/world-state'; -import { - type TelemetryClient, - getConfigEnvVars as getTelemetryClientConfig, - initTelemetryClient, -} from '@aztec/telemetry-client'; +import { getConfigEnvVars as getTelemetryClientConfig, initTelemetryClient } from '@aztec/telemetry-client'; import { EmbeddedWallet } from '@aztec/wallets/embedded'; import { deployFundedSchnorrAccounts } from '@aztec/wallets/testing'; import { getGenesisValues } from '@aztec/world-state/testing'; @@ -37,11 +30,11 @@ import { mnemonicToAccount, privateKeyToAddress } from 'viem/accounts'; import { foundry } from 'viem/chains'; import { createAccountLogs } from '../cli/util.js'; -import { DefaultMnemonic } from '../mnemonic.js'; import { AnvilTestWatcher } from '../testing/anvil_test_watcher.js'; import { EpochTestSettler } from '../testing/epoch_test_settler.js'; import { getTokenAllowedSetupFunctions } from '../testing/token_allowed_setup.js'; import { getBananaFPCAddress, setupBananaFPC } from './banana_fpc.js'; +import type { LocalNetworkConfig } from './config.js'; import { getSponsoredFPCAddress } from './sponsored_fpc.js'; const logger = createLogger('local-network'); @@ -81,20 +74,14 @@ export async function deployContractsToL1( return l1Contracts.l1ContractAddresses; } -/** Local network settings. */ -export type LocalNetworkConfig = AztecNodeConfig & { - /** Mnemonic used to derive the L1 deployer private key.*/ - l1Mnemonic: string; - /** Whether to deploy test accounts on local network start.*/ - testAccounts: boolean; -}; +export { type LocalNetworkConfig, localNetworkConfigMappings, localNetworkCliConfigMappings } from './config.js'; /** * Create and start a new Aztec Node and PXE. Deploys L1 contracts. * Does not start any HTTP services nor populate any initial accounts. - * @param config - Optional local network settings. + * @param config - Fully-resolved local network settings (callers build this via the config resolver). */ -export async function createLocalNetwork(config: Partial = {}, userLog: LogFn) { +export async function createLocalNetwork(config: LocalNetworkConfig, userLog: LogFn) { // local network is meant for test envs. We should only need one l1RpcUrl const l1RpcUrl = config.l1RpcUrls?.[0]; if (!l1RpcUrl) { @@ -109,11 +96,10 @@ export async function createLocalNetwork(config: Partial = { const tokenAllowList = await getTokenAllowedSetupFunctions(); const aztecNodeConfig: AztecNodeConfig = { - ...getConfigEnvVars(), ...config, txPublicSetupAllowListExtend: [...tokenAllowList, ...(config.txPublicSetupAllowListExtend ?? [])], }; - const hdAccount = mnemonicToAccount(config.l1Mnemonic || DefaultMnemonic); + const hdAccount = mnemonicToAccount(config.l1Mnemonic.getValue()); if ( aztecNodeConfig.sequencerPublisherPrivateKeys == undefined || !aztecNodeConfig.sequencerPublisherPrivateKeys.length || @@ -189,9 +175,7 @@ export async function createLocalNetwork(config: Partial = { } const telemetry = await initTelemetryClient(getTelemetryClientConfig()); - // Create a local blob client client inside the local network, no http connectivity - const blobClient = createBlobClient(); - const node = await createAztecNode(aztecNodeConfig, { telemetry, blobClient, dateProvider }, { genesis }); + const node = await AztecNodeService.createAndSync(aztecNodeConfig, { telemetry, dateProvider }, { genesis }); // Now that the node is up, let the watcher check for pending txs so it can skip unfilled slots faster when // transactions are waiting in the mempool. Also let it check if the sequencer is actively building, to avoid @@ -247,31 +231,3 @@ export async function createLocalNetwork(config: Partial = { return { node, stop }; } - -/** - * Create and start a new Aztec RPC HTTP Server - * @param config - Optional Aztec node settings. - */ -export async function createAztecNode( - config: Partial = {}, - deps: { - telemetry?: TelemetryClient; - blobClient?: BlobClientInterface; - dateProvider?: DateProvider; - proverBroker?: ProvingJobBroker; - } = {}, - options: { genesis?: GenesisData } = {}, -) { - // TODO(#12272): will clean this up. This is criminal. - // Not sure why this was ever done. Will be fixed in A-989, A-991, A-990. - const aztecNodeConfig: AztecNodeConfig = { - ...getConfigEnvVars(), - ...config, - }; - const node = await AztecNodeService.createAndSync( - aztecNodeConfig, - { ...deps, proverNodeDeps: { broker: deps.proverBroker } }, - options, - ); - return node; -} diff --git a/yarn-project/blob-client/src/client/config.ts b/yarn-project/blob-client/src/client/config.ts index 85261896d510..3700b680a3e4 100644 --- a/yarn-project/blob-client/src/client/config.ts +++ b/yarn-project/blob-client/src/client/config.ts @@ -8,8 +8,8 @@ import { type ConfigMappingsType, SecretValue, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, optionalNumberConfigHelper, parseCommaSeparated, pickConfigMappings, @@ -146,7 +146,7 @@ export const blobClientConfigMapping: ConfigMappingsType = com * @returns The blob client configuration. */ export function getBlobClientConfigFromEnv(): BlobClientConfig { - return getConfigFromMappings(blobClientConfigMapping); + return buildConfigFromEnv(blobClientConfigMapping); } /** diff --git a/yarn-project/bot/src/config.ts b/yarn-project/bot/src/config.ts index 6443b45856ed..e34b5ed1d440 100644 --- a/yarn-project/bot/src/config.ts +++ b/yarn-project/bot/src/config.ts @@ -3,8 +3,8 @@ import { type ConfigMappingsType, SecretValue, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, getDefaultConfig, numberConfigHelper, optionalNumberConfigHelper, @@ -299,7 +299,7 @@ export const botConfigMappings: ConfigMappingsType = composeConfigMap ); export function getBotConfigFromEnv(): BotConfig { - return getConfigFromMappings(botConfigMappings); + return buildConfigFromEnv(botConfigMappings); } export function getBotDefaultConfig(): BotConfig { diff --git a/yarn-project/cli/src/config/chain_l2_config.ts b/yarn-project/cli/src/config/chain_l2_config.ts index 75f69e615a00..250f8010e316 100644 --- a/yarn-project/cli/src/config/chain_l2_config.ts +++ b/yarn-project/cli/src/config/chain_l2_config.ts @@ -12,48 +12,36 @@ const NetworkConfigs: Partial> = { mainnet: mainnetConfig, }; -function enrichEnvironmentWithNetworkConfig(config: NetworkConfigEnv): void { - for (const [key, value] of Object.entries(config)) { - if (process.env[key] === undefined && value !== undefined) { - process.env[key] = String(value); - } - } -} - function getDefaultDataDir(networkName: NetworkNames): string { return path.join(process.env.HOME || '~', '.aztec', networkName, 'data'); } /** - * Sets up environment for the given network. - * - * For 'local' network: returns early, using hardcoded defaults from DefaultL1ContractsConfig - * and DefaultSlasherConfig (which match the 'defaults' section of defaults.yml). + * Returns the generated spartan defaults for the given network as a string-keyed env var map, + * suitable as the `envSource` argument to `envToTyped`. Includes a DATA_DIRECTORY default. + * For 'local', returns an empty map (local defaults come from the mapping `defaultValue` fields). * - * For deployed networks: applies network configuration from generated defaults.yml, - * merging base defaults with network-specific overrides. - * - * @param networkName - The network name + * NOTE: generated network config will be removed and we'll be fetching directly from L1 */ -export function enrichEnvironmentWithChainName(networkName: NetworkNames) { - // For 'local', we don't inject any env vars - use hardcoded TypeScript/Solidity defaults - // These defaults are defined in defaults.yml 'defaults' section and match: - // - DefaultL1ContractsConfig (yarn-project/ethereum/src/config.ts) - // - Solidity vm.envOr defaults (l1-contracts/script/deploy/RollupConfiguration.sol) +export function getChainConfigLayer(networkName: NetworkNames): Record { if (networkName === 'local') { - return; + return {}; } // Apply generated network config from defaults.yml // For devnet iterations (v4-devnet-1, etc.), use the base devnet config const configKey = /^v\d+-devnet-\d+$/.test(networkName) ? 'devnet' : networkName; const generatedConfig = NetworkConfigs[configKey]; + + const result: Record = {}; if (generatedConfig) { - enrichEnvironmentWithNetworkConfig(generatedConfig); + for (const [key, value] of Object.entries(generatedConfig)) { + result[key] = String(value); + } } - // Set DATA_DIRECTORY if not already set - if (process.env['DATA_DIRECTORY'] === undefined) { - process.env['DATA_DIRECTORY'] = getDefaultDataDir(networkName); - } + // Provide a network-specific DATA_DIRECTORY default (lower priority than explicit env). + result['DATA_DIRECTORY'] = getDefaultDataDir(networkName); + + return result; } diff --git a/yarn-project/cli/src/config/enrich_env.ts b/yarn-project/cli/src/config/enrich_env.ts deleted file mode 100644 index 6f5501b68fad..000000000000 --- a/yarn-project/cli/src/config/enrich_env.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { EthAddress } from '@aztec/aztec.js/addresses'; -import type { EnvVar } from '@aztec/foundation/config'; - -export function enrichVar(envVar: EnvVar, value: string | undefined) { - // Don't override - if (process.env[envVar] || value === undefined) { - return; - } - process.env[envVar] = value; -} - -export function enrichEthAddressVar(envVar: EnvVar, value: string) { - // EthAddress doesn't like being given empty strings - enrichVar(envVar, value || EthAddress.ZERO.toString()); -} diff --git a/yarn-project/cli/src/config/network_config.test.ts b/yarn-project/cli/src/config/network_config.test.ts index dfded691325b..22673c7161e9 100644 --- a/yarn-project/cli/src/config/network_config.test.ts +++ b/yarn-project/cli/src/config/network_config.test.ts @@ -2,7 +2,7 @@ import { mkdir, rm, writeFile } from 'fs/promises'; import { tmpdir } from 'os'; import { join } from 'path'; -import { enrichEnvironmentWithNetworkConfig, getNetworkConfig } from './network_config.js'; +import { getNetworkConfig } from './network_config.js'; describe('Network Config', () => { let tempDir: string; @@ -16,85 +16,15 @@ describe('Network Config', () => { // Reset environment process.env = { ...originalEnv }; delete process.env.NETWORK_CONFIG_LOCATION; - delete process.env.BOOTSTRAP_NODES; - delete process.env.L1_CHAIN_ID; }); afterEach(async () => { - // Clean up temp directory await rm(tempDir, { recursive: true, force: true }); process.env = { ...originalEnv }; }); - describe('enrichEnvironmentWithNetworkConfig', () => { - it('should not throw when network does not exist in valid config', async () => { - const validConfigWithoutNetwork = { - 'some-other-network': { - bootnodes: ['enr:-test1'], - snapshots: ['https://example.com/snapshot.tar.gz'], - registryAddress: '0x1234567890123456789012345678901234567890', - l1ChainId: 11155111, - }, - }; - - const configPath = join(tempDir, 'network_config.json'); - await writeFile(configPath, JSON.stringify(validConfigWithoutNetwork)); - process.env.NETWORK_CONFIG_LOCATION = `file://${configPath}`; - - // Should not throw - network 'testnet' doesn't exist in the config but config is valid - await expect(enrichEnvironmentWithNetworkConfig('testnet')).resolves.toBeUndefined(); - - // Environment should not be enriched since network doesn't exist - expect(process.env.BOOTSTRAP_NODES).toBeUndefined(); - expect(process.env.L1_CHAIN_ID).toBeUndefined(); - }); - - it('should throw when config file does not exist', async () => { - process.env.NETWORK_CONFIG_LOCATION = `file://${join(tempDir, 'nonexistent.json')}`; - - // Should throw because file doesn't exist - await expect(enrichEnvironmentWithNetworkConfig('testnet')).rejects.toThrow(); - }); - - it('should throw when config parsing fails', async () => { - const configPath = join(tempDir, 'invalid_config.json'); - await writeFile(configPath, '{ invalid json'); - process.env.NETWORK_CONFIG_LOCATION = `file://${configPath}`; - - // Should throw because config is invalid JSON - await expect(enrichEnvironmentWithNetworkConfig('testnet')).rejects.toThrow(); - }); - - it('should skip local network', async () => { - // Should return early without fetching - await expect(enrichEnvironmentWithNetworkConfig('local')).resolves.toBeUndefined(); - }); - - it('should enrich environment when network exists in config', async () => { - const validConfig = { - testnet: { - bootnodes: ['enr:-test1', 'enr:-test2'], - snapshots: ['https://example.com/snapshot.tar.gz'], - registryAddress: '0x1234567890123456789012345678901234567890', - l1ChainId: 11155111, - }, - }; - - const configPath = join(tempDir, 'network_config.json'); - await writeFile(configPath, JSON.stringify(validConfig)); - process.env.NETWORK_CONFIG_LOCATION = `file://${configPath}`; - - await enrichEnvironmentWithNetworkConfig('testnet'); - - // Environment should be enriched - expect(process.env.BOOTSTRAP_NODES).toBe('enr:-test1,enr:-test2'); - expect(process.env.L1_CHAIN_ID).toBe('11155111'); - expect(process.env.REGISTRY_CONTRACT_ADDRESS).toBe('0x1234567890123456789012345678901234567890'); - }); - }); - describe('getNetworkConfig', () => { - it('should return config when network exists', async () => { + it('returns config when network exists', async () => { const validConfig = { testnet: { bootnodes: ['enr:-test1'], @@ -113,7 +43,7 @@ describe('Network Config', () => { expect(result?.bootnodes).toEqual(['enr:-test1']); }); - it('should return undefined when network not in config', async () => { + it('returns undefined when network not in config', async () => { const validConfig = { 'other-network': { bootnodes: ['enr:-test1'], @@ -131,10 +61,38 @@ describe('Network Config', () => { expect(result).toBeUndefined(); }); - it('should throw when config file does not exist', async () => { + it('throws when config file does not exist', async () => { process.env.NETWORK_CONFIG_LOCATION = `file://${join(tempDir, 'nonexistent.json')}`; + await expect(getNetworkConfig('testnet')).rejects.toThrow(); + }); + it('throws when config parsing fails', async () => { + const configPath = join(tempDir, 'invalid_config.json'); + await writeFile(configPath, '{ invalid json'); + process.env.NETWORK_CONFIG_LOCATION = `file://${configPath}`; await expect(getNetworkConfig('testnet')).rejects.toThrow(); }); + + it('returns undefined for local network without fetching', async () => { + // local has no remote config; pass a bad URL to confirm no fetch occurs + process.env.NETWORK_CONFIG_LOCATION = `file://${join(tempDir, 'nonexistent.json')}`; + // getNetworkConfig does NOT special-case 'local' — the caller decides not to call it. + // Verify the function still processes normally given a valid config for 'local'. + const validConfig = { + local: { + bootnodes: [], + snapshots: [], + registryAddress: '0x1234567890123456789012345678901234567890', + l1ChainId: 31337, + }, + }; + const configPath = join(tempDir, 'network_config.json'); + await writeFile(configPath, JSON.stringify(validConfig)); + process.env.NETWORK_CONFIG_LOCATION = `file://${configPath}`; + + const result = await getNetworkConfig('local'); + expect(result).toBeDefined(); + expect(result?.l1ChainId).toBe(31337); + }); }); }); diff --git a/yarn-project/cli/src/config/network_config.ts b/yarn-project/cli/src/config/network_config.ts index df3cdd3d52cc..94fc6f93526d 100644 --- a/yarn-project/cli/src/config/network_config.ts +++ b/yarn-project/cli/src/config/network_config.ts @@ -4,7 +4,6 @@ import { readFile } from 'fs/promises'; import { join } from 'path'; import { cachedFetch } from './cached_fetch.js'; -import { enrichEthAddressVar, enrichVar } from './enrich_env.js'; const DEFAULT_CONFIG_URL = 'https://raw.githubusercontent.com/AztecProtocol/networks/refs/heads/main/network_config.json'; @@ -105,47 +104,3 @@ async function fetchNetworkConfigFromUrl( return undefined; } } - -/** - * Enriches environment variables with remote network configuration. - * This function is called before node config initialization to set env vars - * from the remote config. - * - * @param networkName - The network name to fetch remote config for - * @throws Error if network config fetch fails (network errors, parse errors, etc.) - * Does not throw if the network simply doesn't exist in the config - just returns without enriching - */ -export async function enrichEnvironmentWithNetworkConfig(networkName: NetworkNames) { - if (networkName === 'local') { - return; // No remote config for local development - } - - const cacheDir = process.env.DATA_DIRECTORY ? join(process.env.DATA_DIRECTORY, 'cache') : undefined; - const networkConfig = await getNetworkConfig(networkName, cacheDir); - - if (!networkConfig) { - return; // Network not found in config, continue without enriching - } - - enrichVar('BOOTSTRAP_NODES', networkConfig.bootnodes.join(',')); - enrichVar('L1_CHAIN_ID', String(networkConfig.l1ChainId)); - enrichVar('SYNC_SNAPSHOTS_URLS', networkConfig.snapshots.join(',')); - - enrichEthAddressVar('REGISTRY_CONTRACT_ADDRESS', networkConfig.registryAddress.toString()); - if (networkConfig.feeAssetHandlerAddress) { - enrichEthAddressVar('FEE_ASSET_HANDLER_CONTRACT_ADDRESS', networkConfig.feeAssetHandlerAddress.toString()); - } - - if (networkConfig.blobFileStoreUrls?.length) { - enrichVar('BLOB_FILE_STORE_URLS', networkConfig.blobFileStoreUrls.join(',')); - } - if (networkConfig.txCollectionFileStoreUrls?.length) { - enrichVar('TX_COLLECTION_FILE_STORE_URLS', networkConfig.txCollectionFileStoreUrls.join(',')); - } - if (networkConfig.blockDurationMs !== undefined) { - enrichVar('SEQ_BLOCK_DURATION_MS', String(networkConfig.blockDurationMs)); - } - if (networkConfig.txPublicSetupAllowListExtend) { - enrichVar('TX_PUBLIC_SETUP_ALLOWLIST', networkConfig.txPublicSetupAllowListExtend); - } -} diff --git a/yarn-project/end-to-end/src/forward-compatibility/wallet_service.ts b/yarn-project/end-to-end/src/forward-compatibility/wallet_service.ts index 674ef390f4db..8d7e871f8017 100644 --- a/yarn-project/end-to-end/src/forward-compatibility/wallet_service.ts +++ b/yarn-project/end-to-end/src/forward-compatibility/wallet_service.ts @@ -9,9 +9,10 @@ */ import { getSchnorrAccountContractAddress } from '@aztec/accounts/schnorr'; import { getInitialTestAccountsData } from '@aztec/accounts/testing'; -import { createLocalNetwork } from '@aztec/aztec'; +import { type LocalNetworkConfig, createLocalNetwork, localNetworkConfigMappings } from '@aztec/aztec'; import { Fr } from '@aztec/aztec.js/fields'; import { WalletSchema } from '@aztec/aztec.js/wallet'; +import { buildConfigFromEnv } from '@aztec/foundation/config'; import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin'; import { createNamespacedSafeJsonRpcServer, startHttpRpcServer } from '@aztec/foundation/json-rpc/server'; import { createLogger } from '@aztec/foundation/log'; @@ -40,13 +41,20 @@ async function main() { logger.info('Starting wallet service...', { l1RpcUrls }); - // createLocalNetwork deploys L1 contracts, starts the node, and optionally deploys funded test accounts (when - // TEST_ACCOUNTS=true via env). We are not proving anything just like is done when local network is started by - // the `aztecStart` function. The extra account address is passed via prefundAddresses so it gets fee juice at genesis. - const { node, stop: stopNetwork } = await createLocalNetwork( - { l1RpcUrls, realProofs: false, prefundAddresses: [extraAccountAddress.toString()] }, - logger.info, - ); + // createLocalNetwork deploys L1 contracts, starts the node, and optionally deploys funded test accounts. We are not + // proving anything just like is done when local network is started by the `aztecStart` function. The extra account + // address is passed via prefundAddresses so it gets fee juice at genesis. + // + // No CLI options are available in this binary, so we build the full LocalNetworkConfig from env + mapping defaults. + // This binary always wants test accounts deployed; mapping default is false, so override explicitly. + const localNetworkConfig: LocalNetworkConfig = { + ...buildConfigFromEnv(localNetworkConfigMappings), + l1RpcUrls, + realProofs: false, + prefundAddresses: [extraAccountAddress.toString()], + testAccounts: true, + }; + const { node, stop: stopNetwork } = await createLocalNetwork(localNetworkConfig, logger.info); // Create an ephemeral embedded wallet backed by the local node. const wallet = await EmbeddedWallet.create(node, { ephemeral: true }); diff --git a/yarn-project/ethereum/src/config.ts b/yarn-project/ethereum/src/config.ts index 29467b4b98b4..7ce0144266b4 100644 --- a/yarn-project/ethereum/src/config.ts +++ b/yarn-project/ethereum/src/config.ts @@ -2,8 +2,8 @@ import { type ConfigMappingsType, bigintConfigHelper, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, getDefaultConfig, numberConfigHelper, optionalNumberConfigHelper, @@ -265,9 +265,9 @@ export const genesisStateConfigMappings: ConfigMappingsType }; export function getL1ContractsConfigEnvVars(): L1ContractsConfig { - return getConfigFromMappings(l1ContractsConfigMappings); + return buildConfigFromEnv(l1ContractsConfigMappings); } export function getGenesisStateConfigEnvVars(): GenesisStateConfig { - return getConfigFromMappings(genesisStateConfigMappings); + return buildConfigFromEnv(genesisStateConfigMappings); } diff --git a/yarn-project/ethereum/src/l1_reader.ts b/yarn-project/ethereum/src/l1_reader.ts index 817348706d10..89af99f7d829 100644 --- a/yarn-project/ethereum/src/l1_reader.ts +++ b/yarn-project/ethereum/src/l1_reader.ts @@ -1,7 +1,7 @@ import { type ConfigMappingsType, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, numberConfigHelper, optionalNumberConfigHelper, parseCommaSeparated, @@ -76,5 +76,5 @@ export const l1ReaderConfigMappings: ConfigMappingsType = compos ); export function getL1ReaderConfigFromEnv(): L1ReaderConfig { - return getConfigFromMappings(l1ReaderConfigMappings); + return buildConfigFromEnv(l1ReaderConfigMappings); } diff --git a/yarn-project/ethereum/src/l1_tx_utils/config.ts b/yarn-project/ethereum/src/l1_tx_utils/config.ts index 50fa765d27e6..2e46c0983efd 100644 --- a/yarn-project/ethereum/src/l1_tx_utils/config.ts +++ b/yarn-project/ethereum/src/l1_tx_utils/config.ts @@ -1,8 +1,8 @@ import { type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, floatConfigHelper, - getConfigFromMappings, getDefaultConfig, numberConfigHelper, optionalNumberConfigHelper, @@ -181,7 +181,7 @@ export const defaultL1TxUtilsConfig = getDefaultConfig( ) as Required; export function getL1TxUtilsConfigEnvVars(): L1TxUtilsConfig { - return getConfigFromMappings(l1TxUtilsConfigMappings); + return buildConfigFromEnv(l1TxUtilsConfigMappings); } function deprecatedFixedFeeMessage(envVar: string): string { diff --git a/yarn-project/foundation/src/config/config.test.ts b/yarn-project/foundation/src/config/config.test.ts index 4458b9facfed..2839ef2d0e7a 100644 --- a/yarn-project/foundation/src/config/config.test.ts +++ b/yarn-project/foundation/src/config/config.test.ts @@ -4,11 +4,11 @@ import { ConfigLayerName, type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, cliToTyped, composeConfigMappings, envToTyped, findUniversalConfigKeys, - getConfigFromMappings, getDefaultConfig, numberConfigHelper, pickConfigMappings, @@ -16,7 +16,7 @@ import { } from './index.js'; describe('Config', () => { - describe('getConfigFromMappings', () => { + describe('buildConfigFromEnv', () => { describe('deprecatedFallback', () => { const originalEnv = process.env; @@ -59,7 +59,7 @@ describe('Config', () => { }, }; - const config = getConfigFromMappings(configMappings); + const config = buildConfigFromEnv(configMappings); // Value should still be parsed from the deprecated env var expect(config.minimumPriorityFeePerGas).toBe(Number(value)); @@ -97,7 +97,7 @@ describe('Config', () => { }, }; - const config = getConfigFromMappings(configMappings); + const config = buildConfigFromEnv(configMappings); expect(config.minimumPriorityFeePerGas).toBe(Number(value)); @@ -129,7 +129,7 @@ describe('Config', () => { }, }; - const config = getConfigFromMappings(configMappings); + const config = buildConfigFromEnv(configMappings); expect(config.minimumPriorityFeePerGas).toBe(Number(value)); @@ -511,7 +511,7 @@ describe('Config', () => { interface TestConfig { value: bigint; } - const config = getConfigFromMappings({ + const config = buildConfigFromEnv({ value: { env: 'L1_MINIMUM_PRIORITY_FEE_PER_GAS_GWEI', description: 'test', diff --git a/yarn-project/foundation/src/config/index.ts b/yarn-project/foundation/src/config/index.ts index c5ab2a277ee1..eb6544209c81 100644 --- a/yarn-project/foundation/src/config/index.ts +++ b/yarn-project/foundation/src/config/index.ts @@ -50,7 +50,12 @@ export enum ConfigLayerName { } // Ordered list of config layers in order of precedence. -export const ORDERED_CONFIG_LAYERS = [ConfigLayerName.CLI, ConfigLayerName.ENV, ConfigLayerName.NETWORK] as const; +export const ORDERED_CONFIG_LAYERS = [ + ConfigLayerName.CLI, + ConfigLayerName.ENV, + ConfigLayerName.NETWORK, + ConfigLayerName.DEFAULT, +] as const; export type OrderedConfigLayerName = (typeof ORDERED_CONFIG_LAYERS)[number]; export interface ConfigLayer { @@ -74,6 +79,18 @@ export type ResolvedConfig = { [K in keyof T]-?: ResolvedValue; }; +/** + * Flattens a `ResolvedConfig` to a plain `T` by extracting the winning `.value` from each key. + * Use after `resolveConfig` when downstream code expects a plain config object. + */ +export function resolvedToConfig(resolved: ResolvedConfig): T { + const result = {} as T; + for (const key of Object.keys(resolved) as Array) { + result[key] = resolved[key].value; + } + return result; +} + type AnyConfig = Record; type AnyConfigMappings = ConfigMappingsType; type ConfigFromMappings = TMappings extends ConfigMappingsType ? T : never; @@ -86,7 +103,7 @@ type ComposedConfigType = UnionTo /** * Shared utility function to get a value from environment variables with fallback support. - * This can be used by both getConfigFromMappings and CLI utilities. + * This can be used by both buildConfigFromEnv and CLI utilities. * * @param env - The primary environment variable name * @param fallback - Optional array of fallback environment variable names @@ -127,7 +144,7 @@ export function getValueFromEnvWithFallback( return defaultValue; } -export function getConfigFromMappings(configMappings: ConfigMappingsType): T { +export function buildConfigFromEnv(configMappings: ConfigMappingsType): T { return { ...getDefaultConfig(configMappings), ...envToTyped(configMappings) } as T; } @@ -252,6 +269,9 @@ export function resolveConfig(configMappings: ConfigMappingsType, layers: layerSources.set(layer.name, layer.values); } + // extract default values from config mappings and add to layer sources + layerSources.set(ConfigLayerName.DEFAULT, getDefaultConfig(configMappings)); + for (const key of Object.keys(configMappings) as Array) { const mapping = configMappings[key]; const resolvedLayers: LayerEntry[typeof key]>[] = []; @@ -271,13 +291,6 @@ export function resolveConfig(configMappings: ConfigMappingsType, layers: } } - if (mapping.defaultValue !== undefined) { - resolvedLayers.push({ - layer: ConfigLayerName.DEFAULT, - value: mapping.defaultValue as Required[typeof key], - }); - } - // TODO(A-1065): optional config keys (e.g. `slashingQuorum?: number`) legitimately resolve to // undefined when no layer provides a value and no defaultValue exists, so this throw is // incorrect for those cases. Uncomment and gate on a per-mapping `optional` flag once that diff --git a/yarn-project/node-keystore/src/config.ts b/yarn-project/node-keystore/src/config.ts index 76956d7cd284..7799fb8079d1 100644 --- a/yarn-project/node-keystore/src/config.ts +++ b/yarn-project/node-keystore/src/config.ts @@ -1,4 +1,4 @@ -import { type ConfigMappingsType, getConfigFromMappings } from '@aztec/foundation/config'; +import { type ConfigMappingsType, buildConfigFromEnv } from '@aztec/foundation/config'; export type KeyStoreConfig = { keyStoreDirectory: string | undefined; @@ -12,5 +12,5 @@ export const keyStoreConfigMappings: ConfigMappingsType = { }; export function getKeyStoreConfigFromEnv(): KeyStoreConfig { - return getConfigFromMappings(keyStoreConfigMappings); + return buildConfigFromEnv(keyStoreConfigMappings); } diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index 232287db6165..241caddaf7f4 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -3,8 +3,8 @@ import { SecretValue, bigintConfigHelper, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, getDefaultConfig, numberConfigHelper, optionalNumberConfigHelper, @@ -552,7 +552,7 @@ export const p2pConfigMappings: ConfigMappingsType = composeConfigMap * @returns The config values for p2p client. */ export function getP2PConfigFromEnv(): P2PConfig { - return getConfigFromMappings(p2pConfigMappings); + return buildConfigFromEnv(p2pConfigMappings); } export function getP2PDefaultConfig(): P2PConfig { diff --git a/yarn-project/prover-client/src/config.ts b/yarn-project/prover-client/src/config.ts index 385d6ec1054d..278a2144f81f 100644 --- a/yarn-project/prover-client/src/config.ts +++ b/yarn-project/prover-client/src/config.ts @@ -4,7 +4,7 @@ import { acvmConfigMappings, bbConfigMappings as bbProverConfigMappings, } from '@aztec/bb-prover'; -import { type ConfigMappingsType, composeConfigMappings, getConfigFromMappings } from '@aztec/foundation/config'; +import { type ConfigMappingsType, buildConfigFromEnv, composeConfigMappings } from '@aztec/foundation/config'; import { type ProverConfig, proverConfigMappings } from '@aztec/stdlib/interfaces/prover-config'; import { @@ -38,5 +38,5 @@ export const proverClientConfigMappings: ConfigMappingsType(proverClientConfigMappings); + return buildConfigFromEnv(proverClientConfigMappings); } diff --git a/yarn-project/prover-node/src/config.ts b/yarn-project/prover-node/src/config.ts index 93d30578c6cf..cf2e49f78252 100644 --- a/yarn-project/prover-node/src/config.ts +++ b/yarn-project/prover-node/src/config.ts @@ -2,8 +2,8 @@ import type { ACVMConfig, BBConfig } from '@aztec/bb-prover/config'; import { type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, numberConfigHelper, pickConfigMappings, } from '@aztec/foundation/config'; @@ -110,19 +110,19 @@ export const proverNodeConfigMappings: ConfigMappingsType = co ); export function getProverNodeConfigFromEnv(): ProverNodeConfig { - return getConfigFromMappings(proverNodeConfigMappings); + return buildConfigFromEnv(proverNodeConfigMappings); } export function getProverNodeBrokerConfigFromEnv(): ProverBrokerConfig { return { - ...getConfigFromMappings(proverBrokerConfigMappings), + ...buildConfigFromEnv(proverBrokerConfigMappings), }; } export function getProverNodeAgentConfigFromEnv(): ProverAgentConfig & BBConfig & ACVMConfig { return { - ...getConfigFromMappings(proverAgentConfigMappings), - ...getConfigFromMappings(bbConfigMappings), + ...buildConfigFromEnv(proverAgentConfigMappings), + ...buildConfigFromEnv(bbConfigMappings), }; } diff --git a/yarn-project/pxe/src/config/index.ts b/yarn-project/pxe/src/config/index.ts index 31619f678d4a..db5657e0aa98 100644 --- a/yarn-project/pxe/src/config/index.ts +++ b/yarn-project/pxe/src/config/index.ts @@ -1,9 +1,9 @@ import { type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, enumConfigHelper, - getConfigFromMappings, numberConfigHelper, parseBooleanEnv, } from '@aztec/foundation/config'; @@ -99,7 +99,7 @@ export const pxeConfigMappings: ConfigMappingsType = composeConfigMap * Creates an instance of PXEConfig out of environment variables using sensible defaults for integration testing if not set. */ export function getPXEConfig(): PXEConfig { - return getConfigFromMappings(pxeConfigMappings); + return buildConfigFromEnv(pxeConfigMappings); } export const pxeCliConfigMappings: ConfigMappingsType = composeConfigMappings(nodeUrlConfigMappings); @@ -117,7 +117,7 @@ export const allPxeConfigMappings: ConfigMappingsType */ export function getCliPXEOptions(): CliPXEOptions & PXEConfig { const pxeConfig = getPXEConfig(); - const cliOptions = getConfigFromMappings(pxeCliConfigMappings); + const cliOptions = buildConfigFromEnv(pxeCliConfigMappings); return { ...pxeConfig, ...cliOptions, diff --git a/yarn-project/sequencer-client/src/config.ts b/yarn-project/sequencer-client/src/config.ts index 793ce10beec7..808374e0b7df 100644 --- a/yarn-project/sequencer-client/src/config.ts +++ b/yarn-project/sequencer-client/src/config.ts @@ -4,8 +4,8 @@ import { type L1ReaderConfig, l1ReaderConfigMappings } from '@aztec/ethereum/l1- import { type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, numberConfigHelper, optionalNumberConfigHelper, pickConfigMappings, @@ -256,5 +256,5 @@ export const sequencerClientConfigMappings: ConfigMappingsType(sequencerClientConfigMappings); + return buildConfigFromEnv(sequencerClientConfigMappings); } diff --git a/yarn-project/stdlib/src/config/pipelining-config.ts b/yarn-project/stdlib/src/config/pipelining-config.ts index ac7111672756..359d8193f590 100644 --- a/yarn-project/stdlib/src/config/pipelining-config.ts +++ b/yarn-project/stdlib/src/config/pipelining-config.ts @@ -1,4 +1,4 @@ -import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings } from '@aztec/foundation/config'; +import { type ConfigMappingsType, booleanConfigHelper, buildConfigFromEnv } from '@aztec/foundation/config'; import { z } from 'zod'; @@ -27,5 +27,5 @@ export const PipelineConfigSchema = zodFor()( ); export function getPipelineConfigEnvVars(): PipelineConfig { - return getConfigFromMappings(pipelineConfigMappings); + return buildConfigFromEnv(pipelineConfigMappings); } diff --git a/yarn-project/stdlib/src/ha-signing/config.ts b/yarn-project/stdlib/src/ha-signing/config.ts index e2a03a5fbd23..86c937e87c58 100644 --- a/yarn-project/stdlib/src/ha-signing/config.ts +++ b/yarn-project/stdlib/src/ha-signing/config.ts @@ -7,8 +7,8 @@ import { type ConfigMappingsType, SecretValue, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, getDefaultConfig, numberConfigHelper, optionalNumberConfigHelper, @@ -152,7 +152,7 @@ export const defaultValidatorHASignerConfig: ValidatorHASignerConfig = getDefaul * @returns The validator HA signer configuration. */ export function getConfigEnvVars(): ValidatorHASignerConfig { - return getConfigFromMappings(validatorHASignerConfigMappings); + return buildConfigFromEnv(validatorHASignerConfigMappings); } export const ValidatorHASignerConfigSchema = BaseSignerConfigSchema.extend({ diff --git a/yarn-project/stdlib/src/ha-signing/local_config.ts b/yarn-project/stdlib/src/ha-signing/local_config.ts index 448475675ede..bdf49979ce67 100644 --- a/yarn-project/stdlib/src/ha-signing/local_config.ts +++ b/yarn-project/stdlib/src/ha-signing/local_config.ts @@ -1,8 +1,8 @@ import { type ConfigMapping, type ConfigMappingsType, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, optionalNumberConfigHelper, } from '@aztec/foundation/config'; import { zodFor } from '@aztec/foundation/schemas'; @@ -52,5 +52,5 @@ export const LocalSignerConfigSchema = zodFor()( * Returns the local signer configuration from environment variables. */ export function getLocalSignerConfigEnvVars(): LocalSignerConfig { - return getConfigFromMappings(localSignerConfigMappings); + return buildConfigFromEnv(localSignerConfigMappings); } diff --git a/yarn-project/stdlib/src/kv-store/config.ts b/yarn-project/stdlib/src/kv-store/config.ts index 400794550360..dc92c2ed1435 100644 --- a/yarn-project/stdlib/src/kv-store/config.ts +++ b/yarn-project/stdlib/src/kv-store/config.ts @@ -1,8 +1,8 @@ import { type L1ContractAddresses, pickL1ContractAddressMappings } from '@aztec/ethereum/l1-contract-addresses'; import { type ConfigMappingsType, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, numberConfigHelper, } from '@aztec/foundation/config'; @@ -36,5 +36,5 @@ export const dataConfigMappings: ConfigMappingsType = composeCo * @returns The archiver configuration. */ export function getDataConfigFromEnv(): DataStoreConfig { - return getConfigFromMappings(dataConfigMappings); + return buildConfigFromEnv(dataConfigMappings); } diff --git a/yarn-project/telemetry-client/src/config.ts b/yarn-project/telemetry-client/src/config.ts index 9e8b7a56f8f8..58cb8eee1129 100644 --- a/yarn-project/telemetry-client/src/config.ts +++ b/yarn-project/telemetry-client/src/config.ts @@ -1,7 +1,7 @@ import { type ConfigMappingsType, booleanConfigHelper, - getConfigFromMappings, + buildConfigFromEnv, numberConfigHelper, parseCommaSeparated, } from '@aztec/foundation/config'; @@ -84,5 +84,5 @@ export const telemetryClientConfigMappings: ConfigMappingsType(telemetryClientConfigMappings); + return buildConfigFromEnv(telemetryClientConfigMappings); } diff --git a/yarn-project/validator-client/src/config.ts b/yarn-project/validator-client/src/config.ts index a6ecc75e5aa4..09d6bd497de3 100644 --- a/yarn-project/validator-client/src/config.ts +++ b/yarn-project/validator-client/src/config.ts @@ -1,8 +1,8 @@ import { type ConfigMappingsType, booleanConfigHelper, + buildConfigFromEnv, composeConfigMappings, - getConfigFromMappings, numberConfigHelper, parseCommaSeparated, secretValueConfigHelper, @@ -78,5 +78,5 @@ export const validatorClientConfigMappings: ConfigMappingsType(validatorClientConfigMappings); + return buildConfigFromEnv(validatorClientConfigMappings); } diff --git a/yarn-project/validator-client/src/validator.test.ts b/yarn-project/validator-client/src/validator.test.ts index 63c0d5394359..467500a8308b 100644 --- a/yarn-project/validator-client/src/validator.test.ts +++ b/yarn-project/validator-client/src/validator.test.ts @@ -11,7 +11,7 @@ import { } from '@aztec/foundation/branded-types'; import { Buffer32 } from '@aztec/foundation/buffer'; import { times } from '@aztec/foundation/collection'; -import { SecretValue, getConfigFromMappings } from '@aztec/foundation/config'; +import { SecretValue, buildConfigFromEnv } from '@aztec/foundation/config'; import { Secp256k1Signer, makeEthSignDigest } from '@aztec/foundation/crypto/secp256k1-signer'; import { Fr } from '@aztec/foundation/curves/bn254'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -1418,7 +1418,7 @@ describe('ValidatorClient', () => { VALIDATOR_PRIVATE_KEYS: undefined, }; - const config = getConfigFromMappings(validatorClientConfigMappings); + const config = buildConfigFromEnv(validatorClientConfigMappings); expect(config.validatorPrivateKeys!.getValue()).toHaveLength(1); expect(config.validatorPrivateKeys!.getValue()[0]).toBe(process.env.VALIDATOR_PRIVATE_KEY); }); diff --git a/yarn-project/world-state/src/synchronizer/config.ts b/yarn-project/world-state/src/synchronizer/config.ts index c4b75f07484f..0719c82fae1a 100644 --- a/yarn-project/world-state/src/synchronizer/config.ts +++ b/yarn-project/world-state/src/synchronizer/config.ts @@ -1,6 +1,6 @@ import { type ConfigMappingsType, - getConfigFromMappings, + buildConfigFromEnv, numberConfigHelper, optionalNumberConfigHelper, } from '@aztec/foundation/config'; @@ -102,5 +102,5 @@ export const worldStateConfigMappings: ConfigMappingsType = { * @returns The configuration values for the world state synchronizer. */ export function getWorldStateConfigFromEnv(): WorldStateConfig { - return getConfigFromMappings(worldStateConfigMappings); + return buildConfigFromEnv(worldStateConfigMappings); } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 4c76b9a61b4f..4af73d3c395d 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -889,6 +889,7 @@ __metadata: "@types/chalk": "npm:^2.2.0" "@types/jest": "npm:^30.0.0" "@types/koa": "npm:^2.15.0" + "@types/lodash.kebabcase": "npm:^4" "@typescript/native-preview": "npm:7.0.0-dev.20260113.1" abitype: "npm:^0.8.11" chalk: "npm:^5.3.0" @@ -896,6 +897,7 @@ __metadata: jest: "npm:^30.0.0" koa: "npm:^2.16.1" koa-router: "npm:^13.1.1" + lodash.kebabcase: "npm:^4.1.1" ts-node: "npm:^10.9.1" typescript: "npm:^5.3.3" viem: "npm:@aztec/viem@2.38.2" @@ -7580,6 +7582,15 @@ __metadata: languageName: node linkType: hard +"@types/lodash.kebabcase@npm:^4": + version: 4.1.9 + resolution: "@types/lodash.kebabcase@npm:4.1.9" + dependencies: + "@types/lodash": "npm:*" + checksum: 10/66f54d6f94813c6d28896d80a9205d032baa31642f7fd8f74fcc67d113c023eced63ae643143dbbe05bd90866a6cc51b44c632a3b3651a69e8867cf779593a6a + languageName: node + linkType: hard + "@types/lodash.merge@npm:^4.6.9": version: 4.6.9 resolution: "@types/lodash.merge@npm:4.6.9" @@ -15995,6 +16006,13 @@ __metadata: languageName: node linkType: hard +"lodash.kebabcase@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.kebabcase@npm:4.1.1" + checksum: 10/d84ec5441ef8e5c718c50315f35b0a045a77c7e8ee3e54472c06dc31f6f3602e95551a16c0923d689198b51deb8902c4bbc54fc9b965b26c1f86e21df3a05f34 + languageName: node + linkType: hard + "lodash.memoize@npm:^4.1.2": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2"