Skip to content

Commit 02ed704

Browse files
author
AztecBot
committed
Merge branch 'next' into merge-train/barretenberg
2 parents 484b133 + 73f98c7 commit 02ed704

1 file changed

Lines changed: 77 additions & 9 deletions

File tree

yarn-project/end-to-end/src/e2e_fees/fee_settings.test.ts

Lines changed: 77 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import type { AztecAddress } from '@aztec/aztec.js/addresses';
22
import type { AztecNode } from '@aztec/aztec.js/node';
33
import { CheatCodes } from '@aztec/aztec/testing';
4+
import type { BlockNumber } from '@aztec/foundation/branded-types';
45
import { Fr } from '@aztec/foundation/curves/bn254';
56
import { retryUntil } from '@aztec/foundation/retry';
67
import { TestContract } from '@aztec/noir-test-contracts.js/Test';
7-
import type { GasSettings } from '@aztec/stdlib/gas';
8+
import type { GasFees, GasSettings } from '@aztec/stdlib/gas';
89
import { TX_ERROR_INSUFFICIENT_FEE_PER_GAS } from '@aztec/stdlib/tx';
910

11+
import { jest } from '@jest/globals';
1012
import { inspect } from 'util';
1113

1214
import type { TestWallet } from '../test-wallet/test_wallet.js';
@@ -20,13 +22,18 @@ describe('e2e_fees fee settings', () => {
2022
let wallet: TestWallet;
2123
let gasSettings: Partial<GasSettings>;
2224
let testContract: TestContract;
25+
let testContractDeployBlock: BlockNumber;
2326
const t = new FeesTest('fee_juice', 1);
2427

2528
beforeAll(async () => {
2629
await t.setup();
2730
({ aliceAddress, wallet, gasSettings, cheatCodes, aztecNode } = t);
2831

29-
({ contract: testContract } = await TestContract.deploy(wallet).send({ from: aliceAddress }));
32+
const deployedTestContract = await TestContract.deploy(wallet).send({
33+
from: aliceAddress,
34+
});
35+
testContract = deployedTestContract.contract;
36+
testContractDeployBlock = deployedTestContract.receipt.blockNumber!;
3037
gasSettings = { ...gasSettings, maxFeesPerGas: undefined };
3138
});
3239

@@ -39,21 +46,39 @@ describe('e2e_fees fee settings', () => {
3946
const before = await aztecNode.getCurrentMinFees();
4047
t.logger.info(`Initial L2 min fees are ${inspect(before)}`, { minFees: before.toInspect() });
4148
await cheatCodes.rollup.bumpProvingCostPerMana(current => (current * 120n) / 100n);
42-
await retryUntil(
49+
return await retryUntil(
4350
async () => {
4451
const after = await aztecNode.getCurrentMinFees();
4552
t.logger.info(`L2 min fees are now ${inspect(after)}`, {
4653
minFeesBefore: before.toInspect(),
4754
minFeesAfter: after.toInspect(),
4855
});
49-
return after.feePerL2Gas > before.feePerL2Gas;
56+
return after.feePerL2Gas > before.feePerL2Gas ? after : undefined;
5057
},
5158
'L2 min fee increase',
5259
5,
5360
1,
5461
);
5562
};
5663

64+
// Pick a baseline from the post-checkpoint chain state. The prove step itself is
65+
// made deterministic by prepareTxsWithMockedMinFees below.
66+
const getCurrentMinFeesAfterCheckpoint = async (checkpointedBlock: BlockNumber) => {
67+
return await retryUntil(
68+
async () => {
69+
const currentCheckpointedBlock = await aztecNode.getCheckpointedBlockNumber();
70+
if (currentCheckpointedBlock < checkpointedBlock) {
71+
return undefined;
72+
}
73+
74+
return await aztecNode.getCurrentMinFees();
75+
},
76+
`L2 min fees after block ${checkpointedBlock} is checkpointed`,
77+
60,
78+
1,
79+
);
80+
};
81+
5782
const proveTx = async (minFeePadding: number | undefined) => {
5883
t.logger.info(`Preparing tx to be sent with min fee padding ${minFeePadding}`);
5984
wallet.setMinFeePadding(minFeePadding);
@@ -66,18 +91,61 @@ describe('e2e_fees fee settings', () => {
6691
return tx;
6792
};
6893

94+
const prepareTxsWithMockedMinFees = async (noPaddingMinFees: GasFees, defaultPaddingMinFees: GasFees) => {
95+
const getCurrentMinFeesSpy = jest
96+
.spyOn(aztecNode, 'getCurrentMinFees')
97+
.mockResolvedValueOnce(noPaddingMinFees)
98+
.mockResolvedValueOnce(defaultPaddingMinFees);
99+
100+
try {
101+
const txWithNoPadding = await proveTx(0);
102+
const txWithDefaultPadding = await proveTx(undefined);
103+
return { txWithNoPadding, txWithDefaultPadding };
104+
} finally {
105+
getCurrentMinFeesSpy.mockRestore();
106+
}
107+
};
108+
69109
it('handles min fee spikes with default padding', async () => {
70-
// Prepare two txs using the current L2 min fees: one with no padding and one with default padding
71-
const txWithNoPadding = await proveTx(0);
72-
const txWithDefaultPadding = await proveTx(undefined);
110+
const stableMinFees = await getCurrentMinFeesAfterCheckpoint(testContractDeployBlock);
111+
const { txWithNoPadding, txWithDefaultPadding } = await prepareTxsWithMockedMinFees(stableMinFees, stableMinFees);
112+
113+
expect(txWithNoPadding.data.constants.txContext.gasSettings.maxFeesPerGas.equals(stableMinFees)).toBe(true);
114+
expect(
115+
txWithDefaultPadding.data.constants.txContext.gasSettings.maxFeesPerGas.equals(stableMinFees.mul(1.5)),
116+
).toBe(true);
73117

74118
// Now bump the L2 fees before we actually send them
75-
await bumpL2Fees();
119+
const bumpedMinFees = await bumpL2Fees();
120+
expect(stableMinFees.feePerL2Gas).toBeLessThan(bumpedMinFees.feePerL2Gas);
121+
expect(stableMinFees.mul(1.5).feePerL2Gas).toBeGreaterThan(bumpedMinFees.feePerL2Gas);
76122

77123
// And check that the no-padding does not get mined, but the default padding is good enough
78-
t.logger.info(`Sendings txs`);
124+
t.logger.info(`Sending txs`);
79125
await expect(txWithNoPadding.send()).rejects.toThrow(TX_ERROR_INSUFFICIENT_FEE_PER_GAS);
80126
await expect(txWithDefaultPadding.send()).resolves.toBeDefined();
81127
});
128+
129+
it('reproduces the stale fee snapshot race deterministically', async () => {
130+
const lowerMinFees = await getCurrentMinFeesAfterCheckpoint(testContractDeployBlock);
131+
const higherMinFees = lowerMinFees.mul(2);
132+
133+
const { txWithNoPadding, txWithDefaultPadding } = await prepareTxsWithMockedMinFees(higherMinFees, lowerMinFees);
134+
135+
expect(txWithNoPadding.data.constants.txContext.gasSettings.maxFeesPerGas.equals(higherMinFees)).toBe(true);
136+
expect(
137+
txWithDefaultPadding.data.constants.txContext.gasSettings.maxFeesPerGas.equals(lowerMinFees.mul(1.5)),
138+
).toBe(true);
139+
140+
const bumpedMinFees = await bumpL2Fees();
141+
expect(lowerMinFees.feePerL2Gas).toBeLessThan(bumpedMinFees.feePerL2Gas);
142+
expect(higherMinFees.feePerL2Gas).toBeGreaterThan(bumpedMinFees.feePerL2Gas);
143+
expect(lowerMinFees.mul(1.5).feePerL2Gas).toBeGreaterThan(bumpedMinFees.feePerL2Gas);
144+
145+
// This is the original flake: the "no padding" tx only succeeds because it was
146+
// accidentally prepared against an earlier, higher fee snapshot than the padded tx.
147+
await expect(txWithNoPadding.send()).resolves.toBeDefined();
148+
await expect(txWithDefaultPadding.send()).resolves.toBeDefined();
149+
});
82150
});
83151
});

0 commit comments

Comments
 (0)