package ante_test import ( "math/big" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/tharsis/ethermint/app/ante" "github.com/tharsis/ethermint/tests" "github.com/tharsis/ethermint/x/evm/statedb" evmtypes "github.com/tharsis/ethermint/x/evm/types" ethtypes "github.com/ethereum/go-ethereum/core/types" ) func nextFn(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) { return ctx, nil } func (suite AnteTestSuite) TestEthSigVerificationDecorator() { dec := ante.NewEthSigVerificationDecorator(suite.app.EvmKeeper) addr, privKey := tests.NewAddrKey() signedTx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) signedTx.From = addr.Hex() err := signedTx.Sign(suite.ethSigner, tests.NewSigner(privKey)) suite.Require().NoError(err) testCases := []struct { name string tx sdk.Tx reCheckTx bool expPass bool }{ {"ReCheckTx", &invalidTx{}, true, false}, {"invalid transaction type", &invalidTx{}, false, false}, { "invalid sender", evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &addr, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil), false, false, }, {"successful signature verification", signedTx, false, true}, } for _, tc := range testCases { suite.Run(tc.name, func() { _, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, nextFn) if tc.expPass { suite.Require().NoError(err) } else { suite.Require().Error(err) } }) } } func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() { dec := ante.NewEthAccountVerificationDecorator( suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.EvmKeeper, ) addr := tests.GenerateAddress() tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) tx.From = addr.Hex() var vmdb *statedb.StateDB testCases := []struct { name string tx sdk.Tx malleate func() checkTx bool expPass bool }{ {"not CheckTx", nil, func() {}, false, true}, {"invalid transaction type", &invalidTx{}, func() {}, true, false}, { "sender not set to msg", evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil), func() {}, true, false, }, { "sender not EOA", tx, func() { // set not as an EOA vmdb.SetCode(addr, []byte("1")) }, true, false, }, { "not enough balance to cover tx cost", tx, func() { // reset back to EOA vmdb.SetCode(addr, nil) }, true, false, }, { "success new account", tx, func() { vmdb.AddBalance(addr, big.NewInt(1000000)) }, true, true, }, { "success existing account", tx, func() { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes()) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) vmdb.AddBalance(addr, big.NewInt(1000000)) }, true, true, }, } for _, tc := range testCases { suite.Run(tc.name, func() { vmdb = suite.StateDB() tc.malleate() suite.Require().NoError(vmdb.Commit()) _, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(tc.checkTx), tc.tx, false, nextFn) if tc.expPass { suite.Require().NoError(err) } else { suite.Require().Error(err) } }) } } func (suite AnteTestSuite) TestEthNonceVerificationDecorator() { suite.SetupTest() dec := ante.NewEthIncrementSenderSequenceDecorator(suite.app.AccountKeeper) addr := tests.GenerateAddress() tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) tx.From = addr.Hex() testCases := []struct { name string tx sdk.Tx malleate func() reCheckTx bool expPass bool }{ {"ReCheckTx", &invalidTx{}, func() {}, true, false}, {"invalid transaction type", &invalidTx{}, func() {}, false, false}, {"sender account not found", tx, func() {}, false, false}, { "sender nonce missmatch", tx, func() { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes()) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) }, false, false, }, { "success", tx, func() { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes()) suite.Require().NoError(acc.SetSequence(1)) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) }, false, true, }, } for _, tc := range testCases { suite.Run(tc.name, func() { tc.malleate() _, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, nextFn) if tc.expPass { suite.Require().NoError(err) } else { suite.Require().Error(err) } }) } } func (suite AnteTestSuite) TestEthGasConsumeDecorator() { dec := ante.NewEthGasConsumeDecorator(suite.app.EvmKeeper) addr := tests.GenerateAddress() txGasLimit := uint64(1000) tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), txGasLimit, big.NewInt(1), nil, nil, nil, nil) tx.From = addr.Hex() tx2GasLimit := uint64(1000000) tx2 := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), tx2GasLimit, big.NewInt(1), nil, nil, nil, ðtypes.AccessList{{Address: addr, StorageKeys: nil}}) tx2.From = addr.Hex() var vmdb *statedb.StateDB testCases := []struct { name string tx sdk.Tx gasLimit uint64 malleate func() expPass bool expPanic bool }{ {"invalid transaction type", &invalidTx{}, 0, func() {}, false, false}, { "sender not found", evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil), 0, func() {}, false, false, }, { "gas limit too low", tx, 0, func() {}, false, false, }, { "not enough balance for fees", tx2, 0, func() {}, false, false, }, { "not enough tx gas", tx2, 0, func() { vmdb.AddBalance(addr, big.NewInt(1000000)) }, false, true, }, { "not enough block gas", tx2, 0, func() { vmdb.AddBalance(addr, big.NewInt(1000000)) suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1)) }, false, true, }, { "success", tx2, ante.MaxTxGasWanted, // it's capped func() { vmdb.AddBalance(addr, big.NewInt(1000000)) suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(10000000000000000000)) }, true, false, }, } for _, tc := range testCases { suite.Run(tc.name, func() { vmdb = suite.StateDB() tc.malleate() suite.Require().NoError(vmdb.Commit()) if tc.expPanic { suite.Require().Panics(func() { _, _ = dec.AnteHandle(suite.ctx.WithIsCheckTx(true).WithGasMeter(sdk.NewGasMeter(1)), tc.tx, false, nextFn) }) return } ctx, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(true).WithGasMeter(sdk.NewInfiniteGasMeter()), tc.tx, false, nextFn) if tc.expPass { suite.Require().NoError(err) } else { suite.Require().Error(err) } suite.Require().Equal(tc.gasLimit, ctx.GasMeter().Limit()) }) } } func (suite AnteTestSuite) TestCanTransferDecorator() { dec := ante.NewCanTransferDecorator(suite.app.EvmKeeper) addr, privKey := tests.NewAddrKey() suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, big.NewInt(100)) tx := evmtypes.NewTxContract( suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(150), big.NewInt(200), nil, nil, ðtypes.AccessList{}, ) tx2 := evmtypes.NewTxContract( suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(150), big.NewInt(200), nil, nil, ðtypes.AccessList{}, ) tx.From = addr.Hex() err := tx.Sign(suite.ethSigner, tests.NewSigner(privKey)) suite.Require().NoError(err) var vmdb *statedb.StateDB testCases := []struct { name string tx sdk.Tx malleate func() expPass bool }{ {"invalid transaction type", &invalidTx{}, func() {}, false}, {"AsMessage failed", tx2, func() {}, false}, { "evm CanTransfer failed", tx, func() { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes()) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) }, false, }, { "success", tx, func() { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes()) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) vmdb.AddBalance(addr, big.NewInt(1000000)) }, true, }, } for _, tc := range testCases { suite.Run(tc.name, func() { vmdb = suite.StateDB() tc.malleate() suite.Require().NoError(vmdb.Commit()) _, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(true), tc.tx, false, nextFn) if tc.expPass { suite.Require().NoError(err) } else { suite.Require().Error(err) } }) } } func (suite AnteTestSuite) TestEthIncrementSenderSequenceDecorator() { dec := ante.NewEthIncrementSenderSequenceDecorator(suite.app.AccountKeeper) addr, privKey := tests.NewAddrKey() contract := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 0, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) contract.From = addr.Hex() err := contract.Sign(suite.ethSigner, tests.NewSigner(privKey)) suite.Require().NoError(err) to := tests.GenerateAddress() tx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 0, &to, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) tx.From = addr.Hex() err = tx.Sign(suite.ethSigner, tests.NewSigner(privKey)) suite.Require().NoError(err) tx2 := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &to, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) tx2.From = addr.Hex() err = tx2.Sign(suite.ethSigner, tests.NewSigner(privKey)) suite.Require().NoError(err) testCases := []struct { name string tx sdk.Tx malleate func() expPass bool expPanic bool }{ { "invalid transaction type", &invalidTx{}, func() {}, false, false, }, { "no signers", evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &to, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil), func() {}, false, false, }, { "account not set to store", tx, func() {}, false, false, }, { "success - create contract", contract, func() { acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes()) suite.app.AccountKeeper.SetAccount(suite.ctx, acc) }, true, false, }, { "success - call", tx2, func() {}, true, false, }, } for _, tc := range testCases { suite.Run(tc.name, func() { tc.malleate() if tc.expPanic { suite.Require().Panics(func() { _, _ = dec.AnteHandle(suite.ctx, tc.tx, false, nextFn) }) return } _, err := dec.AnteHandle(suite.ctx, tc.tx, false, nextFn) if tc.expPass { suite.Require().NoError(err) msg := tc.tx.(*evmtypes.MsgEthereumTx) txData, err := evmtypes.UnpackTxData(msg.Data) suite.Require().NoError(err) nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, addr) suite.Require().Equal(txData.GetNonce()+1, nonce) } else { suite.Require().Error(err) } }) } } func (suite AnteTestSuite) TestEthSetupContextDecorator() { dec := ante.NewEthSetUpContextDecorator(suite.app.EvmKeeper) tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) testCases := []struct { name string tx sdk.Tx expPass bool }{ {"invalid transaction type - does not implement GasTx", &invalidTx{}, false}, { "success - transaction implement GasTx", tx, true, }, } for _, tc := range testCases { suite.Run(tc.name, func() { _, err := dec.AnteHandle(suite.ctx, tc.tx, false, nextFn) if tc.expPass { suite.Require().NoError(err) } else { suite.Require().Error(err) } }) } }