feat(ante, evm): set priority for eth transactions (#1214)
* Set priority for eth transactions Set the tx priority to the lowest priority in the messages. fix unit tests code cleanup and spec update spec fix go lint add priority integration test add python linter job add access list tx type fix gas limit remove ledger tag, so no need to replace hid dependency fix earlier check ibc-go v5.0.0-beta1 * fix pruned node integration test * Update x/feemarket/spec/09_antehandlers.md Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
53f160cbb0
commit
e1560849dd
@ -47,6 +47,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
recompute eth tx hashes in JSON-RPC APIs to fix old blocks.
|
recompute eth tx hashes in JSON-RPC APIs to fix old blocks.
|
||||||
* (deps) [#1168](https://github.com/evmos/ethermint/pull/1168) Upgrade cosmos-sdk to v0.46.
|
* (deps) [#1168](https://github.com/evmos/ethermint/pull/1168) Upgrade cosmos-sdk to v0.46.
|
||||||
|
|
||||||
|
### API Breaking
|
||||||
|
|
||||||
|
* (ante) [#1214](https://github.com/evmos/ethermint/pull/1214) Set mempool priority to evm transactions.
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
|
|
||||||
* (feemarket) [\#1165](https://github.com/evmos/ethermint/pull/1165) Add hint in specs about different gas terminology for gas in Cosmos and Ethereum.
|
* (feemarket) [\#1165](https://github.com/evmos/ethermint/pull/1165) Add hint in specs about different gas terminology for gas in Cosmos and Ethereum.
|
||||||
|
@ -2,6 +2,7 @@ package ante
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -170,7 +171,7 @@ func NewEthGasConsumeDecorator(
|
|||||||
// - user doesn't have enough balance to deduct the transaction fees (gas_limit * gas_price)
|
// - user doesn't have enough balance to deduct the transaction fees (gas_limit * gas_price)
|
||||||
// - transaction or block gas meter runs out of gas
|
// - transaction or block gas meter runs out of gas
|
||||||
// - sets the gas meter limit
|
// - 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)
|
params := egcd.evmKeeper.GetParams(ctx)
|
||||||
|
|
||||||
ethCfg := params.ChainConfig.EthereumConfig(egcd.evmKeeper.ChainID())
|
ethCfg := params.ChainConfig.EthereumConfig(egcd.evmKeeper.ChainID())
|
||||||
@ -183,6 +184,9 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
|
|||||||
gasWanted := uint64(0)
|
gasWanted := uint64(0)
|
||||||
var events sdk.Events
|
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() {
|
for _, msg := range tx.GetMsgs() {
|
||||||
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -205,7 +209,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
|
|||||||
gasWanted += txData.GetGas()
|
gasWanted += txData.GetGas()
|
||||||
}
|
}
|
||||||
|
|
||||||
fees, err := egcd.evmKeeper.DeductTxCostsFromUserBalance(
|
fees, priority, err := egcd.evmKeeper.DeductTxCostsFromUserBalance(
|
||||||
ctx,
|
ctx,
|
||||||
*msgEthTx,
|
*msgEthTx,
|
||||||
txData,
|
txData,
|
||||||
@ -219,6 +223,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())))
|
events = append(events, sdk.NewEvent(sdk.EventTypeTx, sdk.NewAttribute(sdk.AttributeKeyFee, fees.String())))
|
||||||
|
if priority < minPriority {
|
||||||
|
minPriority = priority
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: change to typed events
|
// TODO: change to typed events
|
||||||
@ -240,8 +247,10 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
|
|||||||
ctx = ctx.WithGasMeter(ethermint.NewInfiniteGasMeterWithLimit(gasWanted))
|
ctx = ctx.WithGasMeter(ethermint.NewInfiniteGasMeterWithLimit(gasWanted))
|
||||||
ctx.GasMeter().ConsumeGas(gasConsumed, "copy gas consumed")
|
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
|
// 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
|
// CanTransferDecorator checks if the sender is allowed to transfer funds according to the EVM block
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/evmos/ethermint/app/ante"
|
"github.com/evmos/ethermint/app/ante"
|
||||||
"github.com/evmos/ethermint/server/config"
|
"github.com/evmos/ethermint/server/config"
|
||||||
"github.com/evmos/ethermint/tests"
|
"github.com/evmos/ethermint/tests"
|
||||||
|
evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
|
||||||
"github.com/evmos/ethermint/x/evm/statedb"
|
"github.com/evmos/ethermint/x/evm/statedb"
|
||||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||||
|
|
||||||
@ -221,9 +222,25 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
|||||||
tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), txGasLimit, big.NewInt(1), nil, nil, nil, nil)
|
tx := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), txGasLimit, big.NewInt(1), nil, nil, nil, nil)
|
||||||
tx.From = addr.Hex()
|
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, evmkeeper.DefaultPriorityReduction.BigInt())
|
||||||
|
|
||||||
tx2GasLimit := uint64(1000000)
|
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()
|
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(evmkeeper.DefaultPriorityReduction.Int64()*2)), // gasFeeCap
|
||||||
|
evmkeeper.DefaultPriorityReduction.BigInt(), // gasTipCap
|
||||||
|
nil, ðtypes.AccessList{{Address: addr, StorageKeys: nil}})
|
||||||
|
dynamicFeeTx.From = addr.Hex()
|
||||||
|
dynamicFeeTxPriority := int64(1)
|
||||||
|
|
||||||
var vmdb *statedb.StateDB
|
var vmdb *statedb.StateDB
|
||||||
|
|
||||||
@ -234,14 +251,16 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
|||||||
malleate func()
|
malleate func()
|
||||||
expPass bool
|
expPass bool
|
||||||
expPanic bool
|
expPanic bool
|
||||||
|
expPriority int64
|
||||||
}{
|
}{
|
||||||
{"invalid transaction type", &invalidTx{}, math.MaxUint64, func() {}, false, false},
|
{"invalid transaction type", &invalidTx{}, math.MaxUint64, func() {}, false, false, 0},
|
||||||
{
|
{
|
||||||
"sender not found",
|
"sender not found",
|
||||||
evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil),
|
evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), 1000, big.NewInt(1), nil, nil, nil, nil),
|
||||||
math.MaxUint64,
|
math.MaxUint64,
|
||||||
func() {},
|
func() {},
|
||||||
false, false,
|
false, false,
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gas limit too low",
|
"gas limit too low",
|
||||||
@ -249,6 +268,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
|||||||
math.MaxUint64,
|
math.MaxUint64,
|
||||||
func() {},
|
func() {},
|
||||||
false, false,
|
false, false,
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"not enough balance for fees",
|
"not enough balance for fees",
|
||||||
@ -256,6 +276,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
|||||||
math.MaxUint64,
|
math.MaxUint64,
|
||||||
func() {},
|
func() {},
|
||||||
false, false,
|
false, false,
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"not enough tx gas",
|
"not enough tx gas",
|
||||||
@ -265,6 +286,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
|||||||
vmdb.AddBalance(addr, big.NewInt(1000000))
|
vmdb.AddBalance(addr, big.NewInt(1000000))
|
||||||
},
|
},
|
||||||
false, true,
|
false, true,
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"not enough block gas",
|
"not enough block gas",
|
||||||
@ -272,21 +294,32 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
|||||||
0,
|
0,
|
||||||
func() {
|
func() {
|
||||||
vmdb.AddBalance(addr, big.NewInt(1000000))
|
vmdb.AddBalance(addr, big.NewInt(1000000))
|
||||||
|
|
||||||
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1))
|
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(1))
|
||||||
},
|
},
|
||||||
false, true,
|
false, true,
|
||||||
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"success",
|
"success - legacy tx",
|
||||||
tx2,
|
tx2,
|
||||||
tx2GasLimit, // it's capped
|
tx2GasLimit, // it's capped
|
||||||
func() {
|
func() {
|
||||||
vmdb.AddBalance(addr, big.NewInt(1000000))
|
vmdb.AddBalance(addr, big.NewInt(1001000000000000))
|
||||||
|
|
||||||
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(10000000000000000000))
|
suite.ctx = suite.ctx.WithBlockGasMeter(sdk.NewGasMeter(10000000000000000000))
|
||||||
},
|
},
|
||||||
true, false,
|
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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,6 +339,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
|||||||
ctx, 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 {
|
if tc.expPass {
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
suite.Require().Equal(tc.expPriority, ctx.Priority())
|
||||||
} else {
|
} else {
|
||||||
suite.Require().Error(err)
|
suite.Require().Error(err)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
|
|||||||
ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
|
ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
|
||||||
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
||||||
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
||||||
ibcante.NewRedundancyDecorator(options.IBCKeeper),
|
ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
|
||||||
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
|
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
|
|||||||
// Note: signature verification uses EIP instead of the cosmos signature validator
|
// Note: signature verification uses EIP instead of the cosmos signature validator
|
||||||
NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
|
||||||
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
|
||||||
ibcante.NewRedundancyDecorator(options.IBCKeeper),
|
ibcante.NewRedundantRelayDecorator(options.IBCKeeper),
|
||||||
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
|
NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ type EVMKeeper interface {
|
|||||||
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) *vm.EVM
|
||||||
DeductTxCostsFromUserBalance(
|
DeductTxCostsFromUserBalance(
|
||||||
ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool,
|
ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool,
|
||||||
) (sdk.Coins, error)
|
) (fees sdk.Coins, priority int64, err error)
|
||||||
GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int
|
GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int
|
||||||
GetBalance(ctx sdk.Context, addr common.Address) *big.Int
|
GetBalance(ctx sdk.Context, addr common.Address) *big.Int
|
||||||
ResetTransientGasUsed(ctx sdk.Context)
|
ResetTransientGasUsed(ctx sdk.Context)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
let
|
let
|
||||||
version = "v0.17.1";
|
version = "v0.17.1";
|
||||||
pname = "ethermintd";
|
pname = "ethermintd";
|
||||||
tags = [ "ledger" "netgo" ];
|
tags = [ "netgo" ];
|
||||||
ldflags = lib.concatStringsSep "\n" ([
|
ldflags = lib.concatStringsSep "\n" ([
|
||||||
"-X github.com/cosmos/cosmos-sdk/version.Name=ethermint"
|
"-X github.com/cosmos/cosmos-sdk/version.Name=ethermint"
|
||||||
"-X github.com/cosmos/cosmos-sdk/version.AppName=${pname}"
|
"-X github.com/cosmos/cosmos-sdk/version.AppName=${pname}"
|
||||||
|
4
go.mod
4
go.mod
@ -9,7 +9,7 @@ require (
|
|||||||
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
|
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
|
||||||
github.com/cosmos/cosmos-sdk v0.46.0
|
github.com/cosmos/cosmos-sdk v0.46.0
|
||||||
github.com/cosmos/go-bip39 v1.0.0
|
github.com/cosmos/go-bip39 v1.0.0
|
||||||
github.com/cosmos/ibc-go/v5 v5.0.0
|
github.com/cosmos/ibc-go/v5 v5.0.0-beta1
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/ethereum/go-ethereum v1.10.19
|
github.com/ethereum/go-ethereum v1.10.19
|
||||||
github.com/gogo/protobuf v1.3.3
|
github.com/gogo/protobuf v1.3.3
|
||||||
@ -185,8 +185,6 @@ require (
|
|||||||
replace (
|
replace (
|
||||||
github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76
|
github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76
|
||||||
|
|
||||||
github.com/cosmos/ibc-go/v5 => github.com/notional-labs/ibc-go/v5 v5.0.0-20220728121949-040aca93dda5
|
|
||||||
|
|
||||||
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
|
// Fix upstream GHSA-h395-qcrw-5vmq vulnerability.
|
||||||
// TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409
|
// TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409
|
||||||
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0
|
github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0
|
||||||
|
4
go.sum
4
go.sum
@ -324,6 +324,8 @@ github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4
|
|||||||
github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw=
|
github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw=
|
||||||
github.com/cosmos/iavl v0.19.0 h1:sgyrjqOkycXiN7Tuupuo4QAldKFg7Sipyfeg/IL7cps=
|
github.com/cosmos/iavl v0.19.0 h1:sgyrjqOkycXiN7Tuupuo4QAldKFg7Sipyfeg/IL7cps=
|
||||||
github.com/cosmos/iavl v0.19.0/go.mod h1:l5h9pAB3m5fihB3pXVgwYqdY8aBsMagqz7T0MUjxZeA=
|
github.com/cosmos/iavl v0.19.0/go.mod h1:l5h9pAB3m5fihB3pXVgwYqdY8aBsMagqz7T0MUjxZeA=
|
||||||
|
github.com/cosmos/ibc-go/v5 v5.0.0-beta1 h1:YqC9giQlZId8Wui8xpaUFI+TpVmEupQZSoDlmxAu6yI=
|
||||||
|
github.com/cosmos/ibc-go/v5 v5.0.0-beta1/go.mod h1:9mmcbzuidgX7nhafIKng/XhXAHDEnRqDjGy/60W1cvg=
|
||||||
github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU=
|
github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76 h1:DdzS1m6o/pCqeZ8VOAit/gyATedRgjvkVI+UCrLpyuU=
|
||||||
github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8=
|
github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76/go.mod h1:0mkLWIoZuQ7uBoospo5Q9zIpqq6rYCPJDSUdeCJvPM8=
|
||||||
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
|
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
|
||||||
@ -1074,8 +1076,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||||||
github.com/nishanths/exhaustive v0.7.11/go.mod h1:gX+MP7DWMKJmNa1HfMozK+u04hQd3na9i0hyqf3/dOI=
|
github.com/nishanths/exhaustive v0.7.11/go.mod h1:gX+MP7DWMKJmNa1HfMozK+u04hQd3na9i0hyqf3/dOI=
|
||||||
github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
|
github.com/nishanths/predeclared v0.0.0-20190419143655-18a43bb90ffc/go.mod h1:62PewwiQTlm/7Rj+cxVYqZvDIUc+JjZq6GHAC1fsObQ=
|
||||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||||
github.com/notional-labs/ibc-go/v5 v5.0.0-20220728121949-040aca93dda5 h1:G/tpnrLpUj8avmWtggo0L3f8u9gS8s4SqCGrXvlEO5w=
|
|
||||||
github.com/notional-labs/ibc-go/v5 v5.0.0-20220728121949-040aca93dda5/go.mod h1:KxDAWaeoibbXS0remZwoFz4nil+6+Bi4X3XQh0+23Io=
|
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||||
|
@ -102,9 +102,8 @@ schema = 3
|
|||||||
version = "v0.19.0"
|
version = "v0.19.0"
|
||||||
hash = "sha256-NunBVGJqJkpOMcTmFUCnXQiJSjgNEuxlMu+bkj33wIs="
|
hash = "sha256-NunBVGJqJkpOMcTmFUCnXQiJSjgNEuxlMu+bkj33wIs="
|
||||||
[mod."github.com/cosmos/ibc-go/v5"]
|
[mod."github.com/cosmos/ibc-go/v5"]
|
||||||
version = "v5.0.0-20220728121949-040aca93dda5"
|
version = "v5.0.0-beta1"
|
||||||
hash = "sha256-G+hffr22KJZlbshH9HkqMP8m9XLKcSSzwpKCx9cphbo="
|
hash = "sha256-v+dGnhNSNkkQzuCUoWmVGMPQCEb6aL/oB7zxbYw9owE="
|
||||||
replaced = "github.com/notional-labs/ibc-go/v5"
|
|
||||||
[mod."github.com/cosmos/ledger-cosmos-go"]
|
[mod."github.com/cosmos/ledger-cosmos-go"]
|
||||||
version = "v0.11.1"
|
version = "v0.11.1"
|
||||||
hash = "sha256-yli+VvVtZmHo2LPvCY6lYVUfcCDn3sBLDL+a8KIlqDA="
|
hash = "sha256-yli+VvVtZmHo2LPvCY6lYVUfcCDn3sBLDL+a8KIlqDA="
|
||||||
|
@ -974,7 +974,7 @@ func (e *PublicAPI) GetTransactionReceipt(hash common.Hash) (map[string]interfac
|
|||||||
// tolerate the error for pruned node.
|
// tolerate the error for pruned node.
|
||||||
e.logger.Error("fetch basefee failed, node is pruned?", "height", res.Height, "error", err)
|
e.logger.Error("fetch basefee failed, node is pruned?", "height", res.Height, "error", err)
|
||||||
} else {
|
} else {
|
||||||
receipt["effectiveGasPrice"] = hexutil.Big(*dynamicTx.GetEffectiveGasPrice(baseFee))
|
receipt["effectiveGasPrice"] = hexutil.Big(*dynamicTx.EffectiveGasPrice(baseFee))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
{
|
{
|
||||||
dotenv: '../../../scripts/.env',
|
dotenv: '../../../scripts/.env',
|
||||||
'ethermintd_777-1': {
|
'ethermint_9000-1': {
|
||||||
cmd: 'ethermintd',
|
cmd: 'ethermintd',
|
||||||
'start-flags': '--trace',
|
'start-flags': '--trace',
|
||||||
|
config: {
|
||||||
|
consensus: {
|
||||||
|
// larger timeout for more stable mempool tests
|
||||||
|
timeout_commit: '10s',
|
||||||
|
},
|
||||||
|
mempool: {
|
||||||
|
// use v1 mempool to enable tx prioritization
|
||||||
|
version: 'v1',
|
||||||
|
},
|
||||||
|
},
|
||||||
'app-config': {
|
'app-config': {
|
||||||
'minimum-gas-prices': '0aphoton',
|
'minimum-gas-prices': '0aphoton',
|
||||||
'index-events': ['ethereum_tx.ethereumTxHash'],
|
'index-events': ['ethereum_tx.ethereumTxHash'],
|
||||||
|
@ -4,7 +4,6 @@ import signal
|
|||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import tomlkit
|
|
||||||
import web3
|
import web3
|
||||||
from pystarport import ports
|
from pystarport import ports
|
||||||
from web3.middleware import geth_poa_middleware
|
from web3.middleware import geth_poa_middleware
|
||||||
@ -61,9 +60,10 @@ class Geth:
|
|||||||
|
|
||||||
|
|
||||||
def setup_ethermint(path, base_port):
|
def setup_ethermint(path, base_port):
|
||||||
cfg = Path(__file__).parent / "../../scripts/ethermint-devnet.yaml"
|
cfg = Path(__file__).parent / "configs/default.jsonnet"
|
||||||
yield from setup_custom_ethermint(path, base_port, cfg)
|
yield from setup_custom_ethermint(path, base_port, cfg)
|
||||||
|
|
||||||
|
|
||||||
def setup_geth(path, base_port):
|
def setup_geth(path, base_port):
|
||||||
with (path / "geth.log").open("w") as logfile:
|
with (path / "geth.log").open("w") as logfile:
|
||||||
cmd = [
|
cmd = [
|
||||||
|
114
tests/integration_tests/test_priority.py
Normal file
114
tests/integration_tests/test_priority.py
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
from .network import Ethermint
|
||||||
|
from .utils import KEYS, sign_transaction
|
||||||
|
|
||||||
|
PRIORITY_REDUCTION = 1000000
|
||||||
|
|
||||||
|
|
||||||
|
def effective_gas_price(tx, base_fee):
|
||||||
|
if "maxFeePerGas" in tx:
|
||||||
|
# dynamic fee tx
|
||||||
|
return min(base_fee + tx["maxPriorityFeePerGas"], tx["maxFeePerGas"])
|
||||||
|
else:
|
||||||
|
# legacy tx
|
||||||
|
return tx["gasPrice"]
|
||||||
|
|
||||||
|
|
||||||
|
def tx_priority(tx, base_fee):
|
||||||
|
if "maxFeePerGas" in tx:
|
||||||
|
# dynamic fee tx
|
||||||
|
return (
|
||||||
|
min(tx["maxPriorityFeePerGas"], tx["maxFeePerGas"] - base_fee)
|
||||||
|
// PRIORITY_REDUCTION
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# legacy tx
|
||||||
|
return (tx["gasPrice"] - base_fee) // PRIORITY_REDUCTION
|
||||||
|
|
||||||
|
|
||||||
|
def test_priority(ethermint: Ethermint):
|
||||||
|
"""
|
||||||
|
test priorities of different tx types
|
||||||
|
"""
|
||||||
|
w3 = ethermint.w3
|
||||||
|
amount = 10000
|
||||||
|
base_fee = w3.eth.get_block("latest").baseFeePerGas
|
||||||
|
|
||||||
|
# [ ( sender, tx ), ... ]
|
||||||
|
# use different senders to avoid nonce conflicts
|
||||||
|
test_cases = [
|
||||||
|
(
|
||||||
|
"validator",
|
||||||
|
{
|
||||||
|
"to": "0x0000000000000000000000000000000000000000",
|
||||||
|
"value": amount,
|
||||||
|
"gas": 21000,
|
||||||
|
"maxFeePerGas": base_fee + PRIORITY_REDUCTION * 6,
|
||||||
|
"maxPriorityFeePerGas": 0,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"community",
|
||||||
|
{
|
||||||
|
"to": "0x0000000000000000000000000000000000000000",
|
||||||
|
"value": amount,
|
||||||
|
"gas": 21000,
|
||||||
|
"gasPrice": base_fee + PRIORITY_REDUCTION * 2,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"signer2",
|
||||||
|
{
|
||||||
|
"to": "0x0000000000000000000000000000000000000000",
|
||||||
|
"value": amount,
|
||||||
|
"gasPrice": base_fee + PRIORITY_REDUCTION * 4,
|
||||||
|
"accessList": [
|
||||||
|
{
|
||||||
|
"address": "0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae",
|
||||||
|
"storageKeys": (
|
||||||
|
"0x00000000000000000000000000000000000000000000000000000000"
|
||||||
|
"00000003",
|
||||||
|
"0x00000000000000000000000000000000000000000000000000000000"
|
||||||
|
"00000007",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"signer1",
|
||||||
|
{
|
||||||
|
"to": "0x0000000000000000000000000000000000000000",
|
||||||
|
"value": amount,
|
||||||
|
"gas": 21000,
|
||||||
|
"maxFeePerGas": base_fee + PRIORITY_REDUCTION * 6,
|
||||||
|
"maxPriorityFeePerGas": PRIORITY_REDUCTION * 6,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# test cases are ordered by priority
|
||||||
|
priorities = [tx_priority(tx, base_fee) for _, tx in test_cases]
|
||||||
|
assert all(a < b for a, b in zip(priorities, priorities[1:]))
|
||||||
|
|
||||||
|
signed = [sign_transaction(w3, tx, key=KEYS[sender]) for sender, tx in test_cases]
|
||||||
|
# send the txs from low priority to high,
|
||||||
|
# but the later sent txs should be included earlier.
|
||||||
|
txhashes = [w3.eth.send_raw_transaction(tx.rawTransaction) for tx in signed]
|
||||||
|
|
||||||
|
receipts = [w3.eth.wait_for_transaction_receipt(txhash) for txhash in txhashes]
|
||||||
|
print(receipts)
|
||||||
|
assert all(receipt.status == 1 for receipt in receipts), "expect all txs success"
|
||||||
|
|
||||||
|
# the later txs should be included earlier because of higher priority
|
||||||
|
# FIXME there's some non-deterministics due to mempool logic
|
||||||
|
assert all(included_earlier(r2, r1) for r1, r2 in zip(receipts, receipts[1:]))
|
||||||
|
|
||||||
|
|
||||||
|
def included_earlier(receipt1, receipt2):
|
||||||
|
"returns true if receipt1 is earlier than receipt2"
|
||||||
|
if receipt1.blockNumber < receipt2.blockNumber:
|
||||||
|
return True
|
||||||
|
elif receipt1.blockNumber == receipt2.blockNumber:
|
||||||
|
return receipt1.transactionIndex < receipt2.transactionIndex
|
||||||
|
else:
|
||||||
|
return False
|
@ -1,6 +1,22 @@
|
|||||||
|
import os
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from eth_account import Account
|
||||||
|
from web3._utils.transactions import fill_nonce, fill_transaction_defaults
|
||||||
|
|
||||||
|
Account.enable_unaudited_hdwallet_features()
|
||||||
|
|
||||||
|
ACCOUNTS = {
|
||||||
|
"validator": Account.from_mnemonic(os.getenv("VALIDATOR1_MNEMONIC")),
|
||||||
|
"community": Account.from_mnemonic(os.getenv("COMMUNITY_MNEMONIC")),
|
||||||
|
"signer1": Account.from_mnemonic(os.getenv("SIGNER1_MNEMONIC")),
|
||||||
|
"signer2": Account.from_mnemonic(os.getenv("SIGNER2_MNEMONIC")),
|
||||||
|
}
|
||||||
|
KEYS = {name: account.key for name, account in ACCOUNTS.items()}
|
||||||
|
ADDRS = {name: account.address for name, account in ACCOUNTS.items()}
|
||||||
|
|
||||||
|
|
||||||
def wait_for_port(port, host="127.0.0.1", timeout=40.0):
|
def wait_for_port(port, host="127.0.0.1", timeout=40.0):
|
||||||
start_time = time.perf_counter()
|
start_time = time.perf_counter()
|
||||||
while True:
|
while True:
|
||||||
@ -14,3 +30,21 @@ def wait_for_port(port, host="127.0.0.1", timeout=40.0):
|
|||||||
"Waited too long for the port {} on host {} to start accepting "
|
"Waited too long for the port {} on host {} to start accepting "
|
||||||
"connections.".format(port, host)
|
"connections.".format(port, host)
|
||||||
) from ex
|
) from ex
|
||||||
|
|
||||||
|
|
||||||
|
def fill_defaults(w3, tx):
|
||||||
|
return fill_nonce(w3, fill_transaction_defaults(w3, tx))
|
||||||
|
|
||||||
|
|
||||||
|
def sign_transaction(w3, tx, key=KEYS["validator"]):
|
||||||
|
"fill default fields and sign"
|
||||||
|
acct = Account.from_key(key)
|
||||||
|
tx["from"] = acct.address
|
||||||
|
tx = fill_defaults(w3, tx)
|
||||||
|
return acct.sign_transaction(tx)
|
||||||
|
|
||||||
|
|
||||||
|
def send_transaction(w3, tx, key=KEYS["validator"]):
|
||||||
|
signed = sign_transaction(w3, tx, key)
|
||||||
|
txhash = w3.eth.send_raw_transaction(signed.rawTransaction)
|
||||||
|
return w3.eth.wait_for_transaction_receipt(txhash)
|
||||||
|
@ -569,13 +569,14 @@ func (suite *EvmTestSuite) TestERC20TransferReverted() {
|
|||||||
k.SetHooks(tc.hooks)
|
k.SetHooks(tc.hooks)
|
||||||
|
|
||||||
// add some fund to pay gas fee
|
// add some fund to pay gas fee
|
||||||
k.SetBalance(suite.ctx, suite.from, big.NewInt(10000000000))
|
k.SetBalance(suite.ctx, suite.from, big.NewInt(1000000000000000))
|
||||||
|
|
||||||
contract := suite.deployERC20Contract()
|
contract := suite.deployERC20Contract()
|
||||||
|
|
||||||
data, err := types.ERC20Contract.ABI.Pack("transfer", suite.from, big.NewInt(10))
|
data, err := types.ERC20Contract.ABI.Pack("transfer", suite.from, big.NewInt(10))
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
|
gasPrice := big.NewInt(1000000000) // must be bigger than or equal to baseFee
|
||||||
nonce := k.GetNonce(suite.ctx, suite.from)
|
nonce := k.GetNonce(suite.ctx, suite.from)
|
||||||
tx := types.NewTx(
|
tx := types.NewTx(
|
||||||
suite.chainID,
|
suite.chainID,
|
||||||
@ -583,7 +584,7 @@ func (suite *EvmTestSuite) TestERC20TransferReverted() {
|
|||||||
&contract,
|
&contract,
|
||||||
big.NewInt(0),
|
big.NewInt(0),
|
||||||
tc.gasLimit,
|
tc.gasLimit,
|
||||||
big.NewInt(1),
|
gasPrice,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
data,
|
data,
|
||||||
@ -595,7 +596,7 @@ func (suite *EvmTestSuite) TestERC20TransferReverted() {
|
|||||||
|
|
||||||
txData, err := types.UnpackTxData(tx.Data)
|
txData, err := types.UnpackTxData(tx.Data)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
_, err = k.DeductTxCostsFromUserBalance(suite.ctx, *tx, txData, "aphoton", true, true, true)
|
_, _, err = k.DeductTxCostsFromUserBalance(suite.ctx, *tx, txData, "aphoton", true, true, true)
|
||||||
suite.Require().NoError(err)
|
suite.Require().NoError(err)
|
||||||
|
|
||||||
res, err := k.EthereumTx(sdk.WrapSDKContext(suite.ctx), tx)
|
res, err := k.EthereumTx(sdk.WrapSDKContext(suite.ctx), tx)
|
||||||
@ -614,7 +615,7 @@ func (suite *EvmTestSuite) TestERC20TransferReverted() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check gas refund works: only deducted fee for gas used, rather than gas limit.
|
// check gas refund works: only deducted fee for gas used, rather than gas limit.
|
||||||
suite.Require().Equal(big.NewInt(int64(res.GasUsed)), new(big.Int).Sub(before, after))
|
suite.Require().Equal(new(big.Int).Mul(gasPrice, big.NewInt(int64(res.GasUsed))), new(big.Int).Sub(before, after))
|
||||||
|
|
||||||
// nonce should not be increased.
|
// nonce should not be increased.
|
||||||
nonce2 := k.GetNonce(suite.ctx, suite.from)
|
nonce2 := k.GetNonce(suite.ctx, suite.from)
|
||||||
|
@ -29,9 +29,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("Feemarket", func() {
|
var _ = Describe("Feemarket", func() {
|
||||||
var (
|
var privKey *ethsecp256k1.PrivKey
|
||||||
privKey *ethsecp256k1.PrivKey
|
|
||||||
)
|
|
||||||
|
|
||||||
Describe("Performing EVM transactions", func() {
|
Describe("Performing EVM transactions", func() {
|
||||||
type txParams struct {
|
type txParams struct {
|
||||||
|
@ -293,7 +293,11 @@ func (k *Keeper) GetBalance(ctx sdk.Context, addr common.Address) *big.Int {
|
|||||||
// - `0`: london hardfork enabled but feemarket is not enabled.
|
// - `0`: london hardfork enabled but feemarket is not enabled.
|
||||||
// - `n`: both london hardfork and feemarket are enabled.
|
// - `n`: both london hardfork and feemarket are enabled.
|
||||||
func (k Keeper) GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
|
func (k Keeper) GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int {
|
||||||
if !types.IsLondon(ethCfg, ctx.BlockHeight()) {
|
return k.getBaseFee(ctx, types.IsLondon(ethCfg, ctx.BlockHeight()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k Keeper) getBaseFee(ctx sdk.Context, london bool) *big.Int {
|
||||||
|
if !london {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package keeper
|
package keeper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
sdkmath "cosmossdk.io/math"
|
sdkmath "cosmossdk.io/math"
|
||||||
@ -14,20 +15,26 @@ import (
|
|||||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DefaultPriorityReduction is the default amount of price values required for 1 unit of priority.
|
||||||
|
// Because priority is `int64` while price is `big.Int`, it's necessary to scale down the range to keep it more pratical.
|
||||||
|
// The default value is the same as the `sdk.DefaultPowerReduction`.
|
||||||
|
var DefaultPriorityReduction = sdk.DefaultPowerReduction
|
||||||
|
|
||||||
// DeductTxCostsFromUserBalance it calculates the tx costs and deducts the fees
|
// DeductTxCostsFromUserBalance it calculates the tx costs and deducts the fees
|
||||||
|
// returns (effectiveFee, priority, error)
|
||||||
func (k Keeper) DeductTxCostsFromUserBalance(
|
func (k Keeper) DeductTxCostsFromUserBalance(
|
||||||
ctx sdk.Context,
|
ctx sdk.Context,
|
||||||
msgEthTx evmtypes.MsgEthereumTx,
|
msgEthTx evmtypes.MsgEthereumTx,
|
||||||
txData evmtypes.TxData,
|
txData evmtypes.TxData,
|
||||||
denom string,
|
denom string,
|
||||||
homestead, istanbul, london bool,
|
homestead, istanbul, london bool,
|
||||||
) (sdk.Coins, error) {
|
) (fees sdk.Coins, priority int64, err error) {
|
||||||
isContractCreation := txData.GetTo() == nil
|
isContractCreation := txData.GetTo() == nil
|
||||||
|
|
||||||
// fetch sender account from signature
|
// fetch sender account from signature
|
||||||
signerAcc, err := authante.GetSignerAcc(ctx, k.accountKeeper, msgEthTx.GetFrom())
|
signerAcc, err := authante.GetSignerAcc(ctx, k.accountKeeper, msgEthTx.GetFrom())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, sdkerrors.Wrapf(err, "account not found for sender %s", msgEthTx.From)
|
return nil, 0, sdkerrors.Wrapf(err, "account not found for sender %s", msgEthTx.From)
|
||||||
}
|
}
|
||||||
|
|
||||||
gasLimit := txData.GetGas()
|
gasLimit := txData.GetGas()
|
||||||
@ -39,7 +46,7 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
|||||||
|
|
||||||
intrinsicGas, err := core.IntrinsicGas(txData.GetData(), accessList, isContractCreation, homestead, istanbul)
|
intrinsicGas, err := core.IntrinsicGas(txData.GetData(), accessList, isContractCreation, homestead, istanbul)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, sdkerrors.Wrapf(
|
return nil, 0, sdkerrors.Wrapf(
|
||||||
err,
|
err,
|
||||||
"failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t",
|
"failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t",
|
||||||
isContractCreation, homestead, istanbul,
|
isContractCreation, homestead, istanbul,
|
||||||
@ -48,7 +55,7 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
|||||||
|
|
||||||
// intrinsic gas verification during CheckTx
|
// intrinsic gas verification during CheckTx
|
||||||
if ctx.IsCheckTx() && gasLimit < intrinsicGas {
|
if ctx.IsCheckTx() && gasLimit < intrinsicGas {
|
||||||
return nil, sdkerrors.Wrapf(
|
return nil, 0, sdkerrors.Wrapf(
|
||||||
sdkerrors.ErrOutOfGas,
|
sdkerrors.ErrOutOfGas,
|
||||||
"gas limit too low: %d (gas limit) < %d (intrinsic gas)", gasLimit, intrinsicGas,
|
"gas limit too low: %d (gas limit) < %d (intrinsic gas)", gasLimit, intrinsicGas,
|
||||||
)
|
)
|
||||||
@ -56,33 +63,42 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
|||||||
|
|
||||||
var feeAmt *big.Int
|
var feeAmt *big.Int
|
||||||
|
|
||||||
feeMktParams := k.feeMarketKeeper.GetParams(ctx)
|
baseFee := k.getBaseFee(ctx, london)
|
||||||
if london && feeMktParams.IsBaseFeeEnabled(ctx.BlockHeight()) && txData.TxType() == ethtypes.DynamicFeeTxType {
|
if baseFee != nil && txData.GetGasFeeCap().Cmp(baseFee) < 0 {
|
||||||
baseFee := k.feeMarketKeeper.GetBaseFee(ctx)
|
return nil, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "the tx gasfeecap is lower than the tx baseFee: %s (gasfeecap), %s (basefee) ", txData.GetGasFeeCap(), baseFee)
|
||||||
if txData.GetGasFeeCap().Cmp(baseFee) < 0 {
|
|
||||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFee, "the tx gasfeecap is lower than the tx baseFee: %s (gasfeecap), %s (basefee) ", txData.GetGasFeeCap(), baseFee)
|
|
||||||
}
|
|
||||||
feeAmt = txData.EffectiveFee(baseFee)
|
|
||||||
} else {
|
|
||||||
feeAmt = txData.Fee()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
feeAmt = txData.EffectiveFee(baseFee)
|
||||||
if feeAmt.Sign() == 0 {
|
if feeAmt.Sign() == 0 {
|
||||||
// zero fee, no need to deduct
|
// zero fee, no need to deduct
|
||||||
return sdk.Coins{}, nil
|
return sdk.Coins{}, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
fees := sdk.Coins{sdk.NewCoin(denom, sdkmath.NewIntFromBigInt(feeAmt))}
|
fees = sdk.Coins{sdk.NewCoin(denom, sdkmath.NewIntFromBigInt(feeAmt))}
|
||||||
|
|
||||||
// deduct the full gas cost from the user balance
|
// deduct the full gas cost from the user balance
|
||||||
if err := authante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil {
|
if err := authante.DeductFees(k.bankKeeper, ctx, signerAcc, fees); err != nil {
|
||||||
return nil, sdkerrors.Wrapf(
|
return nil, 0, sdkerrors.Wrapf(
|
||||||
err,
|
err,
|
||||||
"failed to deduct full gas cost %s from the user %s balance",
|
"failed to deduct full gas cost %s from the user %s balance",
|
||||||
fees, msgEthTx.From,
|
fees, msgEthTx.From,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return fees, nil
|
|
||||||
|
// calculate priority based on effective gas price
|
||||||
|
tipPrice := txData.EffectiveGasPrice(baseFee)
|
||||||
|
// if london hardfork is not enabled, tipPrice is the gasPrice
|
||||||
|
if baseFee != nil {
|
||||||
|
tipPrice = new(big.Int).Sub(tipPrice, baseFee)
|
||||||
|
}
|
||||||
|
priorityBig := new(big.Int).Quo(tipPrice, DefaultPriorityReduction.BigInt())
|
||||||
|
if !priorityBig.IsInt64() {
|
||||||
|
priority = math.MaxInt64
|
||||||
|
} else {
|
||||||
|
priority = priorityBig.Int64()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fees, priority, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckSenderBalance validates that the tx cost value is positive and that the
|
// CheckSenderBalance validates that the tx cost value is positive and that the
|
||||||
|
@ -403,7 +403,7 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
|||||||
|
|
||||||
txData, _ := evmtypes.UnpackTxData(tx.Data)
|
txData, _ := evmtypes.UnpackTxData(tx.Data)
|
||||||
|
|
||||||
fees, err := suite.app.EvmKeeper.DeductTxCostsFromUserBalance(
|
fees, priority, err := suite.app.EvmKeeper.DeductTxCostsFromUserBalance(
|
||||||
suite.ctx,
|
suite.ctx,
|
||||||
*tx,
|
*tx,
|
||||||
txData,
|
txData,
|
||||||
@ -424,6 +424,7 @@ func (suite *KeeperTestSuite) TestDeductTxCostsFromUserBalance() {
|
|||||||
),
|
),
|
||||||
"valid test %d failed, fee value is wrong ", i,
|
"valid test %d failed, fee value is wrong ", i,
|
||||||
)
|
)
|
||||||
|
suite.Require().Equal(int64(0), priority)
|
||||||
} else {
|
} else {
|
||||||
suite.Require().Equal(
|
suite.Require().Equal(
|
||||||
fees,
|
fees,
|
||||||
|
@ -232,6 +232,11 @@ func (tx AccessListTx) Cost() *big.Int {
|
|||||||
return cost(tx.Fee(), tx.GetValue())
|
return cost(tx.Fee(), tx.GetValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EffectiveGasPrice is the same as GasPrice for AccessListTx
|
||||||
|
func (tx AccessListTx) EffectiveGasPrice(baseFee *big.Int) *big.Int {
|
||||||
|
return tx.GetGasPrice()
|
||||||
|
}
|
||||||
|
|
||||||
// EffectiveFee is the same as Fee for AccessListTx
|
// EffectiveFee is the same as Fee for AccessListTx
|
||||||
func (tx AccessListTx) EffectiveFee(baseFee *big.Int) *big.Int {
|
func (tx AccessListTx) EffectiveFee(baseFee *big.Int) *big.Int {
|
||||||
return tx.Fee()
|
return tx.Fee()
|
||||||
|
@ -265,14 +265,14 @@ func (tx DynamicFeeTx) Cost() *big.Int {
|
|||||||
return cost(tx.Fee(), tx.GetValue())
|
return cost(tx.Fee(), tx.GetValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEffectiveGasPrice returns the effective gas price
|
// EffectiveGasPrice returns the effective gas price
|
||||||
func (tx *DynamicFeeTx) GetEffectiveGasPrice(baseFee *big.Int) *big.Int {
|
func (tx *DynamicFeeTx) EffectiveGasPrice(baseFee *big.Int) *big.Int {
|
||||||
return math.BigMin(new(big.Int).Add(tx.GasTipCap.BigInt(), baseFee), tx.GasFeeCap.BigInt())
|
return math.BigMin(new(big.Int).Add(tx.GasTipCap.BigInt(), baseFee), tx.GasFeeCap.BigInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveFee returns effective_gasprice * gaslimit.
|
// EffectiveFee returns effective_gasprice * gaslimit.
|
||||||
func (tx DynamicFeeTx) EffectiveFee(baseFee *big.Int) *big.Int {
|
func (tx DynamicFeeTx) EffectiveFee(baseFee *big.Int) *big.Int {
|
||||||
return fee(tx.GetEffectiveGasPrice(baseFee), tx.GasLimit)
|
return fee(tx.EffectiveGasPrice(baseFee), tx.GasLimit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EffectiveCost returns amount + effective_gasprice * gaslimit.
|
// EffectiveCost returns amount + effective_gasprice * gaslimit.
|
||||||
|
@ -201,6 +201,11 @@ func (tx LegacyTx) Cost() *big.Int {
|
|||||||
return cost(tx.Fee(), tx.GetValue())
|
return cost(tx.Fee(), tx.GetValue())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EffectiveGasPrice is the same as GasPrice for LegacyTx
|
||||||
|
func (tx LegacyTx) EffectiveGasPrice(baseFee *big.Int) *big.Int {
|
||||||
|
return tx.GetGasPrice()
|
||||||
|
}
|
||||||
|
|
||||||
// EffectiveFee is the same as Fee for LegacyTx
|
// EffectiveFee is the same as Fee for LegacyTx
|
||||||
func (tx LegacyTx) EffectiveFee(baseFee *big.Int) *big.Int {
|
func (tx LegacyTx) EffectiveFee(baseFee *big.Int) *big.Int {
|
||||||
return tx.Fee()
|
return tx.Fee()
|
||||||
|
@ -41,7 +41,8 @@ type TxData interface {
|
|||||||
Fee() *big.Int
|
Fee() *big.Int
|
||||||
Cost() *big.Int
|
Cost() *big.Int
|
||||||
|
|
||||||
// effective fee according to current base fee
|
// effective gasPrice/fee/cost according to current base fee
|
||||||
|
EffectiveGasPrice(baseFee *big.Int) *big.Int
|
||||||
EffectiveFee(baseFee *big.Int) *big.Int
|
EffectiveFee(baseFee *big.Int) *big.Int
|
||||||
EffectiveCost(baseFee *big.Int) *big.Int
|
EffectiveCost(baseFee *big.Int) *big.Int
|
||||||
}
|
}
|
||||||
|
@ -380,7 +380,6 @@ var _ = Describe("Feemarket", func() {
|
|||||||
Context("during DeliverTx", func() {
|
Context("during DeliverTx", func() {
|
||||||
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
|
DescribeTable("should reject transactions with gasPrice < MinGasPrices",
|
||||||
func(malleate getprices) {
|
func(malleate getprices) {
|
||||||
|
|
||||||
p := malleate()
|
p := malleate()
|
||||||
to := tests.GenerateAddress()
|
to := tests.GenerateAddress()
|
||||||
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
msgEthereumTx := buildEthTx(privKey, &to, p.gasPrice, p.gasFeeCap, p.gasTipCap, p.accesses)
|
||||||
|
@ -6,7 +6,7 @@ order: 5
|
|||||||
|
|
||||||
The `x/feemarket` module provides `AnteDecorator`s that are recursively chained together into a single [`Antehandler`](https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-alpha1/docs/architecture/adr-010-modular-antehandler.md). These decorators perform basic validity checks on an Ethereum or Cosmos SDK transaction, such that it could be thrown out of the transaction Mempool.
|
The `x/feemarket` module provides `AnteDecorator`s that are recursively chained together into a single [`Antehandler`](https://github.com/cosmos/cosmos-sdk/blob/v0.43.0-alpha1/docs/architecture/adr-010-modular-antehandler.md). These decorators perform basic validity checks on an Ethereum or Cosmos SDK transaction, such that it could be thrown out of the transaction Mempool.
|
||||||
|
|
||||||
Note that the `AnteHandler` is run for every transaction and called on both `CheckTx` and `DeliverTx`.
|
Note that the `AnteHandler` is run for every transaction and called on both `CheckTx` and `DeliverTx`.
|
||||||
|
|
||||||
## Decorators
|
## Decorators
|
||||||
|
|
||||||
@ -23,3 +23,15 @@ Rejects EVM transactions with transactions fees lower than `MinGasPrice * GasLim
|
|||||||
::: tip
|
::: tip
|
||||||
**Note**: For dynamic transactions, if the `feemarket` formula results in a `BaseFee` that lowers `EffectivePrice < MinGasPrices`, the users must increase the `GasTipCap` (priority fee) until `EffectivePrice > MinGasPrices`. Transactions with `MinGasPrices * GasLimit < transaction fee < EffectiveFee` are rejected by the `feemarket` `AnteHandle`.
|
**Note**: For dynamic transactions, if the `feemarket` formula results in a `BaseFee` that lowers `EffectivePrice < MinGasPrices`, the users must increase the `GasTipCap` (priority fee) until `EffectivePrice > MinGasPrices`. Transactions with `MinGasPrices * GasLimit < transaction fee < EffectiveFee` are rejected by the `feemarket` `AnteHandle`.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### `EthGasConsumeDecorator`
|
||||||
|
|
||||||
|
Calculates the effective fees to deduct and the tx priority according to EIP-1559 spec, then deducts the fees and sets the tx priority in the response.
|
||||||
|
|
||||||
|
```
|
||||||
|
effectivePrice = min(baseFee + tipFeeCap, gasFeeCap)
|
||||||
|
effectiveTipFee = effectivePrice - baseFee
|
||||||
|
priority = effectiveTipFee / DefaultPriorityReduction
|
||||||
|
```
|
||||||
|
|
||||||
|
When there are multiple messages in the transaction, choose the lowest priority in them.
|
||||||
|
Loading…
Reference in New Issue
Block a user