forked from cerc-io/laconicd-deprecated
update fork
This commit is contained in:
+9
-2
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, ðtypes.AccessList{{Address: addr, StorageKeys: nil}})
|
||||
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
|
||||
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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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(ðermint.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(ðermint.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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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{}
|
||||
|
||||
Reference in New Issue
Block a user