Skip to content

Commit 925f42f

Browse files
committed
refactor(test-suite): simplify HCU block cap test structure
- Scope save/restore of HCU limits to only the 2 tests that lower them (nested describe with its own beforeEach/afterEach) - Extract mintAndDistribute helper for repeated mint+transfer preamble - Remove blanket whitelist cleanup from afterEach (test cleans up itself) - Parallelize 3 sequential view calls with Promise.all
1 parent 16776c2 commit 925f42f

File tree

1 file changed

+89
-97
lines changed

1 file changed

+89
-97
lines changed

test-suite/e2e/test/encryptedERC20/EncryptedERC20.HCU.ts

Lines changed: 89 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,6 @@ describe('EncryptedERC20:HCU', function () {
105105
});
106106

107107
describe('block cap scenarios', function () {
108-
const TIGHT_DEPTH_PER_TX = 400_000;
109-
const TIGHT_MAX_PER_TX = 600_000;
110-
const TIGHT_PER_BLOCK = 600_000;
111-
112-
let savedHCUPerBlock: bigint;
113-
let savedMaxHCUPerTx: bigint;
114-
let savedMaxHCUDepthPerTx: bigint;
115-
116108
// eslint-disable-next-line @typescript-eslint/no-explicit-any
117109
async function sendEncryptedTransfer(ctx: any, sender: string, recipient: string, amount: number, overrides?: any) {
118110
const erc20 = ctx.erc20.connect(ctx.signers[sender]);
@@ -122,13 +114,12 @@ describe('EncryptedERC20:HCU', function () {
122114
return erc20['transfer(address,bytes32,bytes)'](recipient, enc.handles[0], enc.inputProof, overrides ?? {});
123115
}
124116

125-
// Narrowest-first when lowering to satisfy: hcuPerBlock >= maxHCUPerTx >= maxHCUDepthPerTx
126117
// eslint-disable-next-line @typescript-eslint/no-explicit-any
127-
async function lowerHCULimits(ctx: any) {
128-
const ownerHcuLimit = ctx.hcuLimit.connect(ctx.deployer);
129-
await ownerHcuLimit.setMaxHCUDepthPerTx(TIGHT_DEPTH_PER_TX);
130-
await ownerHcuLimit.setMaxHCUPerTx(TIGHT_MAX_PER_TX);
131-
await ownerHcuLimit.setHCUPerBlock(TIGHT_PER_BLOCK);
118+
async function mintAndDistribute(ctx: any) {
119+
const mintTx = await ctx.erc20.mint(10000);
120+
await mintTx.wait();
121+
const setupTx = await sendEncryptedTransfer(ctx, 'alice', ctx.signers.bob.address, 5000);
122+
await setupTx.wait();
132123
}
133124

134125
before(async function () {
@@ -145,31 +136,12 @@ describe('EncryptedERC20:HCU', function () {
145136
this.deployer = new ethers.Wallet(deployerKey, ethers.provider);
146137
});
147138

148-
beforeEach(async function () {
149-
savedHCUPerBlock = await this.hcuLimit.getGlobalHCUCapPerBlock();
150-
savedMaxHCUPerTx = await this.hcuLimit.getMaxHCUPerTx();
151-
savedMaxHCUDepthPerTx = await this.hcuLimit.getMaxHCUDepthPerTx();
152-
});
153-
154139
afterEach(async function () {
155140
await ethers.provider.send('evm_setAutomine', [true]);
156-
// Widest-first when restoring to satisfy: hcuPerBlock >= maxHCUPerTx >= maxHCUDepthPerTx
157-
const ownerHcuLimit = this.hcuLimit.connect(this.deployer);
158-
await ownerHcuLimit.setHCUPerBlock(savedHCUPerBlock);
159-
await ownerHcuLimit.setMaxHCUPerTx(savedMaxHCUPerTx);
160-
await ownerHcuLimit.setMaxHCUDepthPerTx(savedMaxHCUDepthPerTx);
161-
162-
if (await this.hcuLimit.isBlockHCUWhitelisted(this.contractAddress)) {
163-
await ownerHcuLimit.removeFromBlockHCUWhitelist(this.contractAddress);
164-
}
165141
});
166142

167143
it('should accumulate HCU from multiple users in the same block', async function () {
168-
const mintTx = await this.erc20.mint(10000);
169-
await mintTx.wait();
170-
171-
const setupTx = await sendEncryptedTransfer(this, 'alice', this.signers.bob.address, 5000);
172-
await setupTx.wait();
144+
await mintAndDistribute(this);
173145

174146
await mineNBlocks(1);
175147
await ethers.provider.send('evm_setAutomine', [false]);
@@ -204,69 +176,89 @@ describe('EncryptedERC20:HCU', function () {
204176
expect(usedHCU).to.be.greaterThan(BigInt(hcuBob), 'Block meter should exceed bob HCU alone');
205177
});
206178

207-
it('should revert when block HCU cap is exhausted', async function () {
208-
await lowerHCULimits(this);
209-
210-
const mintTx = await this.erc20.mint(10000);
211-
await mintTx.wait();
212-
213-
const setupTx = await sendEncryptedTransfer(this, 'alice', this.signers.bob.address, 5000);
214-
await setupTx.wait();
215-
216-
await mineNBlocks(1);
217-
await ethers.provider.send('evm_setAutomine', [false]);
218-
219-
// Alice fills the cap, Bob would push block total over — use fixed gasLimit
220-
// to bypass estimateGas (which reverts against pending state)
221-
const tx1 = await sendEncryptedTransfer(this, 'alice', this.signers.carol.address, 100);
222-
const tx2 = await sendEncryptedTransfer(this, 'bob', this.signers.carol.address, 100, { gasLimit: 1_000_000 });
223-
224-
await ethers.provider.send('evm_mine');
225-
await ethers.provider.send('evm_setAutomine', [true]);
226-
227-
const receipt1 = await tx1.wait();
228-
expect(receipt1?.status).to.eq(1, 'First transfer should succeed');
229-
230-
// Use getTransactionReceipt to avoid ethers throwing on reverted tx
231-
const receipt2 = await ethers.provider.getTransactionReceipt(tx2.hash);
232-
expect(receipt2?.status).to.eq(0, 'Second transfer should revert (block cap exceeded)');
233-
expect(receipt1?.blockNumber).to.eq(receipt2?.blockNumber);
234-
});
235-
236-
it('should allow previously blocked caller to succeed after block rollover', async function () {
237-
await lowerHCULimits(this);
238-
239-
const mintTx = await this.erc20.mint(10000);
240-
await mintTx.wait();
241-
242-
const setupTx = await sendEncryptedTransfer(this, 'alice', this.signers.bob.address, 5000);
243-
await setupTx.wait();
244-
245-
// Block N: alice fills the cap, bob gets blocked
246-
await mineNBlocks(1);
247-
await ethers.provider.send('evm_setAutomine', [false]);
248-
249-
const txAlice = await sendEncryptedTransfer(this, 'alice', this.signers.carol.address, 100);
250-
const txBob = await sendEncryptedTransfer(this, 'bob', this.signers.carol.address, 100, { gasLimit: 1_000_000 });
251-
252-
await ethers.provider.send('evm_mine');
253-
await ethers.provider.send('evm_setAutomine', [true]);
254-
255-
const receiptAlice = await txAlice.wait();
256-
expect(receiptAlice?.status).to.eq(1, 'Alice should succeed');
257-
258-
const receiptBob = await ethers.provider.getTransactionReceipt(txBob.hash);
259-
expect(receiptBob?.status).to.eq(0, 'Bob should be blocked in block N');
260-
261-
// Block N+1: meter resets, bob retries and succeeds
262-
await mineNBlocks(1);
263-
264-
const [, usedHCUAfterReset] = await this.hcuLimit.getBlockMeter();
265-
expect(usedHCUAfterReset).to.eq(0n, 'Meter should reset after new block');
266-
267-
const retryBob = await sendEncryptedTransfer(this, 'bob', this.signers.carol.address, 100);
268-
const receiptRetry = await retryBob.wait();
269-
expect(receiptRetry?.status).to.eq(1, 'Bob should succeed after rollover');
179+
describe('with lowered limits', function () {
180+
const TIGHT_DEPTH_PER_TX = 400_000;
181+
const TIGHT_MAX_PER_TX = 600_000;
182+
const TIGHT_PER_BLOCK = 600_000;
183+
184+
let savedHCUPerBlock: bigint;
185+
let savedMaxHCUPerTx: bigint;
186+
let savedMaxHCUDepthPerTx: bigint;
187+
188+
beforeEach(async function () {
189+
[savedHCUPerBlock, savedMaxHCUPerTx, savedMaxHCUDepthPerTx] = await Promise.all([
190+
this.hcuLimit.getGlobalHCUCapPerBlock(),
191+
this.hcuLimit.getMaxHCUPerTx(),
192+
this.hcuLimit.getMaxHCUDepthPerTx(),
193+
]);
194+
195+
// Narrowest-first when lowering: hcuPerBlock >= maxHCUPerTx >= maxHCUDepthPerTx
196+
const ownerHcuLimit = this.hcuLimit.connect(this.deployer);
197+
await ownerHcuLimit.setMaxHCUDepthPerTx(TIGHT_DEPTH_PER_TX);
198+
await ownerHcuLimit.setMaxHCUPerTx(TIGHT_MAX_PER_TX);
199+
await ownerHcuLimit.setHCUPerBlock(TIGHT_PER_BLOCK);
200+
});
201+
202+
afterEach(async function () {
203+
// Widest-first when restoring: hcuPerBlock >= maxHCUPerTx >= maxHCUDepthPerTx
204+
const ownerHcuLimit = this.hcuLimit.connect(this.deployer);
205+
await ownerHcuLimit.setHCUPerBlock(savedHCUPerBlock);
206+
await ownerHcuLimit.setMaxHCUPerTx(savedMaxHCUPerTx);
207+
await ownerHcuLimit.setMaxHCUDepthPerTx(savedMaxHCUDepthPerTx);
208+
});
209+
210+
it('should revert when block HCU cap is exhausted', async function () {
211+
await mintAndDistribute(this);
212+
213+
await mineNBlocks(1);
214+
await ethers.provider.send('evm_setAutomine', [false]);
215+
216+
// Alice fills the cap, Bob would push block total over — use fixed gasLimit
217+
// to bypass estimateGas (which reverts against pending state)
218+
const tx1 = await sendEncryptedTransfer(this, 'alice', this.signers.carol.address, 100);
219+
const tx2 = await sendEncryptedTransfer(this, 'bob', this.signers.carol.address, 100, { gasLimit: 1_000_000 });
220+
221+
await ethers.provider.send('evm_mine');
222+
await ethers.provider.send('evm_setAutomine', [true]);
223+
224+
const receipt1 = await tx1.wait();
225+
expect(receipt1?.status).to.eq(1, 'First transfer should succeed');
226+
227+
// Use getTransactionReceipt to avoid ethers throwing on reverted tx
228+
const receipt2 = await ethers.provider.getTransactionReceipt(tx2.hash);
229+
expect(receipt2?.status).to.eq(0, 'Second transfer should revert (block cap exceeded)');
230+
expect(receipt1?.blockNumber).to.eq(receipt2?.blockNumber);
231+
});
232+
233+
it('should allow previously blocked caller to succeed after block rollover', async function () {
234+
await mintAndDistribute(this);
235+
236+
// Block N: alice fills the cap, bob gets blocked
237+
await mineNBlocks(1);
238+
await ethers.provider.send('evm_setAutomine', [false]);
239+
240+
const txAlice = await sendEncryptedTransfer(this, 'alice', this.signers.carol.address, 100);
241+
const txBob = await sendEncryptedTransfer(this, 'bob', this.signers.carol.address, 100, { gasLimit: 1_000_000 });
242+
243+
await ethers.provider.send('evm_mine');
244+
await ethers.provider.send('evm_setAutomine', [true]);
245+
246+
const receiptAlice = await txAlice.wait();
247+
expect(receiptAlice?.status).to.eq(1, 'Alice should succeed');
248+
249+
const receiptBob = await ethers.provider.getTransactionReceipt(txBob.hash);
250+
expect(receiptBob?.status).to.eq(0, 'Bob should be blocked in block N');
251+
252+
// Block N+1: meter resets, bob retries and succeeds
253+
await mineNBlocks(1);
254+
255+
const [, usedHCUAfterReset] = await this.hcuLimit.getBlockMeter();
256+
expect(usedHCUAfterReset).to.eq(0n, 'Meter should reset after new block');
257+
258+
const retryBob = await sendEncryptedTransfer(this, 'bob', this.signers.carol.address, 100);
259+
const receiptRetry = await retryBob.wait();
260+
expect(receiptRetry?.status).to.eq(1, 'Bob should succeed after rollover');
261+
});
270262
});
271263

272264
it('should count HCU after whitelist removal', async function () {

0 commit comments

Comments
 (0)