Skip to content

Commit 4be0457

Browse files
spalladinoaztec-bot
authored andcommitted
perf(e2e): seed BananaFPC fee juice at genesis instead of bridging (#24564)
## What / why PR 2 of the round-4 e2e speedup effort. The fees-family suites pay a `setup:bridge` span (~48s ≈ 4 production slots per process) in `FeesTest.applyFPCSetup`, bridging fee juice from L1 to the BananaFPC via the gas portal (`bridgeFromL1ToL2` = prepare-on-L1 + advance 2 blocks + a claim tx). That funding is pure setup plumbing — the fees tests snapshot the FPC's gas balance and assert deltas, they don't assert on the bridge itself. Fee-juice balances are just public-data leaves at `computeFeePayerBalanceLeafSlot(address)`, which `getGenesisValues` (`world-state/src/testing.ts`) already prefills for genesis-funded accounts — the same mechanism that funds the initial accounts and (already, on this base) the sponsored FPC. This PR seeds the BananaFPC's fee juice at genesis instead of bridging it during setup. Note: the plan's premise that `fundSponsoredFPC: true` bridges was stale — the sponsored FPC has been genesis-funded since #19532 (setup pushes its deterministic address into the genesis-funded list). The remaining `setup:bridge` cost in the fees family is the BananaFPC bridge, which this PR removes. ## Mechanism - The BananaFPC address depends on the BananaCoin address and the FPC admin (the first setup account), and the accounts are generated *inside* `setup()` with random secrets — so the FPC address can't be precomputed by the test before genesis. Fixed deploy salts are added for BananaCoin and BananaFPC so both addresses become deterministic once the admin is known, and a `computeExtraGenesisFundedAddresses(defaultAccounts)` hook on `SetupOptions` runs after the accounts are generated and before genesis values are computed. `FeesTest` uses it to derive the BananaFPC address from the first account and add it to the genesis-funded list. - The returned address flows through the existing initial-accounts path (`addressesToFund` → `getGenesisValues`), so it is funded with the same fee juice as an initial account (`10^22`) and automatically included in the L1 `FeeJuicePortal` `feeJuicePortalInitialBalance` (`fundingNeeded`) accounting — the portal's locked L1 balance stays consistent with total L2 fee-juice supply, exactly as for every other genesis-funded balance. - `applyFPCSetup` deploys the FPC with the fixed salt and asserts the deployed address equals the seeded one, so any drift in the deterministic deploy params surfaces as a clear error instead of a downstream "insufficient fee payer balance". The `bridgeFromL1ToL2` call is removed. - The `setup:bridge` span wrapper is kept in place for the bridging that remains, so its disappearance from the fees setup hooks is the measured proof. ## Scope decisions (site by site) - **`FeesTest.applyFPCSetup` BananaFPC funding** → moved to genesis. Used by `account_init`, `private_payments.parallel`, `failures`, `gas_estimation.parallel`. This is the only funding that moved. - **`FeesTest.mintAndBridgeFeeJuice`** (bridges to an arbitrary recipient) → kept. It fires in `account_init` test bodies that test the bridge/claim flow ("pays natively in the Fee Juice after Alice bridges funds") — real behavior under test. - **`account_init` `FeeJuicePaymentMethodWithClaim` / `prepareTokensOnL1`** → kept. Tests the atomic claim-in-deploy flow. - **Sponsored FPC** → already genesis-funded via `fundSponsoredFPC: true` on this base; no change. - **`cross_chain_messaging_test`** (`fundSponsoredFPC: true`) → already genesis-funded; its own cross-chain bridging harness is the behavior under test, kept. - **`bench/client_flows_benchmark`** bridges to its BananaFPC → out of scope (benchmark harness, not in the fees e2e timing family), kept. - Production/sandbox funding paths (cli, `aztec sandbox`) → untouched. The new hook defaults to unset; only the e2e fixtures opt in. ## Expected effect ~48s × the number of fees processes that ran the FPC bridge in setup (account_init, private_payments, failures, gas_estimation). No C++, no new config, no change to production genesis roots. ## Local verification - `single-node/fees/account_init -t 'pays privately through an FPC'` passes; the span leaderboard for the run shows **zero `setup:bridge`** occurrences (was ~48s), while `deploy:fpc`/`deploy:token` remain. The FPC-paying test asserts the FPC's gas balance decreases by the fee, so the genesis funding is exercised end to end, and the deployed-address assertion held. - `single-node/fees/account_init -t 'after Alice bridges funds'` passes with `setup:bridge` present in the test body, confirming the retained bridge/claim harness still works. ## Measured impact The `setup:bridge` span is eliminated across the run: 17 occurrences totalling 646.4s of bridge setup time in the baseline drop to zero in the PR run. The 13 fees-hook bridges (~48s each) are the bulk of it. - Fees beforeHooks per shard: `private_payments.parallel` −51.1s (×8 shards), `gas_estimation.parallel` −48.1s (×3), `failures` −47.8s, `account_init` −47.8s — each matching the ~48s (4-slot) bridge cost. - `setup:bridge` busyMs removed per suite: account_init 48.1s, failures 48.2s, gas_estimation ×3 = 144.6s, private_payments ×8 = 384.7s (13 fees-hook bridges ≈ 625.5s), plus the ~5s sponsored-FPC bridges on `amm` and `storage_proof`. - Total beforeHooks reduction over the suites present in both runs: −907.4s summed across shards (wall-clock benefit is smaller, since the fees shards run in parallel). - One suite moved the wrong way within noise: `fee_juice_payments` +11.0s. It keeps its real bridging/claim txs (no `setup:bridge` span removed), so this is run-to-run variance, not a regression from this change. Baseline CI run 1783374468714213 (merge-base e1711d3, full). PR CI run 1783375878950264 (x-fast — the comparison is per-suite over the suites present in both runs). Fixes A-1405 (cherry picked from commit 1321e98)
1 parent 3b0823e commit 4be0457

2 files changed

Lines changed: 69 additions & 6 deletions

File tree

yarn-project/end-to-end/src/fixtures/setup.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,14 @@ export type SetupOptions<TDeployExtraL1ContractsReturnType = unknown> = {
220220
zkPassportArgs?: ZKPassportArgs;
221221
/** Whether to fund the sponsored FPC in genesis (defaults to false). */
222222
fundSponsoredFPC?: boolean;
223+
/**
224+
* Compute extra addresses to fund at genesis from the accounts setup just generated (passed as the
225+
* argument). Runs after the default accounts are created and before genesis values are computed, so a
226+
* test can genesis-fund a contract whose address derives from a default account (e.g. an FPC whose
227+
* admin is the first account) instead of bridging fee juice to it during setup. Each returned address
228+
* is funded with the same fee juice as an initial account and included in the L1 portal `fundingNeeded`.
229+
*/
230+
computeExtraGenesisFundedAddresses?: (defaultAccounts: InitialAccountData[]) => Promise<AztecAddress[]>;
223231
/** L1 contracts deployment arguments. */
224232
l1ContractsArgs?: Partial<DeployAztecL1ContractsArgs>;
225233
/** Wallet minimum fee padding multiplier */
@@ -458,6 +466,12 @@ async function setupInner<TDeployExtraL1ContractsReturnType = unknown>(
458466
const sponsoredFPCAddress = await getSponsoredFPCAddress();
459467
addressesToFund.push(sponsoredFPCAddress);
460468
}
469+
470+
// Fund any extra addresses whose value depends on the just-generated accounts (e.g. an FPC admin'd
471+
// by a default account), so a test can genesis-fund them instead of bridging fee juice during setup.
472+
if (opts.computeExtraGenesisFundedAddresses) {
473+
addressesToFund.push(...(await opts.computeExtraGenesisFundedAddresses(defaultAccounts)));
474+
}
461475
logger.trace('Generated test accounts to fund at genesis');
462476

463477
const genesisTimestamp = BigInt(Math.floor(Date.now() / 1000));

yarn-project/end-to-end/src/single-node/fees/fees_test.ts

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { AztecAddress } from '@aztec/aztec.js/addresses';
2+
import { Fr } from '@aztec/aztec.js/fields';
23
import { createLogger } from '@aztec/aztec.js/log';
34
import type { AztecNode } from '@aztec/aztec.js/node';
45
import { CheatCodes, getTokenAllowedSetupFunctions } from '@aztec/aztec/testing';
@@ -17,6 +18,7 @@ import { TokenContract as BananaCoin } from '@aztec/noir-contracts.js/Token';
1718
import { CounterContract } from '@aztec/noir-test-contracts.js/Counter';
1819
import { ProtocolContractAddress } from '@aztec/protocol-contracts';
1920
import { getCanonicalFeeJuice } from '@aztec/protocol-contracts/fee-juice';
21+
import { getContractInstanceFromInstantiationParams } from '@aztec/stdlib/contract';
2022
import { Gas, GasSettings } from '@aztec/stdlib/gas';
2123
import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
2224

@@ -34,13 +36,41 @@ import {
3436
import { TestWallet } from '../../test-wallet/test_wallet.js';
3537
import { SingleNodeTestContext, type SingleNodeTestOpts } from '../single_node_test_context.js';
3638

39+
// Fixed deploy salts so BananaCoin and its BananaFPC land at deterministic addresses given the FPC
40+
// admin. This lets the BananaFPC's fee-juice balance be seeded at genesis (see `FeesTest.setup`)
41+
// instead of bridged from L1 during setup — the address must be known before genesis is computed.
42+
const BANANA_COIN_SALT = new Fr(0xba4a4a);
43+
const BANANA_FPC_SALT = new Fr(0xfacade);
44+
45+
const BANANA_COIN_CONSTRUCTOR_ARGS = ['BC', 'BC', 18n] as const;
46+
47+
/**
48+
* Computes the deterministic BananaCoin and BananaFPC instances for the given admin/deployer, matching
49+
* what {@link FeesTest.applyDeployBananaToken} and {@link FeesTest.applyFPCSetup} deploy with the fixed
50+
* salts above. Used both to seed the BananaFPC's fee juice at genesis and to assert the deployed
51+
* addresses match the seeded one.
52+
*/
53+
async function computeBananaContractAddresses(admin: AztecAddress) {
54+
const bananaCoin = await getContractInstanceFromInstantiationParams(BananaCoin.artifact, {
55+
salt: BANANA_COIN_SALT,
56+
constructorArgs: [admin, ...BANANA_COIN_CONSTRUCTOR_ARGS],
57+
deployer: admin,
58+
});
59+
const bananaFPC = await getContractInstanceFromInstantiationParams(FPCContract.artifact, {
60+
salt: BANANA_FPC_SALT,
61+
constructorArgs: [bananaCoin.address, admin],
62+
deployer: admin,
63+
});
64+
return { bananaCoinAddress: bananaCoin.address, bananaFPCAddress: bananaFPC.address };
65+
}
66+
3767
/**
3868
* Test fixture for testing fees. Provides the following setup steps:
3969
* InitialAccounts: Initializes 3 Schnorr account contracts.
4070
* PublicDeployAccounts: Deploys the accounts publicly.
4171
* DeployFeeJuice: Deploys the Fee Juice contract.
42-
* FPCSetup: Deploys BananaCoin and FPC contracts, and bridges gas from L1.
43-
* SponsoredFPCSetup: Deploys Sponsored FPC contract, and bridges gas from L1.
72+
* FPCSetup: Deploys BananaCoin and FPC contracts; the FPC's fee juice is seeded at genesis.
73+
* SponsoredFPCSetup: Registers the Sponsored FPC contract, whose fee juice is seeded at genesis.
4474
* FundAlice: Mints private and public bananas to Alice.
4575
* SetupSubscription: Deploys a counter contract and a subscription contract, and mints Fee Juice to the subscription contract.
4676
*
@@ -116,6 +146,11 @@ export class FeesTest extends SingleNodeTestContext {
116146
...this.setupOptions,
117147
...opts,
118148
fundSponsoredFPC: true,
149+
// Seed the BananaFPC's fee juice at genesis instead of bridging it from L1 in applyFPCSetup. The
150+
// FPC admin is the first account, so its address is deterministic once the accounts are generated.
151+
computeExtraGenesisFundedAddresses: async defaultAccounts => [
152+
(await computeBananaContractAddresses(defaultAccounts[0].address)).bananaFPCAddress,
153+
],
119154
l1ContractsArgs: { ...this.setupOptions },
120155
txPublicSetupAllowListExtend: [...(this.setupOptions.txPublicSetupAllowListExtend ?? []), ...tokenAllowList],
121156
});
@@ -247,7 +282,10 @@ export class FeesTest extends SingleNodeTestContext {
247282
this.logger.info('Applying deploy banana token setup');
248283

249284
const { contract: bananaCoin } = await testSpan('deploy:token', () =>
250-
BananaCoin.deploy(this.wallet, this.aliceAddress, 'BC', 'BC', 18n).send({
285+
BananaCoin.deploy(this.wallet, this.aliceAddress, ...BANANA_COIN_CONSTRUCTOR_ARGS, {
286+
salt: BANANA_COIN_SALT,
287+
deployer: this.aliceAddress,
288+
}).send({
251289
from: this.aliceAddress,
252290
}),
253291
);
@@ -270,15 +308,26 @@ export class FeesTest extends SingleNodeTestContext {
270308

271309
const bananaCoin = this.bananaCoin;
272310
const { contract: bananaFPC } = await testSpan('deploy:fpc', () =>
273-
FPCContract.deploy(this.wallet, bananaCoin.address, this.fpcAdmin).send({
311+
FPCContract.deploy(this.wallet, bananaCoin.address, this.fpcAdmin, {
312+
salt: BANANA_FPC_SALT,
313+
deployer: this.aliceAddress,
314+
}).send({
274315
from: this.aliceAddress,
275316
}),
276317
);
277318

278319
this.logger.info(`BananaPay deployed at ${bananaFPC.address}`);
279320

280-
// bridgeFromL1ToL2 carries its own setup:bridge span.
281-
await this.feeJuiceBridgeTestHarness.bridgeFromL1ToL2(bananaFPC.address, this.aliceAddress);
321+
// The BananaFPC's fee juice is seeded at genesis (see FeesTest.setup) rather than bridged here.
322+
// Assert the deploy landed at the seeded address so a params drift surfaces as a clear error rather
323+
// than a downstream "insufficient fee payer balance".
324+
const { bananaFPCAddress } = await computeBananaContractAddresses(this.aliceAddress);
325+
if (!bananaFPC.address.equals(bananaFPCAddress)) {
326+
throw new Error(
327+
`Deployed BananaFPC address ${bananaFPC.address} does not match the genesis-funded address ` +
328+
`${bananaFPCAddress}; the deterministic deploy params drifted from the genesis funding computation.`,
329+
);
330+
}
282331

283332
this.bananaFPC = bananaFPC;
284333

0 commit comments

Comments
 (0)