@aztec/end-to-end 0.0.1-commit.9badcec54 → 0.0.1-commit.9ebd450e8

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 (39) hide show
  1. package/dest/bench/client_flows/client_flows_benchmark.js +2 -2
  2. package/dest/e2e_epochs/epochs_test.d.ts +16 -1
  3. package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
  4. package/dest/e2e_epochs/epochs_test.js +53 -5
  5. package/dest/e2e_fees/fees_test.d.ts +1 -1
  6. package/dest/e2e_fees/fees_test.d.ts.map +1 -1
  7. package/dest/e2e_fees/fees_test.js +2 -2
  8. package/dest/e2e_p2p/p2p_network.d.ts +6 -3
  9. package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
  10. package/dest/e2e_p2p/p2p_network.js +38 -20
  11. package/dest/e2e_p2p/shared.d.ts +1 -1
  12. package/dest/e2e_p2p/shared.d.ts.map +1 -1
  13. package/dest/e2e_p2p/shared.js +20 -5
  14. package/dest/fixtures/fixtures.d.ts +12 -1
  15. package/dest/fixtures/fixtures.d.ts.map +1 -1
  16. package/dest/fixtures/fixtures.js +10 -0
  17. package/dest/fixtures/ha_setup.d.ts +2 -2
  18. package/dest/fixtures/ha_setup.d.ts.map +1 -1
  19. package/dest/fixtures/ha_setup.js +1 -1
  20. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts +25 -0
  21. package/dest/fixtures/schnorr_hardcoded_account_contract.d.ts.map +1 -0
  22. package/dest/fixtures/schnorr_hardcoded_account_contract.js +39 -0
  23. package/dest/fixtures/setup.d.ts +7 -3
  24. package/dest/fixtures/setup.d.ts.map +1 -1
  25. package/dest/fixtures/setup.js +18 -4
  26. package/dest/test-wallet/test_wallet.d.ts +1 -1
  27. package/dest/test-wallet/test_wallet.d.ts.map +1 -1
  28. package/dest/test-wallet/test_wallet.js +3 -1
  29. package/package.json +39 -39
  30. package/src/bench/client_flows/client_flows_benchmark.ts +2 -2
  31. package/src/e2e_epochs/epochs_test.ts +53 -4
  32. package/src/e2e_fees/fees_test.ts +4 -2
  33. package/src/e2e_p2p/p2p_network.ts +41 -18
  34. package/src/e2e_p2p/shared.ts +17 -4
  35. package/src/fixtures/fixtures.ts +22 -0
  36. package/src/fixtures/ha_setup.ts +4 -2
  37. package/src/fixtures/schnorr_hardcoded_account_contract.ts +49 -0
  38. package/src/fixtures/setup.ts +26 -4
  39. package/src/test-wallet/test_wallet.ts +3 -1
@@ -1,5 +1,6 @@
1
- import type { InitialAccountData } from '@aztec/accounts/testing';
1
+ import { type InitialAccountData, generateSchnorrAccounts } from '@aztec/accounts/testing';
2
2
  import type { AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node';
3
+ import { getAccountContractAddress } from '@aztec/aztec.js/account';
3
4
  import { AztecAddress, EthAddress } from '@aztec/aztec.js/addresses';
4
5
  import { Fr } from '@aztec/aztec.js/fields';
5
6
  import { getL1ContractsConfigEnvVars } from '@aztec/ethereum/config';
@@ -20,7 +21,6 @@ import type { BootstrapNode } from '@aztec/p2p/bootstrap';
20
21
  import { createBootstrapNodeFromPrivateKey, getBootstrapNodeEnr } from '@aztec/p2p/test-helpers';
21
22
  import { tryStop } from '@aztec/stdlib/interfaces/server';
22
23
  import { TopicType } from '@aztec/stdlib/p2p';
23
- import { TxStatus } from '@aztec/stdlib/tx';
24
24
  import type { GenesisData } from '@aztec/stdlib/world-state';
25
25
  import { ZkPassportProofParams } from '@aztec/stdlib/zkpassport';
26
26
  import { getGenesisValues } from '@aztec/world-state/testing';
@@ -29,10 +29,13 @@ import getPort from 'get-port';
29
29
  import { type GetContractReturnType, getAddress, getContract } from 'viem';
30
30
  import { privateKeyToAccount } from 'viem/accounts';
31
31
 
32
+ import {
33
+ SCHNORR_HARDCODED_PRIVATE_KEY,
34
+ SchnorrHardcodedKeyAccountContract,
35
+ } from '../fixtures/schnorr_hardcoded_account_contract.js';
32
36
  import {
33
37
  type EndToEndContext,
34
38
  type SetupOptions,
35
- deployAccounts,
36
39
  getPrivateKeyFromIndex,
37
40
  getSponsoredFPCAddress,
38
41
  setup,
@@ -71,7 +74,7 @@ export class P2PNetworkTest {
71
74
  public peerIdPrivateKeys: string[] = [];
72
75
  public validators: Operator[] = [];
73
76
 
74
- public deployedAccounts: InitialAccountData[] = [];
77
+ public hardcodedAccountData!: InitialAccountData;
75
78
  public genesis: GenesisData | undefined;
76
79
 
77
80
  // The re-execution test needs a wallet and a spam contract
@@ -124,7 +127,6 @@ export class P2PNetworkTest {
124
127
  metricsPort: metricsPort,
125
128
  numberOfInitialFundedAccounts: 2,
126
129
  startProverNode,
127
- walletMinFeePadding: 2.0,
128
130
  };
129
131
 
130
132
  this.deployL1ContractsArgs = {
@@ -187,10 +189,10 @@ export class P2PNetworkTest {
187
189
  }
188
190
 
189
191
  get fundedAccount() {
190
- if (!this.deployedAccounts[0]) {
191
- throw new Error('Call setupAccount to create a funded account.');
192
+ if (!this.hardcodedAccountData) {
193
+ throw new Error('Call setup to initialize the hardcoded account.');
192
194
  }
193
- return this.deployedAccounts[0];
195
+ return this.hardcodedAccountData;
194
196
  }
195
197
 
196
198
  async addBootstrapNode() {
@@ -298,18 +300,22 @@ export class P2PNetworkTest {
298
300
  await this._sendDummyTx(this.context.deployL1ContractsValues.l1Client);
299
301
  }
300
302
 
303
+ /** Points the wallet to a P2P-enabled node so transactions can propagate through the network. */
304
+ setupWalletOnNode(node: AztecNodeService) {
305
+ this.logger.info('Pointing wallet to a P2P-enabled node');
306
+ this.context.wallet.updateNode(node);
307
+ }
308
+
309
+ /** Registers the hardcoded account in PXE without on-chain deployment. No sequencer needed. */
301
310
  async setupAccount() {
302
- this.logger.info('Setting up account');
303
- const { deployedAccounts } = await deployAccounts(1, this.logger, {
304
- wait: {
305
- waitForStatus: TxStatus.CHECKPOINTED,
306
- },
307
- })({
308
- wallet: this.context.wallet,
309
- initialFundedAccounts: this.context.initialFundedAccounts,
311
+ this.logger.info('Registering hardcoded account (no deployment)');
312
+ const contract = new SchnorrHardcodedKeyAccountContract();
313
+ const accountManager = await (this.context.wallet as TestWallet).createAccount({
314
+ secret: this.hardcodedAccountData.secret,
315
+ salt: this.hardcodedAccountData.salt,
316
+ contract,
310
317
  });
311
- this.deployedAccounts = deployedAccounts;
312
- [{ address: this.defaultAccountAddress }] = deployedAccounts;
318
+ this.defaultAccountAddress = accountManager.address;
313
319
  this.wallet = this.context.wallet;
314
320
  }
315
321
 
@@ -350,12 +356,29 @@ export class P2PNetworkTest {
350
356
 
351
357
  async setup() {
352
358
  this.logger.info('Setting up subsystems from fresh');
359
+
360
+ // Pre-compute hardcoded account data so it gets funded in genesis.
361
+ const contract = new SchnorrHardcodedKeyAccountContract();
362
+ const secret = Fr.random();
363
+ const salt = Fr.random();
364
+ this.hardcodedAccountData = {
365
+ secret,
366
+ salt,
367
+ signingKey: SCHNORR_HARDCODED_PRIVATE_KEY,
368
+ address: await getAccountContractAddress(contract, secret, salt),
369
+ };
370
+
371
+ // Generate regular Schnorr accounts for tests that need deployable accounts (e.g. add_rollup).
372
+ const regularAccounts = await generateSchnorrAccounts(this.setupOptions.numberOfInitialFundedAccounts ?? 2);
373
+
353
374
  this.context = await setup(
354
375
  0,
355
376
  {
356
377
  ...this.setupOptions,
357
378
  fundSponsoredFPC: true,
358
379
  skipAccountDeployment: true,
380
+ skipInitialSequencer: true,
381
+ initialFundedAccounts: [...regularAccounts, this.hardcodedAccountData],
359
382
  slasherEnabled: this.setupOptions.slasherEnabled ?? this.deployL1ContractsArgs.slasherEnabled ?? false,
360
383
  aztecTargetCommitteeSize: 0,
361
384
  l1ContractsArgs: this.deployL1ContractsArgs,
@@ -19,6 +19,7 @@ import { getPXEConfig, getPXEConfig as getRpcConfig } from '@aztec/pxe/server';
19
19
  import { getRoundForOffense } from '@aztec/slasher';
20
20
  import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
21
21
 
22
+ import { SchnorrHardcodedKeyAccountContract } from '../fixtures/schnorr_hardcoded_account_contract.js';
22
23
  import { submitTxsTo } from '../shared/submit-transactions.js';
23
24
  import { TestWallet } from '../test-wallet/test_wallet.js';
24
25
  import { type ProvenTx, proveInteraction } from '../test-wallet/utils.js';
@@ -55,10 +56,17 @@ export const submitTransactions = async (
55
56
  rpcConfig.proverEnabled = false;
56
57
  const wallet = await TestWallet.create(
57
58
  node,
58
- { ...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' },
59
62
  { loggerActorLabel: 'pxe-tx' },
60
63
  );
61
- 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
+ });
62
70
  return submitTxsTo(wallet, fundedAccountManager.address, numTxs, logger);
63
71
  };
64
72
 
@@ -73,10 +81,15 @@ export async function prepareTransactions(
73
81
 
74
82
  const wallet = await TestWallet.create(
75
83
  node,
76
- { ...getPXEConfig(), proverEnabled: false },
84
+ { ...getPXEConfig(), proverEnabled: false, syncChainTip: 'checkpointed' },
77
85
  { loggerActorLabel: 'pxe-tx' },
78
86
  );
79
- 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
+ });
80
93
 
81
94
  const testContractInstance = await getContractInstanceFromInstantiationParams(TestContractArtifact, {
82
95
  salt: Fr.random(),
@@ -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
+ }
@@ -17,7 +17,7 @@ import { Fr } from '@aztec/aztec.js/fields';
17
17
  import { type Logger, createLogger } from '@aztec/aztec.js/log';
18
18
  import type { AztecNode } from '@aztec/aztec.js/node';
19
19
  import type { Wallet } from '@aztec/aztec.js/wallet';
20
- import { AnvilTestWatcher, CheatCodes } from '@aztec/aztec/testing';
20
+ import { AnvilTestWatcher, type AnvilTestWatcherOpts, CheatCodes } from '@aztec/aztec/testing';
21
21
  import { SPONSORED_FPC_SALT } from '@aztec/constants';
22
22
  import { isAnvilTestChain } from '@aztec/ethereum/chain';
23
23
  import { createExtendedL1Client } from '@aztec/ethereum/client';
@@ -185,6 +185,7 @@ export type SetupOptions = {
185
185
  mockGossipSubNetworkLatency?: number;
186
186
  /** Whether to disable the anvil test watcher (can still be manually started) */
187
187
  disableAnvilTestWatcher?: boolean;
188
+ anvilTestWatcherOpts?: AnvilTestWatcherOpts;
188
189
  /** Whether to enable anvil automine during deployment of L1 contracts (consider defaulting this to true). */
189
190
  automineL1Setup?: boolean;
190
191
  /** How many accounts to seed and unlock in anvil. */
@@ -206,8 +207,11 @@ export type SetupOptions = {
206
207
  skipAccountDeployment?: boolean;
207
208
  /** L1 contracts deployment arguments. */
208
209
  l1ContractsArgs?: Partial<DeployAztecL1ContractsArgs>;
209
- /** Wallet minimum fee padding multiplier (defaults to 0.5, which is 50% padding). */
210
+ /** Wallet minimum fee padding multiplier */
210
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;
211
215
  } & Partial<AztecNodeConfig>;
212
216
 
213
217
  /** Context for an end-to-end test as returned by the `setup` function */
@@ -444,6 +448,7 @@ export async function setup(
444
448
  deployL1ContractsValues.l1ContractAddresses.rollupAddress,
445
449
  deployL1ContractsValues.l1Client,
446
450
  dateProvider,
451
+ opts.anvilTestWatcherOpts,
447
452
  );
448
453
  if (!opts.disableAnvilTestWatcher) {
449
454
  await watcher.start();
@@ -502,8 +507,23 @@ export async function setup(
502
507
  }
503
508
  }
504
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
+
505
521
  const aztecNodeService = await withLoggerBindings({ actor: 'node-0' }, () =>
506
- AztecNodeService.createAndSync(config, { dateProvider, telemetry: telemetryClient, p2pClientDeps }, { genesis }),
522
+ AztecNodeService.createAndSync(
523
+ initialNodeConfig,
524
+ { dateProvider, telemetry: telemetryClient, p2pClientDeps },
525
+ { genesis, dontStartSequencer: opts.skipInitialSequencer },
526
+ ),
507
527
  );
508
528
  const sequencerClient = aztecNodeService.getSequencer();
509
529
 
@@ -563,7 +583,9 @@ export async function setup(
563
583
 
564
584
  let accounts: AztecAddress[] = [];
565
585
 
566
- if (shouldDeployAccounts) {
586
+ if (opts.skipInitialSequencer) {
587
+ logger.info('Sequencer not started on initial node, skipping block progression');
588
+ } else if (shouldDeployAccounts) {
567
589
  logger.info(
568
590
  `${numberOfAccounts} accounts are being deployed. Reliably progressing past genesis by setting minTxsPerBlock to 1 and waiting for the accounts to be deployed`,
569
591
  );
@@ -40,6 +40,7 @@ import { ExecutionPayload, mergeExecutionPayloads } from '@aztec/stdlib/tx';
40
40
  import { BaseWallet, type SimulateViaEntrypointOptions } from '@aztec/wallet-sdk/base-wallet';
41
41
  import type { AccountType } from '@aztec/wallets/embedded';
42
42
 
43
+ import { DEFAULT_MIN_FEE_PADDING } from '../fixtures/fixtures.js';
43
44
  import { AztecNodeProxy, ProvenTx } from './utils.js';
44
45
 
45
46
  /**
@@ -63,6 +64,7 @@ export class TestWallet extends BaseWallet {
63
64
  private readonly nodeRef: AztecNodeProxy,
64
65
  ) {
65
66
  super(pxe, nodeRef);
67
+ this.minFeePadding = DEFAULT_MIN_FEE_PADDING;
66
68
  }
67
69
 
68
70
  static async create(
@@ -178,7 +180,7 @@ export class TestWallet extends BaseWallet {
178
180
  }
179
181
 
180
182
  setMinFeePadding(value?: number) {
181
- this.minFeePadding = value ?? 0.5;
183
+ this.minFeePadding = value ?? DEFAULT_MIN_FEE_PADDING;
182
184
  }
183
185
 
184
186
  protected getAccountFromAddress(address: AztecAddress): Promise<Account> {