Skip to content

Commit

Permalink
Add tests and refactor getGasLimits out of SimulatedTx
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed May 13, 2024
1 parent b2cb4d1 commit 658f611
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { GasSettings } from '@aztec/circuits.js';

import { type Wallet } from '../account/wallet.js';
import { type ExecutionRequestInit, type FeeOptions } from '../entrypoint/entrypoint.js';
import { getGasLimits } from './get_gas_limits.js';
import { SentTx } from './sent_tx.js';

/**
Expand Down Expand Up @@ -71,7 +72,7 @@ export abstract class BaseContractInteraction {
*/
protected async estimateGas(txRequest: TxExecutionRequest) {
const simulationResult = await this.wallet.simulateTx(txRequest, true);
const { totalGas: gasLimits, teardownGas: teardownGasLimits } = simulationResult.getGasLimits();
const { totalGas: gasLimits, teardownGas: teardownGasLimits } = getGasLimits(simulationResult);
return GasSettings.default({ gasLimits, teardownGasLimits });
}

Expand All @@ -84,8 +85,7 @@ export abstract class BaseContractInteraction {
const fee = request.fee;
if (fee) {
const txRequest = await this.wallet.createTxExecutionRequest(request);
const simulationResult = await this.wallet.simulateTx(txRequest, true);
const { totalGas: gasLimits, teardownGas: teardownGasLimits } = simulationResult.getGasLimits();
const { gasLimits, teardownGasLimits } = await this.estimateGas(txRequest);
const gasSettings = GasSettings.default({ ...fee.gasSettings, gasLimits, teardownGasLimits });
return { ...fee, gasSettings };
}
Expand Down
41 changes: 41 additions & 0 deletions yarn-project/aztec.js/src/contract/get_gas_limits.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { PublicKernelType, type SimulatedTx, mockSimulatedTx } from '@aztec/circuit-types';
import { Gas } from '@aztec/circuits.js';

import { getGasLimits } from './get_gas_limits.js';

describe('getGasLimits', () => {
let simulatedTx: SimulatedTx;

beforeEach(() => {
simulatedTx = mockSimulatedTx();
simulatedTx.tx.data.publicInputs.end.gasUsed = Gas.from({ daGas: 100, l2Gas: 200 });
simulatedTx.publicOutput!.gasUsed = {
[PublicKernelType.SETUP]: Gas.from({ daGas: 10, l2Gas: 20 }),
[PublicKernelType.APP_LOGIC]: Gas.from({ daGas: 20, l2Gas: 40 }),
[PublicKernelType.TEARDOWN]: Gas.from({ daGas: 10, l2Gas: 20 }),
};
});

it('returns gas limits from private gas usage only', () => {
simulatedTx.publicOutput = undefined;
// Should be 110 and 220 but oh floating point
expect(getGasLimits(simulatedTx)).toEqual({
totalGas: Gas.from({ daGas: 111, l2Gas: 221 }),
teardownGas: Gas.empty(),
});
});

it('returns gas limits for private and public', () => {
expect(getGasLimits(simulatedTx)).toEqual({
totalGas: Gas.from({ daGas: 154, l2Gas: 308 }),
teardownGas: Gas.from({ daGas: 11, l2Gas: 22 }),
});
});

it('pads gas limits', () => {
expect(getGasLimits(simulatedTx, 1)).toEqual({
totalGas: Gas.from({ daGas: 280, l2Gas: 560 }),
teardownGas: Gas.from({ daGas: 20, l2Gas: 40 }),
});
});
});
24 changes: 24 additions & 0 deletions yarn-project/aztec.js/src/contract/get_gas_limits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { PublicKernelType, type SimulatedTx } from '@aztec/circuit-types';
import { Gas } from '@aztec/circuits.js';

/**
* Returns suggested total and teardown gas limits for a simulated tx.
* Note that public gas usage is only accounted for if the publicOutput is present.
* @param pad - Percentage to pad the suggested gas limits by, defaults to 10%.
*/
export function getGasLimits(simulatedTx: SimulatedTx, pad = 0.1) {
const privateGasUsed = simulatedTx.tx.data.publicInputs.end.gasUsed;
if (simulatedTx.publicOutput) {
const publicGasUsed = Object.values(simulatedTx.publicOutput.gasUsed)
.filter(Boolean)
.reduce((total, current) => total.add(current), Gas.empty());
const teardownGas = simulatedTx.publicOutput.gasUsed[PublicKernelType.TEARDOWN] ?? Gas.empty();

return {
totalGas: privateGasUsed.add(publicGasUsed).mul(1 + pad),
teardownGas: teardownGas.mul(1 + pad),
};
}

return { totalGas: privateGasUsed.mul(1 + pad), teardownGas: Gas.empty() };
}
37 changes: 0 additions & 37 deletions yarn-project/circuit-types/src/tx/simulated_tx.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { Gas } from '@aztec/circuits.js';

import { mockSimulatedTx } from '../mocks.js';
import { PublicKernelType } from './processed_tx.js';
import { SimulatedTx } from './simulated_tx.js';

describe('simulated_tx', () => {
Expand All @@ -22,38 +19,4 @@ describe('simulated_tx', () => {
expect(SimulatedTx.fromJSON(simulatedTx.toJSON())).toEqual(simulatedTx);
});
});

describe('getGasLimits', () => {
beforeEach(() => {
simulatedTx.tx.data.publicInputs.end.gasUsed = Gas.from({ daGas: 100, l2Gas: 200 });
simulatedTx.publicOutput!.gasUsed = {
[PublicKernelType.SETUP]: Gas.from({ daGas: 10, l2Gas: 20 }),
[PublicKernelType.APP_LOGIC]: Gas.from({ daGas: 20, l2Gas: 40 }),
[PublicKernelType.TEARDOWN]: Gas.from({ daGas: 10, l2Gas: 20 }),
};
});

it('returns gas limits from private gas usage only', () => {
simulatedTx.publicOutput = undefined;
// Should be 110 and 220 but oh floating point
expect(simulatedTx.getGasLimits()).toEqual({
totalGas: Gas.from({ daGas: 111, l2Gas: 221 }),
teardownGas: Gas.empty(),
});
});

it('returns gas limits for private and public', () => {
expect(simulatedTx.getGasLimits()).toEqual({
totalGas: Gas.from({ daGas: 154, l2Gas: 308 }),
teardownGas: Gas.from({ daGas: 11, l2Gas: 22 }),
});
});

it('pads gas limits', () => {
expect(simulatedTx.getGasLimits(1)).toEqual({
totalGas: Gas.from({ daGas: 280, l2Gas: 560 }),
teardownGas: Gas.from({ daGas: 20, l2Gas: 40 }),
});
});
});
});
26 changes: 1 addition & 25 deletions yarn-project/circuit-types/src/tx/simulated_tx.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Fr, Gas } from '@aztec/circuits.js';
import { Fr } from '@aztec/circuits.js';

import { PublicKernelType } from './processed_tx.js';
import { type ProcessReturnValues, PublicSimulationOutput } from './public_simulation_output.js';
import { Tx } from './tx.js';

Expand All @@ -17,29 +16,6 @@ export class SimulatedTx {
public publicOutput?: PublicSimulationOutput,
) {}

/**
* Returns suggested total and teardown gas limits for the simulated tx.
* Note that public gas usage is only accounted for if the publicOutput is present.
* @param pad - Percentage to pad the suggested gas limits by, defaults to 10%.
*/
public getGasLimits(pad = 0.1) {
const privateGasUsed = this.tx.data.publicInputs.end.gasUsed;
if (this.publicOutput) {
const publicGasUsed = Object.values(this.publicOutput.gasUsed).reduce(
(total, current) => total.add(current),
Gas.empty(),
);
const teardownGas = this.publicOutput.gasUsed[PublicKernelType.TEARDOWN] ?? Gas.empty();

return {
totalGas: privateGasUsed.add(publicGasUsed).mul(1 + pad),
teardownGas: teardownGas.mul(1 + pad),
};
}

return { totalGas: privateGasUsed.mul(1 + pad), teardownGas: Gas.empty() };
}

/**
* Convert a SimulatedTx class object to a plain JSON object.
* @returns A plain object with SimulatedTx properties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('e2e_fees dapp_subscription', () => {

beforeAll(async () => {
await t.applyBaseSnapshots();
await t.applyFundAlice();
await t.applyFundAliceWithBananas();
await t.applySetupSubscription();

({
Expand Down
12 changes: 11 additions & 1 deletion yarn-project/end-to-end/src/e2e_fees/fees_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export class FeesTest {
);
}

public async applyFundAlice() {
public async applyFundAliceWithBananas() {
await this.snapshotManager.snapshot(
'fund_alice',
async () => {
Expand All @@ -214,6 +214,16 @@ export class FeesTest {
);
}

public async applyFundAliceWithGasToken() {
await this.snapshotManager.snapshot(
'fund_alice_with_gas_token',
async () => {
await this.gasTokenContract.methods.mint_public(this.aliceAddress, this.INITIAL_GAS_BALANCE).send().wait();
},
() => Promise.resolve(),
);
}

public async applySetupSubscription() {
await this.snapshotManager.snapshot(
'setup_subscription',
Expand Down
66 changes: 66 additions & 0 deletions yarn-project/end-to-end/src/e2e_fees/gas_estimation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
type AccountWallet,
type AztecAddress,
type FeePaymentMethod,
NativeFeePaymentMethod,
PublicFeePaymentMethod,
} from '@aztec/aztec.js';
import { GasFees, type GasSettings } from '@aztec/circuits.js';
import { type TokenContract as BananaCoin, type FPCContract } from '@aztec/noir-contracts.js';

import { FeesTest } from './fees_test.js';

describe('e2e_fees gas_estimation', () => {
let aliceWallet: AccountWallet;
let aliceAddress: AztecAddress;
let bobAddress: AztecAddress;
let bananaCoin: BananaCoin;
let bananaFPC: FPCContract;
let gasSettings: GasSettings;
let teardownFixedFee: bigint;

const t = new FeesTest('gas_estimation');

beforeAll(async () => {
await t.applyBaseSnapshots();
await t.applyFundAliceWithBananas();
await t.applyFundAliceWithGasToken();
({ aliceWallet, aliceAddress, bobAddress, bananaCoin, bananaFPC, gasSettings } = await t.setup());

teardownFixedFee = gasSettings.teardownGasLimits.computeFee(GasFees.default()).toBigInt();
});

afterAll(async () => {
await t.teardown();
});

// Sends two tx with transfers of public tokens: one with estimateGas on, one with estimateGas off
const sendTransfers = (paymentMethod: FeePaymentMethod) =>
Promise.all(
[true, false].map(estimateGas =>
bananaCoin.methods
.transfer_public(aliceAddress, bobAddress, 1n, 0n)
.send({ estimateGas, fee: { gasSettings, paymentMethod } })
.wait(),
),
);

it('estimates gas with native fee payment method', async () => {
const paymentMethod = await NativeFeePaymentMethod.create(aliceWallet);
const [withEstimate, withoutEstimate] = await sendTransfers(paymentMethod);

// Estimation should yield that teardown has no cost, so should send the tx with zero for teardown
expect(withEstimate.transactionFee! + teardownFixedFee).toEqual(withoutEstimate.transactionFee!);
});

it('estimates gas with public payment method', async () => {
const paymentMethod = new PublicFeePaymentMethod(bananaCoin.address, bananaFPC.address, aliceWallet);
const [withEstimate, withoutEstimate] = await sendTransfers(paymentMethod);

// Estimation should yield that teardown has reduced cost, but is not zero
// TODO(palla/gas): We set toBeGreaterThanOrEqual because gas in public functions is zero for now (we only meter on AVM).
// We should be able to change this to a strict equality once we meter gas in public functions or we replace brillig with the AVM.
expect(withEstimate.transactionFee!).toBeLessThan(withoutEstimate.transactionFee!);
expect(withEstimate.transactionFee! + teardownFixedFee).toBeGreaterThanOrEqual(withoutEstimate.transactionFee!);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('e2e_fees private_payment', () => {

beforeAll(async () => {
await t.applyBaseSnapshots();
await t.applyFundAlice();
await t.applyFundAliceWithBananas();
({ aliceWallet, aliceAddress, bobAddress, sequencerAddress, gasTokenContract, bananaCoin, bananaFPC, gasSettings } =
await t.setup());
});
Expand Down

0 comments on commit 658f611

Please sign in to comment.