From 516972119cad956b394a51c7256cc8094094a78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Federico=20Kunze=20K=C3=BCllmer?= <31522760+fedekunze@users.noreply.github.com> Date: Fri, 8 Oct 2021 13:11:19 +0200 Subject: [PATCH] evm: unit tests (#619) * evm: unit tests * Add unit tests for DynamicFeeTx.Validate() * Start get and set signature values tests * get set values * Add tests for GetTo() * Add GetNonce test * Add GetValue test * Start copy test * Add WIP newDynamicFeeTx test * Add WIP legacy_tx_test * pair programming session * Add TestLegacyTxValidate * Add TestLegacyTxSetSignatureValues & GetSignatureValues * Add legacyTx tests * Merge main, forgot to save one file * Add AccessList tests * Add chain Config (fork order) * Add invalid genesis account test * Add params tests * Add WIP tracer test * tracer tests * Add FormatLogs tests * Add NewNoOpTracer test * Refactor to test suite * Refactor Tx Test suits to only use TxDataTestSuite * Update link to geth interpreter * Update x/evm/types/params.go * Refactor accessListTx Test suits to use TxDataTestSuite Co-authored-by: Daniel Burckhardt --- x/evm/types/access_list_test.go | 38 +++ x/evm/types/chain_config_test.go | 20 +- x/evm/types/dynamic_fee_tx.go | 4 +- x/evm/types/dynamic_fee_tx_test.go | 501 +++++++++++++++++++++++++++++ x/evm/types/genesis_test.go | 17 + x/evm/types/legacy_tx.go | 2 +- x/evm/types/legacy_tx_test.go | 356 ++++++++++++++++++++ x/evm/types/msg.go | 9 +- x/evm/types/params.go | 8 +- x/evm/types/params_test.go | 40 +++ x/evm/types/tracer.go | 47 ++- x/evm/types/tracer_test.go | 83 +++++ 12 files changed, 1111 insertions(+), 14 deletions(-) create mode 100644 x/evm/types/access_list_test.go create mode 100644 x/evm/types/dynamic_fee_tx_test.go create mode 100644 x/evm/types/legacy_tx_test.go create mode 100644 x/evm/types/tracer_test.go diff --git a/x/evm/types/access_list_test.go b/x/evm/types/access_list_test.go new file mode 100644 index 0000000000..4122f2ecd9 --- /dev/null +++ b/x/evm/types/access_list_test.go @@ -0,0 +1,38 @@ +package types + +import ( + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +func (suite *TxDataTestSuite) TestTestNewAccessList() { + testCases := []struct { + name string + ethAccessList *ethtypes.AccessList + expAl AccessList + }{ + { + "ethAccessList is nil", + nil, + nil, + }, + { + "non-empty ethAccessList", + ðtypes.AccessList{{Address: suite.addr, StorageKeys: []common.Hash{{0}}}}, + AccessList{{Address: suite.hexAddr, StorageKeys: []string{common.Hash{}.Hex()}}}, + }, + } + for _, tc := range testCases { + al := NewAccessList(tc.ethAccessList) + + suite.Require().Equal(tc.expAl, al) + } +} + +func (suite *TxDataTestSuite) TestAccessListToEthAccessList() { + ethAccessList := ethtypes.AccessList{{Address: suite.addr, StorageKeys: []common.Hash{{0}}}} + al := NewAccessList(ðAccessList) + actual := al.ToEthAccessList() + + suite.Require().Equal(ðAccessList, actual) +} diff --git a/x/evm/types/chain_config_test.go b/x/evm/types/chain_config_test.go index 6b61e1e90a..88af67be23 100644 --- a/x/evm/types/chain_config_test.go +++ b/x/evm/types/chain_config_test.go @@ -238,7 +238,6 @@ func TestChainConfigValidate(t *testing.T) { }, true, }, - { "invalid CatalystBlock", ChainConfig{ @@ -258,6 +257,25 @@ func TestChainConfigValidate(t *testing.T) { }, true, }, + { + "invalid fork order - skip HomesteadBlock", + ChainConfig{ + DAOForkBlock: newIntPtr(0), + EIP150Block: newIntPtr(0), + EIP150Hash: defaultEIP150Hash, + EIP155Block: newIntPtr(0), + EIP158Block: newIntPtr(0), + ByzantiumBlock: newIntPtr(0), + ConstantinopleBlock: newIntPtr(0), + PetersburgBlock: newIntPtr(0), + IstanbulBlock: newIntPtr(0), + MuirGlacierBlock: newIntPtr(0), + BerlinBlock: newIntPtr(0), + LondonBlock: newIntPtr(0), + CatalystBlock: newIntPtr(0), + }, + true, + }, } for _, tc := range testCases { diff --git a/x/evm/types/dynamic_fee_tx.go b/x/evm/types/dynamic_fee_tx.go index 9609c1099f..2461723072 100644 --- a/x/evm/types/dynamic_fee_tx.go +++ b/x/evm/types/dynamic_fee_tx.go @@ -103,7 +103,7 @@ func (tx *DynamicFeeTx) GetGasPrice() *big.Int { return tx.GetGasFeeCap() } -// GetGasTipCap returns the gas price field. +// GetGasTipCap returns the gas tip cap field. func (tx *DynamicFeeTx) GetGasTipCap() *big.Int { if tx.GasTipCap == nil { return nil @@ -111,7 +111,7 @@ func (tx *DynamicFeeTx) GetGasTipCap() *big.Int { return tx.GasTipCap.BigInt() } -// GetGasFeeCap returns the gas price field. +// GetGasFeeCap returns the gas fee cap field. func (tx *DynamicFeeTx) GetGasFeeCap() *big.Int { if tx.GasFeeCap == nil { return nil diff --git a/x/evm/types/dynamic_fee_tx_test.go b/x/evm/types/dynamic_fee_tx_test.go new file mode 100644 index 0000000000..a9f8c11e32 --- /dev/null +++ b/x/evm/types/dynamic_fee_tx_test.go @@ -0,0 +1,501 @@ +package types + +import ( + "math/big" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/suite" + "github.com/tharsis/ethermint/tests" +) + +type TxDataTestSuite struct { + suite.Suite + + sdkInt sdk.Int + uint64 uint64 + bigInt *big.Int + sdkZeroInt sdk.Int + sdkMinusOneInt sdk.Int + invalidAddr string + addr common.Address + hexAddr string +} + +func (suite *TxDataTestSuite) SetupTest() { + suite.sdkInt = sdk.NewInt(100) + suite.uint64 = suite.sdkInt.Uint64() + suite.bigInt = big.NewInt(1) + suite.sdkZeroInt = sdk.ZeroInt() + suite.sdkMinusOneInt = sdk.NewInt(-1) + suite.invalidAddr = "123456" + suite.addr = tests.GenerateAddress() + suite.hexAddr = suite.addr.Hex() +} + +func TestTxDataTestSuite(t *testing.T) { + suite.Run(t, new(TxDataTestSuite)) +} + +func (suite *TxDataTestSuite) TestNewDynamicFeeTx() { + testCases := []struct { + name string + tx *ethtypes.Transaction + }{ + { + "non-empty tx", + ethtypes.NewTx(ðtypes.AccessListTx{ // TODO: change to DynamicFeeTx on Geth + Nonce: 1, + Data: []byte("data"), + Gas: 100, + Value: big.NewInt(1), + AccessList: ethtypes.AccessList{}, + To: &suite.addr, + V: suite.bigInt, + R: suite.bigInt, + S: suite.bigInt, + }), + }, + } + for _, tc := range testCases { + tx := newDynamicFeeTx(tc.tx) + + suite.Require().NotEmpty(tx) + suite.Require().Equal(uint8(2), tx.TxType()) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxCopy() { + tx := &DynamicFeeTx{} + txCopy := tx.Copy() + + suite.Require().Equal(&DynamicFeeTx{}, txCopy) + // TODO: Test for different pointers +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetChainID() { + testCases := []struct { + name string + tx DynamicFeeTx + exp *big.Int + }{ + { + "empty chainID", + DynamicFeeTx{ + ChainID: nil, + }, + nil, + }, + { + "non-empty chainID", + DynamicFeeTx{ + ChainID: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetChainID() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetAccessList() { + testCases := []struct { + name string + tx DynamicFeeTx + exp ethtypes.AccessList + }{ + { + "empty accesses", + DynamicFeeTx{ + Accesses: nil, + }, + nil, + }, + { + "nil", + DynamicFeeTx{ + Accesses: NewAccessList(nil), + }, + nil, + }, + { + "non-empty accesses", + DynamicFeeTx{ + Accesses: AccessList{ + { + Address: suite.hexAddr, + StorageKeys: []string{}, + }, + }, + }, + ethtypes.AccessList{ + { + Address: suite.addr, + StorageKeys: []common.Hash{}, + }, + }, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetAccessList() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetData() { + testCases := []struct { + name string + tx DynamicFeeTx + }{ + { + "non-empty transaction", + DynamicFeeTx{ + Data: nil, + }, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetData() + + suite.Require().Equal(tc.tx.Data, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetGas() { + testCases := []struct { + name string + tx DynamicFeeTx + exp uint64 + }{ + { + "non-empty gas", + DynamicFeeTx{ + GasLimit: suite.uint64, + }, + suite.uint64, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGas() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetGasPrice() { + testCases := []struct { + name string + tx DynamicFeeTx + exp *big.Int + }{ + { + "non-empty gasFeeCap", + DynamicFeeTx{ + GasFeeCap: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasPrice() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetGasTipCap() { + testCases := []struct { + name string + tx DynamicFeeTx + exp *big.Int + }{ + { + "empty gasTipCap", + DynamicFeeTx{ + GasTipCap: nil, + }, + nil, + }, + { + "non-empty gasTipCap", + DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasTipCap() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetGasFeeCap() { + testCases := []struct { + name string + tx DynamicFeeTx + exp *big.Int + }{ + { + "empty gasFeeCap", + DynamicFeeTx{ + GasFeeCap: nil, + }, + nil, + }, + { + "non-empty gasFeeCap", + DynamicFeeTx{ + GasFeeCap: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasFeeCap() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetValue() { + testCases := []struct { + name string + tx DynamicFeeTx + exp *big.Int + }{ + { + "empty amount", + DynamicFeeTx{ + Amount: nil, + }, + nil, + }, + { + "non-empty amount", + DynamicFeeTx{ + Amount: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetValue() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetNonce() { + testCases := []struct { + name string + tx DynamicFeeTx + exp uint64 + }{ + { + "non-empty nonce", + DynamicFeeTx{ + Nonce: suite.uint64, + }, + suite.uint64, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetNonce() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxGetTo() { + testCases := []struct { + name string + tx DynamicFeeTx + exp *common.Address + }{ + { + "empty suite.address", + DynamicFeeTx{ + To: "", + }, + nil, + }, + { + "non-empty suite.address", + DynamicFeeTx{ + To: suite.hexAddr, + }, + &suite.addr, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetTo() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxSetSignatureValues() { + testCases := []struct { + name string + chainID *big.Int + r *big.Int + v *big.Int + s *big.Int + }{ + { + "empty values", + nil, + nil, + nil, + nil, + }, + { + "non-empty values", + suite.bigInt, + suite.bigInt, + suite.bigInt, + suite.bigInt, + }, + } + + for _, tc := range testCases { + tx := &DynamicFeeTx{} + tx.SetSignatureValues(tc.chainID, tc.v, tc.r, tc.s) + + v, r, s := tx.GetRawSignatureValues() + chainID := tx.GetChainID() + + suite.Require().Equal(tc.v, v, tc.name) + suite.Require().Equal(tc.r, r, tc.name) + suite.Require().Equal(tc.s, s, tc.name) + suite.Require().Equal(tc.chainID, chainID, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxValidate() { + testCases := []struct { + name string + tx DynamicFeeTx + expError bool + }{ + { + "empty", + DynamicFeeTx{}, + true, + }, + { + "gas tip cap is nil", + DynamicFeeTx{ + GasTipCap: nil, + }, + true, + }, + { + "gas fee cap is nil", + DynamicFeeTx{ + GasTipCap: &suite.sdkZeroInt, + }, + true, + }, + { + "gas tip cap is negative", + DynamicFeeTx{ + GasTipCap: &suite.sdkMinusOneInt, + GasFeeCap: &suite.sdkZeroInt, + }, + true, + }, + { + "gas tip cap is negative", + DynamicFeeTx{ + GasTipCap: &suite.sdkZeroInt, + GasFeeCap: &suite.sdkMinusOneInt, + }, + true, + }, + { + "gas fee cap < gas tip cap", + DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkZeroInt, + }, + true, + }, + { + "amount is negative", + DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkMinusOneInt, + }, + true, + }, + { + "to suite.address is invalid", + DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkInt, + To: suite.invalidAddr, + }, + true, + }, + { + "chain ID not present on AccessList txs", + DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkInt, + To: suite.hexAddr, + ChainID: nil, + }, + true, + }, + { + "no errors", + DynamicFeeTx{ + GasTipCap: &suite.sdkInt, + GasFeeCap: &suite.sdkInt, + Amount: &suite.sdkInt, + To: suite.hexAddr, + ChainID: &suite.sdkInt, + }, + false, + }, + } + + for _, tc := range testCases { + err := tc.tx.Validate() + + if tc.expError { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + } +} + +func (suite *TxDataTestSuite) TestDynamicFeeTxFeeCost() { + tx := &DynamicFeeTx{} + suite.Require().Panics(func() { tx.Fee() }, "should panic") + suite.Require().Panics(func() { tx.Cost() }, "should panic") +} diff --git a/x/evm/types/genesis_test.go b/x/evm/types/genesis_test.go index 57962da037..4f260ee57a 100644 --- a/x/evm/types/genesis_test.go +++ b/x/evm/types/genesis_test.go @@ -126,6 +126,23 @@ func (suite *GenesisTestSuite) TestValidateGenesis() { }, expPass: false, }, + { + name: "invalid genesis account", + genState: &GenesisState{ + Accounts: []GenesisAccount{ + { + Address: "123456", + + Code: suite.code, + Storage: Storage{ + {Key: suite.hash.String()}, + }, + }, + }, + Params: DefaultParams(), + }, + expPass: false, + }, { name: "duplicated genesis account", genState: &GenesisState{ diff --git a/x/evm/types/legacy_tx.go b/x/evm/types/legacy_tx.go index efbfe42fb0..bcc8f24c3c 100644 --- a/x/evm/types/legacy_tx.go +++ b/x/evm/types/legacy_tx.go @@ -155,7 +155,7 @@ func (tx *LegacyTx) SetSignatureValues(_, v, r, s *big.Int) { func (tx LegacyTx) Validate() error { gasPrice := tx.GetGasPrice() if gasPrice == nil { - return sdkerrors.Wrap(ErrInvalidGasPrice, "cannot be nil") + return sdkerrors.Wrap(ErrInvalidGasPrice, "gas price cannot be nil") } if gasPrice.Sign() == -1 { diff --git a/x/evm/types/legacy_tx_test.go b/x/evm/types/legacy_tx_test.go new file mode 100644 index 0000000000..6e298a30b6 --- /dev/null +++ b/x/evm/types/legacy_tx_test.go @@ -0,0 +1,356 @@ +package types + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" +) + +func (suite *TxDataTestSuite) TestNewLegacyTx() { + testCases := []struct { + name string + tx *ethtypes.Transaction + }{ + { + "non-empty Transaction", + ethtypes.NewTx(ðtypes.AccessListTx{ + Nonce: 1, + Data: []byte("data"), + Gas: 100, + Value: big.NewInt(1), + AccessList: ethtypes.AccessList{}, + To: &suite.addr, + V: big.NewInt(1), + R: big.NewInt(1), + S: big.NewInt(1), + }), + }, + } + + for _, tc := range testCases { + tx := newLegacyTx(tc.tx) + + suite.Require().NotEmpty(tc.tx) + suite.Require().Equal(uint8(0), tx.TxType()) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxTxType() { + tx := LegacyTx{} + actual := tx.TxType() + + suite.Require().Equal(uint8(0), actual) +} + +func (suite *TxDataTestSuite) TestLegacyTxCopy() { + tx := &LegacyTx{} + txData := tx.Copy() + + suite.Require().Equal(&LegacyTx{}, txData) + // TODO: Test for different pointers +} + +func (suite *TxDataTestSuite) TestLegacyTxGetChainID() { + tx := LegacyTx{} + actual := tx.GetChainID() + + suite.Require().Nil(actual) +} + +func (suite *TxDataTestSuite) TestLegacyTxGetAccessList() { + tx := LegacyTx{} + actual := tx.GetAccessList() + + suite.Require().Nil(actual) +} + +func (suite *TxDataTestSuite) TestLegacyTxGetData() { + testCases := []struct { + name string + tx LegacyTx + }{ + { + "non-empty transaction", + LegacyTx{ + Data: nil, + }, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetData() + + suite.Require().Equal(tc.tx.Data, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxGetGas() { + testCases := []struct { + name string + tx LegacyTx + exp uint64 + }{ + { + "non-empty gas", + LegacyTx{ + GasLimit: suite.uint64, + }, + suite.uint64, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGas() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxGetGasPrice() { + testCases := []struct { + name string + tx LegacyTx + exp *big.Int + }{ + { + "empty gasPrice", + LegacyTx{ + GasPrice: nil, + }, + nil, + }, + { + "non-empty gasPrice", + LegacyTx{ + GasPrice: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasFeeCap() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxGetGasTipCap() { + testCases := []struct { + name string + tx LegacyTx + exp *big.Int + }{ + { + "non-empty gasPrice", + LegacyTx{ + GasPrice: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasTipCap() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxGetGasFeeCap() { + testCases := []struct { + name string + tx LegacyTx + exp *big.Int + }{ + { + "non-empty gasPrice", + LegacyTx{ + GasPrice: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetGasFeeCap() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxGetValue() { + testCases := []struct { + name string + tx LegacyTx + exp *big.Int + }{ + { + "empty amount", + LegacyTx{ + Amount: nil, + }, + nil, + }, + { + "non-empty amount", + LegacyTx{ + Amount: &suite.sdkInt, + }, + (&suite.sdkInt).BigInt(), + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetValue() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxGetNonce() { + testCases := []struct { + name string + tx LegacyTx + exp uint64 + }{ + { + "none-empty nonce", + LegacyTx{ + Nonce: suite.uint64, + }, + suite.uint64, + }, + } + for _, tc := range testCases { + actual := tc.tx.GetNonce() + + suite.Require().Equal(tc.exp, actual) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxGetTo() { + testCases := []struct { + name string + tx LegacyTx + exp *common.Address + }{ + { + "empty address", + LegacyTx{ + To: "", + }, + nil, + }, + { + "non-empty address", + LegacyTx{ + To: suite.hexAddr, + }, + &suite.addr, + }, + } + + for _, tc := range testCases { + actual := tc.tx.GetTo() + + suite.Require().Equal(tc.exp, actual, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxAsEthereumData() { + tx := &LegacyTx{} + txData := tx.AsEthereumData() + + suite.Require().Equal(ðtypes.LegacyTx{}, txData) +} + +func (suite *TxDataTestSuite) TestLegacyTxSetSignatureValues() { + testCases := []struct { + name string + v *big.Int + r *big.Int + s *big.Int + }{ + { + "non-empty values", + suite.bigInt, + suite.bigInt, + suite.bigInt, + }, + } + for _, tc := range testCases { + tx := &LegacyTx{} + tx.SetSignatureValues(nil, tc.v, tc.r, tc.s) + + v, r, s := tx.GetRawSignatureValues() + + suite.Require().Equal(tc.v, v, tc.name) + suite.Require().Equal(tc.r, r, tc.name) + suite.Require().Equal(tc.s, s, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxValidate() { + testCases := []struct { + name string + tx LegacyTx + expError bool + }{ + { + "empty", + LegacyTx{}, + true, + }, + { + "gas price is nil", + LegacyTx{ + GasPrice: nil, + }, + true, + }, + { + "gas price is negative", + LegacyTx{ + GasPrice: &suite.sdkMinusOneInt, + }, + true, + }, + { + "amount is negative", + LegacyTx{ + GasPrice: &suite.sdkInt, + Amount: &suite.sdkMinusOneInt, + }, + true, + }, + { + "to address is invalid", + LegacyTx{ + GasPrice: &suite.sdkInt, + Amount: &suite.sdkInt, + To: suite.invalidAddr, + }, + true, + }, + } + + for _, tc := range testCases { + err := tc.tx.Validate() + + if tc.expError { + suite.Require().Error(err, tc.name) + continue + } + + suite.Require().NoError(err, tc.name) + } +} + +func (suite *TxDataTestSuite) TestLegacyTxFeeCost() { + tx := &LegacyTx{} + + suite.Require().Panics(func() { tx.Fee() }, "should panice") + suite.Require().Panics(func() { tx.Cost() }, "should panice") +} diff --git a/x/evm/types/msg.go b/x/evm/types/msg.go index fbebd85bbe..ea39300477 100644 --- a/x/evm/types/msg.go +++ b/x/evm/types/msg.go @@ -42,8 +42,13 @@ func NewTx( // NewTxContract returns a reference to a new Ethereum transaction // message designated for contract creation. func NewTxContract( - chainID *big.Int, nonce uint64, amount *big.Int, - gasLimit uint64, gasPrice, gasFeeCap, gasTipCap *big.Int, input []byte, accesses *ethtypes.AccessList, + chainID *big.Int, + nonce uint64, + amount *big.Int, + gasLimit uint64, + gasPrice, gasFeeCap, gasTipCap *big.Int, + input []byte, + accesses *ethtypes.AccessList, ) *MsgEthereumTx { return newMsgEthereumTx(chainID, nonce, nil, amount, gasLimit, gasPrice, gasFeeCap, gasTipCap, input, accesses) } diff --git a/x/evm/types/params.go b/x/evm/types/params.go index 9e4d87d749..921be389b9 100644 --- a/x/evm/types/params.go +++ b/x/evm/types/params.go @@ -23,9 +23,11 @@ var ( ParamStoreKeyExtraEIPs = []byte("EnableExtraEIPs") ParamStoreKeyChainConfig = []byte("ChainConfig") - // AvailableExtraEIPs define the list of all EIPs that can be enabled by the EVM interpreter. These EIPs are applied in - // order and can override the instruction sets from the latest hard fork enabled by the ChainConfig. For more info - // check: https://github.com/ethereum/go-ethereum/blob/v1.10.4/core/vm/interpreter.go#L122 + // AvailableExtraEIPs define the list of all EIPs that can be enabled by the + // EVM interpreter. These EIPs are applied in order and can override the + // instruction sets from the latest hard fork enabled by the ChainConfig. For + // more info check: + // https://github.com/ethereum/go-ethereum/blob/master/core/vm/interpreter.go#L97 AvailableExtraEIPs = []int64{1344, 1884, 2200, 2929, 3198, 3529} ) diff --git a/x/evm/types/params_test.go b/x/evm/types/params_test.go index 6b9445688f..48614b0614 100644 --- a/x/evm/types/params_test.go +++ b/x/evm/types/params_test.go @@ -3,9 +3,14 @@ package types import ( "testing" + paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" "github.com/stretchr/testify/require" ) +func TestParamKeyTable(t *testing.T) { + require.IsType(t, paramtypes.KeyTable{}, ParamKeyTable()) +} + func TestParamsValidate(t *testing.T) { testCases := []struct { name string @@ -56,6 +61,13 @@ func TestParamsValidate(t *testing.T) { } } +func TestParamsEIPs(t *testing.T) { + params := NewParams("ara", true, true, DefaultChainConfig(), 2929, 1884, 1344) + actual := params.EIPs() + + require.Equal(t, []int([]int{2929, 1884, 1344}), actual) +} + func TestParamsValidatePriv(t *testing.T) { require.Error(t, validateEVMDenom(false)) require.NoError(t, validateEVMDenom("inj")) @@ -64,3 +76,31 @@ func TestParamsValidatePriv(t *testing.T) { require.Error(t, validateEIPs("")) require.NoError(t, validateEIPs([]int64{1884})) } + +func TestValidateChainConfig(t *testing.T) { + testCases := []struct { + name string + i interface{} + expError bool + }{ + { + "invalid chain config type", + "string", + true, + }, + { + "valid chain config type", + DefaultChainConfig(), + false, + }, + } + for _, tc := range testCases { + err := validateChainConfig(tc.i) + + if tc.expError { + require.Error(t, err, tc.name) + } else { + require.NoError(t, err, tc.name) + } + } +} diff --git a/x/evm/types/tracer.go b/x/evm/types/tracer.go index 557c378ee0..cbf5261a94 100644 --- a/x/evm/types/tracer.go +++ b/x/evm/types/tracer.go @@ -126,23 +126,60 @@ func NewNoOpTracer() *NoOpTracer { } // CaptureStart implements vm.Tracer interface -func (dt NoOpTracer) CaptureStart(env *vm.EVM, from, to common.Address, create bool, input []byte, gas uint64, value *big.Int) { +func (dt NoOpTracer) CaptureStart( + env *vm.EVM, + from, to common.Address, + create bool, + input []byte, + gas uint64, + value *big.Int, +) { } // CaptureEnter implements vm.Tracer interface -func (dt NoOpTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) { +func (dt NoOpTracer) CaptureEnter( + typ vm.OpCode, + from common.Address, + to common.Address, + input []byte, + gas uint64, + value *big.Int, +) { } // CaptureExit implements vm.Tracer interface func (dt NoOpTracer) CaptureExit(output []byte, gasUsed uint64, err error) {} // CaptureState implements vm.Tracer interface -func (dt NoOpTracer) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) { +func (dt NoOpTracer) CaptureState( + env *vm.EVM, + pc uint64, + op vm.OpCode, + gas, cost uint64, + scope *vm.ScopeContext, + rData []byte, + depth int, + err error, +) { } // CaptureFault implements vm.Tracer interface -func (dt NoOpTracer) CaptureFault(env *vm.EVM, pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) { +func (dt NoOpTracer) CaptureFault( + env *vm.EVM, + pc uint64, + op vm.OpCode, + gas, cost uint64, + scope *vm.ScopeContext, + depth int, + err error, +) { } // CaptureEnd implements vm.Tracer interface -func (dt NoOpTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {} +func (dt NoOpTracer) CaptureEnd( + output []byte, + gasUsed uint64, + t time.Duration, + err error, +) { +} diff --git a/x/evm/types/tracer_test.go b/x/evm/types/tracer_test.go new file mode 100644 index 0000000000..ad7723846a --- /dev/null +++ b/x/evm/types/tracer_test.go @@ -0,0 +1,83 @@ +package types + +import ( + "fmt" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/holiman/uint256" + "github.com/stretchr/testify/require" +) + +func TestFormatLogs(t *testing.T) { + zeroUint256 := []uint256.Int{*uint256.NewInt(0)} + zeroByte := []byte{5} + zeroStorage := make(map[string]string) + + testCases := []struct { + name string + logs []vm.StructLog + exp []StructLogRes + }{ + { + "empty logs", + []vm.StructLog{}, + []StructLogRes{}, + }, + { + "non-empty stack", + []vm.StructLog{ + { + Stack: zeroUint256, + }, + }, + []StructLogRes{ + { + Pc: uint64(0), + Op: "STOP", + Stack: &[]string{fmt.Sprintf("%x", zeroUint256[0])}, + }, + }, + }, + { + "non-empty memory", + []vm.StructLog{ + { + Memory: zeroByte, + }, + }, + []StructLogRes{ + { + Pc: uint64(0), + Op: "STOP", + Memory: &[]string{}, + }, + }, + }, + { + "non-empty storage", + []vm.StructLog{ + { + Storage: make(map[common.Hash]common.Hash), + }, + }, + []StructLogRes{ + { + Pc: uint64(0), + Op: "STOP", + Storage: &zeroStorage, + }, + }, + }, + } + for _, tc := range testCases { + actual := FormatLogs(tc.logs) + + require.Equal(t, tc.exp, actual) + } +} + +func TestNewNoOpTracer(t *testing.T) { + require.Equal(t, &NoOpTracer{}, NewNoOpTracer()) +}