@aztec/cli 0.0.1-commit.e558bd1c → 0.0.1-commit.e57c76e
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/aztec_node/block_number.js +1 -1
- package/dest/cmds/aztec_node/get_logs.d.ts +30 -4
- package/dest/cmds/aztec_node/get_logs.d.ts.map +1 -1
- package/dest/cmds/aztec_node/get_logs.js +39 -29
- package/dest/cmds/aztec_node/get_node_info.d.ts +1 -1
- package/dest/cmds/aztec_node/get_node_info.d.ts.map +1 -1
- package/dest/cmds/aztec_node/get_node_info.js +0 -2
- package/dest/cmds/aztec_node/index.d.ts +1 -1
- package/dest/cmds/aztec_node/index.d.ts.map +1 -1
- package/dest/cmds/aztec_node/index.js +13 -3
- package/dest/cmds/infrastructure/setup_l2_contract.d.ts +1 -1
- package/dest/cmds/infrastructure/setup_l2_contract.d.ts.map +1 -1
- package/dest/cmds/infrastructure/setup_l2_contract.js +8 -3
- package/dest/cmds/l1/deploy_l1_contracts_cmd.d.ts +1 -1
- package/dest/cmds/l1/deploy_l1_contracts_cmd.d.ts.map +1 -1
- package/dest/cmds/l1/deploy_l1_contracts_cmd.js +0 -1
- package/dest/cmds/l1/deploy_new_rollup.d.ts +1 -1
- package/dest/cmds/l1/deploy_new_rollup.d.ts.map +1 -1
- package/dest/cmds/l1/deploy_new_rollup.js +2 -4
- package/dest/cmds/l1/update_l1_validators.d.ts +1 -1
- package/dest/cmds/l1/update_l1_validators.d.ts.map +1 -1
- package/dest/cmds/l1/update_l1_validators.js +5 -6
- package/dest/config/cached_fetch.d.ts +19 -10
- package/dest/config/cached_fetch.d.ts.map +1 -1
- package/dest/config/cached_fetch.js +110 -32
- package/dest/config/chain_l2_config.d.ts +1 -1
- package/dest/config/chain_l2_config.d.ts.map +1 -1
- package/dest/config/chain_l2_config.js +3 -1
- package/dest/config/generated/networks.d.ts +67 -54
- package/dest/config/generated/networks.d.ts.map +1 -1
- package/dest/config/generated/networks.js +68 -55
- package/dest/config/network_config.d.ts +1 -1
- package/dest/config/network_config.d.ts.map +1 -1
- package/dest/config/network_config.js +6 -2
- package/dest/utils/aztec.d.ts +1 -2
- package/dest/utils/aztec.d.ts.map +1 -1
- package/dest/utils/aztec.js +2 -3
- package/dest/utils/commands.d.ts +14 -6
- package/dest/utils/commands.d.ts.map +1 -1
- package/dest/utils/commands.js +19 -9
- package/dest/utils/inspect.d.ts +1 -1
- package/dest/utils/inspect.d.ts.map +1 -1
- package/dest/utils/inspect.js +11 -11
- package/package.json +30 -30
- package/src/cmds/aztec_node/block_number.ts +1 -1
- package/src/cmds/aztec_node/get_logs.ts +70 -38
- package/src/cmds/aztec_node/get_node_info.ts +0 -2
- package/src/cmds/aztec_node/index.ts +13 -8
- package/src/cmds/infrastructure/setup_l2_contract.ts +8 -3
- package/src/cmds/l1/deploy_l1_contracts_cmd.ts +0 -1
- package/src/cmds/l1/deploy_new_rollup.ts +1 -3
- package/src/cmds/l1/update_l1_validators.ts +5 -6
- package/src/config/cached_fetch.ts +119 -31
- package/src/config/chain_l2_config.ts +3 -1
- package/src/config/generated/networks.ts +66 -53
- package/src/config/network_config.ts +6 -2
- package/src/utils/aztec.ts +12 -18
- package/src/utils/commands.ts +22 -9
- package/src/utils/inspect.ts +7 -8
|
@@ -1,22 +1,47 @@
|
|
|
1
1
|
import type { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
2
|
-
import type { LogFilter, LogId } from '@aztec/aztec.js/log';
|
|
3
2
|
import { createAztecNodeClient } from '@aztec/aztec.js/node';
|
|
4
3
|
import type { TxHash } from '@aztec/aztec.js/tx';
|
|
5
|
-
import { BlockNumber } from '@aztec/foundation/branded-types';
|
|
4
|
+
import type { BlockNumber } from '@aztec/foundation/branded-types';
|
|
6
5
|
import type { LogFn } from '@aztec/foundation/log';
|
|
7
6
|
import { sleep } from '@aztec/foundation/sleep';
|
|
7
|
+
import {
|
|
8
|
+
LogCursor,
|
|
9
|
+
type PublicLogsQuery,
|
|
10
|
+
type Tag,
|
|
11
|
+
logResultToHumanReadable,
|
|
12
|
+
queryAllPublicLogsByTags,
|
|
13
|
+
} from '@aztec/stdlib/logs';
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
/** Options for the `get-logs` CLI command. */
|
|
16
|
+
export type GetLogsOptions = {
|
|
17
|
+
/** Contract address that emitted the logs (required). */
|
|
18
|
+
contractAddress: AztecAddress;
|
|
19
|
+
/** Tag to filter logs by (required). */
|
|
20
|
+
tag: Tag;
|
|
21
|
+
/** Restrict the search to this tx hash. Mutually exclusive with `fromBlock`/`toBlock`. */
|
|
22
|
+
txHash?: TxHash;
|
|
23
|
+
/** Lower block bound, inclusive. */
|
|
24
|
+
fromBlock?: BlockNumber;
|
|
25
|
+
/** Upper block bound, exclusive. */
|
|
26
|
+
toBlock?: BlockNumber;
|
|
27
|
+
/** Log cursor to resume pagination strictly after a previously-seen log. */
|
|
28
|
+
afterLog?: LogCursor;
|
|
29
|
+
/** Node RPC URL. */
|
|
30
|
+
nodeUrl: string;
|
|
31
|
+
/** When set, polls indefinitely for new logs. Incompatible with `txHash` and `toBlock`. */
|
|
32
|
+
follow: boolean;
|
|
33
|
+
/** Log function. */
|
|
34
|
+
log: LogFn;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Fetches public logs for a (contract, tag) pair, draining all pages via the stdlib pagination helper.
|
|
39
|
+
* In `--follow` mode, polls indefinitely: each round drains all currently-available logs, then sleeps
|
|
40
|
+
* until the next poll if nothing new was found.
|
|
41
|
+
*/
|
|
42
|
+
export async function getLogs(options: GetLogsOptions): Promise<void> {
|
|
43
|
+
const { txHash, fromBlock, toBlock, contractAddress, tag, nodeUrl, follow, log } = options;
|
|
44
|
+
let afterLog = options.afterLog;
|
|
20
45
|
|
|
21
46
|
if (follow) {
|
|
22
47
|
if (txHash) {
|
|
@@ -26,43 +51,50 @@ export async function getLogs(
|
|
|
26
51
|
throw Error('Cannot use --follow with --to-block');
|
|
27
52
|
}
|
|
28
53
|
}
|
|
54
|
+
if (txHash !== undefined && (fromBlock !== undefined || toBlock !== undefined)) {
|
|
55
|
+
throw Error('Cannot combine --tx-hash with --from-block / --to-block');
|
|
56
|
+
}
|
|
29
57
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const fetchLogs = async () => {
|
|
33
|
-
const response = await node.getPublicLogs(filter);
|
|
34
|
-
const logs = response.logs;
|
|
58
|
+
const node = createAztecNodeClient(nodeUrl);
|
|
35
59
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
logs.forEach(publicLog => log(publicLog.toHumanReadable()));
|
|
49
|
-
// Set the continuation parameter for the following requests
|
|
50
|
-
filter.afterLog = logs[logs.length - 1].id;
|
|
60
|
+
const drainLogs = async () => {
|
|
61
|
+
const query: PublicLogsQuery = {
|
|
62
|
+
contractAddress,
|
|
63
|
+
tags: [afterLog !== undefined ? { tag, afterLog } : tag],
|
|
64
|
+
fromBlock,
|
|
65
|
+
toBlock,
|
|
66
|
+
txHash,
|
|
67
|
+
};
|
|
68
|
+
const [logsForTag] = await queryAllPublicLogsByTags(node, query);
|
|
69
|
+
if (logsForTag.length > 0) {
|
|
70
|
+
afterLog = LogCursor.fromLog(logsForTag[logsForTag.length - 1]);
|
|
51
71
|
}
|
|
52
|
-
return
|
|
72
|
+
return logsForTag;
|
|
53
73
|
};
|
|
54
74
|
|
|
55
75
|
if (follow) {
|
|
56
76
|
log('Fetching logs...');
|
|
57
77
|
while (true) {
|
|
58
|
-
const
|
|
59
|
-
if (
|
|
78
|
+
const results = await drainLogs();
|
|
79
|
+
if (results.length === 0) {
|
|
60
80
|
await sleep(1000);
|
|
81
|
+
} else {
|
|
82
|
+
results.forEach(r => log(logResultToHumanReadable(r)));
|
|
61
83
|
}
|
|
62
84
|
}
|
|
63
85
|
} else {
|
|
64
|
-
|
|
65
|
-
|
|
86
|
+
const results = await drainLogs();
|
|
87
|
+
if (results.length === 0) {
|
|
88
|
+
log(
|
|
89
|
+
`No logs found for {contractAddress: ${contractAddress.toString()}, tag: ${tag.toString()}` +
|
|
90
|
+
`${txHash ? `, txHash: ${txHash.toString()}` : ''}` +
|
|
91
|
+
`${fromBlock !== undefined ? `, fromBlock: ${fromBlock}` : ''}` +
|
|
92
|
+
`${toBlock !== undefined ? `, toBlock: ${toBlock}` : ''}` +
|
|
93
|
+
`${afterLog ? `, afterLog: ${afterLog.toString()}` : ''}}`,
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
log('Logs found: \n');
|
|
97
|
+
results.forEach(r => log(logResultToHumanReadable(r)));
|
|
66
98
|
}
|
|
67
99
|
}
|
|
68
100
|
}
|
|
@@ -23,7 +23,6 @@ export async function getNodeInfo(nodeUrl: string, json: boolean, log: LogFn, lo
|
|
|
23
23
|
rewardDistributor: info.l1ContractAddresses.rewardDistributorAddress.toString(),
|
|
24
24
|
governanceProposer: info.l1ContractAddresses.governanceProposerAddress.toString(),
|
|
25
25
|
governance: info.l1ContractAddresses.governanceAddress.toString(),
|
|
26
|
-
slashFactory: info.l1ContractAddresses.slashFactoryAddress?.toString(),
|
|
27
26
|
feeAssetHandler: info.l1ContractAddresses.feeAssetHandlerAddress?.toString(),
|
|
28
27
|
stakingAssetHandler: info.l1ContractAddresses.stakingAssetHandlerAddress?.toString(),
|
|
29
28
|
},
|
|
@@ -51,7 +50,6 @@ export async function getNodeInfo(nodeUrl: string, json: boolean, log: LogFn, lo
|
|
|
51
50
|
log(` RewardDistributor Address: ${info.l1ContractAddresses.rewardDistributorAddress.toString()}`);
|
|
52
51
|
log(` GovernanceProposer Address: ${info.l1ContractAddresses.governanceProposerAddress.toString()}`);
|
|
53
52
|
log(` Governance Address: ${info.l1ContractAddresses.governanceAddress.toString()}`);
|
|
54
|
-
log(` SlashFactory Address: ${info.l1ContractAddresses.slashFactoryAddress?.toString()}`);
|
|
55
53
|
log(` FeeAssetHandler Address: ${info.l1ContractAddresses.feeAssetHandlerAddress?.toString()}`);
|
|
56
54
|
log(` StakingAssetHandler Address: ${info.l1ContractAddresses.stakingAssetHandlerAddress?.toString()}`);
|
|
57
55
|
log(`L2 Contract Addresses:`);
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
nodeOption,
|
|
8
8
|
parseAztecAddress,
|
|
9
9
|
parseField,
|
|
10
|
-
parseOptionalAztecAddress,
|
|
11
10
|
parseOptionalInteger,
|
|
12
|
-
|
|
11
|
+
parseOptionalLogCursor,
|
|
13
12
|
parseOptionalTxHash,
|
|
13
|
+
parseTag,
|
|
14
14
|
} from '../../utils/commands.js';
|
|
15
15
|
|
|
16
16
|
export function injectCommands(program: Command, log: LogFn, debugLogger: Logger) {
|
|
@@ -47,21 +47,26 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger
|
|
|
47
47
|
|
|
48
48
|
program
|
|
49
49
|
.command('get-logs')
|
|
50
|
-
.description('Gets
|
|
51
|
-
.
|
|
50
|
+
.description('Gets public logs for a contract and tag, optionally restricted by block range or tx hash.')
|
|
51
|
+
.requiredOption('-ca, --contract-address <address>', 'Contract address that emitted the logs.', parseAztecAddress)
|
|
52
|
+
.requiredOption('--tag <tag>', 'Tag (Fr value) to filter logs by.', parseTag)
|
|
53
|
+
.option('-tx, --tx-hash <txHash>', 'A transaction hash to restrict the search to.', parseOptionalTxHash)
|
|
52
54
|
.option(
|
|
53
55
|
'-fb, --from-block <blockNum>',
|
|
54
56
|
'Initial block number for getting logs (defaults to 1).',
|
|
55
57
|
parseOptionalInteger,
|
|
56
58
|
)
|
|
57
59
|
.option('-tb, --to-block <blockNum>', 'Up to which block to fetch logs (defaults to latest).', parseOptionalInteger)
|
|
58
|
-
.option(
|
|
59
|
-
|
|
60
|
+
.option(
|
|
61
|
+
'-al --after-log <cursor>',
|
|
62
|
+
'Log cursor of the form <blockNumber>-<txIndexWithinBlock>-<logIndexWithinTx> to resume pagination after.',
|
|
63
|
+
parseOptionalLogCursor,
|
|
64
|
+
)
|
|
60
65
|
.addOption(nodeOption)
|
|
61
66
|
.option('--follow', 'If set, will keep polling for new logs until interrupted.')
|
|
62
|
-
.action(async ({ txHash, fromBlock, toBlock, afterLog, contractAddress,
|
|
67
|
+
.action(async ({ txHash, fromBlock, toBlock, afterLog, contractAddress, tag, nodeUrl, follow }) => {
|
|
63
68
|
const { getLogs } = await import('./get_logs.js');
|
|
64
|
-
await getLogs(txHash, fromBlock, toBlock, afterLog, contractAddress, nodeUrl, follow, log);
|
|
69
|
+
await getLogs({ txHash, fromBlock, toBlock, afterLog, contractAddress, tag, nodeUrl, follow, log });
|
|
65
70
|
});
|
|
66
71
|
|
|
67
72
|
program
|
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
import { getInitialTestAccountsData } from '@aztec/accounts/testing';
|
|
2
|
-
import
|
|
2
|
+
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
3
3
|
import type { WaitOpts } from '@aztec/aztec.js/contracts';
|
|
4
4
|
import { createAztecNodeClient } from '@aztec/aztec.js/node';
|
|
5
|
+
import { TxStatus } from '@aztec/aztec.js/tx';
|
|
5
6
|
import { AccountManager } from '@aztec/aztec.js/wallet';
|
|
6
7
|
import { jsonStringify } from '@aztec/foundation/json-rpc';
|
|
7
8
|
import type { LogFn } from '@aztec/foundation/log';
|
|
8
9
|
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
|
|
9
|
-
import {
|
|
10
|
+
import { EmbeddedWallet } from '@aztec/wallets/embedded';
|
|
11
|
+
import { deployFundedSchnorrAccounts } from '@aztec/wallets/testing';
|
|
10
12
|
|
|
11
13
|
export async function setupL2Contracts(nodeUrl: string, testAccounts: boolean, json: boolean, log: LogFn) {
|
|
12
14
|
const waitOpts: WaitOpts = {
|
|
13
15
|
timeout: 180,
|
|
14
16
|
interval: 1,
|
|
17
|
+
// The embedded wallet defaults to PROPOSED, which can be dropped if its proposed block is pruned
|
|
18
|
+
// before the checkpoint lands. Wait for the checkpoint so serial setup is reliable.
|
|
19
|
+
waitForStatus: TxStatus.CHECKPOINTED,
|
|
15
20
|
};
|
|
16
21
|
log('setupL2Contracts: Wait options' + jsonStringify(waitOpts));
|
|
17
22
|
log('setupL2Contracts: Creating PXE client...');
|
|
18
23
|
const node = createAztecNodeClient(nodeUrl);
|
|
19
|
-
const wallet = await
|
|
24
|
+
const wallet = await EmbeddedWallet.create(node);
|
|
20
25
|
|
|
21
26
|
let deployedAccountManagers: AccountManager[] = [];
|
|
22
27
|
if (testAccounts) {
|
|
@@ -96,7 +96,6 @@ export async function deployL1ContractsCmd(
|
|
|
96
96
|
log(`RewardDistributor Address: ${l1ContractAddresses.rewardDistributorAddress.toString()}`);
|
|
97
97
|
log(`GovernanceProposer Address: ${l1ContractAddresses.governanceProposerAddress.toString()}`);
|
|
98
98
|
log(`Governance Address: ${l1ContractAddresses.governanceAddress.toString()}`);
|
|
99
|
-
log(`SlashFactory Address: ${l1ContractAddresses.slashFactoryAddress?.toString()}`);
|
|
100
99
|
log(`FeeAssetHandler Address: ${l1ContractAddresses.feeAssetHandlerAddress?.toString()}`);
|
|
101
100
|
log(`StakingAssetHandler Address: ${l1ContractAddresses.stakingAssetHandlerAddress?.toString()}`);
|
|
102
101
|
log(`ZK Passport Verifier Address: ${l1ContractAddresses.zkPassportVerifierAddress?.toString()}`);
|
|
@@ -29,7 +29,7 @@ export async function deployNewRollup(
|
|
|
29
29
|
const initialFundedAccounts = initialAccounts.map(a => a.address).concat(sponsoredFPCAddress);
|
|
30
30
|
const { genesisArchiveRoot, fundingNeeded } = await getGenesisValues(initialFundedAccounts);
|
|
31
31
|
|
|
32
|
-
const { rollup
|
|
32
|
+
const { rollup } = await deployNewRollupContracts(
|
|
33
33
|
registryAddress,
|
|
34
34
|
rpcUrls,
|
|
35
35
|
privateKey,
|
|
@@ -51,7 +51,6 @@ export async function deployNewRollup(
|
|
|
51
51
|
initialFundedAccounts: initialFundedAccounts.map(a => a.toString()),
|
|
52
52
|
initialValidators: initialValidators.map(a => a.attester.toString()),
|
|
53
53
|
genesisArchiveRoot: genesisArchiveRoot.toString(),
|
|
54
|
-
slashFactoryAddress: slashFactoryAddress.toString(),
|
|
55
54
|
},
|
|
56
55
|
null,
|
|
57
56
|
2,
|
|
@@ -62,6 +61,5 @@ export async function deployNewRollup(
|
|
|
62
61
|
log(`Initial funded accounts: ${initialFundedAccounts.map(a => a.toString()).join(', ')}`);
|
|
63
62
|
log(`Initial validators: ${initialValidators.map(a => a.attester.toString()).join(', ')}`);
|
|
64
63
|
log(`Genesis archive root: ${genesisArchiveRoot.toString()}`);
|
|
65
|
-
log(`Slash Factory Address: ${slashFactoryAddress.toString()}`);
|
|
66
64
|
}
|
|
67
65
|
}
|
|
@@ -2,7 +2,7 @@ import { createEthereumChain, isAnvilTestChain } from '@aztec/ethereum/chain';
|
|
|
2
2
|
import { createExtendedL1Client, getPublicClient } from '@aztec/ethereum/client';
|
|
3
3
|
import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
|
|
4
4
|
import { GSEContract, RollupContract } from '@aztec/ethereum/contracts';
|
|
5
|
-
import {
|
|
5
|
+
import { createL1TxUtils } from '@aztec/ethereum/l1-tx-utils';
|
|
6
6
|
import { EthCheatCodes } from '@aztec/ethereum/test';
|
|
7
7
|
import type { EthAddress } from '@aztec/foundation/eth-address';
|
|
8
8
|
import type { LogFn, Logger } from '@aztec/foundation/log';
|
|
@@ -38,7 +38,6 @@ export interface LoggerArgs {
|
|
|
38
38
|
export function generateL1Account() {
|
|
39
39
|
const privateKey = generatePrivateKey();
|
|
40
40
|
const account = privateKeyToAccount(privateKey);
|
|
41
|
-
account.address;
|
|
42
41
|
return {
|
|
43
42
|
privateKey,
|
|
44
43
|
address: account.address,
|
|
@@ -88,7 +87,7 @@ export async function addL1Validator({
|
|
|
88
87
|
const gse = new GSEContract(l1Client, gseAddress);
|
|
89
88
|
const registrationTuple = await gse.makeRegistrationTuple(blsSecretKey);
|
|
90
89
|
|
|
91
|
-
const l1TxUtils =
|
|
90
|
+
const l1TxUtils = createL1TxUtils(l1Client, { logger: debugLogger });
|
|
92
91
|
const proofParamsObj = ZkPassportProofParams.fromBuffer(proofParams);
|
|
93
92
|
|
|
94
93
|
// Step 1: Claim STK tokens from the faucet
|
|
@@ -194,7 +193,7 @@ export async function addL1ValidatorViaRollup({
|
|
|
194
193
|
|
|
195
194
|
const registrationTuple = await gse.makeRegistrationTuple(blsSecretKey);
|
|
196
195
|
|
|
197
|
-
const l1TxUtils =
|
|
196
|
+
const l1TxUtils = createL1TxUtils(l1Client, { logger: debugLogger });
|
|
198
197
|
|
|
199
198
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
200
199
|
to: rollupAddress.toString(),
|
|
@@ -241,7 +240,7 @@ export async function removeL1Validator({
|
|
|
241
240
|
const account = getAccount(privateKey, mnemonic);
|
|
242
241
|
const chain = createEthereumChain(rpcUrls, chainId);
|
|
243
242
|
const l1Client = createExtendedL1Client(rpcUrls, account, chain.chainInfo);
|
|
244
|
-
const l1TxUtils =
|
|
243
|
+
const l1TxUtils = createL1TxUtils(l1Client, { logger: debugLogger });
|
|
245
244
|
|
|
246
245
|
dualLog(`Removing validator ${validatorAddress.toString()} from rollup ${rollupAddress.toString()}`);
|
|
247
246
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
@@ -268,7 +267,7 @@ export async function pruneRollup({
|
|
|
268
267
|
const account = getAccount(privateKey, mnemonic);
|
|
269
268
|
const chain = createEthereumChain(rpcUrls, chainId);
|
|
270
269
|
const l1Client = createExtendedL1Client(rpcUrls, account, chain.chainInfo);
|
|
271
|
-
const l1TxUtils =
|
|
270
|
+
const l1TxUtils = createL1TxUtils(l1Client, { logger: debugLogger });
|
|
272
271
|
|
|
273
272
|
dualLog(`Trying prune`);
|
|
274
273
|
const { receipt } = await l1TxUtils.sendAndMonitorTransaction({
|
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
import { createLogger } from '@aztec/aztec.js/log';
|
|
2
2
|
|
|
3
|
-
import { mkdir, readFile,
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'fs/promises';
|
|
4
4
|
import { dirname } from 'path';
|
|
5
5
|
|
|
6
6
|
export interface CachedFetchOptions {
|
|
7
|
-
/**
|
|
8
|
-
cacheDurationMs: number;
|
|
9
|
-
/** The cache file */
|
|
7
|
+
/** The cache file path for storing data. If not provided, no caching is performed. */
|
|
10
8
|
cacheFile?: string;
|
|
9
|
+
/** Fallback max-age in milliseconds when server sends no Cache-Control header. Defaults to 5 minutes. */
|
|
10
|
+
defaultMaxAgeMs?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Cache metadata stored in a sidecar .meta file alongside the data file. */
|
|
14
|
+
interface CacheMeta {
|
|
15
|
+
etag?: string;
|
|
16
|
+
expiresAt: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DEFAULT_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes
|
|
20
|
+
|
|
21
|
+
/** Extracts max-age value in milliseconds from a Response's Cache-Control header. Returns undefined if not present. */
|
|
22
|
+
export function parseMaxAge(response: { headers: { get(name: string): string | null } }): number | undefined {
|
|
23
|
+
const cacheControl = response.headers.get('cache-control');
|
|
24
|
+
if (!cacheControl) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const match = cacheControl.match(/max-age=(\d+)/);
|
|
28
|
+
if (!match) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return parseInt(match[1], 10) * 1000;
|
|
11
32
|
}
|
|
12
33
|
|
|
13
34
|
/**
|
|
14
|
-
* Fetches data from a URL with file-based caching
|
|
15
|
-
*
|
|
35
|
+
* Fetches data from a URL with file-based HTTP conditional caching.
|
|
36
|
+
*
|
|
37
|
+
* Data is stored as raw JSON in the cache file (same format as the server returns).
|
|
38
|
+
* Caching metadata (ETag, expiry) is stored in a separate sidecar `.meta` file.
|
|
39
|
+
* This keeps the data file human-readable and backward-compatible with older code.
|
|
16
40
|
*
|
|
17
41
|
* @param url - The URL to fetch from
|
|
18
|
-
* @param
|
|
19
|
-
* @param
|
|
20
|
-
* @param
|
|
21
|
-
* @returns The fetched and parsed JSON data, or undefined if fetch fails
|
|
42
|
+
* @param options - Caching options
|
|
43
|
+
* @param fetch - Fetch implementation (defaults to globalThis.fetch)
|
|
44
|
+
* @param log - Logger instance
|
|
45
|
+
* @returns The fetched and parsed JSON data, or undefined if fetch fails
|
|
22
46
|
*/
|
|
23
47
|
export async function cachedFetch<T = any>(
|
|
24
48
|
url: string,
|
|
@@ -26,42 +50,106 @@ export async function cachedFetch<T = any>(
|
|
|
26
50
|
fetch = globalThis.fetch,
|
|
27
51
|
log = createLogger('cached_fetch'),
|
|
28
52
|
): Promise<T | undefined> {
|
|
29
|
-
const {
|
|
53
|
+
const { cacheFile, defaultMaxAgeMs = DEFAULT_MAX_AGE_MS } = options;
|
|
54
|
+
|
|
55
|
+
// If no cacheFile, just fetch normally without caching
|
|
56
|
+
if (!cacheFile) {
|
|
57
|
+
return fetchAndParse<T>(url, fetch, log);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const metaFile = cacheFile + '.meta';
|
|
30
61
|
|
|
31
|
-
// Try to read
|
|
62
|
+
// Try to read metadata
|
|
63
|
+
let meta: CacheMeta | undefined;
|
|
32
64
|
try {
|
|
33
|
-
|
|
34
|
-
const info = await stat(cacheFile);
|
|
35
|
-
if (info.mtimeMs + cacheDurationMs > Date.now()) {
|
|
36
|
-
const cachedData = JSON.parse(await readFile(cacheFile, 'utf-8'));
|
|
37
|
-
return cachedData;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
65
|
+
meta = JSON.parse(await readFile(metaFile, 'utf-8'));
|
|
40
66
|
} catch {
|
|
41
|
-
log.trace('
|
|
67
|
+
log.trace('No usable cache metadata found');
|
|
42
68
|
}
|
|
43
69
|
|
|
70
|
+
// Try to read cached data
|
|
71
|
+
let cachedData: T | undefined;
|
|
44
72
|
try {
|
|
45
|
-
|
|
73
|
+
cachedData = JSON.parse(await readFile(cacheFile, 'utf-8'));
|
|
74
|
+
} catch {
|
|
75
|
+
log.trace('No usable cached data found');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If metadata and data exist and cache is fresh, return directly
|
|
79
|
+
if (meta && cachedData !== undefined && meta.expiresAt > Date.now()) {
|
|
80
|
+
return cachedData;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Cache is stale or missing — make a (possibly conditional) request
|
|
84
|
+
try {
|
|
85
|
+
const headers: Record<string, string> = {};
|
|
86
|
+
if (meta?.etag && cachedData !== undefined) {
|
|
87
|
+
headers['If-None-Match'] = meta.etag;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const response = await fetch(url, { headers });
|
|
91
|
+
|
|
92
|
+
if (response.status === 304 && cachedData !== undefined) {
|
|
93
|
+
// Not modified — recompute expiry from new response headers and return cached data
|
|
94
|
+
const maxAgeMs = parseMaxAge(response) ?? defaultMaxAgeMs;
|
|
95
|
+
await writeMetaFile(metaFile, { etag: meta?.etag, expiresAt: Date.now() + maxAgeMs }, log);
|
|
96
|
+
return cachedData;
|
|
97
|
+
}
|
|
98
|
+
|
|
46
99
|
if (!response.ok) {
|
|
47
100
|
log.warn(`Failed to fetch from ${url}: ${response.status} ${response.statusText}`);
|
|
48
|
-
return
|
|
101
|
+
return cachedData;
|
|
49
102
|
}
|
|
50
103
|
|
|
51
|
-
|
|
104
|
+
// 200 — parse new data and cache it
|
|
105
|
+
const data = (await response.json()) as T;
|
|
106
|
+
const maxAgeMs = parseMaxAge(response) ?? defaultMaxAgeMs;
|
|
107
|
+
const etag = response.headers.get('etag') ?? undefined;
|
|
52
108
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} catch (err) {
|
|
59
|
-
log.warn('Failed to cache data on disk: ' + cacheFile, { cacheFile, err });
|
|
60
|
-
}
|
|
109
|
+
await ensureDir(cacheFile, log);
|
|
110
|
+
await Promise.all([
|
|
111
|
+
writeFile(cacheFile, JSON.stringify(data), 'utf-8'),
|
|
112
|
+
writeFile(metaFile, JSON.stringify({ etag, expiresAt: Date.now() + maxAgeMs }), 'utf-8'),
|
|
113
|
+
]);
|
|
61
114
|
|
|
62
115
|
return data;
|
|
116
|
+
} catch (err) {
|
|
117
|
+
log.warn(`Failed to fetch from ${url}`, { err });
|
|
118
|
+
return cachedData;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function fetchAndParse<T>(
|
|
123
|
+
url: string,
|
|
124
|
+
fetch: typeof globalThis.fetch,
|
|
125
|
+
log: ReturnType<typeof createLogger>,
|
|
126
|
+
): Promise<T | undefined> {
|
|
127
|
+
try {
|
|
128
|
+
const response = await fetch(url);
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
log.warn(`Failed to fetch from ${url}: ${response.status} ${response.statusText}`);
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
return (await response.json()) as T;
|
|
63
134
|
} catch (err) {
|
|
64
135
|
log.warn(`Failed to fetch from ${url}`, { err });
|
|
65
136
|
return undefined;
|
|
66
137
|
}
|
|
67
138
|
}
|
|
139
|
+
|
|
140
|
+
async function ensureDir(filePath: string, log: ReturnType<typeof createLogger>) {
|
|
141
|
+
try {
|
|
142
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
143
|
+
} catch (err) {
|
|
144
|
+
log.warn('Failed to create cache directory for: ' + filePath, { err });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function writeMetaFile(metaFile: string, meta: CacheMeta, log: ReturnType<typeof createLogger>) {
|
|
149
|
+
try {
|
|
150
|
+
await mkdir(dirname(metaFile), { recursive: true });
|
|
151
|
+
await writeFile(metaFile, JSON.stringify(meta), 'utf-8');
|
|
152
|
+
} catch (err) {
|
|
153
|
+
log.warn('Failed to write cache metadata: ' + metaFile, { err });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -45,7 +45,9 @@ export function enrichEnvironmentWithChainName(networkName: NetworkNames) {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// Apply generated network config from defaults.yml
|
|
48
|
-
|
|
48
|
+
// For devnet iterations (v4-devnet-1, etc.), use the base devnet config
|
|
49
|
+
const configKey = /^v\d+-devnet-\d+$/.test(networkName) ? 'devnet' : networkName;
|
|
50
|
+
const generatedConfig = NetworkConfigs[configKey];
|
|
49
51
|
if (generatedConfig) {
|
|
50
52
|
enrichEnvironmentWithNetworkConfig(generatedConfig);
|
|
51
53
|
}
|