Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion yarn-project/aztec/src/deploy/deploy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ describe('runDeployment', () => {
},
} satisfies ActionStep<Contracts>;

// Five more same-account actions: together with `mint` they exceed one batch, exercising
// multi-batch action chunking. They mint to `operator` (declared below), never to admin —
// defToken's deferred name reads the admin balance, and it must not depend on where these
// land relative to its resolution.
const extraMints = Object.fromEntries(
Array.from({ length: 5 }, (_, i) => [
`mintExtra${i}`,
{
kind: 'action',
from: r => r.account('admin'),
dependsOn: ['token'],
call: ctx => ctx.instance('token').methods.mint_to_public(operator, 1n),
done: async ctx => !(await ctx.ran('token')),
} satisfies ActionStep<Contracts>,
]),
);

// Deferred: its name reads runtime state (the token supply `mint` creates), so the address can
// only resolve once `mint` has run. On a re-run the state is already there, so the framework
// resolves it at inventory time and discovers it published — nothing to send.
Expand Down Expand Up @@ -145,7 +162,7 @@ describe('runDeployment', () => {
accounts: { admin: { secret, salt } },
// Admin is genesis-funded well above this threshold ⇒ "funded" ⇒ pays from balance (no bridge).
fees: { kind: 'fee-juice', threshold: 1n, fundAmount: 0n } as const,
steps: { ...contracts, mint, defToken, mintDef, fundOperator },
steps: { ...contracts, mint, ...extraMints, defToken, mintDef, fundOperator },
output: capture,
};

Expand Down
10 changes: 10 additions & 0 deletions yarn-project/aztec/src/deploy/fees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ export interface FeeSession {
* account's first tx: the claim must mine before its balance-paying txs fan out.
*/
hasPendingClaim(account: AztecAddress): boolean;
/**
* How many calls `account`'s next fee payment adds to its tx (0 when it pays from balance, 1 for
* an FPC or a pending claim) — the runner sizes action batches to leave room for them.
*/
feeCallCount(account: AztecAddress): Promise<number>;
}

/** What {@link prepareFeeSession} needs to fund a run's working accounts. */
Expand Down Expand Up @@ -327,5 +332,10 @@ export async function prepareFeeSession(opts: PrepareFeeSessionOpts): Promise<Fe
return { fee: {}, onConsumed: () => {} }; // later txs (or already-funded): pay from the account's balance
},
hasPendingClaim: account => sessions.get(account.toString())?.claim !== undefined,
feeCallCount: async account => {
const session = sessions.get(account.toString());
const method = session?.sponsored ? sponsoredFee?.paymentMethod : session?.claim;
return method ? (await method.getExecutionPayload()).calls.length : 0;
},
};
}
14 changes: 9 additions & 5 deletions yarn-project/aztec/src/deploy/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* AT EXECUTION TIME, once their `dependsOn` has run.
* - Steps execute in topological layers over the single graph, so an action can precede a contract
* it sets up. Within a layer, contract publishes are individual txs and same-account actions batch
* into ≤{@link APP_MAX_CALLS}-call BatchCalls. The one-time fee-juice claim per account is
* into BatchCalls sized to leave room for the fee payment's calls (the entrypoint's limit
* counts those too). The one-time fee-juice claim per account is
* consumed + mined by that account's first tx before the rest fan out.
* - Fund steps provision arbitrary addresses (contracts or accounts that never send) with bridged
* Fee Juice: bridge at execution time, then an L2 claim tx from the step's `from` account. Their
Expand Down Expand Up @@ -408,7 +409,7 @@ class DeploymentRun<C extends Steps> {
public async executeLayers(feeSession: FeeSession): Promise<void> {
for (const layer of this.layers) {
await this.runLayer(
[...this.publishUnits(layer), ...this.fundUnits(layer), ...this.actionUnits(layer)],
[...this.publishUnits(layer), ...this.fundUnits(layer), ...(await this.actionUnits(layer, feeSession))],
feeSession,
);
}
Expand Down Expand Up @@ -771,8 +772,11 @@ class DeploymentRun<C extends Steps> {
return units;
}

/** A layer's actions, batching independent same-account actions into ≤{@link APP_MAX_CALLS}-call BatchCalls. */
private actionUnits(layer: string[]): ExecutionUnit[] {
/**
* A layer's actions, batching independent same-account actions into BatchCalls sized to leave
* room for the account's fee payment calls — the entrypoint's call limit counts those too.
*/
private async actionUnits(layer: string[], feeSession: FeeSession): Promise<ExecutionUnit[]> {
const actionsByAccount = new Map<string, { account: AztecAddress; aliases: string[] }>();
for (const alias of layer.filter(a => this.actionSteps.has(a))) {
const account = getOrThrow(this.actionSteps, alias, 'action').from(this.resolver);
Expand All @@ -785,7 +789,7 @@ class DeploymentRun<C extends Steps> {
}
const units: ExecutionUnit[] = [];
for (const { account, aliases } of actionsByAccount.values()) {
for (const batch of chunk(aliases, APP_MAX_CALLS)) {
for (const batch of chunk(aliases, APP_MAX_CALLS - (await feeSession.feeCallCount(account)))) {
units.push({
label: batch.length === 1 ? `action ${batch[0]}` : `batch [${batch.join(', ')}]`,
kind: 'action',
Expand Down
Loading