@aztec-labs/ethereum 6.0.0-rc.3 → 6.0.0

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.
Files changed (114) hide show
  1. package/dest/blob_tx.d.ts +24 -0
  2. package/dest/blob_tx.d.ts.map +1 -0
  3. package/dest/blob_tx.js +94 -0
  4. package/dest/bn254/hash_to_point.d.ts +32 -0
  5. package/dest/bn254/hash_to_point.d.ts.map +1 -0
  6. package/dest/bn254/hash_to_point.js +80 -0
  7. package/dest/bn254/index.d.ts +3 -0
  8. package/dest/bn254/index.d.ts.map +1 -0
  9. package/dest/bn254/index.js +2 -0
  10. package/dest/bn254/proof_of_possession_gas.d.ts +29 -0
  11. package/dest/bn254/proof_of_possession_gas.d.ts.map +1 -0
  12. package/dest/bn254/proof_of_possession_gas.js +24 -0
  13. package/dest/client.d.ts +1 -1
  14. package/dest/client.d.ts.map +1 -1
  15. package/dest/client.js +5 -3
  16. package/dest/contracts/errors.d.ts +10 -3
  17. package/dest/contracts/errors.d.ts.map +1 -1
  18. package/dest/contracts/errors.js +10 -4
  19. package/dest/contracts/inbox.d.ts +1 -1
  20. package/dest/contracts/inbox.d.ts.map +1 -1
  21. package/dest/contracts/inbox.js +2 -0
  22. package/dest/contracts/log.d.ts +7 -1
  23. package/dest/contracts/log.d.ts.map +1 -1
  24. package/dest/contracts/log.js +6 -0
  25. package/dest/contracts/rollup.d.ts +32 -4
  26. package/dest/contracts/rollup.d.ts.map +1 -1
  27. package/dest/contracts/rollup.js +63 -7
  28. package/dest/deploy_aztec_l1_contracts.d.ts +16 -1
  29. package/dest/deploy_aztec_l1_contracts.d.ts.map +1 -1
  30. package/dest/deploy_aztec_l1_contracts.js +201 -65
  31. package/dest/foundry_binary.d.ts +1 -1
  32. package/dest/foundry_binary.d.ts.map +1 -1
  33. package/dest/foundry_binary.js +2 -1
  34. package/dest/l1_tx_utils/amsterdam.d.ts +1 -1
  35. package/dest/l1_tx_utils/amsterdam.d.ts.map +1 -1
  36. package/dest/l1_tx_utils/amsterdam.js +4 -1
  37. package/dest/l1_tx_utils/constants.d.ts +2 -1
  38. package/dest/l1_tx_utils/constants.d.ts.map +1 -1
  39. package/dest/l1_tx_utils/constants.js +3 -0
  40. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts +1 -1
  41. package/dest/l1_tx_utils/fee-strategies/p75_competitive.d.ts.map +1 -1
  42. package/dest/l1_tx_utils/fee-strategies/p75_competitive.js +6 -1
  43. package/dest/l1_tx_utils/index.d.ts +2 -1
  44. package/dest/l1_tx_utils/index.d.ts.map +1 -1
  45. package/dest/l1_tx_utils/index.js +1 -0
  46. package/dest/l1_tx_utils/l1_tx_utils.d.ts +21 -1
  47. package/dest/l1_tx_utils/l1_tx_utils.d.ts.map +1 -1
  48. package/dest/l1_tx_utils/l1_tx_utils.js +71 -5
  49. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts +8 -1
  50. package/dest/l1_tx_utils/readonly_l1_tx_utils.d.ts.map +1 -1
  51. package/dest/l1_tx_utils/readonly_l1_tx_utils.js +1 -1
  52. package/dest/l1_tx_utils/send_cost.d.ts +15 -0
  53. package/dest/l1_tx_utils/send_cost.d.ts.map +1 -0
  54. package/dest/l1_tx_utils/send_cost.js +10 -0
  55. package/dest/l1_tx_utils/signer.d.ts +2 -2
  56. package/dest/l1_tx_utils/signer.d.ts.map +1 -1
  57. package/dest/l1_tx_utils/signer.js +2 -2
  58. package/dest/l1_tx_utils/tx_delayer.d.ts +1 -1
  59. package/dest/l1_tx_utils/tx_delayer.d.ts.map +1 -1
  60. package/dest/l1_tx_utils/tx_delayer.js +8 -26
  61. package/dest/publisher_manager.d.ts +127 -13
  62. package/dest/publisher_manager.d.ts.map +1 -1
  63. package/dest/publisher_manager.js +320 -79
  64. package/dest/test/anvil_hardfork.d.ts +42 -0
  65. package/dest/test/anvil_hardfork.d.ts.map +1 -0
  66. package/dest/test/anvil_hardfork.js +107 -0
  67. package/dest/test/eth_cheat_codes.d.ts +30 -5
  68. package/dest/test/eth_cheat_codes.d.ts.map +1 -1
  69. package/dest/test/eth_cheat_codes.js +283 -101
  70. package/dest/test/index.d.ts +3 -1
  71. package/dest/test/index.d.ts.map +1 -1
  72. package/dest/test/index.js +2 -0
  73. package/dest/test/l1_hardfork.d.ts +43 -0
  74. package/dest/test/l1_hardfork.d.ts.map +1 -0
  75. package/dest/test/l1_hardfork.js +51 -0
  76. package/dest/test/recording_rpc_server.d.ts +1 -1
  77. package/dest/test/recording_rpc_server.d.ts.map +1 -1
  78. package/dest/test/recording_rpc_server.js +1 -0
  79. package/dest/test/rollup_cheat_codes.d.ts +1 -1
  80. package/dest/test/rollup_cheat_codes.d.ts.map +1 -1
  81. package/dest/test/rollup_cheat_codes.js +7 -7
  82. package/dest/test/start_anvil.d.ts +14 -2
  83. package/dest/test/start_anvil.d.ts.map +1 -1
  84. package/dest/test/start_anvil.js +28 -11
  85. package/package.json +13 -11
  86. package/src/blob_tx.ts +118 -0
  87. package/src/bn254/hash_to_point.ts +95 -0
  88. package/src/bn254/index.ts +2 -0
  89. package/src/bn254/proof_of_possession_gas.ts +54 -0
  90. package/src/client.ts +6 -3
  91. package/src/contracts/errors.ts +12 -4
  92. package/src/contracts/inbox.ts +4 -0
  93. package/src/contracts/log.ts +12 -0
  94. package/src/contracts/rollup.ts +63 -6
  95. package/src/deploy_aztec_l1_contracts.ts +238 -63
  96. package/src/foundry_binary.ts +2 -1
  97. package/src/l1_tx_utils/amsterdam.ts +4 -1
  98. package/src/l1_tx_utils/constants.ts +4 -0
  99. package/src/l1_tx_utils/fee-strategies/p75_competitive.ts +11 -1
  100. package/src/l1_tx_utils/index.ts +1 -0
  101. package/src/l1_tx_utils/l1_tx_utils.ts +72 -5
  102. package/src/l1_tx_utils/readonly_l1_tx_utils.ts +1 -1
  103. package/src/l1_tx_utils/send_cost.ts +21 -0
  104. package/src/l1_tx_utils/signer.ts +3 -2
  105. package/src/l1_tx_utils/tx_delayer.ts +8 -37
  106. package/src/publisher_manager.ts +458 -83
  107. package/src/test/anvil_hardfork.ts +95 -0
  108. package/src/test/eth_cheat_codes.ts +306 -81
  109. package/src/test/fixtures/sepolia_blob_tx.json +10 -0
  110. package/src/test/index.ts +2 -0
  111. package/src/test/l1_hardfork.ts +71 -0
  112. package/src/test/recording_rpc_server.ts +1 -0
  113. package/src/test/rollup_cheat_codes.ts +9 -9
  114. package/src/test/start_anvil.ts +41 -13
package/src/test/index.ts CHANGED
@@ -1,6 +1,8 @@
1
+ export * from './anvil_hardfork.js';
1
2
  export * from './blob_kzg_warmup.js';
2
3
  export * from './eth_cheat_codes.js';
3
4
  export * from './eth_cheat_codes_with_state.js';
5
+ export * from './l1_hardfork.js';
4
6
  export * from './start_anvil.js';
5
7
  export * from './upgrade_utils.js';
6
8
  export * from './chain_monitor.js';
@@ -0,0 +1,71 @@
1
+ import { getEnv } from '@aztec-labs/foundation/config/env';
2
+
3
+ const KNOWN_L1_HARDFORKS = ['osaka', 'amsterdam'] as const;
4
+
5
+ /** An L1 hardfork that test anvils can be started on. */
6
+ export type L1Hardfork = (typeof KNOWN_L1_HARDFORKS)[number];
7
+
8
+ /**
9
+ * Returns the L1 hardfork that test anvils start on, read from `$ANVIL_HARDFORK`, or undefined when it is unset.
10
+ * Throws on a name outside the known forks, so a typo fails at once instead of after anvil's spawn retries.
11
+ */
12
+ export function getL1Hardfork(): L1Hardfork | undefined {
13
+ const value = getEnv().ANVIL_HARDFORK?.trim().toLowerCase();
14
+ if (!value) {
15
+ return undefined;
16
+ }
17
+ if (!(KNOWN_L1_HARDFORKS as readonly string[]).includes(value)) {
18
+ throw new Error(`Unknown ANVIL_HARDFORK '${value}', expected one of: ${KNOWN_L1_HARDFORKS.join(', ')}`);
19
+ }
20
+ return value as L1Hardfork;
21
+ }
22
+
23
+ /**
24
+ * Returns whether this test run starts its anvils on Amsterdam. This is the intended fork, not an observation:
25
+ * a test whose chain changes fork mid-run must ask the chain (`isAmsterdamBlock`) instead.
26
+ */
27
+ export function isAmsterdamTestRun(): boolean {
28
+ return getL1Hardfork() === 'amsterdam';
29
+ }
30
+
31
+ /**
32
+ * Returns a describe block name with the L1 hardfork from `$ANVIL_HARDFORK` appended, e.g. `Registry (amsterdam)`, so
33
+ * test output and reports show which fork a run used. Returns the name unchanged when `$ANVIL_HARDFORK` is unset.
34
+ */
35
+ export function describeNameOnL1Fork(name: string): string {
36
+ const hardfork = getL1Hardfork();
37
+ return hardfork ? `${name} (${hardfork})` : name;
38
+ }
39
+
40
+ type DescribeBlock = (name: string, fn: () => void) => void;
41
+
42
+ /**
43
+ * A `describe` that also tags its test file for CI test-command generation, and appends the L1 hardfork to the block's
44
+ * name when `$ANVIL_HARDFORK` is set.
45
+ */
46
+ export type TaggedDescribe = DescribeBlock & { skip: DescribeBlock; only: DescribeBlock };
47
+
48
+ function taggedDescribe(): TaggedDescribe {
49
+ // Jest's global `describe` is read at call time, so importing this module outside Jest stays safe.
50
+ const tagged: TaggedDescribe = (name, fn) => describe(describeNameOnL1Fork(name), fn);
51
+ tagged.skip = (name, fn) => describe.skip(describeNameOnL1Fork(name), fn);
52
+ // eslint-disable-next-line no-only-tests/no-only-tests
53
+ tagged.only = (name, fn) => describe.only(describeNameOnL1Fork(name), fn);
54
+ return tagged;
55
+ }
56
+
57
+ /**
58
+ * Jest's `describe`, marking the test file to also run on every other L1 hardfork on every CI run. It has no runtime
59
+ * effect beyond `describe`: it exists so CI's test command generation (`yarn-project/scripts/echo_test_cmd.sh`)
60
+ * can grep for it and emit one extra command per fork with `ANVIL_HARDFORK` set. So it must appear in the test file
61
+ * itself, uncommented, not in a shared helper. A file marked with it needs no `describeWithAnvil`.
62
+ */
63
+ export const describeAcrossL1Forks: TaggedDescribe = taggedDescribe();
64
+
65
+ /**
66
+ * Jest's `describe`, marking a test file that starts anvil so that the full CI sweep (`CI_FULL=1` or
67
+ * `HARDFORK_SWEEP=1`) also runs it on every other L1 hardfork. It has no runtime effect beyond `describe`: it exists
68
+ * so CI's test command generation (`yarn-project/scripts/echo_test_cmd.sh`) can grep for it, so it must appear in
69
+ * the test file itself, uncommented. End-to-end tests need no marker: the sweep covers every simple e2e test.
70
+ */
71
+ export const describeWithAnvil: TaggedDescribe = taggedDescribe();
@@ -1,3 +1,4 @@
1
+ import { Buffer } from 'buffer';
1
2
  import { type Server, createServer } from 'node:http';
2
3
 
3
4
  /** A JSON-RPC endpoint that records the method of every request it receives. */
@@ -8,6 +8,7 @@ import { EthAddress } from '@aztec-labs/foundation/eth-address';
8
8
  import { createLogger } from '@aztec-labs/foundation/log';
9
9
  import { retryUntil } from '@aztec-labs/foundation/retry';
10
10
  import type { DateProvider } from '@aztec-labs/foundation/timer';
11
+ import { Buffer } from 'buffer';
11
12
  import {
12
13
  type GetContractReturnType,
13
14
  type Hex,
@@ -295,15 +296,14 @@ export class RollupCheatCodes {
295
296
  );
296
297
  }
297
298
 
298
- public insertOutbox(epoch: EpochNumber, numCheckpointsInEpoch: number, outHash: bigint) {
299
- return this.ethCheatCodes.execWithPausedAnvil(async () => {
300
- const outboxAddress = await this.rollup.read.getOutbox();
301
- const epochRootSlot = OutboxContract.getEpochRootStorageSlot(epoch, numCheckpointsInEpoch);
302
- await this.ethCheatCodes.store(EthAddress.fromString(outboxAddress), epochRootSlot, outHash);
303
- this.logger.warn(
304
- `Advanced outbox to epoch ${epoch} numCheckpointsInEpoch ${numCheckpointsInEpoch} with out hash ${outHash}`,
305
- );
306
- });
299
+ public async insertOutbox(epoch: EpochNumber, numCheckpointsInEpoch: number, outHash: bigint) {
300
+ // A single blind write, so unlike markAsProven it needs no pause to stay atomic against blocks being mined.
301
+ const outboxAddress = await this.rollup.read.getOutbox();
302
+ const epochRootSlot = OutboxContract.getEpochRootStorageSlot(epoch, numCheckpointsInEpoch);
303
+ await this.ethCheatCodes.store(EthAddress.fromString(outboxAddress), epochRootSlot, outHash);
304
+ this.logger.warn(
305
+ `Advanced outbox to epoch ${epoch} numCheckpointsInEpoch ${numCheckpointsInEpoch} with out hash ${outHash}`,
306
+ );
307
307
  }
308
308
 
309
309
  /**
@@ -1,9 +1,12 @@
1
+ import { getEnv } from '@aztec-labs/foundation/config/env';
1
2
  import { createLogger } from '@aztec-labs/foundation/log';
2
3
  import { makeBackoff, retry } from '@aztec-labs/foundation/retry';
3
4
  import type { TestDateProvider } from '@aztec-labs/foundation/timer';
4
5
  import { type ChildProcess, spawn } from 'child_process';
5
6
 
6
7
  import { resolveFoundryBinary } from '../foundry_binary.js';
8
+ import { resetAnvilMiningState } from './eth_cheat_codes.js';
9
+ import { getL1Hardfork } from './l1_hardfork.js';
7
10
 
8
11
  /** Minimal interface matching the @viem/anvil Anvil shape used by callers. */
9
12
  export interface Anvil {
@@ -61,7 +64,10 @@ export async function startAnvil(
61
64
  captureMethodCalls?: boolean;
62
65
  accounts?: number;
63
66
  chainId?: number;
64
- /** The hardfork to use (e.g. 'cancun', 'latest'). */
67
+ /**
68
+ * The hardfork to start on (e.g. 'osaka', 'amsterdam'). Defaults to `$ANVIL_HARDFORK`, else 'osaka'. Anvil's
69
+ * own default moves with each foundry release, so it is never relied on.
70
+ */
65
71
  hardfork?: string;
66
72
  /**
67
73
  * Number of slots per epoch used by anvil to compute the 'finalized' and 'safe' block tags.
@@ -76,6 +82,13 @@ export async function startAnvil(
76
82
  * the wall clock and the L1 chain when computing L1 slot timestamps.
77
83
  */
78
84
  dateProvider?: TestDateProvider;
85
+ /** Plain JSON state file to start from (`--load-state`), i.e. the gunzipped output of `anvil_dumpState`. */
86
+ loadState?: string;
87
+ /**
88
+ * Number of historical states anvil keeps (`--prune-history`). Each kept state is a full snapshot in a state dump
89
+ * with history, so this bounds how long a dump and reload take.
90
+ */
91
+ pruneHistory?: number;
79
92
  } = {},
80
93
  ): Promise<{ anvil: Anvil; methodCalls?: string[]; rpcUrl: string; stop: () => Promise<void> }> {
81
94
  const anvilBinary = resolveFoundryBinary('anvil');
@@ -84,12 +97,16 @@ export async function startAnvil(
84
97
 
85
98
  let detectedPort: number | undefined;
86
99
 
100
+ // Resolved before the retry loop so an unknown $ANVIL_HARDFORK fails at once rather than after every spawn attempt.
101
+ const hardfork = opts.hardfork ?? getL1Hardfork() ?? 'osaka';
102
+
87
103
  const anvil = await retry(
88
104
  async () => {
89
105
  // `--port 0` lets anvil bind an OS-assigned ephemeral port; the actual port is read back from
90
106
  // its "Listening on host:port" stdout below, so independent suites can spawn their own anvil
91
107
  // in parallel without fighting over a fixed port.
92
- const port = opts.port ?? (process.env.ANVIL_PORT ? parseInt(process.env.ANVIL_PORT) : 8545);
108
+ const { ANVIL_PORT } = getEnv();
109
+ const port = opts.port ?? (ANVIL_PORT ? parseInt(ANVIL_PORT) : 8545);
93
110
  const args: string[] = [
94
111
  '--host',
95
112
  '127.0.0.1',
@@ -97,18 +114,23 @@ export async function startAnvil(
97
114
  String(port),
98
115
  '--accounts',
99
116
  String(opts.accounts ?? 20),
117
+ // Above the 50M Amsterdam per-tx cap (AMSTERDAM_MAX_L1_TX_LIMIT), so a bundle sized up to that cap fits a block.
100
118
  '--gas-limit',
101
- String(45_000_000),
119
+ String(60_000_000),
102
120
  '--chain-id',
103
121
  String(opts.chainId ?? 31337),
104
122
  ];
105
123
  if (opts.l1BlockTime !== undefined) {
106
124
  args.push('--block-time', String(opts.l1BlockTime));
107
125
  }
108
- if (opts.hardfork !== undefined) {
109
- args.push('--hardfork', opts.hardfork);
110
- }
126
+ args.push('--hardfork', hardfork);
111
127
  args.push('--slots-in-an-epoch', String(opts.slotsInAnEpoch ?? 1));
128
+ if (opts.pruneHistory !== undefined) {
129
+ args.push('--prune-history', String(opts.pruneHistory));
130
+ }
131
+ if (opts.loadState !== undefined) {
132
+ args.push('--load-state', opts.loadState);
133
+ }
112
134
 
113
135
  // Spawn the watchdog (see ANVIL_WATCHDOG). It launches anvil with these args and reaps it if we
114
136
  // die; `$0` is 'bash' and `$@` is the anvil argv.
@@ -117,7 +139,7 @@ export async function startAnvil(
117
139
  const child = spawn('bash', ['-c', ANVIL_WATCHDOG, 'bash', ...args], {
118
140
  stdio: ['ignore', 'pipe', 'pipe'],
119
141
  detached: true,
120
- env: { ...process.env, ANVIL_BIN: anvilBinary, RAYON_NUM_THREADS: '1' },
142
+ env: { ...getEnv(), ANVIL_BIN: anvilBinary, RAYON_NUM_THREADS: '1' },
121
143
  });
122
144
 
123
145
  // Wait for "Listening on", an early exit, or the startup budget running out. Both streams are
@@ -215,6 +237,7 @@ export async function startAnvil(
215
237
 
216
238
  anvil.once('close', () => {
217
239
  status = 'idle';
240
+ resetAnvilMiningState(port);
218
241
  });
219
242
 
220
243
  let stopping: Promise<void> | undefined;
@@ -222,6 +245,7 @@ export async function startAnvil(
222
245
  if (status === 'idle') {
223
246
  return;
224
247
  }
248
+ resetAnvilMiningState(port);
225
249
  await (stopping ??= killChild(anvil));
226
250
  };
227
251
 
@@ -237,17 +261,21 @@ export async function startAnvil(
237
261
  return { anvil: anvilObj, methodCalls, stop, rpcUrl: `http://127.0.0.1:${port}` };
238
262
  }
239
263
 
240
- /** Extracts block time from anvil stdout and syncs the dateProvider. */
241
- function syncDateProviderFromAnvilOutput(text: string, dateProvider: TestDateProvider): void {
264
+ /** Advances the test clock from mined-block output without rewinding it when Anvil replays a reorg. */
265
+ export function syncDateProviderFromAnvilOutput(text: string, dateProvider: TestDateProvider): void {
242
266
  // Anvil logs mined blocks as:
243
267
  // Block Time: "Fri, 20 Mar 2026 02:10:46 +0000"
244
- const match = text.match(/Block Time:\s*"([^"]+)"/);
245
- if (match) {
268
+ const currentTimeMs = dateProvider.now();
269
+ let newestTimeMs = currentTimeMs;
270
+ for (const match of text.matchAll(/Block Time:\s*"([^"]+)"/g)) {
246
271
  const blockTimeMs = new Date(match[1]).getTime();
247
- if (!isNaN(blockTimeMs)) {
248
- dateProvider.setTime(blockTimeMs);
272
+ if (!Number.isNaN(blockTimeMs)) {
273
+ newestTimeMs = Math.max(newestTimeMs, blockTimeMs);
249
274
  }
250
275
  }
276
+ if (newestTimeMs > currentTimeMs) {
277
+ dateProvider.setTime(newestTimeMs);
278
+ }
251
279
  }
252
280
 
253
281
  /**