package ante_test import ( "math" "math/big" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cerc-io/laconicd/app/ante" "github.com/cerc-io/laconicd/server/config" "github.com/cerc-io/laconicd/tests" "github.com/cerc-io/laconicd/x/evm/statedb" evmtypes "github.com/cerc-io/laconicd/x/evm/types" ethtypes "github.com/ethereum/go-ethereum/core/types" ) func (suite AnteTestSuite) TestEthSigVerificationDecorator() { 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) unprotectedTx := evmtypes.NewTxContract(nil, 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil) unprotectedTx.From = addr.Hex() err = unprotectedTx.Sign(ethtypes.HomesteadSigner{}, tests.NewSigner(privKey)) suite.Require().NoError(err) testCases := []struct { name string tx sdk.Tx allowUnprotectedTxs bool reCheckTx bool expPass bool }{ {"ReCheckTx", &invalidTx{}, false, true, false}, {"invalid transaction type", &invalidTx{}, false, false, false}, { "invalid sender", evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &addr, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil), true, false, false, }, {"successful signature verification", signedTx, false, false, true}, {"invalid, reject unprotected txs", unprotectedTx, false, false, false}, {"successful, allow unprotected txs", unprotectedTx, true, false, true}, } for _, tc := range testCases { suite.Run(tc.name, func() { suite.evmParamsOption = func(params *evmtypes.Params) { params.AllowUnprotectedTxs = tc.allowUnprotectedTxs } suite.SetupTest() dec := ante.NewEthSigVerificationDecorator(suite.app.EvmKeeper) _, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, NextFn) if tc.expPass { suite.Require().NoError(err) } else { suite.Require().Error(err) } }) } suite.evmParamsOption = nil } func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() { dec := ante.NewEthAccountVerificationDecorator( suite.app.AccountKeeper, 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, config.DefaultMaxTxGasWanted) 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() ethCfg := suite.app.EvmKeeper.GetParams(suite.ctx). ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) baseFee := suite.app.EvmKeeper.GetBaseFee(suite.ctx, ethCfg) suite.Require().Equal(int64(1000000000), baseFee.Int64()) gasPrice := new(big.Int).Add(baseFee, evmtypes.DefaultPriorityReduction.BigInt()) tx2GasLimit := uint64(1000000) tx2 := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), tx2GasLimit, gasPrice, nil, nil, nil, ðtypes.AccessList{{Address: addr, StorageKeys: nil}}) tx2.From = addr.Hex() tx2Priority := int64(1) dynamicFeeTx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), tx2GasLimit, nil, // gasPrice new(big.Int).Add(baseFee, big.NewInt(evmtypes.DefaultPriorityReduction.Int64()*2)), // gasFeeCap evmtypes.DefaultPriorityReduction.BigInt(), // gasTipCap nil, ðtypes.AccessList{{Address: addr, StorageKeys: nil}}) dynamicFeeTx.From = addr.Hex() dynamicFeeTxPriority := int64(1) var vmdb *statedb.StateDB testCases := []struct { name string tx sdk.Tx gasLimit uint64 malleate func() expPass bool expPanic bool expPriority int64 }{ {"invalid transaction type", &invalidTx{}, math.MaxUint64, func() {}, false, false, 0}, { "sender not found", evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil), math.MaxUint64, func() {}, false, false, 0, }, { "gas limit too low", tx, math.MaxUint64, func() {}, false, false, 0, }, { "not enough balance for fees", tx2, math.MaxUint64, func() {}, false, false, 0, }, { "not enough tx gas", tx2, 0, func() { vmdb.AddBalance(addr, big.NewInt(1000000)) }, false, true, 0, }, { "not enough block gas", tx2, 0, func() { vmdb.AddBalance(addr, big.NewInt(1000000)) suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1)) }, false, true, 0, }, { "success - legacy tx", tx2, tx2GasLimit, // it's capped func() { vmdb.AddBalance(addr, big.NewInt(1001000000000000)) suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(10000000000000000000)) }, true, false, tx2Priority, }, { "success - dynamic fee tx", dynamicFeeTx, tx2GasLimit, // it's capped func() { vmdb.AddBalance(addr, big.NewInt(1001000000000000)) suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(10000000000000000000)) }, true, false, dynamicFeeTxPriority, }, } 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) suite.Require().Equal(tc.expPriority, ctx.Priority()) } 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) } }) } }