@aztec/aztec 1.1.3 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/cli/aztec_start_options.d.ts +1 -0
- package/dest/cli/aztec_start_options.d.ts.map +1 -1
- package/dest/cli/aztec_start_options.js +2 -1
- package/dest/cli/chain_l2_config.d.ts +3 -5
- package/dest/cli/chain_l2_config.d.ts.map +1 -1
- package/dest/cli/chain_l2_config.js +51 -20
- package/dest/cli/util.d.ts.map +1 -1
- package/dest/cli/util.js +18 -6
- package/package.json +30 -30
- package/src/cli/aztec_start_options.ts +5 -1
- package/src/cli/chain_l2_config.ts +57 -12
- package/src/cli/util.ts +21 -7
|
@@ -5,6 +5,7 @@ export interface AztecStartOption {
|
|
|
5
5
|
defaultValue: any;
|
|
6
6
|
printDefault?: (val: any) => string;
|
|
7
7
|
envVar: EnvVar | undefined;
|
|
8
|
+
fallback?: EnvVar[];
|
|
8
9
|
parseVal?: (val: string) => any;
|
|
9
10
|
}
|
|
10
11
|
export declare const getOptions: (namespace: string, configMappings: Record<string, ConfigMapping>) => AztecStartOption[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aztec_start_options.d.ts","sourceRoot":"","sources":["../../src/cli/aztec_start_options.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,MAAM,EAKZ,MAAM,0BAA0B,CAAC;AAelC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAC;CACjC;AAED,eAAO,MAAM,UAAU,GAAI,WAAW,MAAM,EAAE,gBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"aztec_start_options.d.ts","sourceRoot":"","sources":["../../src/cli/aztec_start_options.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,MAAM,EAKZ,MAAM,0BAA0B,CAAC;AAelC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,GAAG,CAAC;CACjC;AAED,eAAO,MAAM,UAAU,GAAI,WAAW,MAAM,EAAE,gBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,uBAoB1F,CAAC;AAGF,eAAO,MAAM,gBAAgB,UAU5B,CAAC;AAEF,eAAO,MAAM,YAAY,YAAY,CAAC;AAGtC,eAAO,MAAM,iBAAiB,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAAA;CAsXlE,CAAC"}
|
|
@@ -12,7 +12,7 @@ import { telemetryClientConfigMappings } from '@aztec/telemetry-client';
|
|
|
12
12
|
import { DefaultMnemonic } from '../mnemonic.js';
|
|
13
13
|
export const getOptions = (namespace, configMappings)=>{
|
|
14
14
|
const options = [];
|
|
15
|
-
for (const [key, { env, defaultValue: def, parseEnv, description, printDefault }] of Object.entries(configMappings)){
|
|
15
|
+
for (const [key, { env, defaultValue: def, parseEnv, description, printDefault, fallback }] of Object.entries(configMappings)){
|
|
16
16
|
if (universalOptions.includes(key)) {
|
|
17
17
|
continue;
|
|
18
18
|
}
|
|
@@ -23,6 +23,7 @@ export const getOptions = (namespace, configMappings)=>{
|
|
|
23
23
|
defaultValue: def,
|
|
24
24
|
printDefault,
|
|
25
25
|
envVar: env,
|
|
26
|
+
fallback,
|
|
26
27
|
parseVal: parseEnv
|
|
27
28
|
});
|
|
28
29
|
}
|
|
@@ -57,13 +57,11 @@ export type L2ChainConfig = {
|
|
|
57
57
|
slashInvalidBlockEnabled: boolean;
|
|
58
58
|
slashInvalidBlockPenalty: bigint;
|
|
59
59
|
slashInvalidBlockMaxPenalty: bigint;
|
|
60
|
+
sentinelEnabled: boolean;
|
|
60
61
|
};
|
|
61
62
|
export declare const testnetIgnitionL2ChainConfig: L2ChainConfig;
|
|
62
63
|
export declare const alphaTestnetL2ChainConfig: L2ChainConfig;
|
|
63
|
-
export declare function getBootnodes(networkName: NetworkNames): Promise<any>;
|
|
64
|
-
export declare function getL2ChainConfig(networkName: NetworkNames): Promise<
|
|
65
|
-
config: L2ChainConfig;
|
|
66
|
-
networkName: string;
|
|
67
|
-
} | undefined>;
|
|
64
|
+
export declare function getBootnodes(networkName: NetworkNames, cacheDir?: string): Promise<any>;
|
|
65
|
+
export declare function getL2ChainConfig(networkName: NetworkNames, cacheDir?: string): Promise<L2ChainConfig | undefined>;
|
|
68
66
|
export declare function enrichEnvironmentWithChainConfig(networkName: NetworkNames): Promise<void>;
|
|
69
67
|
//# sourceMappingURL=chain_l2_config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain_l2_config.d.ts","sourceRoot":"","sources":["../../src/cli/chain_l2_config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"chain_l2_config.d.ts","sourceRoot":"","sources":["../../src/cli/chain_l2_config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAO/D,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;IAIpC,yCAAyC;IACzC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,uFAAuF;IACvF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wCAAwC;IACxC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,wBAAwB,EAAE,MAAM,CAAC;IACjC,+EAA+E;IAC/E,0BAA0B,EAAE,MAAM,CAAC;IACnC,yCAAyC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kCAAkC;IAClC,wBAAwB,EAAE,MAAM,CAAC;IACjC,sCAAsC;IACtC,2BAA2B,EAAE,MAAM,CAAC;IACpC,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAG3B,sBAAsB,EAAE,MAAM,CAAC;IAC/B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sBAAsB,EAAE,OAAO,CAAC;IAChC,qCAAqC,EAAE,MAAM,CAAC;IAC9C,qCAAqC,EAAE,MAAM,CAAC;IAC9C,4BAA4B,EAAE,MAAM,CAAC;IACrC,yBAAyB,EAAE,MAAM,CAAC;IAClC,wBAAwB,EAAE,OAAO,CAAC;IAClC,wBAAwB,EAAE,MAAM,CAAC;IACjC,2BAA2B,EAAE,MAAM,CAAC;IAEpC,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,aA2D1C,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,aA8DvC,CAAC;AAIF,wBAAsB,YAAY,CAAC,WAAW,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,MAAM,gBAgC9E;AAED,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,YAAY,EACzB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAWpC;AAgCD,wBAAsB,gCAAgC,CAAC,WAAW,EAAE,YAAY,iBA8E/E"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EthAddress } from '@aztec/aztec.js';
|
|
2
2
|
import { DefaultL1ContractsConfig } from '@aztec/ethereum';
|
|
3
|
-
import
|
|
3
|
+
import { mkdir, readFile, stat, writeFile } from 'fs/promises';
|
|
4
|
+
import path, { dirname, join } from 'path';
|
|
4
5
|
import publicIncludeMetrics from '../../public_include_metric_prefixes.json' with {
|
|
5
6
|
type: 'json'
|
|
6
7
|
};
|
|
@@ -46,7 +47,8 @@ export const testnetIgnitionL2ChainConfig = {
|
|
|
46
47
|
slashPrunePenalty: 0n,
|
|
47
48
|
slashPruneMaxPenalty: 0n,
|
|
48
49
|
slashInvalidBlockPenalty: 0n,
|
|
49
|
-
slashInvalidBlockMaxPenalty: 0n
|
|
50
|
+
slashInvalidBlockMaxPenalty: 0n,
|
|
51
|
+
sentinelEnabled: false
|
|
50
52
|
};
|
|
51
53
|
export const alphaTestnetL2ChainConfig = {
|
|
52
54
|
l1ChainId: 11155111,
|
|
@@ -90,41 +92,58 @@ export const alphaTestnetL2ChainConfig = {
|
|
|
90
92
|
slashPruneMaxPenalty: 17n * (DefaultL1ContractsConfig.depositAmount / 100n),
|
|
91
93
|
slashInactivityEnabled: true,
|
|
92
94
|
slashInactivityCreateTargetPercentage: 1,
|
|
93
|
-
slashInactivitySignalTargetPercentage:
|
|
95
|
+
slashInactivitySignalTargetPercentage: 0.67,
|
|
94
96
|
slashInactivityCreatePenalty: 17n * (DefaultL1ContractsConfig.depositAmount / 100n),
|
|
95
97
|
slashInactivityMaxPenalty: 17n * (DefaultL1ContractsConfig.depositAmount / 100n),
|
|
96
98
|
slashInvalidBlockEnabled: true,
|
|
97
99
|
slashInvalidBlockPenalty: DefaultL1ContractsConfig.depositAmount,
|
|
98
|
-
slashInvalidBlockMaxPenalty: DefaultL1ContractsConfig.depositAmount
|
|
100
|
+
slashInvalidBlockMaxPenalty: DefaultL1ContractsConfig.depositAmount,
|
|
101
|
+
sentinelEnabled: true
|
|
99
102
|
};
|
|
100
|
-
|
|
103
|
+
const BOOTNODE_CACHE_DURATION_MS = 60 * 60 * 1000; // 1 hour;
|
|
104
|
+
export async function getBootnodes(networkName, cacheDir) {
|
|
105
|
+
const cacheFile = cacheDir ? join(cacheDir, networkName, 'bootnodes.json') : undefined;
|
|
106
|
+
try {
|
|
107
|
+
if (cacheFile) {
|
|
108
|
+
const info = await stat(cacheFile);
|
|
109
|
+
if (info.mtimeMs + BOOTNODE_CACHE_DURATION_MS > Date.now()) {
|
|
110
|
+
return JSON.parse(await readFile(cacheFile, 'utf-8'))['bootnodes'];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
} catch {
|
|
114
|
+
// no-op. Get the remote-file
|
|
115
|
+
}
|
|
101
116
|
const url = `http://static.aztec.network/${networkName}/bootnodes.json`;
|
|
102
117
|
const response = await fetch(url);
|
|
103
118
|
if (!response.ok) {
|
|
104
119
|
throw new Error(`Failed to fetch basic contract addresses from ${url}. Check you are using a correct network name.`);
|
|
105
120
|
}
|
|
106
121
|
const json = await response.json();
|
|
122
|
+
try {
|
|
123
|
+
if (cacheFile) {
|
|
124
|
+
await mkdir(dirname(cacheFile), {
|
|
125
|
+
recursive: true
|
|
126
|
+
});
|
|
127
|
+
await writeFile(cacheFile, JSON.stringify(json), 'utf-8');
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
// no-op
|
|
131
|
+
}
|
|
107
132
|
return json['bootnodes'];
|
|
108
133
|
}
|
|
109
|
-
export async function getL2ChainConfig(networkName) {
|
|
134
|
+
export async function getL2ChainConfig(networkName, cacheDir) {
|
|
110
135
|
if (networkName === 'testnet-ignition') {
|
|
111
136
|
const config = {
|
|
112
137
|
...testnetIgnitionL2ChainConfig
|
|
113
138
|
};
|
|
114
|
-
config.p2pBootstrapNodes = await getBootnodes(networkName);
|
|
115
|
-
return
|
|
116
|
-
config,
|
|
117
|
-
networkName
|
|
118
|
-
};
|
|
139
|
+
config.p2pBootstrapNodes = await getBootnodes(networkName, cacheDir);
|
|
140
|
+
return config;
|
|
119
141
|
} else if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
120
142
|
const config = {
|
|
121
143
|
...alphaTestnetL2ChainConfig
|
|
122
144
|
};
|
|
123
|
-
config.p2pBootstrapNodes = await getBootnodes('alpha-testnet');
|
|
124
|
-
return
|
|
125
|
-
config,
|
|
126
|
-
networkName: 'alpha-testnet'
|
|
127
|
-
};
|
|
145
|
+
config.p2pBootstrapNodes = await getBootnodes('alpha-testnet', cacheDir);
|
|
146
|
+
return config;
|
|
128
147
|
}
|
|
129
148
|
return undefined;
|
|
130
149
|
}
|
|
@@ -143,15 +162,27 @@ function enrichEthAddressVar(envVar, value) {
|
|
|
143
162
|
}
|
|
144
163
|
enrichVar(envVar, value);
|
|
145
164
|
}
|
|
165
|
+
function getDefaultDataDir(networkName) {
|
|
166
|
+
let prefix;
|
|
167
|
+
if (networkName === 'testnet-ignition') {
|
|
168
|
+
prefix = 'testnet-ignition';
|
|
169
|
+
} else if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
170
|
+
prefix = 'alpha-testnet';
|
|
171
|
+
} else {
|
|
172
|
+
prefix = networkName;
|
|
173
|
+
}
|
|
174
|
+
return path.join(process.env.HOME || '~', '.aztec', prefix, 'data');
|
|
175
|
+
}
|
|
146
176
|
export async function enrichEnvironmentWithChainConfig(networkName) {
|
|
147
177
|
if (networkName === 'local') {
|
|
148
178
|
return;
|
|
149
179
|
}
|
|
150
|
-
|
|
151
|
-
|
|
180
|
+
enrichVar('DATA_DIRECTORY', getDefaultDataDir(networkName));
|
|
181
|
+
const cacheDir = process.env.DATA_DIRECTORY ? join(process.env.DATA_DIRECTORY, 'cache') : undefined;
|
|
182
|
+
const config = await getL2ChainConfig(networkName, cacheDir);
|
|
183
|
+
if (!config) {
|
|
152
184
|
throw new Error(`Unknown network name: ${networkName}`);
|
|
153
185
|
}
|
|
154
|
-
const { config, networkName: name } = result;
|
|
155
186
|
enrichVar('BOOTSTRAP_NODES', config.p2pBootstrapNodes.join(','));
|
|
156
187
|
enrichVar('TEST_ACCOUNTS', config.testAccounts.toString());
|
|
157
188
|
enrichVar('SPONSORED_FPC', config.sponsoredFPC.toString());
|
|
@@ -159,7 +190,6 @@ export async function enrichEnvironmentWithChainConfig(networkName) {
|
|
|
159
190
|
enrichVar('L1_CHAIN_ID', config.l1ChainId.toString());
|
|
160
191
|
enrichVar('SEQ_MIN_TX_PER_BLOCK', config.seqMinTxsPerBlock.toString());
|
|
161
192
|
enrichVar('SEQ_MAX_TX_PER_BLOCK', config.seqMaxTxsPerBlock.toString());
|
|
162
|
-
enrichVar('DATA_DIRECTORY', path.join(process.env.HOME || '~', '.aztec', name, 'data'));
|
|
163
193
|
enrichVar('PROVER_REAL_PROOFS', config.realProofs.toString());
|
|
164
194
|
enrichVar('PXE_PROVER_ENABLED', config.realProofs.toString());
|
|
165
195
|
enrichVar('SYNC_SNAPSHOTS_URL', config.snapshotsUrl);
|
|
@@ -209,4 +239,5 @@ export async function enrichEnvironmentWithChainConfig(networkName) {
|
|
|
209
239
|
enrichVar('SLASH_INVALID_BLOCK_ENABLED', config.slashInvalidBlockEnabled.toString());
|
|
210
240
|
enrichVar('SLASH_INVALID_BLOCK_PENALTY', config.slashInvalidBlockPenalty.toString());
|
|
211
241
|
enrichVar('SLASH_INVALID_BLOCK_MAX_PENALTY', config.slashInvalidBlockMaxPenalty.toString());
|
|
242
|
+
enrichVar('SENTINEL_ENABLED', config.sentinelEnabled.toString());
|
|
212
243
|
}
|
package/dest/cli/util.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/cli/util.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,KAAK,KAAK,EAAgB,MAAM,uBAAuB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAKpE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,EAAE,KAAK,gBAAgB,EAAqB,MAAM,0BAA0B,CAAC;AAEpF,0BAAkB,QAAQ;IACxB,OAAO,IAAI;IACX,KAAK,IAAI;IACT,cAAc,KAAK,CAAE,8EAA8E;IACnG,eAAe,KAAK,CAAE,0CAA0C;IAEhE,MAAM,MAAM;IACZ,MAAM,MAAM;IACZ,OAAO,MAAM;IACb,OAAO,MAAM;CACd;AAGD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAgB1G;AAED,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED,eAAO,MAAM,qBAAqB,GAAI,OAAO,KAAK,EAAE,KAAK,KAAK,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,SAalF,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,sBAAsB,EAAE;IACtB;;OAEG;IACH,OAAO,EAAE,cAAc,CAAC;IACxB;;OAEG;IACH,SAAS,EAAE,EAAE,CAAC;CACf,EAAE,EACH,GAAG,EAAE,UAAU,qBAyBhB;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAAA;CAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAiB/F;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,EACvB,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAKR;
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/cli/util.ts"],"names":[],"mappings":"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"}
|
package/dest/cli/util.js
CHANGED
|
@@ -111,17 +111,29 @@ export function formatHelpLine(option, defaultValue, envVar, maxOptionLength, ma
|
|
|
111
111
|
}
|
|
112
112
|
const getDefaultOrEnvValue = (opt)=>{
|
|
113
113
|
let val;
|
|
114
|
-
// if the option is set in the environment, use that
|
|
115
|
-
if (opt.envVar
|
|
114
|
+
// if the option is set in the environment, use that
|
|
115
|
+
if (opt.envVar) {
|
|
116
116
|
val = process.env[opt.envVar];
|
|
117
|
-
|
|
117
|
+
}
|
|
118
|
+
// if we have fallback env vars, check those
|
|
119
|
+
if (!val && opt.fallback && opt.fallback.length > 0) {
|
|
120
|
+
for (const fallback of opt.fallback){
|
|
121
|
+
val = process.env[fallback];
|
|
122
|
+
if (val) {
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// if we have a value, optionally parse it and return
|
|
128
|
+
if (val) {
|
|
129
|
+
if (opt.parseVal) {
|
|
118
130
|
return opt.parseVal(val);
|
|
119
131
|
}
|
|
120
|
-
|
|
132
|
+
return val;
|
|
121
133
|
} else if (opt.defaultValue) {
|
|
122
|
-
|
|
134
|
+
return opt.defaultValue;
|
|
123
135
|
}
|
|
124
|
-
return
|
|
136
|
+
return undefined;
|
|
125
137
|
};
|
|
126
138
|
// Function to add options dynamically
|
|
127
139
|
export const addOptions = (cmd, options)=>{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/aztec",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dest/index.js"
|
|
@@ -27,35 +27,35 @@
|
|
|
27
27
|
"../package.common.json"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@aztec/accounts": "1.1
|
|
31
|
-
"@aztec/archiver": "1.1
|
|
32
|
-
"@aztec/aztec-faucet": "1.1
|
|
33
|
-
"@aztec/aztec-node": "1.1
|
|
34
|
-
"@aztec/aztec.js": "1.1
|
|
35
|
-
"@aztec/bb-prover": "1.1
|
|
36
|
-
"@aztec/bb.js": "1.1
|
|
37
|
-
"@aztec/blob-sink": "1.1
|
|
38
|
-
"@aztec/bot": "1.1
|
|
39
|
-
"@aztec/builder": "1.1
|
|
40
|
-
"@aztec/cli": "1.1
|
|
41
|
-
"@aztec/cli-wallet": "1.1
|
|
42
|
-
"@aztec/constants": "1.1
|
|
43
|
-
"@aztec/entrypoints": "1.1
|
|
44
|
-
"@aztec/ethereum": "1.1
|
|
45
|
-
"@aztec/foundation": "1.1
|
|
46
|
-
"@aztec/kv-store": "1.1
|
|
47
|
-
"@aztec/noir-contracts.js": "1.1
|
|
48
|
-
"@aztec/noir-protocol-circuits-types": "1.1
|
|
49
|
-
"@aztec/p2p": "1.1
|
|
50
|
-
"@aztec/p2p-bootstrap": "1.1
|
|
51
|
-
"@aztec/protocol-contracts": "1.1
|
|
52
|
-
"@aztec/prover-client": "1.1
|
|
53
|
-
"@aztec/prover-node": "1.1
|
|
54
|
-
"@aztec/pxe": "1.1
|
|
55
|
-
"@aztec/stdlib": "1.1
|
|
56
|
-
"@aztec/telemetry-client": "1.1
|
|
57
|
-
"@aztec/txe": "1.1
|
|
58
|
-
"@aztec/world-state": "1.1
|
|
30
|
+
"@aztec/accounts": "1.2.1",
|
|
31
|
+
"@aztec/archiver": "1.2.1",
|
|
32
|
+
"@aztec/aztec-faucet": "1.2.1",
|
|
33
|
+
"@aztec/aztec-node": "1.2.1",
|
|
34
|
+
"@aztec/aztec.js": "1.2.1",
|
|
35
|
+
"@aztec/bb-prover": "1.2.1",
|
|
36
|
+
"@aztec/bb.js": "1.2.1",
|
|
37
|
+
"@aztec/blob-sink": "1.2.1",
|
|
38
|
+
"@aztec/bot": "1.2.1",
|
|
39
|
+
"@aztec/builder": "1.2.1",
|
|
40
|
+
"@aztec/cli": "1.2.1",
|
|
41
|
+
"@aztec/cli-wallet": "1.2.1",
|
|
42
|
+
"@aztec/constants": "1.2.1",
|
|
43
|
+
"@aztec/entrypoints": "1.2.1",
|
|
44
|
+
"@aztec/ethereum": "1.2.1",
|
|
45
|
+
"@aztec/foundation": "1.2.1",
|
|
46
|
+
"@aztec/kv-store": "1.2.1",
|
|
47
|
+
"@aztec/noir-contracts.js": "1.2.1",
|
|
48
|
+
"@aztec/noir-protocol-circuits-types": "1.2.1",
|
|
49
|
+
"@aztec/p2p": "1.2.1",
|
|
50
|
+
"@aztec/p2p-bootstrap": "1.2.1",
|
|
51
|
+
"@aztec/protocol-contracts": "1.2.1",
|
|
52
|
+
"@aztec/prover-client": "1.2.1",
|
|
53
|
+
"@aztec/prover-node": "1.2.1",
|
|
54
|
+
"@aztec/pxe": "1.2.1",
|
|
55
|
+
"@aztec/stdlib": "1.2.1",
|
|
56
|
+
"@aztec/telemetry-client": "1.2.1",
|
|
57
|
+
"@aztec/txe": "1.2.1",
|
|
58
|
+
"@aztec/world-state": "1.2.1",
|
|
59
59
|
"@types/chalk": "^2.2.0",
|
|
60
60
|
"abitype": "^0.8.11",
|
|
61
61
|
"chalk": "^5.3.0",
|
|
@@ -31,12 +31,15 @@ export interface AztecStartOption {
|
|
|
31
31
|
defaultValue: any;
|
|
32
32
|
printDefault?: (val: any) => string;
|
|
33
33
|
envVar: EnvVar | undefined;
|
|
34
|
+
fallback?: EnvVar[];
|
|
34
35
|
parseVal?: (val: string) => any;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
export const getOptions = (namespace: string, configMappings: Record<string, ConfigMapping>) => {
|
|
38
39
|
const options: AztecStartOption[] = [];
|
|
39
|
-
for (const [key, { env, defaultValue: def, parseEnv, description, printDefault }] of Object.entries(
|
|
40
|
+
for (const [key, { env, defaultValue: def, parseEnv, description, printDefault, fallback }] of Object.entries(
|
|
41
|
+
configMappings,
|
|
42
|
+
)) {
|
|
40
43
|
if (universalOptions.includes(key)) {
|
|
41
44
|
continue;
|
|
42
45
|
}
|
|
@@ -47,6 +50,7 @@ export const getOptions = (namespace: string, configMappings: Record<string, Con
|
|
|
47
50
|
defaultValue: def,
|
|
48
51
|
printDefault,
|
|
49
52
|
envVar: env,
|
|
53
|
+
fallback,
|
|
50
54
|
parseVal: parseEnv,
|
|
51
55
|
});
|
|
52
56
|
}
|
|
@@ -3,7 +3,8 @@ import { DefaultL1ContractsConfig } from '@aztec/ethereum';
|
|
|
3
3
|
import type { EnvVar, NetworkNames } from '@aztec/foundation/config';
|
|
4
4
|
import type { SharedNodeConfig } from '@aztec/node-lib/config';
|
|
5
5
|
|
|
6
|
-
import
|
|
6
|
+
import { mkdir, readFile, stat, writeFile } from 'fs/promises';
|
|
7
|
+
import path, { dirname, join } from 'path';
|
|
7
8
|
|
|
8
9
|
import publicIncludeMetrics from '../../public_include_metric_prefixes.json' with { type: 'json' };
|
|
9
10
|
|
|
@@ -69,6 +70,8 @@ export type L2ChainConfig = {
|
|
|
69
70
|
slashInvalidBlockEnabled: boolean;
|
|
70
71
|
slashInvalidBlockPenalty: bigint;
|
|
71
72
|
slashInvalidBlockMaxPenalty: bigint;
|
|
73
|
+
// control whether sentinel is enabled or not. Needed for slashing
|
|
74
|
+
sentinelEnabled: boolean;
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
export const testnetIgnitionL2ChainConfig: L2ChainConfig = {
|
|
@@ -129,6 +132,7 @@ export const testnetIgnitionL2ChainConfig: L2ChainConfig = {
|
|
|
129
132
|
slashPruneMaxPenalty: 0n,
|
|
130
133
|
slashInvalidBlockPenalty: 0n,
|
|
131
134
|
slashInvalidBlockMaxPenalty: 0n,
|
|
135
|
+
sentinelEnabled: false,
|
|
132
136
|
};
|
|
133
137
|
|
|
134
138
|
export const alphaTestnetL2ChainConfig: L2ChainConfig = {
|
|
@@ -186,15 +190,30 @@ export const alphaTestnetL2ChainConfig: L2ChainConfig = {
|
|
|
186
190
|
slashPruneMaxPenalty: 17n * (DefaultL1ContractsConfig.depositAmount / 100n),
|
|
187
191
|
slashInactivityEnabled: true,
|
|
188
192
|
slashInactivityCreateTargetPercentage: 1,
|
|
189
|
-
slashInactivitySignalTargetPercentage:
|
|
193
|
+
slashInactivitySignalTargetPercentage: 0.67,
|
|
190
194
|
slashInactivityCreatePenalty: 17n * (DefaultL1ContractsConfig.depositAmount / 100n),
|
|
191
195
|
slashInactivityMaxPenalty: 17n * (DefaultL1ContractsConfig.depositAmount / 100n),
|
|
192
196
|
slashInvalidBlockEnabled: true,
|
|
193
197
|
slashInvalidBlockPenalty: DefaultL1ContractsConfig.depositAmount,
|
|
194
198
|
slashInvalidBlockMaxPenalty: DefaultL1ContractsConfig.depositAmount,
|
|
199
|
+
sentinelEnabled: true,
|
|
195
200
|
};
|
|
196
201
|
|
|
197
|
-
|
|
202
|
+
const BOOTNODE_CACHE_DURATION_MS = 60 * 60 * 1000; // 1 hour;
|
|
203
|
+
|
|
204
|
+
export async function getBootnodes(networkName: NetworkNames, cacheDir?: string) {
|
|
205
|
+
const cacheFile = cacheDir ? join(cacheDir, networkName, 'bootnodes.json') : undefined;
|
|
206
|
+
try {
|
|
207
|
+
if (cacheFile) {
|
|
208
|
+
const info = await stat(cacheFile);
|
|
209
|
+
if (info.mtimeMs + BOOTNODE_CACHE_DURATION_MS > Date.now()) {
|
|
210
|
+
return JSON.parse(await readFile(cacheFile, 'utf-8'))['bootnodes'];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} catch {
|
|
214
|
+
// no-op. Get the remote-file
|
|
215
|
+
}
|
|
216
|
+
|
|
198
217
|
const url = `http://static.aztec.network/${networkName}/bootnodes.json`;
|
|
199
218
|
const response = await fetch(url);
|
|
200
219
|
if (!response.ok) {
|
|
@@ -204,20 +223,30 @@ export async function getBootnodes(networkName: NetworkNames) {
|
|
|
204
223
|
}
|
|
205
224
|
const json = await response.json();
|
|
206
225
|
|
|
226
|
+
try {
|
|
227
|
+
if (cacheFile) {
|
|
228
|
+
await mkdir(dirname(cacheFile), { recursive: true });
|
|
229
|
+
await writeFile(cacheFile, JSON.stringify(json), 'utf-8');
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
// no-op
|
|
233
|
+
}
|
|
234
|
+
|
|
207
235
|
return json['bootnodes'];
|
|
208
236
|
}
|
|
209
237
|
|
|
210
238
|
export async function getL2ChainConfig(
|
|
211
239
|
networkName: NetworkNames,
|
|
212
|
-
|
|
240
|
+
cacheDir?: string,
|
|
241
|
+
): Promise<L2ChainConfig | undefined> {
|
|
213
242
|
if (networkName === 'testnet-ignition') {
|
|
214
243
|
const config = { ...testnetIgnitionL2ChainConfig };
|
|
215
|
-
config.p2pBootstrapNodes = await getBootnodes(networkName);
|
|
216
|
-
return
|
|
244
|
+
config.p2pBootstrapNodes = await getBootnodes(networkName, cacheDir);
|
|
245
|
+
return config;
|
|
217
246
|
} else if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
218
247
|
const config = { ...alphaTestnetL2ChainConfig };
|
|
219
|
-
config.p2pBootstrapNodes = await getBootnodes('alpha-testnet');
|
|
220
|
-
return
|
|
248
|
+
config.p2pBootstrapNodes = await getBootnodes('alpha-testnet', cacheDir);
|
|
249
|
+
return config;
|
|
221
250
|
}
|
|
222
251
|
return undefined;
|
|
223
252
|
}
|
|
@@ -239,16 +268,32 @@ function enrichEthAddressVar(envVar: EnvVar, value: string) {
|
|
|
239
268
|
enrichVar(envVar, value);
|
|
240
269
|
}
|
|
241
270
|
|
|
271
|
+
function getDefaultDataDir(networkName: NetworkNames): string {
|
|
272
|
+
let prefix: string;
|
|
273
|
+
if (networkName === 'testnet-ignition') {
|
|
274
|
+
prefix = 'testnet-ignition';
|
|
275
|
+
} else if (networkName === 'alpha-testnet' || networkName === 'testnet') {
|
|
276
|
+
prefix = 'alpha-testnet';
|
|
277
|
+
} else {
|
|
278
|
+
prefix = networkName;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return path.join(process.env.HOME || '~', '.aztec', prefix, 'data');
|
|
282
|
+
}
|
|
283
|
+
|
|
242
284
|
export async function enrichEnvironmentWithChainConfig(networkName: NetworkNames) {
|
|
243
285
|
if (networkName === 'local') {
|
|
244
286
|
return;
|
|
245
287
|
}
|
|
246
288
|
|
|
247
|
-
|
|
248
|
-
|
|
289
|
+
enrichVar('DATA_DIRECTORY', getDefaultDataDir(networkName));
|
|
290
|
+
const cacheDir = process.env.DATA_DIRECTORY ? join(process.env.DATA_DIRECTORY, 'cache') : undefined;
|
|
291
|
+
const config = await getL2ChainConfig(networkName, cacheDir);
|
|
292
|
+
|
|
293
|
+
if (!config) {
|
|
249
294
|
throw new Error(`Unknown network name: ${networkName}`);
|
|
250
295
|
}
|
|
251
|
-
|
|
296
|
+
|
|
252
297
|
enrichVar('BOOTSTRAP_NODES', config.p2pBootstrapNodes.join(','));
|
|
253
298
|
enrichVar('TEST_ACCOUNTS', config.testAccounts.toString());
|
|
254
299
|
enrichVar('SPONSORED_FPC', config.sponsoredFPC.toString());
|
|
@@ -256,7 +301,6 @@ export async function enrichEnvironmentWithChainConfig(networkName: NetworkNames
|
|
|
256
301
|
enrichVar('L1_CHAIN_ID', config.l1ChainId.toString());
|
|
257
302
|
enrichVar('SEQ_MIN_TX_PER_BLOCK', config.seqMinTxsPerBlock.toString());
|
|
258
303
|
enrichVar('SEQ_MAX_TX_PER_BLOCK', config.seqMaxTxsPerBlock.toString());
|
|
259
|
-
enrichVar('DATA_DIRECTORY', path.join(process.env.HOME || '~', '.aztec', name, 'data'));
|
|
260
304
|
enrichVar('PROVER_REAL_PROOFS', config.realProofs.toString());
|
|
261
305
|
enrichVar('PXE_PROVER_ENABLED', config.realProofs.toString());
|
|
262
306
|
enrichVar('SYNC_SNAPSHOTS_URL', config.snapshotsUrl);
|
|
@@ -314,4 +358,5 @@ export async function enrichEnvironmentWithChainConfig(networkName: NetworkNames
|
|
|
314
358
|
enrichVar('SLASH_INVALID_BLOCK_ENABLED', config.slashInvalidBlockEnabled.toString());
|
|
315
359
|
enrichVar('SLASH_INVALID_BLOCK_PENALTY', config.slashInvalidBlockPenalty.toString());
|
|
316
360
|
enrichVar('SLASH_INVALID_BLOCK_MAX_PENALTY', config.slashInvalidBlockMaxPenalty.toString());
|
|
361
|
+
enrichVar('SENTINEL_ENABLED', config.sentinelEnabled.toString());
|
|
317
362
|
}
|
package/src/cli/util.ts
CHANGED
|
@@ -142,18 +142,32 @@ export function formatHelpLine(
|
|
|
142
142
|
|
|
143
143
|
const getDefaultOrEnvValue = (opt: AztecStartOption) => {
|
|
144
144
|
let val;
|
|
145
|
-
|
|
146
|
-
if
|
|
145
|
+
|
|
146
|
+
// if the option is set in the environment, use that
|
|
147
|
+
if (opt.envVar) {
|
|
147
148
|
val = process.env[opt.envVar];
|
|
148
|
-
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// if we have fallback env vars, check those
|
|
152
|
+
if (!val && opt.fallback && opt.fallback.length > 0) {
|
|
153
|
+
for (const fallback of opt.fallback) {
|
|
154
|
+
val = process.env[fallback];
|
|
155
|
+
if (val) {
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// if we have a value, optionally parse it and return
|
|
162
|
+
if (val) {
|
|
163
|
+
if (opt.parseVal) {
|
|
149
164
|
return opt.parseVal(val);
|
|
150
165
|
}
|
|
151
|
-
|
|
166
|
+
return val;
|
|
152
167
|
} else if (opt.defaultValue) {
|
|
153
|
-
|
|
168
|
+
return opt.defaultValue;
|
|
154
169
|
}
|
|
155
|
-
|
|
156
|
-
return val;
|
|
170
|
+
return undefined;
|
|
157
171
|
};
|
|
158
172
|
|
|
159
173
|
// Function to add options dynamically
|