@aztec/end-to-end 0.0.1-commit.f5d02921e → 0.0.1-commit.f7ea82942

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 (87) hide show
  1. package/README.md +27 -0
  2. package/dest/bench/client_flows/client_flows_benchmark.js +3 -3
  3. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts +3 -2
  4. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.d.ts.map +1 -1
  5. package/dest/e2e_blacklist_token_contract/blacklist_token_contract_test.js +1 -1
  6. package/dest/e2e_epochs/epochs_test.d.ts +16 -1
  7. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  8. package/dest/e2e_epochs/epochs_test.js +56 -8
  9. package/dest/e2e_fees/fees_test.d.ts +1 -1
  10. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  11. package/dest/e2e_fees/fees_test.js +3 -3
  12. package/dest/e2e_p2p/inactivity_slash_test.js +2 -2
  13. package/dest/e2e_p2p/p2p_network.d.ts +10 -9
  14. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  15. package/dest/e2e_p2p/p2p_network.js +46 -27
  16. package/dest/e2e_p2p/reqresp/utils.js +1 -1
  17. package/dest/e2e_p2p/shared.d.ts +5 -7
  18. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  19. package/dest/e2e_p2p/shared.js +36 -47
  20. package/dest/fixtures/authwit_proxy.d.ts +1 -1
  21. package/dest/fixtures/authwit_proxy.d.ts.map +1 -1
  22. package/dest/fixtures/authwit_proxy.js +4 -0
  23. package/dest/fixtures/e2e_prover_test.d.ts +1 -1
  24. package/dest/fixtures/e2e_prover_test.d.ts.map +1 -1
  25. package/dest/fixtures/e2e_prover_test.js +2 -2
  26. package/dest/fixtures/fixtures.d.ts +12 -1
  27. package/dest/fixtures/fixtures.d.ts.map +1 -1
  28. package/dest/fixtures/fixtures.js +10 -0
  29. package/dest/fixtures/ha_setup.d.ts +2 -2
  30. package/dest/fixtures/ha_setup.d.ts.map +1 -1
  31. package/dest/fixtures/ha_setup.js +1 -1
  32. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
  33. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
  34. package/dest/fixtures/schnorr_hardcoded_account_contract.js +39 -0
  35. package/dest/fixtures/setup.d.ts +17 -10
  36. package/dest/fixtures/setup.d.ts.map +1 -1
  37. package/dest/fixtures/setup.js +28 -12
  38. package/dest/fixtures/setup_p2p_test.d.ts +6 -6
  39. package/dest/fixtures/setup_p2p_test.d.ts.map +1 -1
  40. package/dest/fixtures/setup_p2p_test.js +8 -8
  41. package/dest/forward-compatibility/wallet_rpc_client.d.ts +7 -0
  42. package/dest/forward-compatibility/wallet_rpc_client.d.ts.map +1 -0
  43. package/dest/forward-compatibility/wallet_rpc_client.js +15 -0
  44. package/dest/forward-compatibility/wallet_service.d.ts +3 -0
  45. package/dest/forward-compatibility/wallet_service.d.ts.map +1 -0
  46. package/dest/forward-compatibility/wallet_service.js +109 -0
  47. package/dest/legacy-jest-resolver.d.cts +3 -0
  48. package/dest/legacy-jest-resolver.d.cts.map +1 -0
  49. package/dest/shared/gas_portal_test_harness.js +1 -1
  50. package/dest/shared/uniswap_l1_l2.d.ts +1 -1
  51. package/dest/shared/uniswap_l1_l2.d.ts.map +1 -1
  52. package/dest/shared/uniswap_l1_l2.js +0 -4
  53. package/dest/spartan/setup_test_wallets.d.ts +1 -1
  54. package/dest/spartan/setup_test_wallets.d.ts.map +1 -1
  55. package/dest/spartan/setup_test_wallets.js +7 -39
  56. package/dest/test-wallet/test_wallet.d.ts +16 -8
  57. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  58. package/dest/test-wallet/test_wallet.js +85 -47
  59. package/dest/test-wallet/worker_wallet.d.ts +4 -4
  60. package/dest/test-wallet/worker_wallet.d.ts.map +1 -1
  61. package/dest/test-wallet/worker_wallet_schema.d.ts +7 -2
  62. package/dest/test-wallet/worker_wallet_schema.d.ts.map +1 -1
  63. package/package.json +40 -39
  64. package/src/bench/client_flows/client_flows_benchmark.ts +3 -3
  65. package/src/e2e_blacklist_token_contract/blacklist_token_contract_test.ts +3 -6
  66. package/src/e2e_epochs/epochs_test.ts +56 -7
  67. package/src/e2e_fees/fees_test.ts +4 -2
  68. package/src/e2e_p2p/inactivity_slash_test.ts +2 -2
  69. package/src/e2e_p2p/p2p_network.ts +57 -39
  70. package/src/e2e_p2p/reqresp/utils.ts +1 -1
  71. package/src/e2e_p2p/shared.ts +33 -61
  72. package/src/fixtures/authwit_proxy.ts +4 -0
  73. package/src/fixtures/e2e_prover_test.ts +5 -2
  74. package/src/fixtures/fixtures.ts +22 -0
  75. package/src/fixtures/ha_setup.ts +4 -2
  76. package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
  77. package/src/fixtures/setup.ts +43 -17
  78. package/src/fixtures/setup_p2p_test.ts +9 -9
  79. package/src/forward-compatibility/wallet_rpc_client.ts +14 -0
  80. package/src/forward-compatibility/wallet_service.ts +104 -0
  81. package/src/guides/up_quick_start.sh +0 -2
  82. package/src/legacy-jest-resolver.cjs +135 -0
  83. package/src/shared/gas_portal_test_harness.ts +0 -1
  84. package/src/shared/uniswap_l1_l2.ts +0 -4
  85. package/src/spartan/setup_test_wallets.ts +5 -31
  86. package/src/test-wallet/test_wallet.ts +103 -51
  87. package/src/test-wallet/worker_wallet.ts +3 -2
@@ -7,11 +7,7 @@ import type { Logger } from '@aztec/aztec.js/log';
7
7
  import { TxHash } from '@aztec/aztec.js/tx';
8
8
  import type { RollupCheatCodes } from '@aztec/aztec/testing';
9
9
  import type { EpochCacheInterface } from '@aztec/epoch-cache';
10
- import type {
11
- EmpireSlashingProposerContract,
12
- RollupContract,
13
- TallySlashingProposerContract,
14
- } from '@aztec/ethereum/contracts';
10
+ import type { RollupContract, SlashingProposerContract } from '@aztec/ethereum/contracts';
15
11
  import { EpochNumber, SlotNumber } from '@aztec/foundation/branded-types';
16
12
  import { timesAsync, unique } from '@aztec/foundation/collection';
17
13
  import { EthAddress } from '@aztec/foundation/eth-address';
@@ -22,8 +18,8 @@ import { TestContract, TestContractArtifact } from '@aztec/noir-test-contracts.j
22
18
  import { getPXEConfig, getPXEConfig as getRpcConfig } from '@aztec/pxe/server';
23
19
  import { getRoundForOffense } from '@aztec/slasher';
24
20
  import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
25
- import type { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
26
21
 
22
+ import { SchnorrHardcodedKeyAccountContract } from '../fixtures/schnorr_hardcoded_account_contract.js';
27
23
  import { submitTxsTo } from '../shared/submit-transactions.js';
28
24
  import { TestWallet } from '../test-wallet/test_wallet.js';
29
25
  import { type ProvenTx, proveInteraction } from '../test-wallet/utils.js';
@@ -60,10 +56,17 @@ export const submitTransactions = async (
60
56
  rpcConfig.proverEnabled = false;
61
57
  const wallet = await TestWallet.create(
62
58
  node,
63
- { ...getPXEConfig(), proverEnabled: false },
59
+ // Use checkpointed chain tip to avoid anchoring on provisional blocks that the archiver can prune
60
+ // when their slot ends without a checkpoint landing on L1.
61
+ { ...getPXEConfig(), proverEnabled: false, syncChainTip: 'checkpointed' },
64
62
  { loggerActorLabel: 'pxe-tx' },
65
63
  );
66
- const fundedAccountManager = await wallet.createSchnorrAccount(fundedAccount.secret, fundedAccount.salt);
64
+ const contract = new SchnorrHardcodedKeyAccountContract();
65
+ const fundedAccountManager = await wallet.createAccount({
66
+ secret: fundedAccount.secret,
67
+ salt: fundedAccount.salt,
68
+ contract,
69
+ });
67
70
  return submitTxsTo(wallet, fundedAccountManager.address, numTxs, logger);
68
71
  };
69
72
 
@@ -78,10 +81,15 @@ export async function prepareTransactions(
78
81
 
79
82
  const wallet = await TestWallet.create(
80
83
  node,
81
- { ...getPXEConfig(), proverEnabled: false },
84
+ { ...getPXEConfig(), proverEnabled: false, syncChainTip: 'checkpointed' },
82
85
  { loggerActorLabel: 'pxe-tx' },
83
86
  );
84
- const fundedAccountManager = await wallet.createSchnorrAccount(fundedAccount.secret, fundedAccount.salt);
87
+ const accountContract = new SchnorrHardcodedKeyAccountContract();
88
+ const fundedAccountManager = await wallet.createAccount({
89
+ secret: fundedAccount.secret,
90
+ salt: fundedAccount.salt,
91
+ contract: accountContract,
92
+ });
85
93
 
86
94
  const testContractInstance = await getContractInstanceFromInstantiationParams(TestContractArtifact, {
87
95
  salt: Fr.random(),
@@ -99,7 +107,7 @@ export async function prepareTransactions(
99
107
  }
100
108
 
101
109
  export function awaitProposalExecution(
102
- slashingProposer: EmpireSlashingProposerContract | TallySlashingProposerContract,
110
+ slashingProposer: SlashingProposerContract,
103
111
  timeoutSeconds: number,
104
112
  logger: Logger,
105
113
  ): Promise<bigint> {
@@ -109,24 +117,12 @@ export function awaitProposalExecution(
109
117
  reject(new Error(`Timeout waiting for proposal execution after ${timeoutSeconds}s`));
110
118
  }, timeoutSeconds * 1000);
111
119
 
112
- if (slashingProposer.type === 'empire') {
113
- const unwatch = slashingProposer.listenToPayloadSubmitted(args => {
114
- logger.warn(`Proposal ${args.payload} from round ${args.round} executed`);
115
- clearTimeout(timeout);
116
- unwatch();
117
- resolve(args.round);
118
- });
119
- } else if (slashingProposer.type === 'tally') {
120
- const unwatch = slashingProposer.listenToRoundExecuted(args => {
121
- logger.warn(`Slash from round ${args.round} executed`);
122
- clearTimeout(timeout);
123
- unwatch();
124
- resolve(args.round);
125
- });
126
- } else {
120
+ const unwatch = slashingProposer.listenToRoundExecuted(args => {
121
+ logger.warn(`Slash from round ${args.round} executed`);
127
122
  clearTimeout(timeout);
128
- reject(new Error(`Unknown slashing proposer type: ${(slashingProposer as any).type}`));
129
- }
123
+ unwatch();
124
+ resolve(args.round);
125
+ });
130
126
  });
131
127
  }
132
128
 
@@ -245,7 +241,6 @@ export async function awaitCommitteeKicked({
245
241
  rollup,
246
242
  cheatCodes,
247
243
  committee,
248
- slashFactory,
249
244
  slashingProposer,
250
245
  slashingRoundSize,
251
246
  aztecSlotDuration,
@@ -256,8 +251,7 @@ export async function awaitCommitteeKicked({
256
251
  rollup: RollupContract;
257
252
  cheatCodes: RollupCheatCodes;
258
253
  committee: readonly `0x${string}`[];
259
- slashFactory: SlashFactoryContract;
260
- slashingProposer: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
254
+ slashingProposer: SlashingProposerContract | undefined;
261
255
  slashingRoundSize: number;
262
256
  aztecSlotDuration: number;
263
257
  aztecEpochDuration: number;
@@ -270,36 +264,14 @@ export async function awaitCommitteeKicked({
270
264
 
271
265
  await cheatCodes.debugRollup();
272
266
 
273
- if (slashingProposer.type === 'empire') {
274
- // Await for the slash payload to be created if empire (no payload is created on tally until execution time)
275
- const targetEpoch = EpochNumber((await cheatCodes.getEpoch()) + (await rollup.getLagInEpochsForValidatorSet()) + 1);
276
- logger.info(`Advancing to epoch ${targetEpoch} so we start slashing`);
277
- await cheatCodes.advanceToEpoch(targetEpoch);
278
-
279
- const slashPayloadEvents = await retryUntil(
280
- async () => {
281
- const events = await slashFactory.getSlashPayloadCreatedEvents();
282
- return events.length > 0 ? events : undefined;
283
- },
284
- 'slash payload created',
285
- 120,
286
- 1,
287
- );
288
- expect(slashPayloadEvents.length).toBe(1);
289
- // The uniqueness check is needed since a validator may be slashed more than once on the same round (eg because they let two epochs be pruned)
290
- expect(unique(slashPayloadEvents[0].slashes.map(slash => slash.validator.toString()))).toHaveLength(
291
- committee.length,
292
- );
293
- } else {
294
- // Use the slash offset to ensure we are in the right epoch for tally
295
- const slashOffsetInRounds = await slashingProposer.getSlashOffsetInRounds();
296
- const slashingRoundSizeInEpochs = slashingRoundSize / aztecEpochDuration;
297
- const slashingOffsetInEpochs = Number(slashOffsetInRounds) * slashingRoundSizeInEpochs;
298
- const firstEpochInOffenseRound = offenseEpoch - (offenseEpoch % slashingRoundSizeInEpochs);
299
- const targetEpoch = firstEpochInOffenseRound + slashingOffsetInEpochs;
300
- logger.info(`Advancing to epoch ${targetEpoch} so we start slashing`);
301
- await cheatCodes.advanceToEpoch(EpochNumber(targetEpoch), { offset: -aztecSlotDuration / 2 });
302
- }
267
+ // Use the slash offset to ensure we are in the right epoch for tally
268
+ const slashOffsetInRounds = await slashingProposer.getSlashOffsetInRounds();
269
+ const slashingRoundSizeInEpochs = slashingRoundSize / aztecEpochDuration;
270
+ const slashingOffsetInEpochs = Number(slashOffsetInRounds) * slashingRoundSizeInEpochs;
271
+ const firstEpochInOffenseRound = offenseEpoch - (offenseEpoch % slashingRoundSizeInEpochs);
272
+ const targetEpoch = firstEpochInOffenseRound + slashingOffsetInEpochs;
273
+ logger.info(`Advancing to epoch ${targetEpoch} so we start slashing`);
274
+ await cheatCodes.advanceToEpoch(EpochNumber(targetEpoch), { offset: -aztecSlotDuration / 2 });
303
275
 
304
276
  const attestersPre = await rollup.getAttesters();
305
277
  expect(attestersPre.length).toBe(committee.length);
@@ -17,6 +17,10 @@ async function buildProxyCall(proxy: GenericProxyContract, action: ContractFunct
17
17
  return proxy.methods.forward_private_3(call.to, call.selector, call.args);
18
18
  } else if (argCount === 4) {
19
19
  return proxy.methods.forward_private_4(call.to, call.selector, call.args);
20
+ } else if (argCount === 5) {
21
+ return proxy.methods.forward_private_5(call.to, call.selector, call.args);
22
+ } else if (argCount === 6) {
23
+ return proxy.methods.forward_private_6(call.to, call.selector, call.args);
20
24
  }
21
25
  throw new Error(`No forward_private_${argCount} method on proxy`);
22
26
  }
@@ -223,8 +223,11 @@ export class FullProverTest {
223
223
 
224
224
  this.logger.verbose('Starting prover node');
225
225
  const sponsoredFPCAddress = await getSponsoredFPCAddress();
226
- const { prefilledPublicData } = await getGenesisValues(
226
+ const { genesis } = await getGenesisValues(
227
227
  this.context.initialFundedAccounts.map(a => a.address).concat(sponsoredFPCAddress),
228
+ undefined,
229
+ undefined,
230
+ this.context.genesis!.genesisTimestamp,
228
231
  );
229
232
 
230
233
  const proverNodeConfig: Parameters<typeof AztecNodeService.createAndSync>[0] = {
@@ -252,7 +255,7 @@ export class FullProverTest {
252
255
  this.proverAztecNode = await AztecNodeService.createAndSync(
253
256
  proverNodeConfig,
254
257
  { dateProvider: this.context.dateProvider, p2pClientDeps: { rpcTxProviders: [this.aztecNode] } },
255
- { prefilledPublicData },
258
+ { genesis },
256
259
  );
257
260
  this.logger.warn(`Proofs are now enabled`, { realProofs: this.realProofs });
258
261
  return this;
@@ -1,5 +1,27 @@
1
+ import type { AztecNode } from '@aztec/aztec.js/node';
2
+ import type { GasFees } from '@aztec/stdlib/gas';
3
+
1
4
  export const METRICS_PORT = 4318;
2
5
 
6
+ /** Default fee padding applied to predicted min fees in e2e tests. */
7
+ export const DEFAULT_MIN_FEE_PADDING = 5;
8
+
9
+ /**
10
+ * Large fee padding for txs that may be mined significantly later than when they were created,
11
+ * such as cloned txs in throughput/capacity benchmarks, where fees may spike between creation and mining.
12
+ */
13
+ export const LARGE_MIN_FEE_PADDING = 15;
14
+
15
+ /** Returns worst-case predicted min fees with padding applied, mirroring the BaseWallet pattern. */
16
+ export async function getPaddedMaxFeesPerGas(node: AztecNode, padding = DEFAULT_MIN_FEE_PADDING): Promise<GasFees> {
17
+ const predicted = await node.getPredictedMinFees();
18
+ const worstCase =
19
+ predicted.length > 0
20
+ ? predicted.reduce((worst, fees) => (fees.feePerL2Gas > worst.feePerL2Gas ? fees : worst))
21
+ : await node.getCurrentMinFees();
22
+ return worstCase.mul(1 + padding);
23
+ }
24
+
3
25
  export const shouldCollectMetrics = () => {
4
26
  if (process.env.COLLECT_METRICS) {
5
27
  return METRICS_PORT;
@@ -11,7 +11,7 @@ import { privateKeyToAccount } from 'viem/accounts';
11
11
  */
12
12
  export interface HADatabaseConfig {
13
13
  /** PostgreSQL connection URL */
14
- databaseUrl: string;
14
+ databaseUrl: SecretValue<string>;
15
15
  /** Node ID for HA coordination */
16
16
  nodeId: string;
17
17
  /** Enable HA signing */
@@ -28,7 +28,9 @@ export interface HADatabaseConfig {
28
28
  * Get database configuration from environment variables
29
29
  */
30
30
  export function createHADatabaseConfig(nodeId: string): HADatabaseConfig {
31
- const databaseUrl = process.env.DATABASE_URL || 'postgresql://aztec:aztec@localhost:5432/aztec_ha_test';
31
+ const databaseUrl = new SecretValue(
32
+ process.env.DATABASE_URL || 'postgresql://aztec:aztec@localhost:5432/aztec_ha_test',
33
+ );
32
34
 
33
35
  return {
34
36
  databaseUrl,
@@ -0,0 +1,49 @@
1
+ import { DefaultAccountContract } from '@aztec/accounts/defaults';
2
+ import type { ContractArtifact } from '@aztec/aztec.js/abi';
3
+ import type { AuthWitnessProvider } from '@aztec/aztec.js/account';
4
+ import type { CompleteAddress } from '@aztec/aztec.js/addresses';
5
+ import { AuthWitness } from '@aztec/aztec.js/authorization';
6
+ import type { Fr } from '@aztec/aztec.js/fields';
7
+ import { Schnorr } from '@aztec/foundation/crypto/schnorr';
8
+ import { GrumpkinScalar } from '@aztec/foundation/curves/grumpkin';
9
+ import { SchnorrHardcodedAccountContractArtifact } from '@aztec/noir-contracts.js/SchnorrHardcodedAccount';
10
+
11
+ /**
12
+ * The private key that matches the hardcoded public key in the SchnorrHardcodedAccountContract.
13
+ * The corresponding public key is baked into the Noir contract as a global constant.
14
+ */
15
+ export const SCHNORR_HARDCODED_PRIVATE_KEY = GrumpkinScalar.fromHexString(
16
+ '0xd35d743ac0dfe3d6dbe6be8c877cb524a00ab1e3d52d7bada095dfc8894ccfa',
17
+ );
18
+
19
+ /**
20
+ * Account contract backed by the SchnorrHardcodedAccount Noir contract.
21
+ * This contract verifies Schnorr signatures against a public key that is hardcoded
22
+ * in the contract artifact (not stored in a note), so it does not require on-chain
23
+ * deployment or initialization. Useful for tests that need a working account without
24
+ * mining any blocks.
25
+ */
26
+ export class SchnorrHardcodedKeyAccountContract extends DefaultAccountContract {
27
+ constructor(private privateKey: GrumpkinScalar = SCHNORR_HARDCODED_PRIVATE_KEY) {
28
+ super();
29
+ }
30
+
31
+ override getContractArtifact(): Promise<ContractArtifact> {
32
+ return Promise.resolve(SchnorrHardcodedAccountContractArtifact);
33
+ }
34
+
35
+ getInitializationFunctionAndArgs() {
36
+ return Promise.resolve(undefined);
37
+ }
38
+
39
+ getAuthWitnessProvider(_address: CompleteAddress): AuthWitnessProvider {
40
+ const privateKey = this.privateKey;
41
+ return {
42
+ async createAuthWit(messageHash: Fr): Promise<AuthWitness> {
43
+ const signer = new Schnorr();
44
+ const signature = await signer.constructSignature(messageHash.toBuffer(), privateKey);
45
+ return new AuthWitness(messageHash, [...signature.toBuffer()]);
46
+ },
47
+ };
48
+ }
49
+ }
@@ -7,6 +7,8 @@ import {
7
7
  BatchCall,
8
8
  type ContractFunctionInteraction,
9
9
  type ContractMethod,
10
+ type DeployInteractionWaitOptions,
11
+ type DeployOptions,
10
12
  getContractClassFromArtifact,
11
13
  waitForProven,
12
14
  } from '@aztec/aztec.js/contracts';
@@ -15,7 +17,7 @@ import { Fr } from '@aztec/aztec.js/fields';
15
17
  import { type Logger, createLogger } from '@aztec/aztec.js/log';
16
18
  import type { AztecNode } from '@aztec/aztec.js/node';
17
19
  import type { Wallet } from '@aztec/aztec.js/wallet';
18
- import { AnvilTestWatcher, CheatCodes } from '@aztec/aztec/testing';
20
+ import { AnvilTestWatcher, type AnvilTestWatcherOpts, CheatCodes } from '@aztec/aztec/testing';
19
21
  import { SPONSORED_FPC_SALT } from '@aztec/constants';
20
22
  import { isAnvilTestChain } from '@aztec/ethereum/chain';
21
23
  import { createExtendedL1Client } from '@aztec/ethereum/client';
@@ -49,9 +51,10 @@ import type { ProverNodeConfig } from '@aztec/prover-node';
49
51
  import { type PXEConfig, getPXEConfig } from '@aztec/pxe/server';
50
52
  import type { SequencerClient } from '@aztec/sequencer-client';
51
53
  import { type ContractInstanceWithAddress, getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
52
- import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
54
+ import type { AztecNodeAdmin, AztecNodeDebug } from '@aztec/stdlib/interfaces/client';
53
55
  import { tryStop } from '@aztec/stdlib/interfaces/server';
54
56
  import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
57
+ import type { GenesisData } from '@aztec/stdlib/world-state';
55
58
  import {
56
59
  type TelemetryClient,
57
60
  type TelemetryClientConfig,
@@ -178,8 +181,11 @@ export type SetupOptions = {
178
181
  proverNodeConfig?: Partial<ProverNodeConfig>;
179
182
  /** Whether to use a mock gossip sub network for p2p clients. */
180
183
  mockGossipSubNetwork?: boolean;
184
+ /** Whether to add simulated latency to the mock gossipsub network (in ms) */
185
+ mockGossipSubNetworkLatency?: number;
181
186
  /** Whether to disable the anvil test watcher (can still be manually started) */
182
187
  disableAnvilTestWatcher?: boolean;
188
+ anvilTestWatcherOpts?: AnvilTestWatcherOpts;
183
189
  /** Whether to enable anvil automine during deployment of L1 contracts (consider defaulting this to true). */
184
190
  automineL1Setup?: boolean;
185
191
  /** How many accounts to seed and unlock in anvil. */
@@ -201,8 +207,11 @@ export type SetupOptions = {
201
207
  skipAccountDeployment?: boolean;
202
208
  /** L1 contracts deployment arguments. */
203
209
  l1ContractsArgs?: Partial<DeployAztecL1ContractsArgs>;
204
- /** Wallet minimum fee padding multiplier (defaults to 0.5, which is 50% padding). */
210
+ /** Wallet minimum fee padding multiplier */
205
211
  walletMinFeePadding?: number;
212
+ /** Whether the initial node should be a lightweight RPC-only node (no sequencer, no validator).
213
+ * Use for tests that create their own validator nodes and don't need the initial sequencer. */
214
+ skipInitialSequencer?: boolean;
206
215
  } & Partial<AztecNodeConfig>;
207
216
 
208
217
  /** Context for an end-to-end test as returned by the `setup` function */
@@ -210,7 +219,7 @@ export type EndToEndContext = {
210
219
  /** The Anvil instance (only set if anvil was started locally). */
211
220
  anvil: Anvil | undefined;
212
221
  /** The Aztec Node service or client a connected to it. */
213
- aztecNode: AztecNode;
222
+ aztecNode: AztecNode & AztecNodeDebug;
214
223
  /** The Aztec Node as a service. */
215
224
  aztecNodeService: AztecNodeService;
216
225
  /** Client to the Aztec Node admin interface. */
@@ -249,8 +258,8 @@ export type EndToEndContext = {
249
258
  sequencerDelayer: Delayer | undefined;
250
259
  /** Delayer for prover node L1 txs (only when enableDelayer and startProverNode are true). */
251
260
  proverDelayer: Delayer | undefined;
252
- /** Prefilled public data used for setting up nodes. */
253
- prefilledPublicData: PublicDataTreeLeaf[] | undefined;
261
+ /** Genesis data used for setting up nodes. */
262
+ genesis: GenesisData | undefined;
254
263
  /** ACVM config (only set if running locally). */
255
264
  acvmConfig: Awaited<ReturnType<typeof getACVMConfig>>;
256
265
  /** BB config (only set if running locally). */
@@ -276,7 +285,7 @@ export async function setup(
276
285
  let anvil: Anvil | undefined;
277
286
  try {
278
287
  opts.aztecTargetCommitteeSize ??= 0;
279
- opts.slasherFlavor ??= 'none';
288
+ opts.slasherEnabled ??= false;
280
289
 
281
290
  const config: AztecNodeConfig & SetupOptions = { ...getConfigEnvVars(), ...opts };
282
291
  // use initialValidators for the node config
@@ -375,10 +384,12 @@ export async function setup(
375
384
  addressesToFund.push(sponsoredFPCAddress);
376
385
  }
377
386
 
378
- const { genesisArchiveRoot, prefilledPublicData, fundingNeeded } = await getGenesisValues(
387
+ const genesisTimestamp = BigInt(Math.floor(Date.now() / 1000));
388
+ const { genesisArchiveRoot, genesis, fundingNeeded } = await getGenesisValues(
379
389
  addressesToFund,
380
390
  opts.initialAccountFeeJuice,
381
391
  opts.genesisPublicData,
392
+ genesisTimestamp,
382
393
  );
383
394
 
384
395
  const wasAutomining = await ethCheatCodes.isAutoMining();
@@ -437,6 +448,7 @@ export async function setup(
437
448
  deployL1ContractsValues.l1ContractAddresses.rollupAddress,
438
449
  deployL1ContractsValues.l1Client,
439
450
  dateProvider,
451
+ opts.anvilTestWatcherOpts,
440
452
  );
441
453
  if (!opts.disableAnvilTestWatcher) {
442
454
  await watcher.start();
@@ -467,7 +479,7 @@ export async function setup(
467
479
  let p2pClientDeps: P2PClientDeps | undefined = undefined;
468
480
 
469
481
  if (opts.mockGossipSubNetwork) {
470
- mockGossipSubNetwork = new MockGossipSubNetwork();
482
+ mockGossipSubNetwork = new MockGossipSubNetwork(opts.mockGossipSubNetworkLatency);
471
483
  p2pClientDeps = { p2pServiceFactory: getMockPubSubP2PServiceFactory(mockGossipSubNetwork) };
472
484
  }
473
485
 
@@ -495,11 +507,22 @@ export async function setup(
495
507
  }
496
508
  }
497
509
 
510
+ // When skipInitialSequencer is set, the initial node is a lightweight RPC-only node.
511
+ // We apply these overrides to a copy so they don't leak into the returned config.
512
+ // Keep P2P enabled if mockGossipSubNetwork is used (needed for tx propagation to validators).
513
+ const initialNodeConfig = opts.skipInitialSequencer
514
+ ? {
515
+ ...config,
516
+ disableValidator: true,
517
+ ...(opts.mockGossipSubNetwork ? {} : { p2pEnabled: false, bootstrapNodes: [] as string[] }),
518
+ }
519
+ : config;
520
+
498
521
  const aztecNodeService = await withLoggerBindings({ actor: 'node-0' }, () =>
499
522
  AztecNodeService.createAndSync(
500
- config,
523
+ initialNodeConfig,
501
524
  { dateProvider, telemetry: telemetryClient, p2pClientDeps },
502
- { prefilledPublicData },
525
+ { genesis, dontStartSequencer: opts.skipInitialSequencer },
503
526
  ),
504
527
  );
505
528
  const sequencerClient = aztecNodeService.getSequencer();
@@ -524,7 +547,7 @@ export async function setup(
524
547
  dataDirectory: proverNodeDataDirectory,
525
548
  },
526
549
  { dateProvider, p2pClientDeps, telemetry: telemetryClient },
527
- { prefilledPublicData },
550
+ { genesis },
528
551
  ));
529
552
  }
530
553
 
@@ -560,7 +583,9 @@ export async function setup(
560
583
 
561
584
  let accounts: AztecAddress[] = [];
562
585
 
563
- if (shouldDeployAccounts) {
586
+ if (opts.skipInitialSequencer) {
587
+ logger.info('Sequencer not started on initial node, skipping block progression');
588
+ } else if (shouldDeployAccounts) {
564
589
  logger.info(
565
590
  `${numberOfAccounts} accounts are being deployed. Reliably progressing past genesis by setting minTxsPerBlock to 1 and waiting for the accounts to be deployed`,
566
591
  );
@@ -629,7 +654,7 @@ export async function setup(
629
654
  initialFundedAccounts,
630
655
  logger,
631
656
  mockGossipSubNetwork,
632
- prefilledPublicData,
657
+ genesis,
633
658
  proverNode,
634
659
  sequencerDelayer,
635
660
  proverDelayer,
@@ -729,7 +754,7 @@ export function createAndSyncProverNode(
729
754
  dateProvider: DateProvider;
730
755
  p2pClientDeps?: P2PClientDeps;
731
756
  },
732
- options: { prefilledPublicData: PublicDataTreeLeaf[]; dontStart?: boolean },
757
+ options: { genesis?: GenesisData; dontStart?: boolean },
733
758
  ): Promise<{ proverNode: AztecNodeService }> {
734
759
  return withLoggerBindings({ actor: 'prover-0' }, async () => {
735
760
  const proverNode = await AztecNodeService.createAndSync(
@@ -742,7 +767,7 @@ export function createAndSyncProverNode(
742
767
  proverPublisherPrivateKeys: [new SecretValue(proverNodePrivateKey)],
743
768
  },
744
769
  deps,
745
- { ...options, dontStartProverNode: options.dontStart },
770
+ { genesis: options.genesis, dontStartProverNode: options.dontStart },
746
771
  );
747
772
 
748
773
  if (!proverNode.getProverNode()) {
@@ -833,7 +858,7 @@ export async function ensureAccountContractsPublished(wallet: Wallet, accountsTo
833
858
  * Returns deployed account data that can be used by tests.
834
859
  */
835
860
  export const deployAccounts =
836
- (numberOfAccounts: number, logger: Logger) =>
861
+ (numberOfAccounts: number, logger: Logger, deployOptions?: Partial<DeployOptions<DeployInteractionWaitOptions>>) =>
837
862
  async ({ wallet, initialFundedAccounts }: { wallet: TestWallet; initialFundedAccounts: InitialAccountData[] }) => {
838
863
  if (initialFundedAccounts.length < numberOfAccounts) {
839
864
  throw new Error(`Cannot deploy more than ${initialFundedAccounts.length} initial accounts.`);
@@ -852,6 +877,7 @@ export const deployAccounts =
852
877
  await deployMethod.send({
853
878
  from: NO_FROM,
854
879
  skipClassPublication: i !== 0, // Publish the contract class at most once.
880
+ ...deployOptions,
855
881
  });
856
882
  }
857
883
 
@@ -7,7 +7,7 @@ import { SecretValue } from '@aztec/foundation/config';
7
7
  import { withLoggerBindings } from '@aztec/foundation/log/server';
8
8
  import { bufferToHex } from '@aztec/foundation/string';
9
9
  import type { DateProvider } from '@aztec/foundation/timer';
10
- import type { PublicDataTreeLeaf } from '@aztec/stdlib/trees';
10
+ import type { GenesisData } from '@aztec/stdlib/world-state';
11
11
 
12
12
  import getPort from 'get-port';
13
13
 
@@ -40,7 +40,7 @@ export async function createNodes(
40
40
  bootstrapNodeEnr: string,
41
41
  numNodes: number,
42
42
  bootNodePort: number,
43
- prefilledPublicData?: PublicDataTreeLeaf[],
43
+ genesis?: GenesisData,
44
44
  dataDirectory?: string,
45
45
  metricsPort?: number,
46
46
  indexOffset = 0,
@@ -65,7 +65,7 @@ export async function createNodes(
65
65
  port,
66
66
  bootstrapNodeEnr,
67
67
  validatorIndices,
68
- prefilledPublicData,
68
+ genesis,
69
69
  dataDir,
70
70
  metricsPort,
71
71
  );
@@ -97,7 +97,7 @@ export async function createNode(
97
97
  tcpPort: number,
98
98
  bootstrapNode: string | undefined,
99
99
  addressIndex: number | number[],
100
- prefilledPublicData?: PublicDataTreeLeaf[],
100
+ genesis?: GenesisData,
101
101
  dataDirectory?: string,
102
102
  metricsPort?: number,
103
103
  ) {
@@ -108,7 +108,7 @@ export async function createNode(
108
108
  return await AztecNodeService.createAndSync(
109
109
  validatorConfig,
110
110
  { telemetry, dateProvider },
111
- { prefilledPublicData, dontStartSequencer: config.dontStartSequencer },
111
+ { genesis, dontStartSequencer: config.dontStartSequencer },
112
112
  );
113
113
  });
114
114
  }
@@ -119,7 +119,7 @@ export async function createNonValidatorNode(
119
119
  dateProvider: DateProvider,
120
120
  tcpPort: number,
121
121
  bootstrapNode: string | undefined,
122
- prefilledPublicData?: PublicDataTreeLeaf[],
122
+ genesis?: GenesisData,
123
123
  dataDirectory?: string,
124
124
  metricsPort?: number,
125
125
  ) {
@@ -133,7 +133,7 @@ export async function createNonValidatorNode(
133
133
  sequencerPublisherPrivateKeys: [],
134
134
  };
135
135
  const telemetry = await getEndToEndTestTelemetryClient(metricsPort);
136
- return await AztecNodeService.createAndSync(config, { telemetry, dateProvider }, { prefilledPublicData });
136
+ return await AztecNodeService.createAndSync(config, { telemetry, dateProvider }, { genesis });
137
137
  });
138
138
  }
139
139
 
@@ -143,7 +143,7 @@ export async function createProverNode(
143
143
  bootstrapNode: string | undefined,
144
144
  addressIndex: number,
145
145
  deps: { dateProvider: DateProvider },
146
- prefilledPublicData?: PublicDataTreeLeaf[],
146
+ genesis?: GenesisData,
147
147
  dataDirectory?: string,
148
148
  metricsPort?: number,
149
149
  ): Promise<{ proverNode: AztecNodeService }> {
@@ -159,7 +159,7 @@ export async function createProverNode(
159
159
  { ...config, ...p2pConfig },
160
160
  { dataDirectory },
161
161
  { ...deps, telemetry },
162
- { prefilledPublicData: prefilledPublicData ?? [] },
162
+ { genesis },
163
163
  );
164
164
  });
165
165
  }
@@ -0,0 +1,14 @@
1
+ import type { Wallet } from '@aztec/aztec.js/wallet';
2
+ import { WalletSchema } from '@aztec/aztec.js/wallet';
3
+ import { createSafeJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client';
4
+
5
+ /**
6
+ * Creates a JSON-RPC client that connects to a remote wallet service.
7
+ * The returned object implements the {@link Wallet} interface, proxying all calls over HTTP to the specified URL.
8
+ */
9
+ export function createWalletClient(url: string): Wallet {
10
+ return createSafeJsonRpcClient<Wallet>(url, WalletSchema, {
11
+ namespaceMethods: 'wallet',
12
+ fetch: makeFetch([1, 2, 3], false),
13
+ });
14
+ }