Skip to content

Commit 5030c97

Browse files
committed
chore: scaffold verify gas usage [TEMP]
1 parent 6c68ce1 commit 5030c97

File tree

6 files changed

+87
-4
lines changed

6 files changed

+87
-4
lines changed

src/vms/pvm/etna-builder/spend-reducers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ export { handleFeeAndChange } from './handleFeeAndChange';
22
export { useSpendableLockedUTXOs } from './useSpendableLockedUTXOs';
33
export { useUnlockedUTXOs } from './useUnlockedUTXOs';
44
export { verifyAssetsConsumed } from './verifyAssetsConsumed';
5+
export { verifyGasUsage } from './verifyGasUsage';
56

67
export type * from './types';
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, expect, test, vi } from 'vitest';
2+
import { testContext } from '../../../../fixtures/context';
3+
import { getInitialReducerState, getSpendHelper } from './fixtures/reducers';
4+
import { verifyGasUsage } from './verifyGasUsage';
5+
6+
describe('verifyGasUsage', () => {
7+
test('returns original state if gas is under the threshold', () => {
8+
const initialState = getInitialReducerState();
9+
const spendHelper = getSpendHelper();
10+
const spy = vi.spyOn(spendHelper, 'verifyAssetsConsumed');
11+
12+
const state = verifyAssetsConsumed(initialState, spendHelper, testContext);
13+
14+
expect(state).toBe(initialState);
15+
expect(spy).toHaveBeenCalledTimes(1);
16+
});
17+
18+
test('throws an error if gas is over the threshold', () => {
19+
const initialState = getInitialReducerState();
20+
const spendHelper = getSpendHelper();
21+
22+
// Mock the verifyAssetsConsumed method to throw an error
23+
// Testing for this function can be found in the spendHelper.test.ts file
24+
spendHelper.verifyAssetsConsumed = vi.fn(() => {
25+
throw new Error('Test error');
26+
});
27+
28+
expect(() =>
29+
verifyAssetsConsumed(initialState, spendHelper, testContext),
30+
).toThrow('Test error');
31+
});
32+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { SpendReducerFunction } from './types';
2+
3+
/**
4+
* Verify that gas usage is within limits.
5+
*
6+
* Calls the spendHelper's verifyGasUsage method.
7+
*/
8+
export const verifyGasUsage: SpendReducerFunction = (
9+
state,
10+
spendHelper,
11+
) => {
12+
const verifyError = spendHelper.verifyGasUsage();
13+
14+
if (verifyError) {
15+
throw verifyError;
16+
}
17+
18+
return state;
19+
};

src/vms/pvm/etna-builder/spend.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Address, OutputOwners } from '../../../serializable';
44
import { createDimensions } from '../../common/fees/dimensions';
55
import {
66
verifyAssetsConsumed,
7+
verifyGasUsage,
78
type SpendReducerFunction,
89
type SpendReducerState,
910
handleFeeAndChange,
@@ -14,6 +15,7 @@ import { feeState as testFeeState } from '../../../fixtures/pvm';
1415
import { bech32ToBytes } from '../../../utils';
1516

1617
vi.mock('./spend-reducers', () => ({
18+
verifyGasUsage: vi.fn<SpendReducerFunction>((state) => state),
1719
verifyAssetsConsumed: vi.fn<SpendReducerFunction>((state) => state),
1820
handleFeeAndChange: vi.fn<SpendReducerFunction>((state) => state),
1921
}));
@@ -51,6 +53,7 @@ describe('./src/vms/pvm/etna-builder/spend.test.ts', () => {
5153

5254
expect(testReducer).toHaveBeenCalledTimes(1);
5355
expect(verifyAssetsConsumed).toHaveBeenCalledTimes(1);
56+
expect(verifyGasUsage).toHaveBeenCalledTimes(1);
5457
expect(handleFeeAndChange).toHaveBeenCalledTimes(1);
5558
});
5659

src/vms/pvm/etna-builder/spend.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { Dimensions } from '../../common/fees/dimensions';
99
import type { Context } from '../../context';
1010
import type { FeeState } from '../models';
1111
import type { SpendReducerFunction, SpendReducerState } from './spend-reducers';
12-
import { handleFeeAndChange, verifyAssetsConsumed } from './spend-reducers';
12+
import { handleFeeAndChange, verifyAssetsConsumed, verifyGasUsage } from './spend-reducers';
1313
import { SpendHelper } from './spendHelper';
1414

1515
type SpendResult = Readonly<{
@@ -145,6 +145,7 @@ export const spend = (
145145
...spendReducers,
146146
verifyAssetsConsumed,
147147
handleFeeAndChange,
148+
verifyGasUsage // This should happen after change is added
148149
// Consolidation and sorting happens in the SpendHelper.
149150
];
150151

src/vms/pvm/etna-builder/spendHelper.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,13 @@ export class SpendHelper {
218218
}
219219

220220
/**
221-
* Calculates the fee for the SpendHelper based on its complexity and gas price.
221+
* Calculates the gas usage for the SpendHelper based on its complexity and the weights.
222222
* Provide an empty change output as a parameter to calculate the fee as if the change output was already added.
223223
*
224224
* @param {TransferableOutput} additionalOutput - The change output that has not yet been added to the SpendHelper.
225-
* @returns {bigint} The fee for the SpendHelper.
225+
* @returns {bigint} The gas usage for the SpendHelper.
226226
*/
227-
calculateFee(additionalOutput?: TransferableOutput): bigint {
227+
private calculateGas(additionalOutput?: TransferableOutput): bigint {
228228
this.consolidateOutputs();
229229

230230
const gas = dimensionsToGas(
@@ -234,6 +234,19 @@ export class SpendHelper {
234234
this.weights,
235235
);
236236

237+
return gas;
238+
}
239+
240+
/**
241+
* Calculates the fee for the SpendHelper based on its complexity and gas price.
242+
* Provide an empty change output as a parameter to calculate the fee as if the change output was already added.
243+
*
244+
* @param {TransferableOutput} additionalOutput - The change output that has not yet been added to the SpendHelper.
245+
* @returns {bigint} The fee for the SpendHelper.
246+
*/
247+
calculateFee(additionalOutput?: TransferableOutput): bigint {
248+
const gas = this.calculateGas(additionalOutput);
249+
237250
const gasPrice = this.feeState.price;
238251

239252
return gas * gasPrice;
@@ -283,6 +296,20 @@ export class SpendHelper {
283296

284297
return null;
285298
}
299+
300+
/**
301+
* Verifies that gas usage does not exceed the fee state maximum.
302+
*
303+
* @returns {Error | null} An error if gas usage exceeds maximum, null otherwise.
304+
*/
305+
verifyGasUsage(): Error | null {
306+
const gas = this.calculateGas();
307+
if (this.feeState.capacity <= gas) {
308+
return new Error(`Gas usage of transaction (${gas.toString()}) exceeds capacity (${this.feeState.capacity.toString()})`)
309+
}
310+
311+
return null;
312+
}
286313

287314
/**
288315
* Gets the inputs, outputs, and UTXOs for the SpendHelper.

0 commit comments

Comments
 (0)