1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00
metamask-extension/app/scripts/controllers/transactions/lib/util.test.js
Harry fd819451e1
🐛 Calldata validation (#17326)
Co-authored-by: Dan J Miller <danjm.com@gmail.com>
Co-authored-by: Pedro Figueiredo <pedro.figueiredo@consensys.net>
Co-authored-by: brad-decker <bhdecker84@gmail.com>
2023-02-01 16:34:55 -06:00

492 lines
14 KiB
JavaScript

import { strict as assert } from 'assert';
import { TransactionEnvelopeType } from '../../../../../shared/constants/transaction';
import { BURN_ADDRESS } from '../../../../../shared/modules/hexstring-utils';
import { GasRecommendations } from '../../../../../shared/constants/gas';
import * as txUtils from './util';
describe('txUtils', function () {
describe('#validateTxParams', function () {
it('does not throw for positive values', function () {
const sample = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
value: '0x01',
};
txUtils.validateTxParams(sample);
});
it('throws for invalid params value', function () {
assert.throws(() => txUtils.validateTxParams(), {
message: 'Invalid transaction params: must be an object.',
});
assert.throws(() => txUtils.validateTxParams(null), {
message: 'Invalid transaction params: must be an object.',
});
assert.throws(() => txUtils.validateTxParams(true), {
message: 'Invalid transaction params: must be an object.',
});
assert.throws(() => txUtils.validateTxParams([]), {
message: 'Invalid transaction params: must be an object.',
});
});
it('throws for data out of bounds buffer overrun', function () {
const sample = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0xfbb5595c18ca76bab52d66188e4ca50c7d95f77a',
data: '0xa9059cbb00000000000000000000000011b6A5fE2906F3354145613DB0d99CEB51f604C90000000000000000000000000000000000000000000000004563918244F400',
};
assert.throws(() => txUtils.validateTxParams(sample), {
message:
'Invalid transaction params: data out-of-bounds, BUFFER_OVERRUN.',
});
});
it('throws for missing "to" and "data"', function () {
const sample = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
value: '0x01',
};
assert.throws(() => txUtils.validateTxParams(sample), {
message:
'Invalid transaction params: must specify "data" for contract deployments, or "to" (and optionally "data") for all other types of transactions.',
});
});
it('throws for negative values', function () {
const sample = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0xc42edfcc21ed14dda456aa0756c153f7985d8813',
value: '-0x01',
};
assert.throws(() => txUtils.validateTxParams(sample), {
message: 'Invalid transaction value "-0x01": not a positive number.',
});
});
describe('when validating gasPrice', function () {
it('should error when specifying incorrect type', function () {
const txParams = {
gasPrice: '0x1',
type: TransactionEnvelopeType.feeMarket,
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message: `Invalid transaction envelope type: specified type "0x2" but included a gasPrice instead of maxFeePerGas and maxPriorityFeePerGas`,
},
);
});
it('should error when gasPrice is not a string', function () {
const txParams = {
gasPrice: 1,
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction params: gasPrice is not a string. got: (1)',
},
);
});
it('should error when specifying maxFeePerGas', function () {
const txParams = {
gasPrice: '0x1',
maxFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction params: specified gasPrice but also included maxFeePerGas, these cannot be mixed',
},
);
});
it('should error when specifying maxPriorityFeePerGas', function () {
const txParams = {
gasPrice: '0x1',
maxPriorityFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction params: specified gasPrice but also included maxPriorityFeePerGas, these cannot be mixed',
},
);
});
it('should validate if gasPrice is set with no type or EIP-1559 gas fields', function () {
const txParams = {
gasPrice: '0x1',
to: BURN_ADDRESS,
};
assert.doesNotThrow(() => txUtils.validateTxParams(txParams));
});
it('should validate if gasPrice is set with a type of "0x0"', function () {
const txParams = {
gasPrice: '0x1',
type: TransactionEnvelopeType.legacy,
to: BURN_ADDRESS,
};
assert.doesNotThrow(() => txUtils.validateTxParams(txParams));
});
});
describe('when validating maxFeePerGas', function () {
it('should error when specifying incorrect type', function () {
const txParams = {
maxFeePerGas: '0x1',
type: TransactionEnvelopeType.legacy,
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction envelope type: specified type "0x0" but including maxFeePerGas and maxPriorityFeePerGas requires type: "0x2"',
},
);
});
it('should error when maxFeePerGas is not a string', function () {
const txParams = {
maxFeePerGas: 1,
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction params: maxFeePerGas is not a string. got: (1)',
},
);
});
it('should error when specifying gasPrice', function () {
const txParams = {
gasPrice: '0x1',
maxFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction params: specified gasPrice but also included maxFeePerGas, these cannot be mixed',
},
);
});
it('should validate if maxFeePerGas is set with no type or gasPrice field', function () {
const txParams = {
maxFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.doesNotThrow(() => txUtils.validateTxParams(txParams));
});
it('should validate if maxFeePerGas is set with a type of "0x2"', function () {
const txParams = {
maxFeePerGas: '0x1',
type: TransactionEnvelopeType.feeMarket,
to: BURN_ADDRESS,
};
assert.doesNotThrow(() => txUtils.validateTxParams(txParams));
});
});
describe('when validating maxPriorityFeePerGas', function () {
it('should error when specifying incorrect type', function () {
const txParams = {
maxPriorityFeePerGas: '0x1',
type: TransactionEnvelopeType.legacy,
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction envelope type: specified type "0x0" but including maxFeePerGas and maxPriorityFeePerGas requires type: "0x2"',
},
);
});
it('should error when maxFeePerGas is not a string', function () {
const txParams = {
maxPriorityFeePerGas: 1,
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction params: maxPriorityFeePerGas is not a string. got: (1)',
},
);
});
it('should error when specifying gasPrice', function () {
const txParams = {
gasPrice: '0x1',
maxPriorityFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams);
},
{
message:
'Invalid transaction params: specified gasPrice but also included maxPriorityFeePerGas, these cannot be mixed',
},
);
});
it('should validate if maxPriorityFeePerGas is set with no type or gasPrice field', function () {
const txParams = {
maxPriorityFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.doesNotThrow(() => txUtils.validateTxParams(txParams));
});
it('should validate if maxPriorityFeePerGas is set with a type of "0x2"', function () {
const txParams = {
maxPriorityFeePerGas: '0x1',
type: TransactionEnvelopeType.feeMarket,
to: BURN_ADDRESS,
};
assert.doesNotThrow(() => txUtils.validateTxParams(txParams));
});
});
describe('when validating EIP-1559 transactions', function () {
it('should error when network does not support EIP-1559', function () {
const txParams = {
maxPriorityFeePerGas: '0x1',
maxFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.throws(
() => {
txUtils.validateTxParams(txParams, false);
},
{
message:
'Invalid transaction params: params specify an EIP-1559 transaction but the current network does not support EIP-1559',
},
);
});
it('should validate when network does support EIP-1559', function () {
const txParams = {
maxPriorityFeePerGas: '0x1',
maxFeePerGas: '0x1',
to: BURN_ADDRESS,
};
assert.doesNotThrow(() => txUtils.validateTxParams(txParams, true));
});
});
});
describe('#normalizeTxParams', function () {
it('should normalize txParams', function () {
const txParams = {
chainId: '0x1',
from: 'a7df1beDBF813f57096dF77FCd515f0B3900e402',
to: null,
data: '68656c6c6f20776f726c64',
random: 'hello world',
gasPrice: '1',
maxFeePerGas: '1',
maxPriorityFeePerGas: '1',
estimateSuggested: GasRecommendations.medium,
estimateUsed: GasRecommendations.high,
type: '1',
};
let normalizedTxParams = txUtils.normalizeTxParams(txParams);
assert.ok(!normalizedTxParams.chainId, 'there should be no chainId');
assert.ok(
!normalizedTxParams.to,
'there should be no to address if null',
);
assert.equal(
normalizedTxParams.from.slice(0, 2),
'0x',
'from should be hex-prefixed',
);
assert.equal(
normalizedTxParams.data.slice(0, 2),
'0x',
'data should be hex-prefixed',
);
assert.ok(
!('random' in normalizedTxParams),
'there should be no random key in normalizedTxParams',
);
txParams.to = 'a7df1beDBF813f57096dF77FCd515f0B3900e402';
normalizedTxParams = txUtils.normalizeTxParams(txParams);
assert.equal(
normalizedTxParams.to.slice(0, 2),
'0x',
'to should be hex-prefixed',
);
assert.equal(
normalizedTxParams.gasPrice,
'0x1',
'gasPrice should be hex-prefixed',
);
assert.equal(
normalizedTxParams.maxFeePerGas,
'0x1',
'maxFeePerGas should be hex-prefixed',
);
assert.equal(
normalizedTxParams.maxPriorityFeePerGas,
'0x1',
'maxPriorityFeePerGas should be hex-prefixed',
);
assert.equal(
normalizedTxParams.type,
'0x1',
'type should be hex-prefixed',
);
assert.equal(
normalizedTxParams.estimateSuggested,
GasRecommendations.medium,
'estimateSuggested should be the string originally provided',
);
assert.equal(
normalizedTxParams.estimateUsed,
GasRecommendations.high,
'estimateSuggested should be the string originally provided',
);
});
});
describe('#validateRecipient', function () {
it('removes recipient for txParams with 0x when contract data is provided', function () {
const zeroRecipientDataTxParams = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0x',
data: 'bytecode',
};
const sanitizedTxParams = txUtils.validateRecipient(
zeroRecipientDataTxParams,
);
assert.deepEqual(
sanitizedTxParams,
{
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
data: 'bytecode',
},
'no recipient with 0x',
);
});
it('should error when recipient is 0x', function () {
const zeroRecipientTxParams = {
from: '0x1678a085c290ebd122dc42cba69373b5953b831d',
to: '0x',
};
assert.throws(
() => {
txUtils.validateRecipient(zeroRecipientTxParams);
},
Error,
'Invalid recipient address',
);
});
});
describe('#validateFrom', function () {
it('should error when from is not a hex string', function () {
// where from is undefined
const txParams = {};
assert.throws(
() => {
txUtils.validateFrom(txParams);
},
Error,
`Invalid from address ${txParams.from} not a string`,
);
// where from is array
txParams.from = [];
assert.throws(
() => {
txUtils.validateFrom(txParams);
},
Error,
`Invalid from address ${txParams.from} not a string`,
);
// where from is a object
txParams.from = {};
assert.throws(
() => {
txUtils.validateFrom(txParams);
},
Error,
`Invalid from address ${txParams.from} not a string`,
);
// where from is a invalid address
txParams.from = 'im going to fail';
assert.throws(
() => {
txUtils.validateFrom(txParams);
},
Error,
`Invalid from address`,
);
// should run
txParams.from = '0x1678a085c290ebd122dc42cba69373b5953b831d';
txUtils.validateFrom(txParams);
});
});
});