@aztec-labs/cli 6.0.0-nightly.20260918 → 6.0.0-nightly.20260920
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/cmds/l1/compute_genesis_values.d.ts +8 -2
- package/dest/cmds/l1/compute_genesis_values.d.ts.map +1 -1
- package/dest/cmds/l1/compute_genesis_values.js +16 -3
- package/dest/cmds/l1/index.d.ts +1 -1
- package/dest/cmds/l1/index.d.ts.map +1 -1
- package/dest/cmds/l1/index.js +27 -0
- package/dest/cmds/l1/update_l1_validators.d.ts +14 -2
- package/dest/cmds/l1/update_l1_validators.d.ts.map +1 -1
- package/dest/cmds/l1/update_l1_validators.js +82 -1
- package/package.json +23 -23
- package/src/cmds/l1/compute_genesis_values.ts +15 -2
- package/src/cmds/l1/index.ts +53 -0
- package/src/cmds/l1/update_l1_validators.ts +117 -3
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { LogFn } from '@aztec-labs/foundation/log';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Computes and prints genesis values needed for L1 contract deployment.
|
|
4
|
+
*
|
|
5
|
+
* `genesisArchiveRoot` is this deployment's root, which depends on the accounts it prefunds. The `canonical` block is
|
|
6
|
+
* the prefunding-free genesis that the `GENESIS_*` protocol constants record, and is what
|
|
7
|
+
* `noir-projects/fnd/scripts/regenerate_genesis_constants.sh` reads to refresh them.
|
|
8
|
+
*/
|
|
3
9
|
export declare function computeGenesisValuesCmd(testAccounts: boolean, sponsoredFPC: boolean, log: LogFn): Promise<void>;
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcHV0ZV9nZW5lc2lzX3ZhbHVlcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NtZHMvbDEvY29tcHV0ZV9nZW5lc2lzX3ZhbHVlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQU14RDs7Ozs7O0dBTUc7QUFDSCx3QkFBc0IsdUJBQXVCLENBQUMsWUFBWSxFQUFFLE9BQU8sRUFBRSxZQUFZLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxLQUFLLGlCQTJCckcifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compute_genesis_values.d.ts","sourceRoot":"","sources":["../../../src/cmds/l1/compute_genesis_values.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAMxD
|
|
1
|
+
{"version":3,"file":"compute_genesis_values.d.ts","sourceRoot":"","sources":["../../../src/cmds/l1/compute_genesis_values.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAMxD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAAC,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,iBA2BrG"}
|
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import { getInitialTestAccountsData } from '@aztec-labs/accounts/testing';
|
|
2
2
|
import { protocolContractsHash } from '@aztec-labs/protocol-contracts';
|
|
3
|
-
import { getGenesisValues } from '@aztec-labs/world-state/testing';
|
|
3
|
+
import { getGenesisValues, measureCanonicalGenesis } from '@aztec-labs/world-state/testing';
|
|
4
4
|
import { getSponsoredFPCAddress } from '../../utils/setup_contracts.js';
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Computes and prints genesis values needed for L1 contract deployment.
|
|
7
|
+
*
|
|
8
|
+
* `genesisArchiveRoot` is this deployment's root, which depends on the accounts it prefunds. The `canonical` block is
|
|
9
|
+
* the prefunding-free genesis that the `GENESIS_*` protocol constants record, and is what
|
|
10
|
+
* `noir-projects/fnd/scripts/regenerate_genesis_constants.sh` reads to refresh them.
|
|
11
|
+
*/ export async function computeGenesisValuesCmd(testAccounts, sponsoredFPC, log) {
|
|
6
12
|
const initialAccounts = testAccounts ? await getInitialTestAccountsData() : [];
|
|
7
13
|
const sponsoredFPCAddresses = sponsoredFPC ? await getSponsoredFPCAddress() : [];
|
|
8
14
|
const initialFundedAccounts = initialAccounts.map((a)=>a.address).concat(sponsoredFPCAddresses);
|
|
9
15
|
const { genesisArchiveRoot } = await getGenesisValues(initialFundedAccounts);
|
|
16
|
+
const canonical = await measureCanonicalGenesis();
|
|
10
17
|
const { getVKTreeRoot } = await import('@aztec-labs/noir-protocol-circuits-types/vk-tree');
|
|
11
18
|
const vkTreeRoot = getVKTreeRoot();
|
|
12
19
|
log(JSON.stringify({
|
|
13
20
|
vkTreeRoot: vkTreeRoot.toString(),
|
|
14
21
|
protocolContractsHash: protocolContractsHash.toString(),
|
|
15
|
-
genesisArchiveRoot: genesisArchiveRoot.toString()
|
|
22
|
+
genesisArchiveRoot: genesisArchiveRoot.toString(),
|
|
23
|
+
canonical: {
|
|
24
|
+
prefilledNullifiers: canonical.prefilledNullifiers.map((n)=>n.toString()),
|
|
25
|
+
nullifierTreeRoot: canonical.nullifierTreeRoot.toString(),
|
|
26
|
+
blockHeaderHash: canonical.blockHeaderHash.toString(),
|
|
27
|
+
archiveRoot: canonical.archiveRoot.toString()
|
|
28
|
+
}
|
|
16
29
|
}, null, 2));
|
|
17
30
|
}
|
package/dest/cmds/l1/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { LogFn, Logger } from '@aztec-labs/foundation/log';
|
|
2
2
|
import { type Command } from 'commander';
|
|
3
3
|
export declare function injectCommands(program: Command, log: LogFn, debugLogger: Logger): Command;
|
|
4
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jbWRzL2wxL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUNoRSxPQUFPLEVBQUUsS0FBSyxPQUFPLEVBQVUsTUFBTSxXQUFXLENBQUM7QUF1QmpELHdCQUFnQixjQUFjLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLFdBNmdCL0UifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmds/l1/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;AAuBjD,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cmds/l1/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,KAAK,OAAO,EAAU,MAAM,WAAW,CAAC;AAuBjD,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,WA6gB/E"}
|
package/dest/cmds/l1/index.js
CHANGED
|
@@ -120,6 +120,33 @@ export function injectCommands(program, log, debugLogger) {
|
|
|
120
120
|
debugLogger
|
|
121
121
|
});
|
|
122
122
|
});
|
|
123
|
+
program.command('initiate-withdraw-by-attester').description("Initiates a withdrawal signed by the position's attester.").addOption(l1RpcUrlsOption).addOption(l1ChainIdOption).option('-pk, --private-key <string>', 'The attester private key', PRIVATE_KEY).option('-m, --mnemonic <string>', 'Mnemonic for the attester account', MNEMONIC).requiredOption('--attester <address>', 'Attester address of the position to exit', parseEthereumAddress).requiredOption('--rollup <address>', 'Rollup holding the position', parseEthereumAddress).action(async (options)=>{
|
|
124
|
+
const { initiateWithdrawByAttester } = await import('./update_l1_validators.js');
|
|
125
|
+
await initiateWithdrawByAttester({
|
|
126
|
+
rpcUrls: options.l1RpcUrls,
|
|
127
|
+
chainId: options.l1ChainId,
|
|
128
|
+
privateKey: options.privateKey,
|
|
129
|
+
mnemonic: options.mnemonic,
|
|
130
|
+
attesterAddress: options.attester,
|
|
131
|
+
rollupAddress: options.rollup,
|
|
132
|
+
log,
|
|
133
|
+
debugLogger
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
program.command('initiate-withdraw-by-attester-batch').description('Relays a JSON array of attester-signed withdrawal authorizations.').addOption(l1RpcUrlsOption).addOption(l1ChainIdOption).option('-pk, --private-key <string>', 'The relayer private key', PRIVATE_KEY).option('-m, --mnemonic <string>', 'Mnemonic for the relayer account', MNEMONIC).requiredOption('--authorizations <path>', 'JSON file containing attester, decimal deadline, and signature fields').requiredOption('--rollup <address>', 'Rollup holding the positions', parseEthereumAddress).option('--up-to-limit', 'Process the largest permitted prefix instead of reverting when the whole batch is too large').action(async (options)=>{
|
|
137
|
+
const { initiateWithdrawByAttesterBatch, readAttesterExitAuthorizations } = await import('./update_l1_validators.js');
|
|
138
|
+
await initiateWithdrawByAttesterBatch({
|
|
139
|
+
rpcUrls: options.l1RpcUrls,
|
|
140
|
+
chainId: options.l1ChainId,
|
|
141
|
+
privateKey: options.privateKey,
|
|
142
|
+
mnemonic: options.mnemonic,
|
|
143
|
+
authorizations: await readAttesterExitAuthorizations(options.authorizations),
|
|
144
|
+
upToLimit: options.upToLimit,
|
|
145
|
+
rollupAddress: options.rollup,
|
|
146
|
+
log,
|
|
147
|
+
debugLogger
|
|
148
|
+
});
|
|
149
|
+
});
|
|
123
150
|
program.command('remove-l1-validator').description('Removes a validator to the L1 rollup contract.').addOption(l1RpcUrlsOption).option('-pk, --private-key <string>', 'The private key to use for deployment', PRIVATE_KEY).option('-m, --mnemonic <string>', 'The mnemonic to use in deployment', 'test test test test test test test test test test test junk').addOption(l1ChainIdOption).option('--validator <address>', 'ethereum address of the validator', parseEthereumAddress).option('--rollup <address>', 'ethereum address of the rollup contract', parseEthereumAddress).action(async (options)=>{
|
|
124
151
|
const { removeL1Validator } = await import('./update_l1_validators.js');
|
|
125
152
|
await removeL1Validator({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AttesterExitAuthorization } from '@aztec-labs/ethereum/contracts';
|
|
2
|
+
import { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
2
3
|
import type { LogFn, Logger } from '@aztec-labs/foundation/log';
|
|
3
4
|
export interface RollupCommandArgs {
|
|
4
5
|
rpcUrls: string[];
|
|
@@ -37,9 +38,20 @@ export declare function addL1ValidatorViaRollup({ rpcUrls, chainId, privateKey,
|
|
|
37
38
|
export declare function removeL1Validator({ rpcUrls, chainId, privateKey, mnemonic, validatorAddress, rollupAddress, log, debugLogger }: RollupCommandArgs & LoggerArgs & {
|
|
38
39
|
validatorAddress: EthAddress;
|
|
39
40
|
}): Promise<void>;
|
|
41
|
+
/** Reads relayed attester exit authorizations from a JSON array. */
|
|
42
|
+
export declare function readAttesterExitAuthorizations(path: string): Promise<AttesterExitAuthorization[]>;
|
|
43
|
+
/** Relays a batch of attester-signed exits. */
|
|
44
|
+
export declare function initiateWithdrawByAttesterBatch({ rpcUrls, chainId, privateKey, mnemonic, authorizations, upToLimit, rollupAddress, log, debugLogger }: Omit<RollupCommandArgs, 'withdrawerAddress'> & LoggerArgs & {
|
|
45
|
+
authorizations: AttesterExitAuthorization[];
|
|
46
|
+
upToLimit: boolean;
|
|
47
|
+
}): Promise<import("viem").TransactionReceipt>;
|
|
48
|
+
/** Initiates an attester exit without changing the registered withdrawer's control over the payout. */
|
|
49
|
+
export declare function initiateWithdrawByAttester({ rpcUrls, chainId, privateKey, mnemonic, attesterAddress, rollupAddress, log, debugLogger }: Omit<RollupCommandArgs, 'withdrawerAddress'> & LoggerArgs & {
|
|
50
|
+
attesterAddress: EthAddress;
|
|
51
|
+
}): Promise<import("viem").TransactionReceipt>;
|
|
40
52
|
export declare function pruneRollup({ rpcUrls, chainId, privateKey, mnemonic, rollupAddress, log, debugLogger }: RollupCommandArgs & LoggerArgs): Promise<void>;
|
|
41
53
|
export declare function fastForwardEpochs({ rpcUrls, chainId, rollupAddress, numEpochs, log, debugLogger }: RollupCommandArgs & LoggerArgs & {
|
|
42
54
|
numEpochs: bigint;
|
|
43
55
|
}): Promise<void>;
|
|
44
56
|
export declare function debugRollup({ rpcUrls, chainId, rollupAddress, log }: RollupCommandArgs & LoggerArgs): Promise<void>;
|
|
45
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
57
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlX2wxX3ZhbGlkYXRvcnMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jbWRzL2wxL3VwZGF0ZV9sMV92YWxpZGF0b3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUtBLE9BQU8sRUFBRSxLQUFLLHlCQUF5QixFQUErQixNQUFNLGdDQUFnQyxDQUFDO0FBRzdHLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUVoRSxPQUFPLEtBQUssRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFPaEUsTUFBTSxXQUFXLGlCQUFpQjtJQUNoQyxPQUFPLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFDbEIsT0FBTyxFQUFFLE1BQU0sQ0FBQztJQUNoQixVQUFVLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDcEIsUUFBUSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2xCLGFBQWEsRUFBRSxVQUFVLENBQUM7SUFDMUIsaUJBQWlCLENBQUMsRUFBRSxVQUFVLENBQUM7Q0FDaEM7QUFFRCxNQUFNLFdBQVcsOEJBQThCO0lBQzdDLE9BQU8sRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUNsQixPQUFPLEVBQUUsTUFBTSxDQUFDO0lBQ2hCLFVBQVUsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUNwQixRQUFRLENBQUMsRUFBRSxNQUFNLENBQUM7SUFDbEIsMEJBQTBCLEVBQUUsVUFBVSxDQUFDO0NBQ3hDO0FBRUQsTUFBTSxXQUFXLFVBQVU7SUFDekIsR0FBRyxFQUFFLEtBQUssQ0FBQztJQUNYLFdBQVcsRUFBRSxNQUFNLENBQUM7Q0FDckI7QUFFRCx3QkFBZ0IsaUJBQWlCOzs7RUFPaEM7QUFFRCx3QkFBc0IsY0FBYyxDQUFDLEVBQ25DLE9BQU8sRUFDUCxPQUFPLEVBQ1AsVUFBVSxFQUNWLFFBQVEsRUFDUixlQUFlLEVBQ2YsaUJBQWlCLEVBQ2pCLDBCQUEwQixFQUMxQixXQUFXLEVBQ1gsWUFB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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update_l1_validators.d.ts","sourceRoot":"","sources":["../../../src/cmds/l1/update_l1_validators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update_l1_validators.d.ts","sourceRoot":"","sources":["../../../src/cmds/l1/update_l1_validators.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,9 +5,12 @@ import { getL1ContractsConfigEnvVars } from '@aztec-labs/ethereum/config';
|
|
|
5
5
|
import { GSEContract, RollupContract } from '@aztec-labs/ethereum/contracts';
|
|
6
6
|
import { createL1TxUtils } from '@aztec-labs/ethereum/l1-tx-utils';
|
|
7
7
|
import { EthCheatCodes } from '@aztec-labs/ethereum/test';
|
|
8
|
+
import { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
9
|
+
import { Signature } from '@aztec-labs/foundation/eth-signature';
|
|
8
10
|
import { DateProvider } from '@aztec-labs/foundation/timer';
|
|
9
11
|
import { ZkPassportProofParams } from '@aztec-labs/stdlib/zkpassport';
|
|
10
|
-
import {
|
|
12
|
+
import { readFile } from 'node:fs/promises';
|
|
13
|
+
import { encodeFunctionData, formatEther, getContract, isHex, maxUint256 } from 'viem';
|
|
11
14
|
import { generatePrivateKey, mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
12
15
|
export function generateL1Account() {
|
|
13
16
|
const privateKey = generatePrivateKey();
|
|
@@ -188,6 +191,84 @@ export async function removeL1Validator({ rpcUrls, chainId, privateKey, mnemonic
|
|
|
188
191
|
});
|
|
189
192
|
dualLog(`Transaction hash: ${receipt.transactionHash}`);
|
|
190
193
|
}
|
|
194
|
+
/** Reads relayed attester exit authorizations from a JSON array. */ export async function readAttesterExitAuthorizations(path) {
|
|
195
|
+
const parsed = JSON.parse(await readFile(path, 'utf-8'));
|
|
196
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
197
|
+
throw new Error('Attester exit authorization file must contain a non-empty JSON array');
|
|
198
|
+
}
|
|
199
|
+
return parsed.map((value, index)=>parseAttesterExitAuthorization(value, index));
|
|
200
|
+
}
|
|
201
|
+
function isRecord(value) {
|
|
202
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
203
|
+
}
|
|
204
|
+
function parseAttesterExitAuthorization(value, index) {
|
|
205
|
+
if (!isRecord(value)) {
|
|
206
|
+
throw new Error(`Attester exit authorization ${index} must be an object`);
|
|
207
|
+
}
|
|
208
|
+
const authorization = value;
|
|
209
|
+
if (typeof authorization.attester !== 'string') {
|
|
210
|
+
throw new Error(`Attester exit authorization ${index} has an invalid attester`);
|
|
211
|
+
}
|
|
212
|
+
if (typeof authorization.deadline !== 'string' || !/^\d+$/.test(authorization.deadline)) {
|
|
213
|
+
throw new Error(`Attester exit authorization ${index} deadline must be a decimal string`);
|
|
214
|
+
}
|
|
215
|
+
if (typeof authorization.signature !== 'string' || !isHex(authorization.signature)) {
|
|
216
|
+
throw new Error(`Attester exit authorization ${index} has an invalid signature`);
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
attester: EthAddress.fromString(authorization.attester),
|
|
220
|
+
deadline: BigInt(authorization.deadline),
|
|
221
|
+
signature: Signature.fromString(authorization.signature).toViemSignature()
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/** Relays a batch of attester-signed exits. */ export async function initiateWithdrawByAttesterBatch({ rpcUrls, chainId, privateKey, mnemonic, authorizations, upToLimit, rollupAddress, log, debugLogger }) {
|
|
225
|
+
const account = getAccount(privateKey, mnemonic);
|
|
226
|
+
const chain = createEthereumChain(rpcUrls, chainId);
|
|
227
|
+
const client = createExtendedL1Client(rpcUrls, account, chain.chainInfo);
|
|
228
|
+
const rollup = new RollupContract(client, rollupAddress);
|
|
229
|
+
const l1TxUtils = createL1TxUtils(client, {
|
|
230
|
+
logger: debugLogger
|
|
231
|
+
});
|
|
232
|
+
const { receipt } = upToLimit ? await rollup.initiateWithdrawByAttesterBatchUpToLimit(l1TxUtils, authorizations) : await rollup.initiateWithdrawByAttesterBatch(l1TxUtils, authorizations);
|
|
233
|
+
if (receipt.status !== 'success') {
|
|
234
|
+
throw new Error(`Attester exit batch reverted: ${receipt.transactionHash}`);
|
|
235
|
+
}
|
|
236
|
+
log(`Submitted ${authorizations.length} attester exit authorizations. Transaction hash: ${receipt.transactionHash}`);
|
|
237
|
+
if (upToLimit) {
|
|
238
|
+
log('The rollup processed the largest permitted prefix of the authorization list.');
|
|
239
|
+
}
|
|
240
|
+
debugLogger.info('Attester exit batch submitted', {
|
|
241
|
+
authorizationCount: authorizations.length,
|
|
242
|
+
upToLimit,
|
|
243
|
+
rollup: rollupAddress.toString(),
|
|
244
|
+
transactionHash: receipt.transactionHash
|
|
245
|
+
});
|
|
246
|
+
return receipt;
|
|
247
|
+
}
|
|
248
|
+
/** Initiates an attester exit without changing the registered withdrawer's control over the payout. */ export async function initiateWithdrawByAttester({ rpcUrls, chainId, privateKey, mnemonic, attesterAddress, rollupAddress, log, debugLogger }) {
|
|
249
|
+
const account = getAccount(privateKey, mnemonic);
|
|
250
|
+
if (account.address.toLowerCase() !== attesterAddress.toString().toLowerCase()) {
|
|
251
|
+
throw new Error('The transaction signer must match the attester address');
|
|
252
|
+
}
|
|
253
|
+
const chain = createEthereumChain(rpcUrls, chainId);
|
|
254
|
+
const client = createExtendedL1Client(rpcUrls, account, chain.chainInfo);
|
|
255
|
+
const rollup = new RollupContract(client, rollupAddress);
|
|
256
|
+
const l1TxUtils = createL1TxUtils(client, {
|
|
257
|
+
logger: debugLogger
|
|
258
|
+
});
|
|
259
|
+
const { receipt } = await rollup.initiateWithdrawByAttester(l1TxUtils, attesterAddress);
|
|
260
|
+
if (receipt.status !== 'success') {
|
|
261
|
+
throw new Error(`Attester exit reverted: ${receipt.transactionHash}`);
|
|
262
|
+
}
|
|
263
|
+
log(`Attester exit initiated for ${attesterAddress}. Transaction hash: ${receipt.transactionHash}`);
|
|
264
|
+
log('The registered withdrawer must select a recipient using initiateWithdraw before finalization.');
|
|
265
|
+
debugLogger.info('Attester exit initiated', {
|
|
266
|
+
attester: attesterAddress.toString(),
|
|
267
|
+
rollup: rollupAddress.toString(),
|
|
268
|
+
transactionHash: receipt.transactionHash
|
|
269
|
+
});
|
|
270
|
+
return receipt;
|
|
271
|
+
}
|
|
191
272
|
export async function pruneRollup({ rpcUrls, chainId, privateKey, mnemonic, rollupAddress, log, debugLogger }) {
|
|
192
273
|
const dualLog = makeDualLog(log, debugLogger);
|
|
193
274
|
const account = getAccount(privateKey, mnemonic);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec-labs/cli",
|
|
3
|
-
"version": "6.0.0-nightly.
|
|
3
|
+
"version": "6.0.0-nightly.20260920",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./devnet": "./dest/cmds/devnet/index.js",
|
|
@@ -76,19 +76,19 @@
|
|
|
76
76
|
]
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@aztec-foundation/l1-artifacts": "6.0.0-nightly.
|
|
80
|
-
"@aztec-labs/accounts": "6.0.0-nightly.
|
|
81
|
-
"@aztec-labs/archiver": "6.0.0-nightly.
|
|
82
|
-
"@aztec-labs/aztec.js": "6.0.0-nightly.
|
|
83
|
-
"@aztec-labs/constants": "6.0.0-nightly.
|
|
84
|
-
"@aztec-labs/ethereum": "6.0.0-nightly.
|
|
85
|
-
"@aztec-labs/foundation": "6.0.0-nightly.
|
|
86
|
-
"@aztec-labs/node-keystore": "6.0.0-nightly.
|
|
87
|
-
"@aztec-labs/p2p": "6.0.0-nightly.
|
|
88
|
-
"@aztec-labs/protocol-contracts": "6.0.0-nightly.
|
|
89
|
-
"@aztec-labs/stdlib": "6.0.0-nightly.
|
|
90
|
-
"@aztec-labs/wallets": "6.0.0-nightly.
|
|
91
|
-
"@aztec-labs/world-state": "6.0.0-nightly.
|
|
79
|
+
"@aztec-foundation/l1-artifacts": "6.0.0-nightly.20260918.a",
|
|
80
|
+
"@aztec-labs/accounts": "6.0.0-nightly.20260920",
|
|
81
|
+
"@aztec-labs/archiver": "6.0.0-nightly.20260920",
|
|
82
|
+
"@aztec-labs/aztec.js": "6.0.0-nightly.20260920",
|
|
83
|
+
"@aztec-labs/constants": "6.0.0-nightly.20260920",
|
|
84
|
+
"@aztec-labs/ethereum": "6.0.0-nightly.20260920",
|
|
85
|
+
"@aztec-labs/foundation": "6.0.0-nightly.20260920",
|
|
86
|
+
"@aztec-labs/node-keystore": "6.0.0-nightly.20260920",
|
|
87
|
+
"@aztec-labs/p2p": "6.0.0-nightly.20260920",
|
|
88
|
+
"@aztec-labs/protocol-contracts": "6.0.0-nightly.20260920",
|
|
89
|
+
"@aztec-labs/stdlib": "6.0.0-nightly.20260920",
|
|
90
|
+
"@aztec-labs/wallets": "6.0.0-nightly.20260920",
|
|
91
|
+
"@aztec-labs/world-state": "6.0.0-nightly.20260920",
|
|
92
92
|
"@ethersproject/wallet": "^5.8.0",
|
|
93
93
|
"@iarna/toml": "^2.2.5",
|
|
94
94
|
"@libp2p/peer-id-factory": "^3.0.4",
|
|
@@ -118,15 +118,15 @@
|
|
|
118
118
|
"typescript": "^5.3.3"
|
|
119
119
|
},
|
|
120
120
|
"peerDependencies": {
|
|
121
|
-
"@aztec-foundation/l1-artifacts": "6.0.0-nightly.
|
|
122
|
-
"@aztec-labs/accounts": "6.0.0-nightly.
|
|
123
|
-
"@aztec-labs/bb-prover": "6.0.0-nightly.
|
|
124
|
-
"@aztec-labs/ethereum": "6.0.0-nightly.
|
|
125
|
-
"@aztec-labs/noir-contracts.js": "6.0.0-nightly.
|
|
126
|
-
"@aztec-labs/noir-protocol-circuits-types": "6.0.0-nightly.
|
|
127
|
-
"@aztec-labs/noir-test-contracts.js": "6.0.0-nightly.
|
|
128
|
-
"@aztec-labs/protocol-contracts": "6.0.0-nightly.
|
|
129
|
-
"@aztec-labs/stdlib": "6.0.0-nightly.
|
|
121
|
+
"@aztec-foundation/l1-artifacts": "6.0.0-nightly.20260918.a",
|
|
122
|
+
"@aztec-labs/accounts": "6.0.0-nightly.20260920",
|
|
123
|
+
"@aztec-labs/bb-prover": "6.0.0-nightly.20260920",
|
|
124
|
+
"@aztec-labs/ethereum": "6.0.0-nightly.20260920",
|
|
125
|
+
"@aztec-labs/noir-contracts.js": "6.0.0-nightly.20260920",
|
|
126
|
+
"@aztec-labs/noir-protocol-circuits-types": "6.0.0-nightly.20260920",
|
|
127
|
+
"@aztec-labs/noir-test-contracts.js": "6.0.0-nightly.20260920",
|
|
128
|
+
"@aztec-labs/protocol-contracts": "6.0.0-nightly.20260920",
|
|
129
|
+
"@aztec-labs/stdlib": "6.0.0-nightly.20260920"
|
|
130
130
|
},
|
|
131
131
|
"files": [
|
|
132
132
|
"dest",
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { getInitialTestAccountsData } from '@aztec-labs/accounts/testing';
|
|
2
2
|
import type { LogFn } from '@aztec-labs/foundation/log';
|
|
3
3
|
import { protocolContractsHash } from '@aztec-labs/protocol-contracts';
|
|
4
|
-
import { getGenesisValues } from '@aztec-labs/world-state/testing';
|
|
4
|
+
import { getGenesisValues, measureCanonicalGenesis } from '@aztec-labs/world-state/testing';
|
|
5
5
|
|
|
6
6
|
import { getSponsoredFPCAddress } from '../../utils/setup_contracts.js';
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Computes and prints genesis values needed for L1 contract deployment.
|
|
10
|
+
*
|
|
11
|
+
* `genesisArchiveRoot` is this deployment's root, which depends on the accounts it prefunds. The `canonical` block is
|
|
12
|
+
* the prefunding-free genesis that the `GENESIS_*` protocol constants record, and is what
|
|
13
|
+
* `noir-projects/fnd/scripts/regenerate_genesis_constants.sh` reads to refresh them.
|
|
14
|
+
*/
|
|
9
15
|
export async function computeGenesisValuesCmd(testAccounts: boolean, sponsoredFPC: boolean, log: LogFn) {
|
|
10
16
|
const initialAccounts = testAccounts ? await getInitialTestAccountsData() : [];
|
|
11
17
|
const sponsoredFPCAddresses = sponsoredFPC ? await getSponsoredFPCAddress() : [];
|
|
12
18
|
const initialFundedAccounts = initialAccounts.map(a => a.address).concat(sponsoredFPCAddresses);
|
|
13
19
|
const { genesisArchiveRoot } = await getGenesisValues(initialFundedAccounts);
|
|
20
|
+
const canonical = await measureCanonicalGenesis();
|
|
14
21
|
|
|
15
22
|
const { getVKTreeRoot } = await import('@aztec-labs/noir-protocol-circuits-types/vk-tree');
|
|
16
23
|
const vkTreeRoot = getVKTreeRoot();
|
|
@@ -21,6 +28,12 @@ export async function computeGenesisValuesCmd(testAccounts: boolean, sponsoredFP
|
|
|
21
28
|
vkTreeRoot: vkTreeRoot.toString(),
|
|
22
29
|
protocolContractsHash: protocolContractsHash.toString(),
|
|
23
30
|
genesisArchiveRoot: genesisArchiveRoot.toString(),
|
|
31
|
+
canonical: {
|
|
32
|
+
prefilledNullifiers: canonical.prefilledNullifiers.map(n => n.toString()),
|
|
33
|
+
nullifierTreeRoot: canonical.nullifierTreeRoot.toString(),
|
|
34
|
+
blockHeaderHash: canonical.blockHeaderHash.toString(),
|
|
35
|
+
archiveRoot: canonical.archiveRoot.toString(),
|
|
36
|
+
},
|
|
24
37
|
},
|
|
25
38
|
null,
|
|
26
39
|
2,
|
package/src/cmds/l1/index.ts
CHANGED
|
@@ -337,6 +337,59 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger
|
|
|
337
337
|
});
|
|
338
338
|
});
|
|
339
339
|
|
|
340
|
+
program
|
|
341
|
+
.command('initiate-withdraw-by-attester')
|
|
342
|
+
.description("Initiates a withdrawal signed by the position's attester.")
|
|
343
|
+
.addOption(l1RpcUrlsOption)
|
|
344
|
+
.addOption(l1ChainIdOption)
|
|
345
|
+
.option('-pk, --private-key <string>', 'The attester private key', PRIVATE_KEY)
|
|
346
|
+
.option('-m, --mnemonic <string>', 'Mnemonic for the attester account', MNEMONIC)
|
|
347
|
+
.requiredOption('--attester <address>', 'Attester address of the position to exit', parseEthereumAddress)
|
|
348
|
+
.requiredOption('--rollup <address>', 'Rollup holding the position', parseEthereumAddress)
|
|
349
|
+
.action(async options => {
|
|
350
|
+
const { initiateWithdrawByAttester } = await import('./update_l1_validators.js');
|
|
351
|
+
await initiateWithdrawByAttester({
|
|
352
|
+
rpcUrls: options.l1RpcUrls,
|
|
353
|
+
chainId: options.l1ChainId,
|
|
354
|
+
privateKey: options.privateKey,
|
|
355
|
+
mnemonic: options.mnemonic,
|
|
356
|
+
attesterAddress: options.attester,
|
|
357
|
+
rollupAddress: options.rollup,
|
|
358
|
+
log,
|
|
359
|
+
debugLogger,
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
program
|
|
364
|
+
.command('initiate-withdraw-by-attester-batch')
|
|
365
|
+
.description('Relays a JSON array of attester-signed withdrawal authorizations.')
|
|
366
|
+
.addOption(l1RpcUrlsOption)
|
|
367
|
+
.addOption(l1ChainIdOption)
|
|
368
|
+
.option('-pk, --private-key <string>', 'The relayer private key', PRIVATE_KEY)
|
|
369
|
+
.option('-m, --mnemonic <string>', 'Mnemonic for the relayer account', MNEMONIC)
|
|
370
|
+
.requiredOption('--authorizations <path>', 'JSON file containing attester, decimal deadline, and signature fields')
|
|
371
|
+
.requiredOption('--rollup <address>', 'Rollup holding the positions', parseEthereumAddress)
|
|
372
|
+
.option(
|
|
373
|
+
'--up-to-limit',
|
|
374
|
+
'Process the largest permitted prefix instead of reverting when the whole batch is too large',
|
|
375
|
+
)
|
|
376
|
+
.action(async options => {
|
|
377
|
+
const { initiateWithdrawByAttesterBatch, readAttesterExitAuthorizations } = await import(
|
|
378
|
+
'./update_l1_validators.js'
|
|
379
|
+
);
|
|
380
|
+
await initiateWithdrawByAttesterBatch({
|
|
381
|
+
rpcUrls: options.l1RpcUrls,
|
|
382
|
+
chainId: options.l1ChainId,
|
|
383
|
+
privateKey: options.privateKey,
|
|
384
|
+
mnemonic: options.mnemonic,
|
|
385
|
+
authorizations: await readAttesterExitAuthorizations(options.authorizations),
|
|
386
|
+
upToLimit: options.upToLimit,
|
|
387
|
+
rollupAddress: options.rollup,
|
|
388
|
+
log,
|
|
389
|
+
debugLogger,
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
340
393
|
program
|
|
341
394
|
.command('remove-l1-validator')
|
|
342
395
|
.description('Removes a validator to the L1 rollup contract.')
|
|
@@ -3,14 +3,16 @@ import { RollupAbi, StakingAssetHandlerAbi, TestERC20Abi } from '@aztec-foundati
|
|
|
3
3
|
import { createEthereumChain, isAnvilTestChain } from '@aztec-labs/ethereum/chain';
|
|
4
4
|
import { createExtendedL1Client, getPublicClient } from '@aztec-labs/ethereum/client';
|
|
5
5
|
import { getL1ContractsConfigEnvVars } from '@aztec-labs/ethereum/config';
|
|
6
|
-
import { GSEContract, RollupContract } from '@aztec-labs/ethereum/contracts';
|
|
6
|
+
import { type AttesterExitAuthorization, GSEContract, RollupContract } from '@aztec-labs/ethereum/contracts';
|
|
7
7
|
import { createL1TxUtils } from '@aztec-labs/ethereum/l1-tx-utils';
|
|
8
8
|
import { EthCheatCodes } from '@aztec-labs/ethereum/test';
|
|
9
|
-
import
|
|
9
|
+
import { EthAddress } from '@aztec-labs/foundation/eth-address';
|
|
10
|
+
import { Signature } from '@aztec-labs/foundation/eth-signature';
|
|
10
11
|
import type { LogFn, Logger } from '@aztec-labs/foundation/log';
|
|
11
12
|
import { DateProvider } from '@aztec-labs/foundation/timer';
|
|
12
13
|
import { ZkPassportProofParams } from '@aztec-labs/stdlib/zkpassport';
|
|
13
|
-
import {
|
|
14
|
+
import { readFile } from 'node:fs/promises';
|
|
15
|
+
import { encodeFunctionData, formatEther, getContract, isHex, maxUint256 } from 'viem';
|
|
14
16
|
import { generatePrivateKey, mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
|
|
15
17
|
|
|
16
18
|
export interface RollupCommandArgs {
|
|
@@ -254,6 +256,118 @@ export async function removeL1Validator({
|
|
|
254
256
|
dualLog(`Transaction hash: ${receipt.transactionHash}`);
|
|
255
257
|
}
|
|
256
258
|
|
|
259
|
+
/** Reads relayed attester exit authorizations from a JSON array. */
|
|
260
|
+
export async function readAttesterExitAuthorizations(path: string): Promise<AttesterExitAuthorization[]> {
|
|
261
|
+
const parsed: unknown = JSON.parse(await readFile(path, 'utf-8'));
|
|
262
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
263
|
+
throw new Error('Attester exit authorization file must contain a non-empty JSON array');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return parsed.map((value, index) => parseAttesterExitAuthorization(value, index));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
270
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function parseAttesterExitAuthorization(value: unknown, index: number): AttesterExitAuthorization {
|
|
274
|
+
if (!isRecord(value)) {
|
|
275
|
+
throw new Error(`Attester exit authorization ${index} must be an object`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const authorization = value;
|
|
279
|
+
if (typeof authorization.attester !== 'string') {
|
|
280
|
+
throw new Error(`Attester exit authorization ${index} has an invalid attester`);
|
|
281
|
+
}
|
|
282
|
+
if (typeof authorization.deadline !== 'string' || !/^\d+$/.test(authorization.deadline)) {
|
|
283
|
+
throw new Error(`Attester exit authorization ${index} deadline must be a decimal string`);
|
|
284
|
+
}
|
|
285
|
+
if (typeof authorization.signature !== 'string' || !isHex(authorization.signature)) {
|
|
286
|
+
throw new Error(`Attester exit authorization ${index} has an invalid signature`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
attester: EthAddress.fromString(authorization.attester),
|
|
291
|
+
deadline: BigInt(authorization.deadline),
|
|
292
|
+
signature: Signature.fromString(authorization.signature).toViemSignature(),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Relays a batch of attester-signed exits. */
|
|
297
|
+
export async function initiateWithdrawByAttesterBatch({
|
|
298
|
+
rpcUrls,
|
|
299
|
+
chainId,
|
|
300
|
+
privateKey,
|
|
301
|
+
mnemonic,
|
|
302
|
+
authorizations,
|
|
303
|
+
upToLimit,
|
|
304
|
+
rollupAddress,
|
|
305
|
+
log,
|
|
306
|
+
debugLogger,
|
|
307
|
+
}: Omit<RollupCommandArgs, 'withdrawerAddress'> &
|
|
308
|
+
LoggerArgs & {
|
|
309
|
+
authorizations: AttesterExitAuthorization[];
|
|
310
|
+
upToLimit: boolean;
|
|
311
|
+
}) {
|
|
312
|
+
const account = getAccount(privateKey, mnemonic);
|
|
313
|
+
const chain = createEthereumChain(rpcUrls, chainId);
|
|
314
|
+
const client = createExtendedL1Client(rpcUrls, account, chain.chainInfo);
|
|
315
|
+
const rollup = new RollupContract(client, rollupAddress);
|
|
316
|
+
const l1TxUtils = createL1TxUtils(client, { logger: debugLogger });
|
|
317
|
+
const { receipt } = upToLimit
|
|
318
|
+
? await rollup.initiateWithdrawByAttesterBatchUpToLimit(l1TxUtils, authorizations)
|
|
319
|
+
: await rollup.initiateWithdrawByAttesterBatch(l1TxUtils, authorizations);
|
|
320
|
+
|
|
321
|
+
if (receipt.status !== 'success') {
|
|
322
|
+
throw new Error(`Attester exit batch reverted: ${receipt.transactionHash}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
log(`Submitted ${authorizations.length} attester exit authorizations. Transaction hash: ${receipt.transactionHash}`);
|
|
326
|
+
if (upToLimit) {
|
|
327
|
+
log('The rollup processed the largest permitted prefix of the authorization list.');
|
|
328
|
+
}
|
|
329
|
+
debugLogger.info('Attester exit batch submitted', {
|
|
330
|
+
authorizationCount: authorizations.length,
|
|
331
|
+
upToLimit,
|
|
332
|
+
rollup: rollupAddress.toString(),
|
|
333
|
+
transactionHash: receipt.transactionHash,
|
|
334
|
+
});
|
|
335
|
+
return receipt;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Initiates an attester exit without changing the registered withdrawer's control over the payout. */
|
|
339
|
+
export async function initiateWithdrawByAttester({
|
|
340
|
+
rpcUrls,
|
|
341
|
+
chainId,
|
|
342
|
+
privateKey,
|
|
343
|
+
mnemonic,
|
|
344
|
+
attesterAddress,
|
|
345
|
+
rollupAddress,
|
|
346
|
+
log,
|
|
347
|
+
debugLogger,
|
|
348
|
+
}: Omit<RollupCommandArgs, 'withdrawerAddress'> & LoggerArgs & { attesterAddress: EthAddress }) {
|
|
349
|
+
const account = getAccount(privateKey, mnemonic);
|
|
350
|
+
if (account.address.toLowerCase() !== attesterAddress.toString().toLowerCase()) {
|
|
351
|
+
throw new Error('The transaction signer must match the attester address');
|
|
352
|
+
}
|
|
353
|
+
const chain = createEthereumChain(rpcUrls, chainId);
|
|
354
|
+
const client = createExtendedL1Client(rpcUrls, account, chain.chainInfo);
|
|
355
|
+
const rollup = new RollupContract(client, rollupAddress);
|
|
356
|
+
const l1TxUtils = createL1TxUtils(client, { logger: debugLogger });
|
|
357
|
+
const { receipt } = await rollup.initiateWithdrawByAttester(l1TxUtils, attesterAddress);
|
|
358
|
+
if (receipt.status !== 'success') {
|
|
359
|
+
throw new Error(`Attester exit reverted: ${receipt.transactionHash}`);
|
|
360
|
+
}
|
|
361
|
+
log(`Attester exit initiated for ${attesterAddress}. Transaction hash: ${receipt.transactionHash}`);
|
|
362
|
+
log('The registered withdrawer must select a recipient using initiateWithdraw before finalization.');
|
|
363
|
+
debugLogger.info('Attester exit initiated', {
|
|
364
|
+
attester: attesterAddress.toString(),
|
|
365
|
+
rollup: rollupAddress.toString(),
|
|
366
|
+
transactionHash: receipt.transactionHash,
|
|
367
|
+
});
|
|
368
|
+
return receipt;
|
|
369
|
+
}
|
|
370
|
+
|
|
257
371
|
export async function pruneRollup({
|
|
258
372
|
rpcUrls,
|
|
259
373
|
chainId,
|