Skip to content

Commit f2f9ef3

Browse files
authored
feat(config): add fallbacks (#12593)
## Overview Again like #12589 this is added after a chat with charlie where we want to remove the need for a complex script. #12566 asks the user for a private key as it needs to set two env vars to the same key. This pr introduces the notion of a fallback, such that a config variable will also check a list of fallbacks before using the default value. In this case, if `VALIDATOR_PRIVATE_KEY` is set, then `SEQ_PUBLISHER_PRIVATE_KEY`` does not need to be set, removing the need for it in the script.
1 parent 88f0711 commit f2f9ef3

File tree

5 files changed

+76
-20
lines changed

5 files changed

+76
-20
lines changed

yarn-project/aztec/src/cli/cmds/start_node.ts

+18-7
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,17 @@ export async function startNode(
2626
): Promise<{ config: AztecNodeConfig }> {
2727
// options specifically namespaced with --node.<option>
2828
const nodeSpecificOptions = extractNamespacedOptions(options, 'node');
29+
30+
// All options set from environment variables
31+
const configFromEnvVars = getConfigEnvVars();
32+
33+
// Extract relevant options from command line arguments
34+
const relevantOptions = extractRelevantOptions(options, aztecNodeConfigMappings, 'node');
35+
2936
// All options that are relevant to the Aztec Node
3037
let nodeConfig: AztecNodeConfig = {
31-
...getConfigEnvVars(),
32-
...extractRelevantOptions(options, aztecNodeConfigMappings, 'node'),
38+
...configFromEnvVars,
39+
...relevantOptions,
3340
};
3441

3542
if (options.proverNode) {
@@ -96,22 +103,26 @@ export async function startNode(
96103
if (!options.sequencer) {
97104
nodeConfig.disableValidator = true;
98105
} else {
99-
const sequencerConfig = extractNamespacedOptions(options, 'sequencer');
106+
const sequencerConfig = {
107+
...configFromEnvVars,
108+
...extractNamespacedOptions(options, 'sequencer'),
109+
};
100110
let account;
101111
if (!sequencerConfig.publisherPrivateKey || sequencerConfig.publisherPrivateKey === NULL_KEY) {
102-
if (!options.l1Mnemonic) {
112+
if (sequencerConfig.validatorPrivateKey) {
113+
sequencerConfig.publisherPrivateKey = sequencerConfig.validatorPrivateKey as `0x${string}`;
114+
} else if (!options.l1Mnemonic) {
103115
userLog(
104116
'--sequencer.publisherPrivateKey or --l1-mnemonic is required to start Aztec Node with --sequencer option',
105117
);
106118
throw new Error('Private key or Mnemonic is required to start Aztec Node with --sequencer option');
107119
} else {
108120
account = mnemonicToAccount(options.l1Mnemonic);
109121
const privKey = account.getHdKey().privateKey;
110-
nodeConfig.publisherPrivateKey = `0x${Buffer.from(privKey!).toString('hex')}`;
122+
sequencerConfig.publisherPrivateKey = `0x${Buffer.from(privKey!).toString('hex')}`;
111123
}
112-
} else {
113-
nodeConfig.publisherPrivateKey = sequencerConfig.publisherPrivateKey;
114124
}
125+
nodeConfig.publisherPrivateKey = sequencerConfig.publisherPrivateKey;
115126
}
116127

117128
if (nodeConfig.p2pEnabled) {

yarn-project/end-to-end/scripts/native-network/validator.sh

+7-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ exec > >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log") 2> >(tee -a "$(dirname
1111
PORT="$1"
1212
P2P_PORT="$2"
1313
ADDRESS="${3:-${ADDRESS:-}}"
14-
export VALIDATOR_PRIVATE_KEY="${4:-${VALIDATOR_PRIVATE_KEY:-}}"
14+
validator_private_key="${4:-${VALIDATOR_PRIVATE_KEY:-}}"
1515

1616
# Starts the Validator Node
1717
REPO=$(git rev-parse --show-toplevel)
@@ -47,15 +47,16 @@ export BOOTSTRAP_NODES=$(echo "$output" | grep -oP 'Node ENR: \K.*')
4747
echo "BOOTSTRAP_NODES: $BOOTSTRAP_NODES"
4848

4949
# Generate a private key for the validator only if not already set
50-
if [ -z "${VALIDATOR_PRIVATE_KEY:-}" ] || [ -z "${ADDRESS:-}" ]; then
50+
if [ -z "${validator_private_key:-}" ] || [ -z "${ADDRESS:-}" ]; then
5151
echo "Generating new L1 Validator account..."
5252
json_account=$(node --no-warnings "$REPO"/yarn-project/aztec/dest/bin/index.js generate-l1-account)
5353
export ADDRESS=$(echo $json_account | jq -r '.address')
54-
export VALIDATOR_PRIVATE_KEY=$(echo $json_account | jq -r '.privateKey')
54+
validator_private_key=$(echo $json_account | jq -r '.privateKey')
5555
fi
5656

57-
export L1_PRIVATE_KEY=$VALIDATOR_PRIVATE_KEY
58-
export SEQ_PUBLISHER_PRIVATE_KEY=$VALIDATOR_PRIVATE_KEY
57+
## We want to use the private key from the cli
58+
unset VALIDATOR_PRIVATE_KEY
59+
5960
export DEBUG=${DEBUG:-""}
6061
export LOG_LEVEL=${LOG_LEVEL:-"verbose"}
6162
export L1_CONSENSUS_HOST_URL=${L1_CONSENSUS_HOST_URL:-}
@@ -90,4 +91,4 @@ else
9091
fi
9192

9293
# Start the Validator Node with the sequencer and archiver
93-
node --no-warnings "$REPO"/yarn-project/aztec/dest/bin/index.js start --port="$PORT" --node --archiver --sequencer
94+
node --no-warnings "$REPO"/yarn-project/aztec/dest/bin/index.js start --port="$PORT" --node --archiver --sequencer --sequencer.validatorPrivateKey="$validator_private_key"

yarn-project/foundation/src/config/index.ts

+47-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface ConfigMapping {
1010
description: string;
1111
isBoolean?: boolean;
1212
nested?: Record<string, ConfigMapping>;
13+
fallback?: EnvVar[];
1314
}
1415

1516
export function isBooleanConfigValue<T>(obj: T, key: keyof T): boolean {
@@ -18,20 +19,59 @@ export function isBooleanConfigValue<T>(obj: T, key: keyof T): boolean {
1819

1920
export type ConfigMappingsType<T> = Record<keyof T, ConfigMapping>;
2021

22+
/**
23+
* Shared utility function to get a value from environment variables with fallback support.
24+
* This can be used by both getConfigFromMappings and CLI utilities.
25+
*
26+
* @param env - The primary environment variable name
27+
* @param fallback - Optional array of fallback environment variable names
28+
* @param parseFunc - Optional function to parse the environment variable value
29+
* @param defaultValue - Optional default value to use if no environment variable is set
30+
* @returns The parsed value from environment variables or the default value
31+
*/
32+
export function getValueFromEnvWithFallback<T>(
33+
env: EnvVar | undefined,
34+
parseFunc: ((val: string) => T) | undefined,
35+
defaultValue: T | undefined,
36+
fallback?: EnvVar[],
37+
): T | undefined {
38+
let value: string | undefined;
39+
40+
// Try primary env var
41+
if (env) {
42+
value = process.env[env];
43+
}
44+
45+
// If primary not found, try fallbacks
46+
if (value === undefined && fallback && fallback.length > 0) {
47+
for (const fallbackEnv of fallback) {
48+
const fallbackVal = process.env[fallbackEnv];
49+
if (fallbackVal !== undefined) {
50+
value = fallbackVal;
51+
break;
52+
}
53+
}
54+
}
55+
56+
// Parse the value if needed
57+
if (value !== undefined) {
58+
return parseFunc ? parseFunc(value) : (value as unknown as T);
59+
}
60+
61+
// Return default if no env var found
62+
return defaultValue;
63+
}
64+
2165
export function getConfigFromMappings<T>(configMappings: ConfigMappingsType<T>): T {
2266
const config = {} as T;
2367

2468
for (const key in configMappings) {
25-
const { env, parseEnv, defaultValue: def, nested } = configMappings[key];
69+
const { env, parseEnv, defaultValue, nested, fallback } = configMappings[key];
2670
if (nested) {
2771
(config as any)[key] = getConfigFromMappings(nested);
2872
} else {
29-
const val = env ? process.env[env] : undefined;
30-
if (val !== undefined) {
31-
(config as any)[key] = parseEnv ? parseEnv(val) : val;
32-
} else if (def !== undefined) {
33-
(config as any)[key] = def;
34-
}
73+
// Use the shared utility function
74+
(config as any)[key] = getValueFromEnvWithFallback(env, parseEnv, defaultValue, fallback);
3575
}
3676
}
3777

yarn-project/sequencer-client/src/config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { Fr } from '@aztec/foundation/fields';
1616
import { FunctionSelector } from '@aztec/stdlib/abi';
1717
import { AztecAddress } from '@aztec/stdlib/aztec-address';
1818
import { type AllowedElement, type ChainConfig, type SequencerConfig, chainConfigMappings } from '@aztec/stdlib/config';
19+
import { type ValidatorClientConfig, validatorClientConfigMappings } from '@aztec/validator-client';
1920

2021
import {
2122
type PublisherConfig,
@@ -31,6 +32,7 @@ export type { SequencerConfig };
3132
* Configuration settings for the SequencerClient.
3233
*/
3334
export type SequencerClientConfig = PublisherConfig &
35+
ValidatorClientConfig &
3436
TxSenderConfig &
3537
SequencerConfig &
3638
L1ReaderConfig &
@@ -113,6 +115,7 @@ export const sequencerConfigMappings: ConfigMappingsType<SequencerConfig> = {
113115
};
114116

115117
export const sequencerClientConfigMappings: ConfigMappingsType<SequencerClientConfig> = {
118+
...validatorClientConfigMappings,
116119
...sequencerConfigMappings,
117120
...l1ReaderConfigMappings,
118121
...getTxSenderConfigMappings('SEQ'),

yarn-project/sequencer-client/src/publisher/config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const getTxSenderConfigMappings: (
5050
description: 'The private key to be used by the publisher.',
5151
parseEnv: (val: string) => (val ? `0x${val.replace('0x', '')}` : NULL_KEY),
5252
defaultValue: NULL_KEY,
53+
fallback: ['VALIDATOR_PRIVATE_KEY'],
5354
},
5455
});
5556

0 commit comments

Comments
 (0)