Skip to content

Commit 5be659b

Browse files
authored
Merge pull request #915 from ava-labs/fix/validate-burn-for-new-txs
fix: burn amount validation for new tx types
2 parents d36033b + d54bb02 commit 5be659b

File tree

3 files changed

+131
-25
lines changed

3 files changed

+131
-25
lines changed

src/utils/getBurnedAmountByTx.test.ts

Lines changed: 99 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { newExportTxFromBaseFee, newImportTxFromBaseFee } from '../vms/evm';
77
import { getBurnedAmountByTx } from './getBurnedAmountByTx';
88
import { testContext } from '../fixtures/context';
99
import { testEthAddress1, testAddress1, testAddress2 } from '../fixtures/vms';
10-
import { nodeId } from '../fixtures/common';
10+
import { id, nodeId } from '../fixtures/common';
1111
import { Utxo } from '../serializable/avax/utxo';
1212
import { OutputOwners, TransferOutput } from '../serializable/fxs/secp256k1';
1313
import { Address, Id } from '../serializable/fxs/common';
@@ -30,6 +30,18 @@ import type { EVMTx } from '../serializable/evm/abstractTx';
3030
import { testUTXOID1, testUTXOID2 } from '../fixtures/transactions';
3131
import { costCorethTx } from './costs';
3232
import { StakeableLockOut } from '../serializable/pvm';
33+
import {
34+
newConvertSubnetToL1Tx,
35+
newIncreaseL1ValidatorBalanceTx,
36+
newRegisterL1ValidatorTx,
37+
} from '../vms/pvm/etna-builder';
38+
import { feeState, l1Validator } from '../fixtures/pvm';
39+
import {
40+
bigIntPr,
41+
blsSignatureBytes,
42+
bytesBytes,
43+
stringPr,
44+
} from '../fixtures/primitives';
3345

3446
const getUtxoMock = (
3547
utxoId: Id,
@@ -95,7 +107,7 @@ describe('getBurnedAmountByTx', () => {
95107
1n,
96108
);
97109

98-
const amounts = getBurnedAmountByTx(tx.getTx() as EVMTx);
110+
const amounts = getBurnedAmountByTx(tx.getTx() as EVMTx, testContext);
99111
expect(amounts.size).toEqual(1);
100112
expect(amounts.get(testContext.avaxAssetID)).toEqual(
101113
baseFee * costCorethTx(tx),
@@ -116,7 +128,7 @@ describe('getBurnedAmountByTx', () => {
116128
baseFee,
117129
);
118130

119-
const amounts = getBurnedAmountByTx(tx.getTx() as EVMTx);
131+
const amounts = getBurnedAmountByTx(tx.getTx() as EVMTx, testContext);
120132
expect(amounts.size).toEqual(1);
121133
expect(amounts.get(testContext.avaxAssetID)).toEqual(
122134
baseFee * costCorethTx(tx),
@@ -139,7 +151,7 @@ describe('getBurnedAmountByTx', () => {
139151
[output],
140152
).getTx() as AvaxTx;
141153

142-
const amounts = getBurnedAmountByTx(tx);
154+
const amounts = getBurnedAmountByTx(tx, testContext);
143155
expect(amounts.size).toEqual(1);
144156
expect(amounts.get(testContext.avaxAssetID)).toEqual(
145157
testContext.baseTxFee,
@@ -164,7 +176,7 @@ describe('getBurnedAmountByTx', () => {
164176
[output1, output2, output3],
165177
).getTx() as AvaxTx;
166178

167-
const amounts = getBurnedAmountByTx(tx);
179+
const amounts = getBurnedAmountByTx(tx, testContext);
168180
expect(amounts.size).toEqual(1);
169181
expect(amounts.get(testContext.avaxAssetID)).toEqual(
170182
testContext.baseTxFee,
@@ -192,7 +204,7 @@ describe('getBurnedAmountByTx', () => {
192204
[output1, output2, output3],
193205
).getTx() as AvaxTx;
194206

195-
const amounts = getBurnedAmountByTx(tx);
207+
const amounts = getBurnedAmountByTx(tx, testContext);
196208
expect(amounts.size).toEqual(1);
197209
expect(amounts.get(testContext.avaxAssetID)).toEqual(
198210
testContext.baseTxFee,
@@ -221,7 +233,7 @@ describe('getBurnedAmountByTx', () => {
221233
[output],
222234
).getTx() as AvaxTx;
223235

224-
const amounts = getBurnedAmountByTx(tx);
236+
const amounts = getBurnedAmountByTx(tx, testContext);
225237
expect(amounts.size).toEqual(1);
226238
expect(amounts.get(testContext.avaxAssetID)).toEqual(
227239
testContext.baseTxFee,
@@ -243,7 +255,7 @@ describe('getBurnedAmountByTx', () => {
243255
[output1, output2, output3],
244256
).getTx() as AvaxTx;
245257

246-
const amounts = getBurnedAmountByTx(tx);
258+
const amounts = getBurnedAmountByTx(tx, testContext);
247259
expect(amounts.size).toEqual(1);
248260
expect(amounts.get(testContext.avaxAssetID)).toEqual(
249261
testContext.baseTxFee,
@@ -266,7 +278,7 @@ describe('getBurnedAmountByTx', () => {
266278
[output1, output2, output3],
267279
).getTx() as AvaxTx;
268280

269-
const amounts = getBurnedAmountByTx(tx);
281+
const amounts = getBurnedAmountByTx(tx, testContext);
270282
expect(amounts.size).toEqual(1);
271283
expect(amounts.get(testContext.avaxAssetID)).toEqual(
272284
testContext.baseTxFee,
@@ -288,7 +300,7 @@ describe('getBurnedAmountByTx', () => {
288300
[testAddress1],
289301
).getTx() as AvaxTx;
290302

291-
const amounts = getBurnedAmountByTx(tx);
303+
const amounts = getBurnedAmountByTx(tx, testContext);
292304
expect(amounts.size).toEqual(1);
293305
expect(amounts.get(testContext.avaxAssetID)).toEqual(
294306
testContext.baseTxFee,
@@ -313,7 +325,7 @@ describe('getBurnedAmountByTx', () => {
313325
[output],
314326
).getTx() as AvaxTx;
315327

316-
const amounts = getBurnedAmountByTx(tx);
328+
const amounts = getBurnedAmountByTx(tx, testContext);
317329
expect(amounts.size).toEqual(1);
318330
expect(amounts.get(testContext.avaxAssetID)).toEqual(
319331
testContext.baseTxFee,
@@ -335,7 +347,7 @@ describe('getBurnedAmountByTx', () => {
335347
[output1, output2, output3],
336348
).getTx() as AvaxTx;
337349

338-
const amounts = getBurnedAmountByTx(tx);
350+
const amounts = getBurnedAmountByTx(tx, testContext);
339351
expect(amounts.size).toEqual(1);
340352
expect(amounts.get(testContext.avaxAssetID)).toEqual(
341353
testContext.baseTxFee,
@@ -358,7 +370,7 @@ describe('getBurnedAmountByTx', () => {
358370
[output1, output2, output3],
359371
).getTx() as AvaxTx;
360372

361-
const amounts = getBurnedAmountByTx(tx);
373+
const amounts = getBurnedAmountByTx(tx, testContext);
362374
expect(amounts.size).toEqual(1);
363375
expect(amounts.get(testContext.avaxAssetID)).toEqual(
364376
testContext.baseTxFee,
@@ -380,7 +392,7 @@ describe('getBurnedAmountByTx', () => {
380392
[testAddress1],
381393
).getTx() as AvaxTx;
382394

383-
const amounts = getBurnedAmountByTx(tx);
395+
const amounts = getBurnedAmountByTx(tx, testContext);
384396
expect(amounts.size).toEqual(1);
385397
expect(amounts.get(testContext.avaxAssetID)).toEqual(
386398
testContext.baseTxFee,
@@ -407,7 +419,7 @@ describe('getBurnedAmountByTx', () => {
407419
3,
408420
).getTx() as AvaxTx;
409421

410-
const amounts = getBurnedAmountByTx(tx);
422+
const amounts = getBurnedAmountByTx(tx, testContext);
411423
expect(amounts.size).toEqual(1);
412424
expect(amounts.get(testContext.avaxAssetID)).toEqual(
413425
testContext.addPrimaryNetworkValidatorFee,
@@ -442,7 +454,7 @@ describe('getBurnedAmountByTx', () => {
442454
3,
443455
).getTx() as AvaxTx;
444456

445-
const amounts = getBurnedAmountByTx(tx);
457+
const amounts = getBurnedAmountByTx(tx, testContext);
446458
expect(amounts.size).toEqual(1);
447459
expect(amounts.get(testContext.avaxAssetID)).toEqual(
448460
testContext.addPrimaryNetworkValidatorFee,
@@ -473,7 +485,7 @@ describe('getBurnedAmountByTx', () => {
473485
3,
474486
).getTx() as AvaxTx;
475487

476-
const amounts = getBurnedAmountByTx(tx);
488+
const amounts = getBurnedAmountByTx(tx, testContext);
477489
expect(amounts.size).toEqual(1);
478490
expect(amounts.get(testContext.avaxAssetID)).toEqual(
479491
testContext.addPrimaryNetworkValidatorFee,
@@ -499,7 +511,7 @@ describe('getBurnedAmountByTx', () => {
499511
[testAddress1],
500512
).getTx() as AvaxTx;
501513

502-
const amounts = getBurnedAmountByTx(tx);
514+
const amounts = getBurnedAmountByTx(tx, testContext);
503515
expect(amounts.size).toEqual(1);
504516
expect(amounts.get(testContext.avaxAssetID)).toEqual(
505517
testContext.addPrimaryNetworkDelegatorFee,
@@ -533,7 +545,7 @@ describe('getBurnedAmountByTx', () => {
533545
[testAddress1],
534546
).getTx() as AvaxTx;
535547

536-
const amounts = getBurnedAmountByTx(tx);
548+
const amounts = getBurnedAmountByTx(tx, testContext);
537549
expect(amounts.size).toEqual(1);
538550
expect(amounts.get(testContext.avaxAssetID)).toEqual(
539551
testContext.addPrimaryNetworkDelegatorFee,
@@ -563,7 +575,7 @@ describe('getBurnedAmountByTx', () => {
563575
[testAddress1],
564576
).getTx() as AvaxTx;
565577

566-
const amounts = getBurnedAmountByTx(tx);
578+
const amounts = getBurnedAmountByTx(tx, testContext);
567579
expect(amounts.size).toEqual(1);
568580
expect(amounts.get(testContext.avaxAssetID)).toEqual(
569581
testContext.addPrimaryNetworkDelegatorFee,
@@ -584,7 +596,7 @@ describe('getBurnedAmountByTx', () => {
584596
[testAddress1],
585597
).getTx() as AvaxTx;
586598

587-
const amounts = getBurnedAmountByTx(tx);
599+
const amounts = getBurnedAmountByTx(tx, testContext);
588600
expect(amounts.size).toEqual(1);
589601
expect(amounts.get(testContext.avaxAssetID)).toEqual(
590602
testContext.createSubnetTxFee,
@@ -610,7 +622,7 @@ describe('getBurnedAmountByTx', () => {
610622
[0],
611623
).getTx() as AvaxTx;
612624

613-
const amounts = getBurnedAmountByTx(tx);
625+
const amounts = getBurnedAmountByTx(tx, testContext);
614626
expect(amounts.size).toEqual(1);
615627
expect(amounts.get(testContext.avaxAssetID)).toEqual(
616628
testContext.createBlockchainTxFee,
@@ -637,12 +649,76 @@ describe('getBurnedAmountByTx', () => {
637649
[0],
638650
).getTx() as AvaxTx;
639651

640-
const amounts = getBurnedAmountByTx(tx);
652+
const amounts = getBurnedAmountByTx(tx, testContext);
641653
expect(amounts.size).toEqual(1);
642654
expect(amounts.get(testContext.avaxAssetID)).toEqual(
643655
testContext.addSubnetValidatorFee,
644656
);
645657
});
646658
});
659+
660+
it('calculates the burned amount of ConvertSubnetToL1 tx correctly', () => {
661+
const utxo1 = getUtxoMock(testUTXOID1, 5000000n);
662+
const utxo2 = getUtxoMock(testUTXOID2, 6000000n);
663+
664+
const tx = newConvertSubnetToL1Tx(
665+
{
666+
address: testAddress1,
667+
chainId: id().toString(),
668+
feeState: feeState(),
669+
fromAddressesBytes: [testAddress1],
670+
subnetAuth: [0],
671+
subnetId: id().toString(),
672+
utxos: [utxo1, utxo2],
673+
validators: [l1Validator()],
674+
},
675+
testContext,
676+
).getTx() as AvaxTx;
677+
678+
const amounts = getBurnedAmountByTx(tx, testContext);
679+
expect(amounts.size).toEqual(1);
680+
expect(amounts.get(testContext.avaxAssetID)).toEqual(749n);
681+
});
682+
});
683+
684+
it('calculates the burned amount of RegisterL1Validator tx correctly', () => {
685+
const utxo1 = getUtxoMock(testUTXOID1, 5000000n);
686+
const utxo2 = getUtxoMock(testUTXOID2, 6000000n);
687+
688+
const tx = newRegisterL1ValidatorTx(
689+
{
690+
feeState: feeState(),
691+
fromAddressesBytes: [testAddress1],
692+
utxos: [utxo1, utxo2],
693+
balance: bigIntPr().value(),
694+
blsSignature: blsSignatureBytes(),
695+
message: bytesBytes(),
696+
},
697+
testContext,
698+
).getTx() as AvaxTx;
699+
700+
const amounts = getBurnedAmountByTx(tx, testContext);
701+
expect(amounts.size).toEqual(1);
702+
expect(amounts.get(testContext.avaxAssetID)).toEqual(416n);
703+
});
704+
705+
it('calculates the burned amount of RegisterL1Validator tx correctly', () => {
706+
const utxo1 = getUtxoMock(testUTXOID1, 5000000n);
707+
const utxo2 = getUtxoMock(testUTXOID2, 6000000n);
708+
709+
const tx = newIncreaseL1ValidatorBalanceTx(
710+
{
711+
feeState: feeState(),
712+
fromAddressesBytes: [testAddress1],
713+
utxos: [utxo1, utxo2],
714+
balance: bigIntPr().value(),
715+
validationId: stringPr().value(),
716+
},
717+
testContext,
718+
).getTx() as AvaxTx;
719+
720+
const amounts = getBurnedAmountByTx(tx, testContext);
721+
expect(amounts.size).toEqual(1);
722+
expect(amounts.get(testContext.avaxAssetID)).toEqual(342n);
647723
});
648724
});

src/utils/getBurnedAmountByTx.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import { isTransferableOutput } from '../serializable/avax';
1010
import { getTransferableInputsByTx } from './getTransferableInputsByTx';
1111
import { getTransferableOutputsByTx } from './getTransferableOutputsByTx';
1212
import type { EVMTx } from '../serializable/evm/abstractTx';
13+
import {
14+
isConvertSubnetToL1Tx,
15+
isIncreaseL1ValidatorBalanceTx,
16+
isRegisterL1ValidatorTx,
17+
} from '../serializable/pvm';
18+
import type { Context } from '../vms/context';
1319

1420
const _reducer = (
1521
assetAmountMap: Map<string, bigint>,
@@ -56,15 +62,39 @@ export const getOutputAmounts = (tx: AvaxTx | EVMTx) => {
5662
}, new Map<string, bigint>());
5763
};
5864

59-
export const getBurnedAmountByTx = (tx: AvaxTx | EVMTx) => {
65+
const getValidatorBalanceSpendByTx = (tx: AvaxTx | EVMTx) => {
66+
if (isConvertSubnetToL1Tx(tx)) {
67+
return tx.validators.reduce(
68+
(sum, validator) => sum + validator.balance.value(),
69+
0n,
70+
);
71+
} else if (isRegisterL1ValidatorTx(tx)) {
72+
return tx.balance.value();
73+
} else if (isIncreaseL1ValidatorBalanceTx(tx)) {
74+
return tx.balance.value();
75+
}
76+
77+
return 0n;
78+
};
79+
80+
export const getBurnedAmountByTx = (tx: AvaxTx | EVMTx, context: Context) => {
6081
const inputAmounts = getInputAmounts(tx);
6182
const outputAmounts = getOutputAmounts(tx);
6283
const burnedAmounts = new Map<string, bigint>();
84+
const validatorBalance = getValidatorBalanceSpendByTx(tx);
6385

6486
for (const [id, inputAmount] of inputAmounts.entries()) {
6587
const outputAmount = outputAmounts.get(id) ?? 0n;
6688
burnedAmounts.set(id, inputAmount - outputAmount);
6789
}
6890

91+
if (validatorBalance) {
92+
const burnedAvax = burnedAmounts.get(context.avaxAssetID);
93+
94+
if (burnedAvax) {
95+
burnedAmounts.set(context.avaxAssetID, burnedAvax - validatorBalance);
96+
}
97+
}
98+
6999
return burnedAmounts;
70100
};

src/utils/validateBurnedAmount/validateBurnedAmount.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import {
3030
} from '../../serializable/pvm';
3131

3232
const _getBurnedAmount = (tx: Transaction, context: Context) => {
33-
const burnedAmounts = getBurnedAmountByTx(tx as AvaxTx | EVMTx);
33+
const burnedAmounts = getBurnedAmountByTx(tx as AvaxTx | EVMTx, context);
3434
return burnedAmounts.get(context.avaxAssetID) ?? 0n;
3535
};
3636

0 commit comments

Comments
 (0)