update fork

This commit is contained in:
0xmuralik
2022-10-10 16:08:33 +05:30
parent 632d53e34a
commit 56d59feaa0
383 changed files with 36784 additions and 21462 deletions
+9 -2
View File
@@ -23,7 +23,11 @@ const (
// Ethereum or SDK transaction to an internal ante handler for performing
// transaction-level processing (e.g. fee payment, signature verification) before
// being passed onto it's respective handler.
func NewAnteHandler(options HandlerOptions) sdk.AnteHandler {
func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
if err := options.validate(); err != nil {
return nil, err
}
return func(
ctx sdk.Context, tx sdk.Tx, sim bool,
) (newCtx sdk.Context, err error) {
@@ -42,6 +46,9 @@ func NewAnteHandler(options HandlerOptions) sdk.AnteHandler {
case "/ethermint.types.v1.ExtensionOptionsWeb3Tx":
// handle as normal Cosmos SDK tx, except signature is checked for EIP712 representation
anteHandler = newCosmosAnteHandlerEip712(options)
case "/ethermint.types.v1.ExtensionOptionDynamicFeeTx":
// cosmos-sdk tx with dynamic fee extension
anteHandler = newCosmosAnteHandler(options)
default:
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrUnknownExtensionOptions,
@@ -62,7 +69,7 @@ func NewAnteHandler(options HandlerOptions) sdk.AnteHandler {
}
return anteHandler(ctx, tx, sim)
}
}, nil
}
func Recover(logger tmlog.Logger, err *error) {
+380 -152
View File
@@ -1,10 +1,16 @@
package ante_test
import (
"errors"
"math/big"
"strings"
"time"
sdkmath "cosmossdk.io/math"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/authz"
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -12,22 +18,27 @@ import (
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
"github.com/ethereum/go-ethereum/core/types"
ethparams "github.com/ethereum/go-ethereum/params"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
func (suite AnteTestSuite) TestAnteHandler() {
suite.enableFeemarket = false
suite.SetupTest() // reset
var acc authtypes.AccountI
addr, privKey := tests.NewAddrKey()
to := tests.GenerateAddress()
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
setup := func() {
suite.enableFeemarket = false
suite.SetupTest() // reset
suite.app.EvmKeeper.SetBalance(suite.ctx, addr, big.NewInt(10000000000))
acc = suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, big.NewInt(100))
suite.app.EvmKeeper.SetBalance(suite.ctx, addr, big.NewInt(10000000000))
suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, big.NewInt(100))
}
testCases := []struct {
name string
@@ -62,7 +73,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
func() sdk.Tx {
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
2,
1,
big.NewInt(10),
100000,
big.NewInt(150),
@@ -83,7 +94,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
func() sdk.Tx {
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
3,
1,
big.NewInt(10),
100000,
big.NewInt(150),
@@ -104,7 +115,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
func() sdk.Tx {
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
4,
1,
&to,
big.NewInt(10),
100000,
@@ -126,7 +137,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
func() sdk.Tx {
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
5,
1,
&to,
big.NewInt(10),
100000,
@@ -148,7 +159,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
func() sdk.Tx {
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
6,
1,
&to,
big.NewInt(10),
100000,
@@ -169,7 +180,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
func() sdk.Tx {
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
7,
1,
&to,
big.NewInt(10),
100000,
@@ -188,7 +199,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
{
"fail - CheckTx (cosmos tx is not valid)",
func() sdk.Tx {
signedTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 8, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil)
signedTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil)
signedTx.From = addr.Hex()
txBuilder := suite.CreateTestTxBuilder(signedTx, privKey, 1, false)
@@ -200,7 +211,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
{
"fail - CheckTx (memo too long)",
func() sdk.Tx {
signedTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 5, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil)
signedTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil)
signedTx.From = addr.Hex()
txBuilder := suite.CreateTestTxBuilder(signedTx, privKey, 1, false)
@@ -211,7 +222,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
{
"fail - CheckTx (ExtensionOptionsEthereumTx not set)",
func() sdk.Tx {
signedTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 5, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil)
signedTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil)
signedTx.From = addr.Hex()
txBuilder := suite.CreateTestTxBuilder(signedTx, privKey, 1, false, true)
@@ -273,7 +284,7 @@ func (suite AnteTestSuite) TestAnteHandler() {
expFee := txData.Fee()
invalidFee := new(big.Int).Add(expFee, big.NewInt(1))
invalidFeeAmount := sdk.Coins{sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewIntFromBigInt(invalidFee))}
invalidFeeAmount := sdk.Coins{sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewIntFromBigInt(invalidFee))}
txBuilder.SetFeeAmount(invalidFeeAmount)
return txBuilder.GetTx()
}, false, false, false,
@@ -298,20 +309,95 @@ func (suite AnteTestSuite) TestAnteHandler() {
"success - DeliverTx EIP712 signed Cosmos Tx with MsgSend",
func() sdk.Tx {
from := acc.GetAddress()
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
gas := uint64(200000)
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100*int64(gas))))
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9000-1", gas, amount)
return txBuilder.GetTx()
}, false, false, true,
},
{
"success - DeliverTx EIP712 signed Cosmos Tx with DelegateMsg",
func() sdk.Tx {
from := acc.GetAddress()
gas := uint64(200000)
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100*int64(gas)))
amount := sdk.NewCoins(coinAmount)
txBuilder := suite.CreateTestEIP712TxBuilderMsgDelegate(from, privKey, "ethermint_9000-1", gas, amount)
return txBuilder.GetTx()
}, false, false, true,
},
{
"success- DeliverTx EIP712 create validator",
func() sdk.Tx {
from := acc.GetAddress()
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
amount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
txBuilder := suite.CreateTestEIP712TxBuilderMsgDelegate(from, privKey, "ethermint_9000-1", gas, amount)
txBuilder := suite.CreateTestEIP712MsgCreateValidator(from, privKey, "ethermint_9000-1", gas, amount)
return txBuilder.GetTx()
}, false, false, true,
},
{
"success- DeliverTx EIP712 MsgSubmitProposal",
func() sdk.Tx {
from := acc.GetAddress()
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
gasAmount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
//reusing the gasAmount for deposit
deposit := sdk.NewCoins(coinAmount)
txBuilder := suite.CreateTestEIP712SubmitProposal(from, privKey, "ethermint_9000-1", gas, gasAmount, deposit)
return txBuilder.GetTx()
}, false, false, true,
},
{
"success- DeliverTx EIP712 MsgGrant",
func() sdk.Tx {
from := acc.GetAddress()
grantee := sdk.AccAddress("_______grantee______")
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
gasAmount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
blockTime := time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)
expiresAt := blockTime.Add(time.Hour)
msg, err := authz.NewMsgGrant(
from, grantee, &banktypes.SendAuthorization{SpendLimit: gasAmount}, &expiresAt,
)
suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, privKey, "ethermint_9000-1", gas, gasAmount, msg).GetTx()
}, false, false, true,
},
{
"success- DeliverTx EIP712 MsgGrantAllowance",
func() sdk.Tx {
from := acc.GetAddress()
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
gasAmount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
txBuilder := suite.CreateTestEIP712GrantAllowance(from, privKey, "ethermint_9000-1", gas, gasAmount)
return txBuilder.GetTx()
}, false, false, true,
},
{
"success- DeliverTx EIP712 edit validator",
func() sdk.Tx {
from := acc.GetAddress()
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
amount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
txBuilder := suite.CreateTestEIP712MsgEditValidator(from, privKey, "ethermint_9000-1", gas, amount)
return txBuilder.GetTx()
}, false, false, true,
},
{
"success- DeliverTx EIP712 submit evidence",
func() sdk.Tx {
from := acc.GetAddress()
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20))
amount := sdk.NewCoins(coinAmount)
gas := uint64(200000)
txBuilder := suite.CreateTestEIP712MsgEditValidator(from, privKey, "ethermint_9000-1", gas, amount)
return txBuilder.GetTx()
}, false, false, true,
},
@@ -319,8 +405,8 @@ func (suite AnteTestSuite) TestAnteHandler() {
"fails - DeliverTx EIP712 signed Cosmos Tx with wrong Chain ID",
func() sdk.Tx {
from := acc.GetAddress()
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
gas := uint64(200000)
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100*int64(gas))))
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9002-1", gas, amount)
return txBuilder.GetTx()
}, false, false, false,
@@ -329,11 +415,11 @@ func (suite AnteTestSuite) TestAnteHandler() {
"fails - DeliverTx EIP712 signed Cosmos Tx with different gas fees",
func() sdk.Tx {
from := acc.GetAddress()
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
gas := uint64(200000)
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100*int64(gas))))
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
txBuilder.SetGasLimit(uint64(300000))
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(30))))
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(30))))
return txBuilder.GetTx()
}, false, false, false,
},
@@ -341,8 +427,8 @@ func (suite AnteTestSuite) TestAnteHandler() {
"fails - DeliverTx EIP712 signed Cosmos Tx with empty signature",
func() sdk.Tx {
from := acc.GetAddress()
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
gas := uint64(200000)
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100*int64(gas))))
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
sigsV2 := signing.SignatureV2{}
txBuilder.SetSignatures(sigsV2)
@@ -353,8 +439,8 @@ func (suite AnteTestSuite) TestAnteHandler() {
"fails - DeliverTx EIP712 signed Cosmos Tx with invalid sequence",
func() sdk.Tx {
from := acc.GetAddress()
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
gas := uint64(200000)
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100*int64(gas))))
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, acc.GetAddress())
suite.Require().NoError(err)
@@ -373,8 +459,8 @@ func (suite AnteTestSuite) TestAnteHandler() {
"fails - DeliverTx EIP712 signed Cosmos Tx with invalid signMode",
func() sdk.Tx {
from := acc.GetAddress()
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
gas := uint64(200000)
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(100*int64(gas))))
txBuilder := suite.CreateTestEIP712TxBuilderMsgSend(from, privKey, "ethermint_9001-1", gas, amount)
nonce, err := suite.app.AccountKeeper.GetSequence(suite.ctx, acc.GetAddress())
suite.Require().NoError(err)
@@ -389,10 +475,33 @@ func (suite AnteTestSuite) TestAnteHandler() {
return txBuilder.GetTx()
}, false, false, false,
},
{
"fails - invalid from",
func() sdk.Tx {
msg := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
big.NewInt(150),
big.NewInt(200),
nil,
nil,
nil,
)
msg.From = addr.Hex()
tx := suite.CreateTestTx(msg, privKey, 1, false)
msg = tx.GetMsgs()[0].(*evmtypes.MsgEthereumTx)
msg.From = addr.Hex()
return tx
}, true, false, false,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
setup()
suite.ctx = suite.ctx.WithIsCheckTx(tc.checkTx).WithIsReCheckTx(tc.reCheckTx)
// expConsumed := params.TxGasContractCreation + params.TxGas
@@ -425,18 +534,17 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"success - DeliverTx (contract)",
func() sdk.Tx {
signedContractTx :=
evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx.From = addr.Hex()
tx := suite.CreateTestTx(signedContractTx, privKey, 1, false)
@@ -448,18 +556,17 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"success - CheckTx (contract)",
func() sdk.Tx {
signedContractTx :=
evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx.From = addr.Hex()
tx := suite.CreateTestTx(signedContractTx, privKey, 1, false)
@@ -471,18 +578,17 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"success - ReCheckTx (contract)",
func() sdk.Tx {
signedContractTx :=
evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx.From = addr.Hex()
tx := suite.CreateTestTx(signedContractTx, privKey, 1, false)
@@ -494,19 +600,18 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"success - DeliverTx",
func() sdk.Tx {
signedTx :=
evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
tx := suite.CreateTestTx(signedTx, privKey, 1, false)
@@ -518,19 +623,18 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"success - CheckTx",
func() sdk.Tx {
signedTx :=
evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
tx := suite.CreateTestTx(signedTx, privKey, 1, false)
@@ -542,19 +646,18 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"success - ReCheckTx",
func() sdk.Tx {
signedTx :=
evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
tx := suite.CreateTestTx(signedTx, privKey, 1, false)
@@ -566,19 +669,18 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"success - CheckTx (cosmos tx not signed)",
func() sdk.Tx {
signedTx :=
evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
tx := suite.CreateTestTx(signedTx, privKey, 1, false)
@@ -590,19 +692,18 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"fail - CheckTx (cosmos tx is not valid)",
func() sdk.Tx {
signedTx :=
evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
txBuilder := suite.CreateTestTxBuilder(signedTx, privKey, 1, false)
@@ -616,19 +717,18 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"fail - CheckTx (memo too long)",
func() sdk.Tx {
signedTx :=
evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
txBuilder := suite.CreateTestTxBuilder(signedTx, privKey, 1, false)
@@ -641,18 +741,17 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
{
"fail - DynamicFeeTx without london hark fork",
func() sdk.Tx {
signedContractTx :=
evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx.From = addr.Hex()
tx := suite.CreateTestTx(signedContractTx, privKey, 1, false)
@@ -686,3 +785,132 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() {
suite.enableFeemarket = false
suite.enableLondonHF = true
}
func (suite AnteTestSuite) TestAnteHandlerWithParams() {
addr, privKey := tests.NewAddrKey()
to := tests.GenerateAddress()
testCases := []struct {
name string
txFn func() sdk.Tx
enableCall bool
enableCreate bool
expErr error
}{
{
"fail - Contract Creation Disabled",
func() sdk.Tx {
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx.From = addr.Hex()
tx := suite.CreateTestTx(signedContractTx, privKey, 1, false)
return tx
},
true, false,
evmtypes.ErrCreateDisabled,
},
{
"success - Contract Creation Enabled",
func() sdk.Tx {
signedContractTx := evmtypes.NewTxContract(
suite.app.EvmKeeper.ChainID(),
1,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedContractTx.From = addr.Hex()
tx := suite.CreateTestTx(signedContractTx, privKey, 1, false)
return tx
},
true, true,
nil,
},
{
"fail - EVM Call Disabled",
func() sdk.Tx {
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
tx := suite.CreateTestTx(signedTx, privKey, 1, false)
return tx
},
false, true,
evmtypes.ErrCallDisabled,
},
{
"success - EVM Call Enabled",
func() sdk.Tx {
signedTx := evmtypes.NewTx(
suite.app.EvmKeeper.ChainID(),
1,
&to,
big.NewInt(10),
100000,
nil,
big.NewInt(ethparams.InitialBaseFee+1),
big.NewInt(1),
nil,
&types.AccessList{},
)
signedTx.From = addr.Hex()
tx := suite.CreateTestTx(signedTx, privKey, 1, false)
return tx
},
true, true,
nil,
},
}
for _, tc := range testCases {
suite.Run(tc.name, func() {
suite.evmParamsOption = func(params *evmtypes.Params) {
params.EnableCall = tc.enableCall
params.EnableCreate = tc.enableCreate
}
suite.SetupTest() // reset
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes())
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
suite.ctx = suite.ctx.WithIsCheckTx(true)
suite.app.EvmKeeper.SetBalance(suite.ctx, addr, big.NewInt((ethparams.InitialBaseFee+10)*100000))
_, err := suite.anteHandler(suite.ctx, tc.txFn(), false)
if tc.expErr == nil {
suite.Require().NoError(err)
} else {
suite.Require().Error(err)
suite.Require().True(errors.Is(err, tc.expErr))
}
})
}
suite.evmParamsOption = nil
}
+2 -1
View File
@@ -1,4 +1,5 @@
/*Package ante defines the SDK auth module's AnteHandler as well as an internal
/*
Package ante defines the SDK auth module's AnteHandler as well as an internal
AnteHandler for an Ethereum transaction (i.e MsgEthereumTx).
During CheckTx, the transaction is passed through a series of
+6 -8
View File
@@ -49,7 +49,11 @@ func NewEip712SigVerificationDecorator(ak evmtypes.AccountKeeper, signModeHandle
// AnteHandle handles validation of EIP712 signed cosmos txs.
// it is not run on RecheckTx
func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
func (svd Eip712SigVerificationDecorator) AnteHandle(ctx sdk.Context,
tx sdk.Tx,
simulate bool,
next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
// no need to verify signatures on recheck tx
if ctx.IsReCheckTx() {
return next(ctx, tx, simulate)
@@ -188,13 +192,7 @@ func VerifySignature(
return sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, "tx doesnt contain expected amount of extension options")
}
var optIface ethermint.ExtensionOptionsWeb3TxI
if err := ethermintCodec.UnpackAny(opts[0], &optIface); err != nil {
return sdkerrors.Wrap(err, "failed to proto-unpack ExtensionOptionsWeb3Tx")
}
extOpt, ok := optIface.(*ethermint.ExtensionOptionsWeb3Tx)
extOpt, ok := opts[0].GetCachedValue().(*ethermint.ExtensionOptionsWeb3Tx)
if !ok {
return sdkerrors.Wrap(sdkerrors.ErrInvalidChainID, "unknown extension option")
}
+136 -66
View File
@@ -2,7 +2,11 @@ package ante
import (
"errors"
"math"
"math/big"
"strconv"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -49,12 +53,18 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
sender, err := signer.Sender(msgEthTx.AsTransaction())
ethTx := msgEthTx.AsTransaction()
if !params.AllowUnprotectedTxs && !ethTx.Protected() {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrNotSupported,
"rejected unprotected Ethereum txs. Please EIP155 sign your transaction to protect it against replay-attacks")
}
sender, err := signer.Sender(ethTx)
if err != nil {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrorInvalidSigner,
"couldn't retrieve sender address ('%s') from the ethereum transaction: %s",
msgEthTx.From,
"couldn't retrieve sender address from the ethereum transaction: %s",
err.Error(),
)
}
@@ -68,17 +78,15 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
// EthAccountVerificationDecorator validates an account balance checks
type EthAccountVerificationDecorator struct {
ak evmtypes.AccountKeeper
bankKeeper evmtypes.BankKeeper
evmKeeper EVMKeeper
ak evmtypes.AccountKeeper
evmKeeper EVMKeeper
}
// NewEthAccountVerificationDecorator creates a new EthAccountVerificationDecorator
func NewEthAccountVerificationDecorator(ak evmtypes.AccountKeeper, bankKeeper evmtypes.BankKeeper, ek EVMKeeper) EthAccountVerificationDecorator {
func NewEthAccountVerificationDecorator(ak evmtypes.AccountKeeper, ek EVMKeeper) EthAccountVerificationDecorator {
return EthAccountVerificationDecorator{
ak: ak,
bankKeeper: bankKeeper,
evmKeeper: ek,
ak: ak,
evmKeeper: ek,
}
}
@@ -88,7 +96,12 @@ func NewEthAccountVerificationDecorator(ak evmtypes.AccountKeeper, bankKeeper ev
// - any of the msgs is not a MsgEthereumTx
// - from address is empty
// - account balance is lower than the transaction cost
func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
func (avd EthAccountVerificationDecorator) AnteHandle(
ctx sdk.Context,
tx sdk.Tx,
simulate bool,
next sdk.AnteHandler,
) (newCtx sdk.Context, err error) {
if !ctx.IsCheckTx() {
return next(ctx, tx, simulate)
}
@@ -123,10 +136,9 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx
"the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash)
}
if err := evmkeeper.CheckSenderBalance(sdk.NewIntFromBigInt(acct.Balance), txData); err != nil {
if err := evmkeeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(acct.Balance), txData); err != nil {
return ctx, sdkerrors.Wrap(err, "failed to check sender balance")
}
}
return next(ctx, tx, simulate)
}
@@ -163,7 +175,7 @@ func NewEthGasConsumeDecorator(
// - user doesn't have enough balance to deduct the transaction fees (gas_limit * gas_price)
// - transaction or block gas meter runs out of gas
// - sets the gas meter limit
func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
params := egcd.evmKeeper.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(egcd.evmKeeper.ChainID())
@@ -176,6 +188,9 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
gasWanted := uint64(0)
var events sdk.Events
// Use the lowest priority of all the messages as the final one.
minPriority := int64(math.MaxInt64)
for _, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
@@ -187,7 +202,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
return ctx, sdkerrors.Wrap(err, "failed to unpack tx data")
}
if ctx.IsCheckTx() {
if ctx.IsCheckTx() && egcd.maxGasWanted != 0 {
// We can't trust the tx gas limit, because we'll refund the unused gas.
if txData.GetGas() > egcd.maxGasWanted {
gasWanted += egcd.maxGasWanted
@@ -198,7 +213,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
gasWanted += txData.GetGas()
}
fees, err := egcd.evmKeeper.DeductTxCostsFromUserBalance(
fees, priority, err := egcd.evmKeeper.DeductTxCostsFromUserBalance(
ctx,
*msgEthTx,
txData,
@@ -212,6 +227,9 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
}
events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeyFee, fees.String())))
if priority < minPriority {
minPriority = priority
}
}
// TODO: change to typed events
@@ -233,8 +251,10 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
ctx = ctx.WithGasMeter(ethermint.NewInfiniteGasMeterWithLimit(gasWanted))
ctx.GasMeter().ConsumeGas(gasConsumed, "copy gas consumed")
newCtx := ctx.WithPriority(minPriority)
// we know that we have enough gas on the pool to cover the intrinsic gas
return next(ctx, tx, simulate)
return next(newCtx, tx, simulate)
}
// CanTransferDecorator checks if the sender is allowed to transfer funds according to the EVM block
@@ -263,7 +283,7 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
baseFee := ctd.evmKeeper.BaseFee(ctx, ethCfg)
baseFee := ctd.evmKeeper.GetBaseFee(ctx, ethCfg)
coreMsg, err := msgEthTx.AsMessage(signer, baseFee)
if err != nil {
@@ -285,7 +305,7 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
// check that caller has enough balance to cover asset transfer for **topmost** call
// NOTE: here the gas consumed is from the context with the infinite gas meter
if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) {
if coreMsg.Value().Sign() > 0 && !evm.Context().CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrInsufficientFunds,
"failed to transfer %s from address %s using the EVM block context transfer function",
@@ -397,65 +417,82 @@ func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu
// For eth type cosmos tx, some fields should be veified as zero values,
// since we will only verify the signature against the hash of the MsgEthereumTx.Data
if wrapperTx, ok := tx.(protoTxProvider); ok {
protoTx := wrapperTx.GetProtoTx()
body := protoTx.Body
if body.Memo != "" || body.TimeoutHeight != uint64(0) || len(body.NonCriticalExtensionOptions) > 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
"for eth tx body Memo TimeoutHeight NonCriticalExtensionOptions should be empty")
wrapperTx, ok := tx.(protoTxProvider)
if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid tx type %T, didn't implement interface protoTxProvider", tx)
}
protoTx := wrapperTx.GetProtoTx()
body := protoTx.Body
if body.Memo != "" || body.TimeoutHeight != uint64(0) || len(body.NonCriticalExtensionOptions) > 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest,
"for eth tx body Memo TimeoutHeight NonCriticalExtensionOptions should be empty")
}
if len(body.ExtensionOptions) != 1 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx length of ExtensionOptions should be 1")
}
txFee := sdk.Coins{}
txGasLimit := uint64(0)
params := vbd.evmKeeper.GetParams(ctx)
chainID := vbd.evmKeeper.ChainID()
ethCfg := params.ChainConfig.EthereumConfig(chainID)
baseFee := vbd.evmKeeper.GetBaseFee(ctx, ethCfg)
for _, msg := range protoTx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
if len(body.ExtensionOptions) != 1 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx length of ExtensionOptions should be 1")
// Validate `From` field
if msgEthTx.From != "" {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid From %s, expect empty string", msgEthTx.From)
}
txFee := sdk.Coins{}
txGasLimit := uint64(0)
txGasLimit += msgEthTx.GetGas()
for _, msg := range protoTx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
txGasLimit += msgEthTx.GetGas()
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
if err != nil {
return ctx, sdkerrors.Wrap(err, "failed to unpack MsgEthereumTx Data")
}
params := vbd.evmKeeper.GetParams(ctx)
chainID := vbd.evmKeeper.ChainID()
ethCfg := params.ChainConfig.EthereumConfig(chainID)
baseFee := vbd.evmKeeper.BaseFee(ctx, ethCfg)
if baseFee == nil && txData.TxType() == ethtypes.DynamicFeeTxType {
return ctx, sdkerrors.Wrap(ethtypes.ErrTxTypeNotSupported, "dynamic fee tx not supported")
}
txFee = txFee.Add(sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(txData.Fee())))
txData, err := evmtypes.UnpackTxData(msgEthTx.Data)
if err != nil {
return ctx, sdkerrors.Wrap(err, "failed to unpack MsgEthereumTx Data")
}
authInfo := protoTx.AuthInfo
if len(authInfo.SignerInfos) > 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo SignerInfos should be empty")
// return error if contract creation or call are disabled through governance
if !params.EnableCreate && txData.GetTo() == nil {
return ctx, sdkerrors.Wrap(evmtypes.ErrCreateDisabled, "failed to create new contract")
} else if !params.EnableCall && txData.GetTo() != nil {
return ctx, sdkerrors.Wrap(evmtypes.ErrCallDisabled, "failed to call contract")
}
if authInfo.Fee.Payer != "" || authInfo.Fee.Granter != "" {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo Fee payer and granter should be empty")
if baseFee == nil && txData.TxType() == ethtypes.DynamicFeeTxType {
return ctx, sdkerrors.Wrap(ethtypes.ErrTxTypeNotSupported, "dynamic fee tx not supported")
}
if !authInfo.Fee.Amount.IsEqual(txFee) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid AuthInfo Fee Amount (%s != %s)", authInfo.Fee.Amount, txFee)
}
txFee = txFee.Add(sdk.NewCoin(params.EvmDenom, sdkmath.NewIntFromBigInt(txData.Fee())))
}
if authInfo.Fee.GasLimit != txGasLimit {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid AuthInfo Fee GasLimit (%d != %d)", authInfo.Fee.GasLimit, txGasLimit)
}
authInfo := protoTx.AuthInfo
if len(authInfo.SignerInfos) > 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo SignerInfos should be empty")
}
sigs := protoTx.Signatures
if len(sigs) > 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx Signatures should be empty")
}
if authInfo.Fee.Payer != "" || authInfo.Fee.Granter != "" {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx AuthInfo Fee payer and granter should be empty")
}
if !authInfo.Fee.Amount.IsEqual(txFee) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid AuthInfo Fee Amount (%s != %s)", authInfo.Fee.Amount, txFee)
}
if authInfo.Fee.GasLimit != txGasLimit {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid AuthInfo Fee GasLimit (%d != %d)", authInfo.Fee.GasLimit, txGasLimit)
}
sigs := protoTx.Signatures
if len(sigs) > 0 {
return ctx, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "for eth tx Signatures should be empty")
}
return next(ctx, tx, simulate)
@@ -511,7 +548,7 @@ func (mfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat
if ctx.IsCheckTx() && !simulate {
params := mfd.evmKeeper.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(mfd.evmKeeper.ChainID())
baseFee := mfd.evmKeeper.BaseFee(ctx, ethCfg)
baseFee := mfd.evmKeeper.GetBaseFee(ctx, ethCfg)
if baseFee == nil {
for _, msg := range tx.GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
@@ -532,3 +569,36 @@ func (mfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulat
return next(ctx, tx, simulate)
}
// EthEmitEventDecorator emit events in ante handler in case of tx execution failed (out of block gas limit).
type EthEmitEventDecorator struct {
evmKeeper EVMKeeper
}
// NewEthEmitEventDecorator creates a new EthEmitEventDecorator
func NewEthEmitEventDecorator(evmKeeper EVMKeeper) EthEmitEventDecorator {
return EthEmitEventDecorator{evmKeeper}
}
// AnteHandle emits some basic events for the eth messages
func (eeed EthEmitEventDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
// After eth tx passed ante handler, the fee is deducted and nonce increased, it shouldn't be ignored by json-rpc,
// we need to emit some basic events at the very end of ante handler to be indexed by tendermint.
txIndex := eeed.evmKeeper.GetTxIndexTransient(ctx)
for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil))
}
// emit ethereum tx hash as event, should be indexed by tm tx indexer for query purpose.
// it's emitted in ante handler so we can query failed transaction (out of block gas limit).
ctx.EventManager().EmitEvent(sdk.NewEvent(
evmtypes.EventTypeEthereumTx,
sdk.NewAttribute(evmtypes.AttributeKeyEthereumTxHash, msgEthTx.Hash),
sdk.NewAttribute(evmtypes.AttributeKeyTxIndex, strconv.FormatUint(txIndex+uint64(i), 10)),
))
}
return next(ctx, tx, simulate)
}
+83 -40
View File
@@ -1,6 +1,7 @@
package ante_test
import (
"math"
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
@@ -14,12 +15,7 @@ import (
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)
@@ -27,26 +23,40 @@ func (suite AnteTestSuite) TestEthSigVerificationDecorator() {
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
reCheckTx bool
expPass bool
name string
tx sdk.Tx
allowUnprotectedTxs bool
reCheckTx bool
expPass bool
}{
{"ReCheckTx", &invalidTx{}, true, false},
{"invalid transaction type", &invalidTx{}, false, false},
{"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, true},
{"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() {
_, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, nextFn)
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)
@@ -55,11 +65,12 @@ func (suite AnteTestSuite) TestEthSigVerificationDecorator() {
}
})
}
suite.evmParamsOption = nil
}
func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
dec := ante.NewEthAccountVerificationDecorator(
suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.EvmKeeper,
suite.app.AccountKeeper, suite.app.EvmKeeper,
)
addr := tests.GenerateAddress()
@@ -134,7 +145,7 @@ func (suite AnteTestSuite) TestNewEthAccountVerificationDecorator() {
tc.malleate()
suite.Require().NoError(vmdb.Commit())
_, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(tc.checkTx), tc.tx, false, nextFn)
_, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(tc.checkTx), tc.tx, false, NextFn)
if tc.expPass {
suite.Require().NoError(err)
@@ -190,7 +201,7 @@ func (suite AnteTestSuite) TestEthNonceVerificationDecorator() {
for _, tc := range testCases {
suite.Run(tc.name, func() {
tc.malleate()
_, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, nextFn)
_, err := dec.AnteHandle(suite.ctx.WithIsReCheckTx(tc.reCheckTx), tc.tx, false, NextFn)
if tc.expPass {
suite.Require().NoError(err)
@@ -210,41 +221,61 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
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, big.NewInt(1), nil, nil, nil, &ethtypes.AccessList{{Address: addr, StorageKeys: nil}})
tx2 := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), tx2GasLimit, gasPrice, nil, nil, nil, &ethtypes.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, &ethtypes.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
name string
tx sdk.Tx
gasLimit uint64
malleate func()
expPass bool
expPanic bool
expPriority int64
}{
{"invalid transaction type", &invalidTx{}, 0, func() {}, false, false},
{"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),
0,
math.MaxUint64,
func() {},
false, false,
0,
},
{
"gas limit too low",
tx,
0,
math.MaxUint64,
func() {},
false, false,
0,
},
{
"not enough balance for fees",
tx2,
0,
math.MaxUint64,
func() {},
false, false,
0,
},
{
"not enough tx gas",
@@ -254,6 +285,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
vmdb.AddBalance(addr, big.NewInt(1000000))
},
false, true,
0,
},
{
"not enough block gas",
@@ -261,21 +293,32 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
0,
func() {
vmdb.AddBalance(addr, big.NewInt(1000000))
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1))
},
false, true,
0,
},
{
"success",
"success - legacy tx",
tx2,
config.DefaultMaxTxGasWanted, // it's capped
tx2GasLimit, // it's capped
func() {
vmdb.AddBalance(addr, big.NewInt(1000000))
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,
},
}
@@ -287,19 +330,19 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
if tc.expPanic {
suite.Require().Panics(func() {
_, _ = dec.AnteHandle(suite.ctx.WithIsCheckTx(true).WithGasMeter(sdk.NewGasMeter(1)), tc.tx, false, nextFn)
_, _ = dec.AnteHandle(suite.ctx.WithIsCheckTx(true).WithGasMeter(sdk.NewGasMeter(1)), tc.tx, false, NextFn)
})
return
}
_, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(true).WithGasMeter(sdk.NewInfiniteGasMeter()), tc.tx, false, nextFn)
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)
}
// TODO: needs to check the gasmeter limit issue
// suite.Require().Equal(tc.gasLimit, ctx.GasMeter().Limit())
suite.Require().Equal(tc.gasLimit, ctx.GasMeter().Limit())
})
}
}
@@ -377,7 +420,7 @@ func (suite AnteTestSuite) TestCanTransferDecorator() {
tc.malleate()
suite.Require().NoError(vmdb.Commit())
_, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(true), tc.tx, false, nextFn)
_, err := dec.AnteHandle(suite.ctx.WithIsCheckTx(true), tc.tx, false, NextFn)
if tc.expPass {
suite.Require().NoError(err)
@@ -456,12 +499,12 @@ func (suite AnteTestSuite) TestEthIncrementSenderSequenceDecorator() {
if tc.expPanic {
suite.Require().Panics(func() {
_, _ = dec.AnteHandle(suite.ctx, tc.tx, false, nextFn)
_, _ = dec.AnteHandle(suite.ctx, tc.tx, false, NextFn)
})
return
}
_, err := dec.AnteHandle(suite.ctx, tc.tx, false, nextFn)
_, err := dec.AnteHandle(suite.ctx, tc.tx, false, NextFn)
if tc.expPass {
suite.Require().NoError(err)
@@ -498,7 +541,7 @@ func (suite AnteTestSuite) TestEthSetupContextDecorator() {
for _, tc := range testCases {
suite.Run(tc.name, func() {
_, err := dec.AnteHandle(suite.ctx, tc.tx, false, nextFn)
_, err := dec.AnteHandle(suite.ctx, tc.tx, false, NextFn)
if tc.expPass {
suite.Require().NoError(err)
+143
View File
@@ -0,0 +1,143 @@
package ante
import (
"fmt"
"math"
sdkmath "cosmossdk.io/math"
ethermint "github.com/cerc-io/laconicd/types"
"github.com/cerc-io/laconicd/x/evm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
)
// NewDynamicFeeChecker returns a `TxFeeChecker` that applies a dynamic fee to
// Cosmos txs using the EIP-1559 fee market logic.
// This can be called in both CheckTx and deliverTx modes.
// a) feeCap = tx.fees / tx.gas
// b) tipFeeCap = tx.MaxPriorityPrice (default) or MaxInt64
// - when `ExtensionOptionDynamicFeeTx` is omitted, `tipFeeCap` defaults to `MaxInt64`.
// - when london hardfork is not enabled, it fallbacks to SDK default behavior (validator min-gas-prices).
// - Tx priority is set to `effectiveGasPrice / DefaultPriorityReduction`.
func NewDynamicFeeChecker(k DynamicFeeEVMKeeper) authante.TxFeeChecker {
return func(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return nil, 0, fmt.Errorf("tx must be a FeeTx")
}
if ctx.BlockHeight() == 0 {
// genesis transactions: fallback to min-gas-price logic
return checkTxFeeWithValidatorMinGasPrices(ctx, feeTx)
}
params := k.GetParams(ctx)
denom := params.EvmDenom
ethCfg := params.ChainConfig.EthereumConfig(k.ChainID())
baseFee := k.GetBaseFee(ctx, ethCfg)
if baseFee == nil {
// london hardfork is not enabled: fallback to min-gas-prices logic
return checkTxFeeWithValidatorMinGasPrices(ctx, feeTx)
}
// default to `MaxInt64` when there's no extension option.
maxPriorityPrice := sdkmath.NewInt(math.MaxInt64)
// get the priority tip cap from the extension option.
if hasExtOptsTx, ok := tx.(authante.HasExtensionOptionsTx); ok {
for _, opt := range hasExtOptsTx.GetExtensionOptions() {
if extOpt, ok := opt.GetCachedValue().(*ethermint.ExtensionOptionDynamicFeeTx); ok {
maxPriorityPrice = extOpt.MaxPriorityPrice
break
}
}
}
gas := feeTx.GetGas()
feeCoins := feeTx.GetFee()
fee := feeCoins.AmountOfNoDenomValidation(denom)
feeCap := fee.Quo(sdkmath.NewIntFromUint64(gas))
baseFeeInt := sdkmath.NewIntFromBigInt(baseFee)
if feeCap.LT(baseFeeInt) {
return nil, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient gas prices; got: %s required: %s", feeCap, baseFeeInt)
}
// calculate the effective gas price using the EIP-1559 logic.
effectivePrice := sdkmath.NewIntFromBigInt(types.EffectiveGasPrice(baseFeeInt.BigInt(), feeCap.BigInt(), maxPriorityPrice.BigInt()))
// NOTE: create a new coins slice without having to validate the denom
effectiveFee := sdk.Coins{
{
Denom: denom,
Amount: effectivePrice.Mul(sdkmath.NewIntFromUint64(gas)),
},
}
bigPriority := effectivePrice.Sub(baseFeeInt).Quo(types.DefaultPriorityReduction)
priority := int64(math.MaxInt64)
if bigPriority.IsInt64() {
priority = bigPriority.Int64()
}
return effectiveFee, priority, nil
}
}
// checkTxFeeWithValidatorMinGasPrices implements the default fee logic, where the minimum price per
// unit of gas is fixed and set by each validator, and the tx priority is computed from the gas price.
func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.FeeTx) (sdk.Coins, int64, error) {
feeCoins := tx.GetFee()
gas := tx.GetGas()
minGasPrices := ctx.MinGasPrices()
// Ensure that the provided fees meet a minimum threshold for the validator,
// if this is a CheckTx. This is only for local mempool purposes, and thus
// is only ran on check tx.
if ctx.IsCheckTx() && !minGasPrices.IsZero() {
requiredFees := make(sdk.Coins, len(minGasPrices))
// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
glDec := sdk.NewDec(int64(gas))
for i, gp := range minGasPrices {
fee := gp.Amount.Mul(glDec)
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
}
if !feeCoins.IsAnyGTE(requiredFees) {
return nil, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
}
}
priority := getTxPriority(feeCoins, int64(gas))
return feeCoins, priority, nil
}
// getTxPriority returns a naive tx priority based on the amount of the smallest denomination of the gas price
// provided in a transaction.
func getTxPriority(fees sdk.Coins, gas int64) int64 {
var priority int64
for _, fee := range fees {
gasPrice := fee.Amount.QuoRaw(gas)
amt := gasPrice.Quo(types.DefaultPriorityReduction)
p := int64(math.MaxInt64)
if amt.IsInt64() {
p = amt.Int64()
}
if priority == 0 || p < priority {
priority = p
}
}
return priority
}
+219
View File
@@ -0,0 +1,219 @@
package ante
import (
"math/big"
"testing"
"github.com/stretchr/testify/require"
"github.com/cerc-io/laconicd/encoding"
ethermint "github.com/cerc-io/laconicd/types"
"github.com/cerc-io/laconicd/x/evm/types"
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
"github.com/ethereum/go-ethereum/params"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)
var _ DynamicFeeEVMKeeper = MockEVMKeeper{}
type MockEVMKeeper struct {
BaseFee *big.Int
EnableLondonHF bool
}
func (m MockEVMKeeper) GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
if m.EnableLondonHF {
return m.BaseFee
}
return nil
}
func (m MockEVMKeeper) GetParams(ctx sdk.Context) evmtypes.Params {
return evmtypes.DefaultParams()
}
func (m MockEVMKeeper) ChainID() *big.Int {
return big.NewInt(9000)
}
func TestSDKTxFeeChecker(t *testing.T) {
// testCases:
// fallback
// genesis tx
// checkTx, validate with min-gas-prices
// deliverTx, no validation
// dynamic fee
// with extension option
// without extension option
// london hardfork enableness
encodingConfig := encoding.MakeConfig(module.NewBasicManager())
minGasPrices := sdk.NewDecCoins(sdk.NewDecCoin("aphoton", sdk.NewInt(10)))
genesisCtx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger())
checkTxCtx := sdk.NewContext(nil, tmproto.Header{Height: 1}, true, log.NewNopLogger()).WithMinGasPrices(minGasPrices)
deliverTxCtx := sdk.NewContext(nil, tmproto.Header{Height: 1}, false, log.NewNopLogger())
testCases := []struct {
name string
ctx sdk.Context
keeper DynamicFeeEVMKeeper
buildTx func() sdk.Tx
expFees string
expPriority int64
expSuccess bool
}{
{
"success, genesis tx",
genesisCtx,
MockEVMKeeper{},
func() sdk.Tx {
return encodingConfig.TxConfig.NewTxBuilder().GetTx()
},
"",
0,
true,
},
{
"fail, min-gas-prices",
checkTxCtx,
MockEVMKeeper{},
func() sdk.Tx {
return encodingConfig.TxConfig.NewTxBuilder().GetTx()
},
"",
0,
false,
},
{
"success, min-gas-prices",
checkTxCtx,
MockEVMKeeper{},
func() sdk.Tx {
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
txBuilder.SetGasLimit(1)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("aphoton", sdk.NewInt(10))))
return txBuilder.GetTx()
},
"10aphoton",
0,
true,
},
{
"success, min-gas-prices deliverTx",
deliverTxCtx,
MockEVMKeeper{},
func() sdk.Tx {
return encodingConfig.TxConfig.NewTxBuilder().GetTx()
},
"",
0,
true,
},
{
"fail, dynamic fee",
deliverTxCtx,
MockEVMKeeper{
EnableLondonHF: true, BaseFee: big.NewInt(1),
},
func() sdk.Tx {
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
txBuilder.SetGasLimit(1)
return txBuilder.GetTx()
},
"",
0,
false,
},
{
"success, dynamic fee",
deliverTxCtx,
MockEVMKeeper{
EnableLondonHF: true, BaseFee: big.NewInt(10),
},
func() sdk.Tx {
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
txBuilder.SetGasLimit(1)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("aphoton", sdk.NewInt(10))))
return txBuilder.GetTx()
},
"10aphoton",
0,
true,
},
{
"success, dynamic fee priority",
deliverTxCtx,
MockEVMKeeper{
EnableLondonHF: true, BaseFee: big.NewInt(10),
},
func() sdk.Tx {
txBuilder := encodingConfig.TxConfig.NewTxBuilder()
txBuilder.SetGasLimit(1)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("aphoton", sdk.NewInt(10).Mul(types.DefaultPriorityReduction).Add(sdk.NewInt(10)))))
return txBuilder.GetTx()
},
"10000010aphoton",
10,
true,
},
{
"success, dynamic fee empty tipFeeCap",
deliverTxCtx,
MockEVMKeeper{
EnableLondonHF: true, BaseFee: big.NewInt(10),
},
func() sdk.Tx {
txBuilder := encodingConfig.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
txBuilder.SetGasLimit(1)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("aphoton", sdk.NewInt(10).Mul(types.DefaultPriorityReduction))))
option, err := codectypes.NewAnyWithValue(&ethermint.ExtensionOptionDynamicFeeTx{})
require.NoError(t, err)
txBuilder.SetExtensionOptions(option)
return txBuilder.GetTx()
},
"10aphoton",
0,
true,
},
{
"success, dynamic fee tipFeeCap",
deliverTxCtx,
MockEVMKeeper{
EnableLondonHF: true, BaseFee: big.NewInt(10),
},
func() sdk.Tx {
txBuilder := encodingConfig.TxConfig.NewTxBuilder().(authtx.ExtensionOptionsTxBuilder)
txBuilder.SetGasLimit(1)
txBuilder.SetFeeAmount(sdk.NewCoins(sdk.NewCoin("aphoton", sdk.NewInt(10).Mul(types.DefaultPriorityReduction).Add(sdk.NewInt(10)))))
option, err := codectypes.NewAnyWithValue(&ethermint.ExtensionOptionDynamicFeeTx{
MaxPriorityPrice: sdk.NewInt(5).Mul(types.DefaultPriorityReduction),
})
require.NoError(t, err)
txBuilder.SetExtensionOptions(option)
return txBuilder.GetTx()
},
"5000010aphoton",
5,
true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fees, priority, err := NewDynamicFeeChecker(tc.keeper)(tc.ctx, tc.buildTx())
if tc.expSuccess {
require.Equal(t, tc.expFees, fees.String())
require.Equal(t, tc.expPriority, priority)
} else {
require.Error(t, err)
}
})
}
}
+52
View File
@@ -0,0 +1,52 @@
package ante
import (
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
// GasWantedDecorator keeps track of the gasWanted amount on the current block in transient store
// for BaseFee calculation.
// NOTE: This decorator does not perform any validation
type GasWantedDecorator struct {
evmKeeper EVMKeeper
feeMarketKeeper FeeMarketKeeper
}
// NewGasWantedDecorator creates a new NewGasWantedDecorator
func NewGasWantedDecorator(
evmKeeper EVMKeeper,
feeMarketKeeper FeeMarketKeeper,
) GasWantedDecorator {
return GasWantedDecorator{
evmKeeper,
feeMarketKeeper,
}
}
func (gwd GasWantedDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
params := gwd.evmKeeper.GetParams(ctx)
ethCfg := params.ChainConfig.EthereumConfig(gwd.evmKeeper.ChainID())
blockHeight := big.NewInt(ctx.BlockHeight())
isLondon := ethCfg.IsLondon(blockHeight)
feeTx, ok := tx.(sdk.FeeTx)
if !ok || !isLondon {
return next(ctx, tx, simulate)
}
gasWanted := feeTx.GetGas()
feeMktParams := gwd.feeMarketKeeper.GetParams(ctx)
// Add total gasWanted to cumulative in block transientStore in FeeMarket module
if feeMktParams.IsBaseFeeEnabled(ctx.BlockHeight()) {
if _, err := gwd.feeMarketKeeper.AddTransientGasWanted(ctx, gasWanted); err != nil {
return ctx, sdkerrors.Wrapf(err, "failed to add gas wanted to transient store")
}
}
return next(ctx, tx, simulate)
}
+95
View File
@@ -0,0 +1,95 @@
package ante_test
import (
"math/big"
sdkmath "cosmossdk.io/math"
"github.com/cerc-io/laconicd/app/ante"
"github.com/cerc-io/laconicd/tests"
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
func (suite AnteTestSuite) TestGasWantedDecorator() {
suite.enableFeemarket = true
suite.SetupTest()
dec := ante.NewGasWantedDecorator(suite.app.EvmKeeper, suite.app.FeeMarketKeeper)
from, fromPrivKey := tests.NewAddrKey()
to := tests.GenerateAddress()
testCases := []struct {
name string
expectedGasWanted uint64
malleate func() sdk.Tx
}{
{
"Cosmos Tx",
TestGasLimit,
func() sdk.Tx {
denom := evmtypes.DefaultEVMDenom
testMsg := banktypes.MsgSend{
FromAddress: "evmos1x8fhpj9nmhqk8z9kpgjt95ck2xwyue0ptzkucp",
ToAddress: "evmos1dx67l23hz9l0k9hcher8xz04uj7wf3yu26l2yn",
Amount: sdk.Coins{sdk.Coin{Amount: sdkmath.NewInt(10), Denom: denom}},
}
txBuilder := suite.CreateTestCosmosTxBuilder(sdkmath.NewInt(10), "stake", &testMsg)
return txBuilder.GetTx()
},
},
{
"Ethereum Legacy Tx",
TestGasLimit,
func() sdk.Tx {
msg := suite.BuildTestEthTx(from, to, nil, make([]byte, 0), big.NewInt(0), nil, nil, nil)
return suite.CreateTestTx(msg, fromPrivKey, 1, false)
},
},
{
"Ethereum Access List Tx",
TestGasLimit,
func() sdk.Tx {
emptyAccessList := ethtypes.AccessList{}
msg := suite.BuildTestEthTx(from, to, nil, make([]byte, 0), big.NewInt(0), nil, nil, &emptyAccessList)
return suite.CreateTestTx(msg, fromPrivKey, 1, false)
},
},
{
"Ethereum Dynamic Fee Tx (EIP1559)",
TestGasLimit,
func() sdk.Tx {
emptyAccessList := ethtypes.AccessList{}
msg := suite.BuildTestEthTx(from, to, nil, make([]byte, 0), big.NewInt(0), big.NewInt(100), big.NewInt(50), &emptyAccessList)
return suite.CreateTestTx(msg, fromPrivKey, 1, false)
},
},
{
"EIP712 message",
200000,
func() sdk.Tx {
amount := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20)))
gas := uint64(200000)
acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, from.Bytes())
suite.Require().NoError(acc.SetSequence(1))
suite.app.AccountKeeper.SetAccount(suite.ctx, acc)
tx := suite.CreateTestEIP712TxBuilderMsgSend(acc.GetAddress(), fromPrivKey, suite.ctx.ChainID(), gas, amount)
return tx.GetTx()
},
},
}
// cumulative gas wanted from all test transactions in the same block
var expectedGasWanted uint64
for _, tc := range testCases {
suite.Run(tc.name, func() {
_, err := dec.AnteHandle(suite.ctx, tc.malleate(), false, NextFn)
suite.Require().NoError(err)
gasWanted := suite.app.FeeMarketKeeper.GetTransientGasWanted(suite.ctx)
expectedGasWanted += tc.expectedGasWanted
suite.Require().Equal(expectedGasWanted, gasWanted)
})
}
}
+145
View File
@@ -0,0 +1,145 @@
package ante
import (
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
// MinGasPriceDecorator will check if the transaction's fee is at least as large
// as the MinGasPrices param. If fee is too low, decorator returns error and tx
// is rejected. This applies for both CheckTx and DeliverTx
// If fee is high enough, then call next AnteHandler
// CONTRACT: Tx must implement FeeTx to use MinGasPriceDecorator
type MinGasPriceDecorator struct {
feesKeeper FeeMarketKeeper
evmKeeper EVMKeeper
}
func NewMinGasPriceDecorator(fk FeeMarketKeeper, ek EVMKeeper) MinGasPriceDecorator {
return MinGasPriceDecorator{feesKeeper: fk, evmKeeper: ek}
}
func (mpd MinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}
minGasPrice := mpd.feesKeeper.GetParams(ctx).MinGasPrice
// Short-circuit if min gas price is 0 or if simulating
if minGasPrice.IsZero() || simulate {
return next(ctx, tx, simulate)
}
evmParams := mpd.evmKeeper.GetParams(ctx)
minGasPrices := sdk.DecCoins{
{
Denom: evmParams.EvmDenom,
Amount: minGasPrice,
},
}
feeCoins := feeTx.GetFee()
gas := feeTx.GetGas()
requiredFees := make(sdk.Coins, 0)
// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
gasLimit := sdk.NewDecFromBigInt(new(big.Int).SetUint64(gas))
for _, gp := range minGasPrices {
fee := gp.Amount.Mul(gasLimit).Ceil().RoundInt()
if fee.IsPositive() {
requiredFees = requiredFees.Add(sdk.Coin{Denom: gp.Denom, Amount: fee})
}
}
if !feeCoins.IsAnyGTE(requiredFees) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee,
"provided fee < minimum global fee (%s < %s). Please increase the gas price.",
feeCoins,
requiredFees)
}
return next(ctx, tx, simulate)
}
// EthMinGasPriceDecorator will check if the transaction's fee is at least as large
// as the MinGasPrices param. If fee is too low, decorator returns error and tx
// is rejected. This applies to both CheckTx and DeliverTx and regardless
// if London hard fork or fee market params (EIP-1559) are enabled.
// If fee is high enough, then call next AnteHandler
type EthMinGasPriceDecorator struct {
feesKeeper FeeMarketKeeper
evmKeeper EVMKeeper
}
func NewEthMinGasPriceDecorator(fk FeeMarketKeeper, ek EVMKeeper) EthMinGasPriceDecorator {
return EthMinGasPriceDecorator{feesKeeper: fk, evmKeeper: ek}
}
func (empd EthMinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
minGasPrice := empd.feesKeeper.GetParams(ctx).MinGasPrice
// short-circuit if min gas price is 0
if minGasPrice.IsZero() {
return next(ctx, tx, simulate)
}
paramsEvm := empd.evmKeeper.GetParams(ctx)
ethCfg := paramsEvm.ChainConfig.EthereumConfig(empd.evmKeeper.ChainID())
baseFee := empd.evmKeeper.GetBaseFee(ctx, ethCfg)
for _, msg := range tx.GetMsgs() {
ethMsg, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrUnknownRequest,
"invalid message type %T, expected %T",
msg, (*evmtypes.MsgEthereumTx)(nil),
)
}
feeAmt := ethMsg.GetFee()
// For dynamic transactions, GetFee() uses the GasFeeCap value, which
// is the maximum gas price that the signer can pay. In practice, the
// signer can pay less, if the block's BaseFee is lower. So, in this case,
// we use the EffectiveFee. If the feemarket formula results in a BaseFee
// that lowers EffectivePrice until it is < MinGasPrices, the users must
// increase the GasTipCap (priority fee) until EffectivePrice > MinGasPrices.
// Transactions with MinGasPrices * gasUsed < tx fees < EffectiveFee are rejected
// by the feemarket AnteHandle
txData, err := evmtypes.UnpackTxData(ethMsg.Data)
if err != nil {
return ctx, sdkerrors.Wrapf(err, "failed to unpack tx data %s", ethMsg.Hash)
}
if txData.TxType() != ethtypes.LegacyTxType {
feeAmt = ethMsg.GetEffectiveFee(baseFee)
}
gasLimit := sdk.NewDecFromBigInt(new(big.Int).SetUint64(ethMsg.GetGas()))
requiredFee := minGasPrice.Mul(gasLimit)
fee := sdk.NewDecFromBigInt(feeAmt)
if fee.LT(requiredFee) {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrInsufficientFee,
"provided fee < minimum global fee (%d < %d). Please increase the priority tip (for EIP-1559 txs) or the gas prices (for access list or legacy txs)", //nolint:lll
fee.TruncateInt().Int64(), requiredFee.TruncateInt().Int64(),
)
}
}
return next(ctx, tx, simulate)
}
+347
View File
@@ -0,0 +1,347 @@
package ante_test
import (
"math/big"
sdkmath "cosmossdk.io/math"
"github.com/cerc-io/laconicd/app/ante"
"github.com/cerc-io/laconicd/tests"
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
ethtypes "github.com/ethereum/go-ethereum/core/types"
)
var execTypes = []struct {
name string
isCheckTx bool
simulate bool
}{
{"deliverTx", false, false},
{"deliverTxSimulate", false, true},
}
func (s AnteTestSuite) TestMinGasPriceDecorator() {
denom := evmtypes.DefaultEVMDenom
testMsg := banktypes.MsgSend{
FromAddress: "evmos1x8fhpj9nmhqk8z9kpgjt95ck2xwyue0ptzkucp",
ToAddress: "evmos1dx67l23hz9l0k9hcher8xz04uj7wf3yu26l2yn",
Amount: sdk.Coins{sdk.Coin{Amount: sdkmath.NewInt(10), Denom: denom}},
}
testCases := []struct {
name string
malleate func() sdk.Tx
expPass bool
errMsg string
allowPassOnSimulate bool
}{
{
"invalid cosmos tx type",
func() sdk.Tx {
return &invalidTx{}
},
false,
"must be a FeeTx",
false,
},
{
"valid cosmos tx with MinGasPrices = 0, gasPrice = 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.ZeroDec()
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
txBuilder := s.CreateTestCosmosTxBuilder(sdkmath.NewInt(0), denom, &testMsg)
return txBuilder.GetTx()
},
true,
"",
false,
},
{
"valid cosmos tx with MinGasPrices = 0, gasPrice > 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.ZeroDec()
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
txBuilder := s.CreateTestCosmosTxBuilder(sdkmath.NewInt(10), denom, &testMsg)
return txBuilder.GetTx()
},
true,
"",
false,
},
{
"valid cosmos tx with MinGasPrices = 10, gasPrice = 10",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
txBuilder := s.CreateTestCosmosTxBuilder(sdkmath.NewInt(10), denom, &testMsg)
return txBuilder.GetTx()
},
true,
"",
false,
},
{
"invalid cosmos tx with MinGasPrices = 10, gasPrice = 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
txBuilder := s.CreateTestCosmosTxBuilder(sdkmath.NewInt(0), denom, &testMsg)
return txBuilder.GetTx()
},
false,
"provided fee < minimum global fee",
true,
},
{
"invalid cosmos tx with wrong denom",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
txBuilder := s.CreateTestCosmosTxBuilder(sdkmath.NewInt(10), "stake", &testMsg)
return txBuilder.GetTx()
},
false,
"provided fee < minimum global fee",
true,
},
}
for _, et := range execTypes {
for _, tc := range testCases {
s.Run(et.name+"_"+tc.name, func() {
// s.SetupTest(et.isCheckTx)
ctx := s.ctx.WithIsReCheckTx(et.isCheckTx)
dec := ante.NewMinGasPriceDecorator(s.app.FeeMarketKeeper, s.app.EvmKeeper)
_, err := dec.AnteHandle(ctx, tc.malleate(), et.simulate, NextFn)
if tc.expPass || (et.simulate && tc.allowPassOnSimulate) {
s.Require().NoError(err, tc.name)
} else {
s.Require().Error(err, tc.name)
s.Require().Contains(err.Error(), tc.errMsg, tc.name)
}
})
}
}
}
func (s AnteTestSuite) TestEthMinGasPriceDecorator() {
denom := evmtypes.DefaultEVMDenom
from, privKey := tests.NewAddrKey()
to := tests.GenerateAddress()
emptyAccessList := ethtypes.AccessList{}
testCases := []struct {
name string
malleate func() sdk.Tx
expPass bool
errMsg string
}{
{
"invalid tx type",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
return &invalidTx{}
},
false,
"invalid message type",
},
{
"wrong tx type",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
testMsg := banktypes.MsgSend{
FromAddress: "evmos1x8fhpj9nmhqk8z9kpgjt95ck2xwyue0ptzkucp",
ToAddress: "evmos1dx67l23hz9l0k9hcher8xz04uj7wf3yu26l2yn",
Amount: sdk.Coins{sdk.Coin{Amount: sdkmath.NewInt(10), Denom: denom}},
}
txBuilder := s.CreateTestCosmosTxBuilder(sdkmath.NewInt(0), denom, &testMsg)
return txBuilder.GetTx()
},
false,
"invalid message type",
},
{
"valid: invalid tx type with MinGasPrices = 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.ZeroDec()
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
return &invalidTx{}
},
true,
"",
},
{
"valid legacy tx with MinGasPrices = 0, gasPrice = 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.ZeroDec()
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), big.NewInt(0), nil, nil, nil)
return s.CreateTestTx(msg, privKey, 1, false)
},
true,
"",
},
{
"valid legacy tx with MinGasPrices = 0, gasPrice > 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.ZeroDec()
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), big.NewInt(10), nil, nil, nil)
return s.CreateTestTx(msg, privKey, 1, false)
},
true,
"",
},
{
"valid legacy tx with MinGasPrices = 10, gasPrice = 10",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), big.NewInt(10), nil, nil, nil)
return s.CreateTestTx(msg, privKey, 1, false)
},
true,
"",
},
{
"invalid legacy tx with MinGasPrices = 10, gasPrice = 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), big.NewInt(0), nil, nil, nil)
return s.CreateTestTx(msg, privKey, 1, false)
},
false,
"provided fee < minimum global fee",
},
{
"valid dynamic tx with MinGasPrices = 0, EffectivePrice = 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.ZeroDec()
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), nil, big.NewInt(0), big.NewInt(0), &emptyAccessList)
return s.CreateTestTx(msg, privKey, 1, false)
},
true,
"",
},
{
"valid dynamic tx with MinGasPrices = 0, EffectivePrice > 0",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.ZeroDec()
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), nil, big.NewInt(100), big.NewInt(50), &emptyAccessList)
return s.CreateTestTx(msg, privKey, 1, false)
},
true,
"",
},
{
"valid dynamic tx with MinGasPrices < EffectivePrice",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), nil, big.NewInt(100), big.NewInt(100), &emptyAccessList)
return s.CreateTestTx(msg, privKey, 1, false)
},
true,
"",
},
{
"invalid dynamic tx with MinGasPrices > EffectivePrice",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), nil, big.NewInt(0), big.NewInt(0), &emptyAccessList)
return s.CreateTestTx(msg, privKey, 1, false)
},
false,
"provided fee < minimum global fee",
},
{
"invalid dynamic tx with MinGasPrices > BaseFee, MinGasPrices > EffectivePrice",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(100)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
feemarketParams := s.app.FeeMarketKeeper.GetParams(s.ctx)
feemarketParams.BaseFee = sdkmath.NewInt(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, feemarketParams)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), nil, big.NewInt(1000), big.NewInt(0), &emptyAccessList)
return s.CreateTestTx(msg, privKey, 1, false)
},
false,
"provided fee < minimum global fee",
},
{
"valid dynamic tx with MinGasPrices > BaseFee, MinGasPrices < EffectivePrice (big GasTipCap)",
func() sdk.Tx {
params := s.app.FeeMarketKeeper.GetParams(s.ctx)
params.MinGasPrice = sdk.NewDec(100)
s.app.FeeMarketKeeper.SetParams(s.ctx, params)
feemarketParams := s.app.FeeMarketKeeper.GetParams(s.ctx)
feemarketParams.BaseFee = sdkmath.NewInt(10)
s.app.FeeMarketKeeper.SetParams(s.ctx, feemarketParams)
msg := s.BuildTestEthTx(from, to, nil, make([]byte, 0), nil, big.NewInt(1000), big.NewInt(101), &emptyAccessList)
return s.CreateTestTx(msg, privKey, 1, false)
},
true,
"",
},
}
for _, et := range execTypes {
for _, tc := range testCases {
s.Run(et.name+"_"+tc.name, func() {
// s.SetupTest(et.isCheckTx)
s.SetupTest()
dec := ante.NewEthMinGasPriceDecorator(s.app.FeeMarketKeeper, s.app.EvmKeeper)
_, err := dec.AnteHandle(s.ctx, tc.malleate(), et.simulate, NextFn)
if tc.expPass {
s.Require().NoError(err, tc.name)
} else {
s.Require().Error(err, tc.name)
s.Require().Contains(err.Error(), tc.errMsg, tc.name)
}
})
}
}
}
+24 -15
View File
@@ -17,19 +17,20 @@ import (
// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC
// channel keeper, EVM Keeper and Fee Market Keeper.
type HandlerOptions struct {
AccountKeeper evmtypes.AccountKeeper
BankKeeper evmtypes.BankKeeper
IBCKeeper *ibckeeper.Keeper
FeeMarketKeeper evmtypes.FeeMarketKeeper
EvmKeeper EVMKeeper
FeegrantKeeper ante.FeegrantKeeper
SignModeHandler authsigning.SignModeHandler
SigGasConsumer func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
MaxTxGasWanted uint64
TxFeeChecker ante.TxFeeChecker
AccountKeeper evmtypes.AccountKeeper
BankKeeper evmtypes.BankKeeper
IBCKeeper *ibckeeper.Keeper
FeeMarketKeeper evmtypes.FeeMarketKeeper
EvmKeeper EVMKeeper
FeegrantKeeper ante.FeegrantKeeper
SignModeHandler authsigning.SignModeHandler
SigGasConsumer func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
MaxTxGasWanted uint64
ExtensionOptionChecker ante.ExtensionOptionChecker
TxFeeChecker ante.TxFeeChecker
}
func (options HandlerOptions) Validate() error {
func (options HandlerOptions) validate() error {
if options.AccountKeeper == nil {
return sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler")
}
@@ -50,14 +51,17 @@ func (options HandlerOptions) Validate() error {
func newEthAnteHandler(options HandlerOptions) sdk.AnteHandler {
return sdk.ChainAnteDecorators(
NewEthSetUpContextDecorator(options.EvmKeeper), // outermost AnteDecorator. SetUpContext must be called first
NewEthMempoolFeeDecorator(options.EvmKeeper), // Check eth effective gas price against minimal-gas-prices
NewEthSetUpContextDecorator(options.EvmKeeper), // outermost AnteDecorator. SetUpContext must be called first
NewEthMempoolFeeDecorator(options.EvmKeeper), // Check eth effective gas price against minimal-gas-prices
NewEthMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), // Check eth effective gas price against the global MinGasPrice
NewEthValidateBasicDecorator(options.EvmKeeper),
NewEthSigVerificationDecorator(options.EvmKeeper),
NewEthAccountVerificationDecorator(options.AccountKeeper, options.BankKeeper, options.EvmKeeper),
NewEthGasConsumeDecorator(options.EvmKeeper, options.MaxTxGasWanted),
NewEthAccountVerificationDecorator(options.AccountKeeper, options.EvmKeeper),
NewCanTransferDecorator(options.EvmKeeper),
NewEthGasConsumeDecorator(options.EvmKeeper, options.MaxTxGasWanted),
NewEthIncrementSenderSequenceDecorator(options.AccountKeeper), // innermost AnteDecorator.
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
NewEthEmitEventDecorator(options.EvmKeeper), // emit eth tx hash and index at the very last ante handler.
)
}
@@ -65,6 +69,8 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
return sdk.ChainAnteDecorators(
RejectMessagesDecorator{}, // reject MsgEthereumTxs
ante.NewSetUpContextDecorator(),
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
ante.NewValidateBasicDecorator(),
ante.NewTxTimeoutHeightDecorator(),
ante.NewValidateMemoDecorator(options.AccountKeeper),
@@ -77,6 +83,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
)
}
@@ -86,6 +93,7 @@ func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
ante.NewSetUpContextDecorator(),
// NOTE: extensions option decorator removed
// ante.NewRejectExtensionOptionsDecorator(),
NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
ante.NewValidateBasicDecorator(),
ante.NewTxTimeoutHeightDecorator(),
ante.NewValidateMemoDecorator(options.AccountKeeper),
@@ -99,5 +107,6 @@ func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
)
}
+19 -5
View File
@@ -5,6 +5,8 @@ import (
"github.com/cerc-io/laconicd/x/evm/statedb"
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
evm "github.com/cerc-io/laconicd/x/evm/vm"
feemarkettypes "github.com/cerc-io/laconicd/x/feemarket/types"
sdk "github.com/cosmos/cosmos-sdk/types"
tx "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/ethereum/go-ethereum/common"
@@ -13,21 +15,33 @@ import (
"github.com/ethereum/go-ethereum/params"
)
// DynamicFeeEVMKeeper is a subset of EVMKeeper interface that supports dynamic fee checker
type DynamicFeeEVMKeeper interface {
ChainID() *big.Int
GetParams(ctx sdk.Context) evmtypes.Params
GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int
}
// EVMKeeper defines the expected keeper interface used on the Eth AnteHandler
type EVMKeeper interface {
statedb.Keeper
DynamicFeeEVMKeeper
ChainID() *big.Int
GetParams(ctx sdk.Context) evmtypes.Params
NewEVM(ctx sdk.Context, msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) *vm.EVM
NewEVM(ctx sdk.Context, msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.EVMLogger, stateDB vm.StateDB) evm.EVM
DeductTxCostsFromUserBalance(
ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool,
) (sdk.Coins, error)
BaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int
) (fees sdk.Coins, priority int64, err error)
GetBalance(ctx sdk.Context, addr common.Address) *big.Int
ResetTransientGasUsed(ctx sdk.Context)
GetTxIndexTransient(ctx sdk.Context) uint64
}
type protoTxProvider interface {
GetProtoTx() *tx.Tx
}
// FeeMarketKeeper defines the expected keeper interface used on the AnteHandler
type FeeMarketKeeper interface {
GetParams(ctx sdk.Context) (params feemarkettypes.Params)
AddTransientGasWanted(ctx sdk.Context, gasWanted uint64) (uint64, error)
}
+186 -15
View File
@@ -1,10 +1,16 @@
package ante_test
import (
"encoding/json"
"fmt"
"math"
"math/big"
"testing"
"time"
sdkmath "cosmossdk.io/math"
"github.com/stretchr/testify/suite"
"github.com/cerc-io/laconicd/ethereum/eip712"
"github.com/cerc-io/laconicd/types"
"github.com/cosmos/cosmos-sdk/codec"
@@ -16,8 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/suite"
cryptocodec "github.com/cerc-io/laconicd/crypto/codec"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
@@ -25,6 +30,7 @@ import (
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
@@ -37,6 +43,10 @@ import (
"github.com/cerc-io/laconicd/x/evm/statedb"
evmtypes "github.com/cerc-io/laconicd/x/evm/types"
feemarkettypes "github.com/cerc-io/laconicd/x/feemarket/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
evtypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
"github.com/cosmos/cosmos-sdk/x/feegrant"
types5 "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)
@@ -51,8 +61,11 @@ type AnteTestSuite struct {
ethSigner ethtypes.Signer
enableFeemarket bool
enableLondonHF bool
evmParamsOption func(*evmtypes.Params)
}
const TestGasLimit uint64 = 100000
func (suite *AnteTestSuite) StateDB() *statedb.StateDB {
return statedb.New(suite.ctx, suite.app.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(suite.ctx.HeaderHash().Bytes())))
}
@@ -60,7 +73,7 @@ func (suite *AnteTestSuite) StateDB() *statedb.StateDB {
func (suite *AnteTestSuite) SetupTest() {
checkTx := false
suite.app = app.Setup(suite.T(), checkTx, func(app *app.EthermintApp, genesis simapp.GenesisState) simapp.GenesisState {
suite.app = app.Setup(checkTx, func(app *app.EthermintApp, genesis simapp.GenesisState) simapp.GenesisState {
if suite.enableFeemarket {
// setup feemarketGenesis params
feemarketGenesis := feemarkettypes.DefaultGenesisState()
@@ -71,14 +84,19 @@ func (suite *AnteTestSuite) SetupTest() {
suite.Require().NoError(err)
genesis[feemarkettypes.ModuleName] = app.AppCodec().MustMarshalJSON(feemarketGenesis)
}
evmGenesis := evmtypes.DefaultGenesisState()
evmGenesis.Params.AllowUnprotectedTxs = false
if !suite.enableLondonHF {
evmGenesis := evmtypes.DefaultGenesisState()
maxInt := sdk.NewInt(math.MaxInt64)
maxInt := sdkmath.NewInt(math.MaxInt64)
evmGenesis.Params.ChainConfig.LondonBlock = &maxInt
evmGenesis.Params.ChainConfig.ArrowGlacierBlock = &maxInt
evmGenesis.Params.ChainConfig.MergeForkBlock = &maxInt
genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
evmGenesis.Params.ChainConfig.GrayGlacierBlock = &maxInt
evmGenesis.Params.ChainConfig.MergeNetsplitBlock = &maxInt
}
if suite.evmParamsOption != nil {
suite.evmParamsOption(&evmGenesis.Params)
}
genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis)
return genesis
})
@@ -96,7 +114,7 @@ func (suite *AnteTestSuite) SetupTest() {
suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig)
options := ante.HandlerOptions{
anteHandler, err := ante.NewAnteHandler(ante.HandlerOptions{
AccountKeeper: suite.app.AccountKeeper,
BankKeeper: suite.app.BankKeeper,
EvmKeeper: suite.app.EvmKeeper,
@@ -105,11 +123,10 @@ func (suite *AnteTestSuite) SetupTest() {
FeeMarketKeeper: suite.app.FeeMarketKeeper,
SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
}
})
suite.Require().NoError(err)
suite.Require().NoError(options.Validate())
suite.anteHandler = ante.NewAnteHandler(options)
suite.anteHandler = anteHandler
suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID())
}
@@ -119,6 +136,38 @@ func TestAnteTestSuite(t *testing.T) {
})
}
func (s *AnteTestSuite) BuildTestEthTx(
from common.Address,
to common.Address,
amount *big.Int,
input []byte,
gasPrice *big.Int,
gasFeeCap *big.Int,
gasTipCap *big.Int,
accesses *ethtypes.AccessList,
) *evmtypes.MsgEthereumTx {
chainID := s.app.EvmKeeper.ChainID()
nonce := s.app.EvmKeeper.GetNonce(
s.ctx,
common.BytesToAddress(from.Bytes()),
)
msgEthereumTx := evmtypes.NewTx(
chainID,
nonce,
&to,
amount,
TestGasLimit,
gasPrice,
gasFeeCap,
gasTipCap,
input,
accesses,
)
msgEthereumTx.From = from.String()
return msgEthereumTx
}
// CreateTestTx is a helper function to create a tx given multiple inputs.
func (suite *AnteTestSuite) CreateTestTx(
msg *evmtypes.MsgEthereumTx, priv cryptotypes.PrivKey, accNum uint64, signCosmosTx bool,
@@ -150,13 +199,14 @@ func (suite *AnteTestSuite) CreateTestTxBuilder(
err = msg.Sign(suite.ethSigner, tests.NewSigner(priv))
suite.Require().NoError(err)
msg.From = ""
err = builder.SetMsgs(msg)
suite.Require().NoError(err)
txData, err := evmtypes.UnpackTxData(msg.Data)
suite.Require().NoError(err)
fees := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewIntFromBigInt(txData.Fee())))
fees := sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewIntFromBigInt(txData.Fee())))
builder.SetFeeAmount(fees)
builder.SetGasLimit(msg.GetGas())
@@ -199,10 +249,21 @@ func (suite *AnteTestSuite) CreateTestTxBuilder(
return txBuilder
}
func (suite *AnteTestSuite) CreateTestCosmosTxBuilder(gasPrice sdkmath.Int, denom string, msgs ...sdk.Msg) client.TxBuilder {
txBuilder := suite.clientCtx.TxConfig.NewTxBuilder()
txBuilder.SetGasLimit(TestGasLimit)
fees := &sdk.Coins{{Denom: denom, Amount: gasPrice.MulRaw(int64(TestGasLimit))}}
txBuilder.SetFeeAmount(*fees)
err := txBuilder.SetMsgs(msgs...)
suite.Require().NoError(err)
return txBuilder
}
func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgSend(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
// Build MsgSend
recipient := sdk.AccAddress(common.Address{}.Bytes())
msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(1))))
msgSend := types2.NewMsgSend(from, recipient, sdk.NewCoins(sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(1))))
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
}
@@ -210,10 +271,111 @@ func (suite *AnteTestSuite) CreateTestEIP712TxBuilderMsgDelegate(from sdk.AccAdd
// Build MsgSend
valEthAddr := tests.GenerateAddress()
valAddr := sdk.ValAddress(valEthAddr.Bytes())
msgSend := types3.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)))
msgSend := types3.NewMsgDelegate(from, valAddr, sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20)))
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSend)
}
func (suite *AnteTestSuite) CreateTestEIP712MsgCreateValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
// Build MsgCreateValidator
valAddr := sdk.ValAddress(from.Bytes())
privEd := ed25519.GenPrivKey()
msgCreate, err := types3.NewMsgCreateValidator(
valAddr,
privEd.PubKey(),
sdk.NewCoin(evmtypes.DefaultEVMDenom, sdk.NewInt(20)),
// TODO: can this values be empty strings?
types3.NewDescription("moniker", "indentity", "website", "security_contract", "details"),
types3.NewCommissionRates(sdk.OneDec(), sdk.OneDec(), sdk.OneDec()),
sdk.OneInt(),
)
suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgCreate)
}
func (suite *AnteTestSuite) CreateTestEIP712SubmitProposal(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, deposit sdk.Coins) client.TxBuilder {
proposal, ok := types5.ContentFromProposalType("My proposal", "My description", types5.ProposalTypeText)
suite.Require().True(ok)
msgSubmit, err := types5.NewMsgSubmitProposal(proposal, deposit, from)
suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgSubmit)
}
func (suite *AnteTestSuite) CreateTestEIP712GrantAllowance(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
spendLimit := sdk.NewCoins(sdk.NewInt64Coin(evmtypes.DefaultEVMDenom, 10))
threeHours := time.Now().Add(3 * time.Hour)
basic := &feegrant.BasicAllowance{
SpendLimit: spendLimit,
Expiration: &threeHours,
}
granted := tests.GenerateAddress()
grantedAddr := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, granted.Bytes())
msgGrant, err := feegrant.NewMsgGrantAllowance(basic, from, grantedAddr.GetAddress())
suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgGrant)
}
func (suite *AnteTestSuite) CreateTestEIP712MsgEditValidator(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
valAddr := sdk.ValAddress(from.Bytes())
msgEdit := types3.NewMsgEditValidator(
valAddr,
types3.NewDescription("moniker", "identity", "website", "security_contract", "details"),
nil,
nil,
)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEdit)
}
func (suite *AnteTestSuite) CreateTestEIP712MsgSubmitEvidence(from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins) client.TxBuilder {
pk := ed25519.GenPrivKey()
msgEvidence, err := evtypes.NewMsgSubmitEvidence(from, &evtypes.Equivocation{
Height: 11,
Time: time.Now().UTC(),
Power: 100,
ConsensusAddress: pk.PubKey().Address().String(),
})
suite.Require().NoError(err)
return suite.CreateTestEIP712CosmosTxBuilder(from, priv, chainId, gas, gasAmount, msgEvidence)
}
// StdSignBytes returns the bytes to sign for a transaction.
func StdSignBytes(cdc *codec.LegacyAmino, chainID string, accnum uint64, sequence uint64, timeout uint64, fee legacytx.StdFee, msgs []sdk.Msg, memo string, tip *txtypes.Tip) []byte {
msgsBytes := make([]json.RawMessage, 0, len(msgs))
for _, msg := range msgs {
legacyMsg, ok := msg.(legacytx.LegacyMsg)
if !ok {
panic(fmt.Errorf("expected %T when using amino JSON", (*legacytx.LegacyMsg)(nil)))
}
msgsBytes = append(msgsBytes, json.RawMessage(legacyMsg.GetSignBytes()))
}
var stdTip *legacytx.StdTip
if tip != nil {
if tip.Tipper == "" {
panic(fmt.Errorf("tipper cannot be empty"))
}
stdTip = &legacytx.StdTip{Amount: tip.Amount, Tipper: tip.Tipper}
}
bz, err := cdc.MarshalJSON(legacytx.StdSignDoc{
AccountNumber: accnum,
ChainID: chainID,
Fee: json.RawMessage(fee.Bytes()),
Memo: memo,
Msgs: msgsBytes,
Sequence: sequence,
TimeoutHeight: timeout,
Tip: stdTip,
})
if err != nil {
panic(err)
}
return sdk.MustSortJSON(bz)
}
func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
from sdk.AccAddress, priv cryptotypes.PrivKey, chainId string, gas uint64, gasAmount sdk.Coins, msg sdk.Msg,
) client.TxBuilder {
@@ -228,6 +390,11 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
// GenerateTypedData TypedData
var ethermintCodec codec.ProtoCodecMarshaler
registry := codectypes.NewInterfaceRegistry()
types.RegisterInterfaces(registry)
ethermintCodec = codec.NewProtoCodec(registry)
cryptocodec.RegisterInterfaces(registry)
fee := legacytx.NewStdFee(gas, gasAmount)
accNumber := suite.app.AccountKeeper.GetAccount(suite.ctx, from).GetAccountNumber()
@@ -281,6 +448,10 @@ func (suite *AnteTestSuite) CreateTestEIP712CosmosTxBuilder(
return builder
}
func NextFn(ctx sdk.Context, _ sdk.Tx, _ bool) (sdk.Context, error) {
return ctx, nil
}
var _ sdk.Tx = &invalidTx{}
type invalidTx struct{}