Sync from fork #74
@ -50,6 +50,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* (ante) [#1176](https://github.com/evmos/ethermint/pull/1176) Fix invalid tx hashes; Remove `Size_` field and validate `Hash`/`From` fields in ante handler,
|
||||
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.
|
||||
* (feemarket) [#1194](https://github.com/evmos/ethermint/pull/1194) Apply feemarket to native cosmos tx.
|
||||
|
||||
### API Breaking
|
||||
|
||||
|
@ -27,6 +27,7 @@ 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) {
|
||||
@ -45,6 +46,9 @@ func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
|
||||
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,
|
||||
|
@ -305,8 +305,8 @@ 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, sdkmath.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,
|
||||
@ -315,9 +315,9 @@ func (suite AnteTestSuite) TestAnteHandler() {
|
||||
"success - DeliverTx EIP712 signed Cosmos Tx with DelegateMsg",
|
||||
func() sdk.Tx {
|
||||
from := acc.GetAddress()
|
||||
coinAmount := sdk.NewCoin(evmtypes.DefaultEVMDenom, sdkmath.NewInt(20))
|
||||
amount := sdk.NewCoins(coinAmount)
|
||||
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,
|
||||
@ -326,8 +326,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, sdkmath.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,
|
||||
@ -336,8 +336,8 @@ 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, sdkmath.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, sdkmath.NewInt(30))))
|
||||
@ -348,8 +348,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, sdkmath.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)
|
||||
@ -360,8 +360,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, sdkmath.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)
|
||||
@ -380,8 +380,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, sdkmath.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)
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"github.com/evmos/ethermint/app/ante"
|
||||
"github.com/evmos/ethermint/server/config"
|
||||
"github.com/evmos/ethermint/tests"
|
||||
evmkeeper "github.com/evmos/ethermint/x/evm/keeper"
|
||||
"github.com/evmos/ethermint/x/evm/statedb"
|
||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||
|
||||
@ -227,7 +226,7 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
||||
baseFee := suite.app.EvmKeeper.GetBaseFee(suite.ctx, ethCfg)
|
||||
suite.Require().Equal(int64(1000000000), baseFee.Int64())
|
||||
|
||||
gasPrice := new(big.Int).Add(baseFee, evmkeeper.DefaultPriorityReduction.BigInt())
|
||||
gasPrice := new(big.Int).Add(baseFee, evmtypes.DefaultPriorityReduction.BigInt())
|
||||
|
||||
tx2GasLimit := uint64(1000000)
|
||||
tx2 := evmtypes.NewTxContract(suite.app.EvmKeeper.ChainID(), 1, big.NewInt(10), tx2GasLimit, gasPrice, nil, nil, nil, ðtypes.AccessList{{Address: addr, StorageKeys: nil}})
|
||||
@ -236,8 +235,8 @@ func (suite AnteTestSuite) TestEthGasConsumeDecorator() {
|
||||
|
||||
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
|
||||
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)
|
||||
|
142
app/ante/fee_checker.go
Normal file
142
app/ante/fee_checker.go
Normal file
@ -0,0 +1,142 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
|
||||
ethermint "github.com/evmos/ethermint/types"
|
||||
"github.com/evmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
// 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)
|
||||
return feeCoins, priority, nil
|
||||
}
|
||||
|
||||
// getTxPriority returns a naive tx priority based on the amount of the smallest denomination of the fee
|
||||
// provided in a transaction.
|
||||
func getTxPriority(fees sdk.Coins) int64 {
|
||||
var priority int64
|
||||
|
||||
for _, fee := range fees {
|
||||
amt := fee.Amount.Quo(types.DefaultPriorityReduction)
|
||||
p := int64(math.MaxInt64)
|
||||
|
||||
if amt.IsInt64() {
|
||||
p = amt.Int64()
|
||||
}
|
||||
|
||||
if priority == 0 || p < priority {
|
||||
priority = p
|
||||
}
|
||||
}
|
||||
|
||||
return priority
|
||||
}
|
219
app/ante/fee_checker_test.go
Normal file
219
app/ante/fee_checker_test.go
Normal file
@ -0,0 +1,219 @@
|
||||
package ante
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
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/evmos/ethermint/encoding"
|
||||
ethermint "github.com/evmos/ethermint/types"
|
||||
"github.com/evmos/ethermint/x/evm/types"
|
||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||
"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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -14,17 +14,22 @@ import (
|
||||
feemarkettypes "github.com/evmos/ethermint/x/feemarket/types"
|
||||
)
|
||||
|
||||
// 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
|
||||
DeductTxCostsFromUserBalance(
|
||||
ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool,
|
||||
) (fees sdk.Coins, priority int64, err error)
|
||||
GetBaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int
|
||||
GetBalance(ctx sdk.Context, addr common.Address) *big.Int
|
||||
ResetTransientGasUsed(ctx sdk.Context)
|
||||
GetTxIndexTransient(ctx sdk.Context) uint64
|
||||
|
20
app/app.go
20
app/app.go
@ -600,15 +600,17 @@ func NewEthermintApp(
|
||||
// use Ethermint's custom AnteHandler
|
||||
func (app *EthermintApp) setAnteHandler(txConfig client.TxConfig, maxGasWanted uint64) {
|
||||
anteHandler, err := ante.NewAnteHandler(ante.HandlerOptions{
|
||||
AccountKeeper: app.AccountKeeper,
|
||||
BankKeeper: app.BankKeeper,
|
||||
SignModeHandler: txConfig.SignModeHandler(),
|
||||
FeegrantKeeper: app.FeeGrantKeeper,
|
||||
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
|
||||
IBCKeeper: app.IBCKeeper,
|
||||
EvmKeeper: app.EvmKeeper,
|
||||
FeeMarketKeeper: app.FeeMarketKeeper,
|
||||
MaxTxGasWanted: maxGasWanted,
|
||||
AccountKeeper: app.AccountKeeper,
|
||||
BankKeeper: app.BankKeeper,
|
||||
SignModeHandler: txConfig.SignModeHandler(),
|
||||
FeegrantKeeper: app.FeeGrantKeeper,
|
||||
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
|
||||
IBCKeeper: app.IBCKeeper,
|
||||
EvmKeeper: app.EvmKeeper,
|
||||
FeeMarketKeeper: app.FeeMarketKeeper,
|
||||
MaxTxGasWanted: maxGasWanted,
|
||||
ExtensionOptionChecker: ethermint.HasDynamicFeeExtensionOption,
|
||||
TxFeeChecker: ante.NewDynamicFeeChecker(app.EvmKeeper),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -32,6 +32,7 @@ import (
|
||||
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
ibctransfertypes "github.com/cosmos/ibc-go/v5/modules/apps/transfer/types"
|
||||
ibchost "github.com/cosmos/ibc-go/v5/modules/core/24-host"
|
||||
"github.com/evmos/ethermint/app/ante"
|
||||
evmenc "github.com/evmos/ethermint/encoding"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@ -62,6 +63,32 @@ func fauxMerkleModeOpt(bapp *baseapp.BaseApp) {
|
||||
bapp.SetFauxMerkleMode()
|
||||
}
|
||||
|
||||
// NewSimApp disable feemarket on native tx, otherwise the cosmos-sdk simulation tests will fail.
|
||||
func NewSimApp(logger log.Logger, db dbm.DB) (*EthermintApp, error) {
|
||||
encodingConfig := MakeEncodingConfig()
|
||||
app := NewEthermintApp(logger, db, nil, false, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, encodingConfig, simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
// disable feemarket on native tx
|
||||
anteHandler, err := ante.NewAnteHandler(ante.HandlerOptions{
|
||||
AccountKeeper: app.AccountKeeper,
|
||||
BankKeeper: app.BankKeeper,
|
||||
SignModeHandler: encodingConfig.TxConfig.SignModeHandler(),
|
||||
FeegrantKeeper: app.FeeGrantKeeper,
|
||||
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
|
||||
IBCKeeper: app.IBCKeeper,
|
||||
EvmKeeper: app.EvmKeeper,
|
||||
FeeMarketKeeper: app.FeeMarketKeeper,
|
||||
MaxTxGasWanted: 0,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
app.SetAnteHandler(anteHandler)
|
||||
if err := app.LoadLatestVersion(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return app, nil
|
||||
}
|
||||
|
||||
// interBlockCacheOpt returns a BaseApp option function that sets the persistent
|
||||
// inter-block write-through cache.
|
||||
func interBlockCacheOpt() func(*baseapp.BaseApp) {
|
||||
@ -82,8 +109,9 @@ func TestFullAppSimulation(t *testing.T) {
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.Equal(t, appName, app.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
// run randomized simulation
|
||||
_, simParams, simErr := simulation.SimulateFromSeed(
|
||||
@ -121,8 +149,8 @@ func TestAppImportExport(t *testing.T) {
|
||||
require.NoError(t, db.Close())
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, appName, app.Name())
|
||||
|
||||
// Run randomized simulation
|
||||
@ -163,8 +191,9 @@ func TestAppImportExport(t *testing.T) {
|
||||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp := NewEthermintApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
||||
require.Equal(t, appName, newApp.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
var genesisState simapp.GenesisState
|
||||
err = json.Unmarshal(exported.AppState, &genesisState)
|
||||
@ -236,8 +265,9 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
||||
require.NoError(t, os.RemoveAll(dir))
|
||||
}()
|
||||
|
||||
app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.Equal(t, appName, app.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Run randomized simulation
|
||||
stopEarly, simParams, simErr := simulation.SimulateFromSeed(
|
||||
@ -281,8 +311,9 @@ func TestAppSimulationAfterImport(t *testing.T) {
|
||||
require.NoError(t, os.RemoveAll(newDir))
|
||||
}()
|
||||
|
||||
newApp := NewEthermintApp(log.NewNopLogger(), newDB, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, fauxMerkleModeOpt)
|
||||
newApp, err := NewSimApp(log.NewNopLogger(), newDB)
|
||||
require.Equal(t, appName, newApp.Name())
|
||||
require.NoError(t, err)
|
||||
|
||||
newApp.InitChain(abci.RequestInitChain{
|
||||
ChainId: SimAppChainID,
|
||||
@ -333,14 +364,15 @@ func TestAppStateDeterminism(t *testing.T) {
|
||||
}
|
||||
|
||||
db := dbm.NewMemDB()
|
||||
app := NewEthermintApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, simapp.FlagPeriodValue, MakeEncodingConfig(), simapp.EmptyAppOptions{}, interBlockCacheOpt())
|
||||
app, err := NewSimApp(logger, db)
|
||||
require.NoError(t, err)
|
||||
|
||||
fmt.Printf(
|
||||
"running non-determinism simulation; seed %d: %d/%d, attempt: %d/%d\n",
|
||||
config.Seed, i+1, numSeeds, j+1, numTimesToRunPerSeed,
|
||||
)
|
||||
|
||||
_, _, err := simulation.SimulateFromSeed(
|
||||
_, _, err = simulation.SimulateFromSeed(
|
||||
t,
|
||||
os.Stdout,
|
||||
app.BaseApp,
|
||||
|
@ -79,6 +79,9 @@
|
||||
- [ethermint/types/v1/account.proto](#ethermint/types/v1/account.proto)
|
||||
- [EthAccount](#ethermint.types.v1.EthAccount)
|
||||
|
||||
- [ethermint/types/v1/dynamic_fee.proto](#ethermint/types/v1/dynamic_fee.proto)
|
||||
- [ExtensionOptionDynamicFeeTx](#ethermint.types.v1.ExtensionOptionDynamicFeeTx)
|
||||
|
||||
- [ethermint/types/v1/web3.proto](#ethermint/types/v1/web3.proto)
|
||||
- [ExtensionOptionsWeb3Tx](#ethermint.types.v1.ExtensionOptionsWeb3Tx)
|
||||
|
||||
@ -1134,6 +1137,37 @@ authtypes.BaseAccount type. It is compatible with the auth AccountKeeper.
|
||||
|
||||
|
||||
|
||||
<!-- end messages -->
|
||||
|
||||
<!-- end enums -->
|
||||
|
||||
<!-- end HasExtensions -->
|
||||
|
||||
<!-- end services -->
|
||||
|
||||
|
||||
|
||||
<a name="ethermint/types/v1/dynamic_fee.proto"></a>
|
||||
<p align="right"><a href="#top">Top</a></p>
|
||||
|
||||
## ethermint/types/v1/dynamic_fee.proto
|
||||
|
||||
|
||||
|
||||
<a name="ethermint.types.v1.ExtensionOptionDynamicFeeTx"></a>
|
||||
|
||||
### ExtensionOptionDynamicFeeTx
|
||||
ExtensionOptionDynamicFeeTx is an extension option that specify the maxPrioPrice for cosmos tx
|
||||
|
||||
|
||||
| Field | Type | Label | Description |
|
||||
| ----- | ---- | ----- | ----------- |
|
||||
| `max_priority_price` | [string](#string) | | the same as `max_priority_fee_per_gas` in eip-1559 spec |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- end messages -->
|
||||
|
||||
<!-- end enums -->
|
||||
|
15
proto/ethermint/types/v1/dynamic_fee.proto
Normal file
15
proto/ethermint/types/v1/dynamic_fee.proto
Normal file
@ -0,0 +1,15 @@
|
||||
syntax = "proto3";
|
||||
package ethermint.types.v1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
|
||||
option go_package = "github.com/evmos/ethermint/types";
|
||||
|
||||
// ExtensionOptionDynamicFeeTx is an extension option that specify the maxPrioPrice for cosmos tx
|
||||
message ExtensionOptionDynamicFeeTx {
|
||||
// the same as `max_priority_fee_per_gas` in eip-1559 spec
|
||||
string max_priority_price = 1 [
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
}
|
@ -973,7 +973,6 @@ func (suite *BackendTestSuite) TestGetEthereumMsgsFromTendermintBlock() {
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
suite.Run(fmt.Sprintf("Case %s", tc.name), func() {
|
||||
|
||||
suite.SetupTest() // reset test and queries
|
||||
|
||||
msgs := suite.backend.GetEthereumMsgsFromTendermintBlock(tc.resBlock, tc.blockRes)
|
||||
|
828
tests/integration_tests/cosmoscli.py
Normal file
828
tests/integration_tests/cosmoscli.py
Normal file
@ -0,0 +1,828 @@
|
||||
import json
|
||||
import tempfile
|
||||
|
||||
import requests
|
||||
from dateutil.parser import isoparse
|
||||
from pystarport.utils import build_cli_args_safe, interact
|
||||
|
||||
DEFAULT_GAS_PRICE = "5000000000000aphoton"
|
||||
|
||||
|
||||
class ChainCommand:
|
||||
def __init__(self, cmd):
|
||||
self.cmd = cmd
|
||||
|
||||
def __call__(self, cmd, *args, stdin=None, **kwargs):
|
||||
"execute chain-maind"
|
||||
args = " ".join(build_cli_args_safe(cmd, *args, **kwargs))
|
||||
return interact(f"{self.cmd} {args}", input=stdin)
|
||||
|
||||
|
||||
class CosmosCLI:
|
||||
"the apis to interact with wallet and blockchain"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_dir,
|
||||
node_rpc,
|
||||
cmd,
|
||||
):
|
||||
self.data_dir = data_dir
|
||||
self._genesis = json.loads(
|
||||
(self.data_dir / "config" / "genesis.json").read_text()
|
||||
)
|
||||
self.chain_id = self._genesis["chain_id"]
|
||||
self.node_rpc = node_rpc
|
||||
self.raw = ChainCommand(cmd)
|
||||
self.output = None
|
||||
self.error = None
|
||||
|
||||
@property
|
||||
def node_rpc_http(self):
|
||||
return "http" + self.node_rpc.removeprefix("tcp")
|
||||
|
||||
def node_id(self):
|
||||
"get tendermint node id"
|
||||
output = self.raw("tendermint", "show-node-id", home=self.data_dir)
|
||||
return output.decode().strip()
|
||||
|
||||
def delete_account(self, name):
|
||||
"delete wallet account in node's keyring"
|
||||
return self.raw(
|
||||
"keys",
|
||||
"delete",
|
||||
name,
|
||||
"-y",
|
||||
"--force",
|
||||
home=self.data_dir,
|
||||
output="json",
|
||||
keyring_backend="test",
|
||||
)
|
||||
|
||||
def create_account(self, name, mnemonic=None):
|
||||
"create new keypair in node's keyring"
|
||||
if mnemonic is None:
|
||||
output = self.raw(
|
||||
"keys",
|
||||
"add",
|
||||
name,
|
||||
home=self.data_dir,
|
||||
output="json",
|
||||
keyring_backend="test",
|
||||
)
|
||||
else:
|
||||
output = self.raw(
|
||||
"keys",
|
||||
"add",
|
||||
name,
|
||||
"--recover",
|
||||
home=self.data_dir,
|
||||
output="json",
|
||||
keyring_backend="test",
|
||||
stdin=mnemonic.encode() + b"\n",
|
||||
)
|
||||
return json.loads(output)
|
||||
|
||||
def init(self, moniker):
|
||||
"the node's config is already added"
|
||||
return self.raw(
|
||||
"init",
|
||||
moniker,
|
||||
chain_id=self.chain_id,
|
||||
home=self.data_dir,
|
||||
)
|
||||
|
||||
def validate_genesis(self):
|
||||
return self.raw("validate-genesis", home=self.data_dir)
|
||||
|
||||
def add_genesis_account(self, addr, coins, **kwargs):
|
||||
return self.raw(
|
||||
"add-genesis-account",
|
||||
addr,
|
||||
coins,
|
||||
home=self.data_dir,
|
||||
output="json",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def gentx(self, name, coins, min_self_delegation=1, pubkey=None):
|
||||
return self.raw(
|
||||
"gentx",
|
||||
name,
|
||||
coins,
|
||||
min_self_delegation=str(min_self_delegation),
|
||||
home=self.data_dir,
|
||||
chain_id=self.chain_id,
|
||||
keyring_backend="test",
|
||||
pubkey=pubkey,
|
||||
)
|
||||
|
||||
def collect_gentxs(self, gentx_dir):
|
||||
return self.raw("collect-gentxs", gentx_dir, home=self.data_dir)
|
||||
|
||||
def status(self):
|
||||
return json.loads(self.raw("status", node=self.node_rpc))
|
||||
|
||||
def block_height(self):
|
||||
return int(self.status()["SyncInfo"]["latest_block_height"])
|
||||
|
||||
def block_time(self):
|
||||
return isoparse(self.status()["SyncInfo"]["latest_block_time"])
|
||||
|
||||
def balances(self, addr):
|
||||
return json.loads(
|
||||
self.raw("query", "bank", "balances", addr, home=self.data_dir)
|
||||
)["balances"]
|
||||
|
||||
def balance(self, addr, denom="aphoton"):
|
||||
denoms = {coin["denom"]: int(coin["amount"]) for coin in self.balances(addr)}
|
||||
return denoms.get(denom, 0)
|
||||
|
||||
def query_tx(self, tx_type, tx_value):
|
||||
tx = self.raw(
|
||||
"query",
|
||||
"tx",
|
||||
"--type",
|
||||
tx_type,
|
||||
tx_value,
|
||||
home=self.data_dir,
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
return json.loads(tx)
|
||||
|
||||
def query_all_txs(self, addr):
|
||||
txs = self.raw(
|
||||
"query",
|
||||
"txs-all",
|
||||
addr,
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
return json.loads(txs)
|
||||
|
||||
def distribution_commission(self, addr):
|
||||
coin = json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"distribution",
|
||||
"commission",
|
||||
addr,
|
||||
output="json",
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)["commission"][0]
|
||||
return float(coin["amount"])
|
||||
|
||||
def distribution_community(self):
|
||||
coin = json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"distribution",
|
||||
"community-pool",
|
||||
output="json",
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)["pool"][0]
|
||||
return float(coin["amount"])
|
||||
|
||||
def distribution_reward(self, delegator_addr):
|
||||
coin = json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"distribution",
|
||||
"rewards",
|
||||
delegator_addr,
|
||||
output="json",
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)["total"][0]
|
||||
return float(coin["amount"])
|
||||
|
||||
def address(self, name, bech="acc"):
|
||||
output = self.raw(
|
||||
"keys",
|
||||
"show",
|
||||
name,
|
||||
"-a",
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
bech=bech,
|
||||
)
|
||||
return output.strip().decode()
|
||||
|
||||
def account(self, addr):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"query", "auth", "account", addr, output="json", node=self.node_rpc
|
||||
)
|
||||
)
|
||||
|
||||
def tx_search(self, events: str):
|
||||
"/tx_search"
|
||||
return json.loads(
|
||||
self.raw("query", "txs", events=events, output="json", node=self.node_rpc)
|
||||
)
|
||||
|
||||
def tx_search_rpc(self, events: str):
|
||||
rsp = requests.get(
|
||||
f"{self.node_rpc_http}/tx_search",
|
||||
params={
|
||||
"query": f'"{events}"',
|
||||
},
|
||||
).json()
|
||||
assert "error" not in rsp, rsp["error"]
|
||||
return rsp["result"]["txs"]
|
||||
|
||||
def tx(self, value, **kwargs):
|
||||
"/tx"
|
||||
default_kwargs = {
|
||||
"home": self.data_dir,
|
||||
}
|
||||
return json.loads(self.raw("query", "tx", value, **(default_kwargs | kwargs)))
|
||||
|
||||
def total_supply(self):
|
||||
return json.loads(
|
||||
self.raw("query", "bank", "total", output="json", node=self.node_rpc)
|
||||
)
|
||||
|
||||
def validator(self, addr):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"staking",
|
||||
"validator",
|
||||
addr,
|
||||
output="json",
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def validators(self):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"query", "staking", "validators", output="json", node=self.node_rpc
|
||||
)
|
||||
)["validators"]
|
||||
|
||||
def staking_params(self):
|
||||
return json.loads(
|
||||
self.raw("query", "staking", "params", output="json", node=self.node_rpc)
|
||||
)
|
||||
|
||||
def staking_pool(self, bonded=True):
|
||||
return int(
|
||||
json.loads(
|
||||
self.raw("query", "staking", "pool", output="json", node=self.node_rpc)
|
||||
)["bonded_tokens" if bonded else "not_bonded_tokens"]
|
||||
)
|
||||
|
||||
def transfer(self, from_, to, coins, generate_only=False, **kwargs):
|
||||
kwargs.setdefault("gas_prices", DEFAULT_GAS_PRICE)
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"bank",
|
||||
"send",
|
||||
from_,
|
||||
to,
|
||||
coins,
|
||||
"-y",
|
||||
"--generate-only" if generate_only else None,
|
||||
home=self.data_dir,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
|
||||
def get_delegated_amount(self, which_addr):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"staking",
|
||||
"delegations",
|
||||
which_addr,
|
||||
home=self.data_dir,
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
output="json",
|
||||
)
|
||||
)
|
||||
|
||||
def delegate_amount(self, to_addr, amount, from_addr, gas_price=None):
|
||||
if gas_price is None:
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"staking",
|
||||
"delegate",
|
||||
to_addr,
|
||||
amount,
|
||||
"-y",
|
||||
home=self.data_dir,
|
||||
from_=from_addr,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
else:
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"staking",
|
||||
"delegate",
|
||||
to_addr,
|
||||
amount,
|
||||
"-y",
|
||||
home=self.data_dir,
|
||||
from_=from_addr,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
gas_prices=gas_price,
|
||||
)
|
||||
)
|
||||
|
||||
# to_addr: croclcl1... , from_addr: cro1...
|
||||
def unbond_amount(self, to_addr, amount, from_addr):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"staking",
|
||||
"unbond",
|
||||
to_addr,
|
||||
amount,
|
||||
"-y",
|
||||
home=self.data_dir,
|
||||
from_=from_addr,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
# to_validator_addr: crocncl1... , from_from_validator_addraddr: crocl1...
|
||||
def redelegate_amount(
|
||||
self, to_validator_addr, from_validator_addr, amount, from_addr
|
||||
):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"staking",
|
||||
"redelegate",
|
||||
from_validator_addr,
|
||||
to_validator_addr,
|
||||
amount,
|
||||
"-y",
|
||||
home=self.data_dir,
|
||||
from_=from_addr,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
# from_delegator can be account name or address
|
||||
def withdraw_all_rewards(self, from_delegator):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"distribution",
|
||||
"withdraw-all-rewards",
|
||||
"-y",
|
||||
from_=from_delegator,
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def make_multisig(self, name, signer1, signer2):
|
||||
self.raw(
|
||||
"keys",
|
||||
"add",
|
||||
name,
|
||||
multisig=f"{signer1},{signer2}",
|
||||
multisig_threshold="2",
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
)
|
||||
|
||||
def sign_multisig_tx(self, tx_file, multi_addr, signer_name):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"sign",
|
||||
tx_file,
|
||||
from_=signer_name,
|
||||
multisig=multi_addr,
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def sign_batch_multisig_tx(
|
||||
self, tx_file, multi_addr, signer_name, account_number, sequence_number
|
||||
):
|
||||
r = self.raw(
|
||||
"tx",
|
||||
"sign-batch",
|
||||
"--offline",
|
||||
tx_file,
|
||||
account_number=account_number,
|
||||
sequence=sequence_number,
|
||||
from_=signer_name,
|
||||
multisig=multi_addr,
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
return r.decode("utf-8")
|
||||
|
||||
def encode_signed_tx(self, signed_tx):
|
||||
return self.raw(
|
||||
"tx",
|
||||
"encode",
|
||||
signed_tx,
|
||||
)
|
||||
|
||||
def sign_tx(self, tx_file, signer):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"sign",
|
||||
tx_file,
|
||||
from_=signer,
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def sign_tx_json(self, tx, signer, max_priority_price=None):
|
||||
if max_priority_price is not None:
|
||||
tx["body"]["extension_options"].append(
|
||||
{
|
||||
"@type": "/ethermint.types.v1.ExtensionOptionDynamicFeeTx",
|
||||
"max_priority_price": str(max_priority_price),
|
||||
}
|
||||
)
|
||||
with tempfile.NamedTemporaryFile("w") as fp:
|
||||
json.dump(tx, fp)
|
||||
fp.flush()
|
||||
return self.sign_tx(fp.name, signer)
|
||||
|
||||
def combine_multisig_tx(self, tx_file, multi_name, signer1_file, signer2_file):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"multisign",
|
||||
tx_file,
|
||||
multi_name,
|
||||
signer1_file,
|
||||
signer2_file,
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def combine_batch_multisig_tx(
|
||||
self, tx_file, multi_name, signer1_file, signer2_file
|
||||
):
|
||||
r = self.raw(
|
||||
"tx",
|
||||
"multisign-batch",
|
||||
tx_file,
|
||||
multi_name,
|
||||
signer1_file,
|
||||
signer2_file,
|
||||
home=self.data_dir,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
node=self.node_rpc,
|
||||
)
|
||||
return r.decode("utf-8")
|
||||
|
||||
def broadcast_tx(self, tx_file, **kwargs):
|
||||
kwargs.setdefault("broadcast_mode", "block")
|
||||
kwargs.setdefault("output", "json")
|
||||
return json.loads(
|
||||
self.raw("tx", "broadcast", tx_file, node=self.node_rpc, **kwargs)
|
||||
)
|
||||
|
||||
def broadcast_tx_json(self, tx, **kwargs):
|
||||
with tempfile.NamedTemporaryFile("w") as fp:
|
||||
json.dump(tx, fp)
|
||||
fp.flush()
|
||||
return self.broadcast_tx(fp.name, **kwargs)
|
||||
|
||||
def unjail(self, addr):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"slashing",
|
||||
"unjail",
|
||||
"-y",
|
||||
from_=addr,
|
||||
home=self.data_dir,
|
||||
node=self.node_rpc,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
)
|
||||
)
|
||||
|
||||
def create_validator(
|
||||
self,
|
||||
amount,
|
||||
moniker=None,
|
||||
commission_max_change_rate="0.01",
|
||||
commission_rate="0.1",
|
||||
commission_max_rate="0.2",
|
||||
min_self_delegation="1",
|
||||
identity="",
|
||||
website="",
|
||||
security_contact="",
|
||||
details="",
|
||||
):
|
||||
"""MsgCreateValidator
|
||||
create the node with create_node before call this"""
|
||||
pubkey = (
|
||||
"'"
|
||||
+ (
|
||||
self.raw(
|
||||
"tendermint",
|
||||
"show-validator",
|
||||
home=self.data_dir,
|
||||
)
|
||||
.strip()
|
||||
.decode()
|
||||
)
|
||||
+ "'"
|
||||
)
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"staking",
|
||||
"create-validator",
|
||||
"-y",
|
||||
from_=self.address("validator"),
|
||||
amount=amount,
|
||||
pubkey=pubkey,
|
||||
min_self_delegation=min_self_delegation,
|
||||
# commision
|
||||
commission_rate=commission_rate,
|
||||
commission_max_rate=commission_max_rate,
|
||||
commission_max_change_rate=commission_max_change_rate,
|
||||
# description
|
||||
moniker=moniker,
|
||||
identity=identity,
|
||||
website=website,
|
||||
security_contact=security_contact,
|
||||
details=details,
|
||||
# basic
|
||||
home=self.data_dir,
|
||||
node=self.node_rpc,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
)
|
||||
)
|
||||
|
||||
def edit_validator(
|
||||
self,
|
||||
commission_rate=None,
|
||||
moniker=None,
|
||||
identity=None,
|
||||
website=None,
|
||||
security_contact=None,
|
||||
details=None,
|
||||
):
|
||||
"""MsgEditValidator"""
|
||||
options = dict(
|
||||
commission_rate=commission_rate,
|
||||
# description
|
||||
moniker=moniker,
|
||||
identity=identity,
|
||||
website=website,
|
||||
security_contact=security_contact,
|
||||
details=details,
|
||||
)
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"staking",
|
||||
"edit-validator",
|
||||
"-y",
|
||||
from_=self.address("validator"),
|
||||
home=self.data_dir,
|
||||
node=self.node_rpc,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
**{k: v for k, v in options.items() if v is not None},
|
||||
)
|
||||
)
|
||||
|
||||
def gov_propose(self, proposer, kind, proposal, **kwargs):
|
||||
kwargs.setdefault("gas_prices", DEFAULT_GAS_PRICE)
|
||||
if kind == "software-upgrade":
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"gov",
|
||||
"submit-proposal",
|
||||
kind,
|
||||
proposal["name"],
|
||||
"-y",
|
||||
from_=proposer,
|
||||
# content
|
||||
title=proposal.get("title"),
|
||||
description=proposal.get("description"),
|
||||
upgrade_height=proposal.get("upgrade-height"),
|
||||
upgrade_time=proposal.get("upgrade-time"),
|
||||
upgrade_info=proposal.get("upgrade-info"),
|
||||
deposit=proposal.get("deposit"),
|
||||
# basic
|
||||
home=self.data_dir,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
elif kind == "cancel-software-upgrade":
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"gov",
|
||||
"submit-proposal",
|
||||
kind,
|
||||
"-y",
|
||||
from_=proposer,
|
||||
# content
|
||||
title=proposal.get("title"),
|
||||
description=proposal.get("description"),
|
||||
deposit=proposal.get("deposit"),
|
||||
# basic
|
||||
home=self.data_dir,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
else:
|
||||
with tempfile.NamedTemporaryFile("w") as fp:
|
||||
json.dump(proposal, fp)
|
||||
fp.flush()
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"gov",
|
||||
"submit-proposal",
|
||||
kind,
|
||||
fp.name,
|
||||
"-y",
|
||||
from_=proposer,
|
||||
# basic
|
||||
home=self.data_dir,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
|
||||
def gov_vote(self, voter, proposal_id, option, **kwargs):
|
||||
kwargs.setdefault("gas_prices", DEFAULT_GAS_PRICE)
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"gov",
|
||||
"vote",
|
||||
proposal_id,
|
||||
option,
|
||||
"-y",
|
||||
from_=voter,
|
||||
home=self.data_dir,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
|
||||
def gov_deposit(self, depositor, proposal_id, amount):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"gov",
|
||||
"deposit",
|
||||
proposal_id,
|
||||
amount,
|
||||
"-y",
|
||||
from_=depositor,
|
||||
home=self.data_dir,
|
||||
node=self.node_rpc,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
)
|
||||
)
|
||||
|
||||
def query_proposals(self, depositor=None, limit=None, status=None, voter=None):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"gov",
|
||||
"proposals",
|
||||
depositor=depositor,
|
||||
count_total=limit,
|
||||
status=status,
|
||||
voter=voter,
|
||||
output="json",
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def query_proposal(self, proposal_id):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"gov",
|
||||
"proposal",
|
||||
proposal_id,
|
||||
output="json",
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def query_tally(self, proposal_id):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"query",
|
||||
"gov",
|
||||
"tally",
|
||||
proposal_id,
|
||||
output="json",
|
||||
node=self.node_rpc,
|
||||
)
|
||||
)
|
||||
|
||||
def ibc_transfer(
|
||||
self,
|
||||
from_,
|
||||
to,
|
||||
amount,
|
||||
channel, # src channel
|
||||
target_version, # chain version number of target chain
|
||||
i=0,
|
||||
):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"ibc-transfer",
|
||||
"transfer",
|
||||
"transfer", # src port
|
||||
channel,
|
||||
to,
|
||||
amount,
|
||||
"-y",
|
||||
# FIXME https://github.com/cosmos/cosmos-sdk/issues/8059
|
||||
"--absolute-timeouts",
|
||||
from_=from_,
|
||||
home=self.data_dir,
|
||||
node=self.node_rpc,
|
||||
keyring_backend="test",
|
||||
chain_id=self.chain_id,
|
||||
packet_timeout_height=f"{target_version}-10000000000",
|
||||
packet_timeout_timestamp=0,
|
||||
)
|
||||
)
|
||||
|
||||
def export(self):
|
||||
return self.raw("export", home=self.data_dir)
|
||||
|
||||
def unsaferesetall(self):
|
||||
return self.raw("unsafe-reset-all")
|
||||
|
||||
def build_evm_tx(self, raw_tx: str, **kwargs):
|
||||
return json.loads(
|
||||
self.raw(
|
||||
"tx",
|
||||
"evm",
|
||||
"raw",
|
||||
raw_tx,
|
||||
"-y",
|
||||
"--generate-only",
|
||||
home=self.data_dir,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
|
||||
def query_base_fee(self, **kwargs):
|
||||
default_kwargs = {"home": self.data_dir}
|
||||
return int(
|
||||
json.loads(
|
||||
self.raw(
|
||||
"q",
|
||||
"feemarket",
|
||||
"base-fee",
|
||||
**(default_kwargs | kwargs),
|
||||
)
|
||||
)["base_fee"]
|
||||
)
|
@ -8,6 +8,7 @@ import web3
|
||||
from pystarport import ports
|
||||
from web3.middleware import geth_poa_middleware
|
||||
|
||||
from .cosmoscli import CosmosCLI
|
||||
from .utils import wait_for_port
|
||||
|
||||
|
||||
@ -53,6 +54,9 @@ class Ethermint:
|
||||
self._w3 = None
|
||||
self._use_websockets = use
|
||||
|
||||
def cosmos_cli(self, i=0):
|
||||
return CosmosCLI(self.base_dir / f"node{i}", self.node_rpc(i), "ethermintd")
|
||||
|
||||
|
||||
class Geth:
|
||||
def __init__(self, w3):
|
||||
|
@ -1,5 +1,7 @@
|
||||
import sys
|
||||
|
||||
from .network import Ethermint
|
||||
from .utils import KEYS, sign_transaction
|
||||
from .utils import ADDRS, KEYS, eth_to_bech32, sign_transaction, wait_for_new_blocks
|
||||
|
||||
PRIORITY_REDUCTION = 1000000
|
||||
|
||||
@ -101,14 +103,95 @@ def test_priority(ethermint: Ethermint):
|
||||
|
||||
# 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:]))
|
||||
tx_indexes = [(r.blockNumber, r.transactionIndex) for r in receipts]
|
||||
print(tx_indexes)
|
||||
# the first sent tx are included later, because of lower priority
|
||||
assert all(i1 > i2 for i1, i2 in zip(tx_indexes, tx_indexes[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
|
||||
"returns true if receipt1 is included earlier than receipt2"
|
||||
return (receipt1.blockNumber, receipt1.transactionIndex) < (
|
||||
receipt2.blockNumber,
|
||||
receipt2.transactionIndex,
|
||||
)
|
||||
|
||||
|
||||
def test_native_tx_priority(ethermint: Ethermint):
|
||||
cli = ethermint.cosmos_cli()
|
||||
base_fee = cli.query_base_fee()
|
||||
print("base_fee", base_fee)
|
||||
test_cases = [
|
||||
{
|
||||
"from": eth_to_bech32(ADDRS["community"]),
|
||||
"to": eth_to_bech32(ADDRS["validator"]),
|
||||
"amount": "1000aphoton",
|
||||
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 6}aphoton",
|
||||
"max_priority_price": 0,
|
||||
},
|
||||
{
|
||||
"from": eth_to_bech32(ADDRS["signer1"]),
|
||||
"to": eth_to_bech32(ADDRS["signer2"]),
|
||||
"amount": "1000aphoton",
|
||||
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 6}aphoton",
|
||||
"max_priority_price": PRIORITY_REDUCTION * 2,
|
||||
},
|
||||
{
|
||||
"from": eth_to_bech32(ADDRS["signer2"]),
|
||||
"to": eth_to_bech32(ADDRS["signer1"]),
|
||||
"amount": "1000aphoton",
|
||||
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 4}aphoton",
|
||||
"max_priority_price": PRIORITY_REDUCTION * 4,
|
||||
},
|
||||
{
|
||||
"from": eth_to_bech32(ADDRS["validator"]),
|
||||
"to": eth_to_bech32(ADDRS["community"]),
|
||||
"amount": "1000aphoton",
|
||||
"gas_prices": f"{base_fee + PRIORITY_REDUCTION * 6}aphoton",
|
||||
"max_priority_price": None, # no extension, maximum tipFeeCap
|
||||
},
|
||||
]
|
||||
txs = []
|
||||
expect_priorities = []
|
||||
for tc in test_cases:
|
||||
tx = cli.transfer(
|
||||
tc["from"],
|
||||
tc["to"],
|
||||
tc["amount"],
|
||||
gas_prices=tc["gas_prices"],
|
||||
generate_only=True,
|
||||
)
|
||||
txs.append(
|
||||
cli.sign_tx_json(
|
||||
tx, tc["from"], max_priority_price=tc.get("max_priority_price")
|
||||
)
|
||||
)
|
||||
gas_price = int(tc["gas_prices"].removesuffix("aphoton"))
|
||||
expect_priorities.append(
|
||||
min(
|
||||
get_max_priority_price(tc.get("max_priority_price")),
|
||||
gas_price - base_fee,
|
||||
)
|
||||
// PRIORITY_REDUCTION
|
||||
)
|
||||
assert expect_priorities == [0, 2, 4, 6]
|
||||
|
||||
txhashes = []
|
||||
for tx in txs:
|
||||
rsp = cli.broadcast_tx_json(tx, broadcast_mode="sync")
|
||||
assert rsp["code"] == 0, rsp["raw_log"]
|
||||
txhashes.append(rsp["txhash"])
|
||||
|
||||
print("wait for two new blocks, so the sent txs are all included")
|
||||
wait_for_new_blocks(cli, 2)
|
||||
|
||||
tx_results = [cli.tx_search_rpc(f"tx.hash='{txhash}'")[0] for txhash in txhashes]
|
||||
tx_indexes = [(int(r["height"]), r["index"]) for r in tx_results]
|
||||
print(tx_indexes)
|
||||
# the first sent tx are included later, because of lower priority
|
||||
assert all(i1 > i2 for i1, i2 in zip(tx_indexes, tx_indexes[1:]))
|
||||
|
||||
|
||||
def get_max_priority_price(max_priority_price):
|
||||
"default to max int64 if None"
|
||||
return max_priority_price if max_priority_price is not None else sys.maxsize
|
||||
|
@ -4,8 +4,10 @@ import socket
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import bech32
|
||||
from dotenv import load_dotenv
|
||||
from eth_account import Account
|
||||
from hexbytes import HexBytes
|
||||
from web3._utils.transactions import fill_nonce, fill_transaction_defaults
|
||||
|
||||
load_dotenv(Path(__file__).parent.parent.parent / "scripts/.env")
|
||||
@ -64,6 +66,15 @@ def w3_wait_for_new_blocks(w3, n):
|
||||
break
|
||||
|
||||
|
||||
def wait_for_new_blocks(cli, n):
|
||||
begin_height = int((cli.status())["SyncInfo"]["latest_block_height"])
|
||||
while True:
|
||||
time.sleep(0.5)
|
||||
cur_height = int((cli.status())["SyncInfo"]["latest_block_height"])
|
||||
if cur_height - begin_height >= n:
|
||||
break
|
||||
|
||||
|
||||
def deploy_contract(w3, jsonfile, args=(), key=KEYS["validator"]):
|
||||
"""
|
||||
deploy contract and return the deployed contract instance
|
||||
@ -95,3 +106,8 @@ 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)
|
||||
|
||||
|
||||
def eth_to_bech32(addr, prefix=ETHERMINT_ADDRESS_PREFIX):
|
||||
bz = bech32.convertbits(HexBytes(addr), 8, 5)
|
||||
return bech32.bech32_encode(prefix, bz)
|
||||
|
@ -20,5 +20,6 @@ func RegisterInterfaces(registry codectypes.InterfaceRegistry) {
|
||||
registry.RegisterImplementations(
|
||||
(*tx.TxExtensionOptionI)(nil),
|
||||
&ExtensionOptionsWeb3Tx{},
|
||||
&ExtensionOptionDynamicFeeTx{},
|
||||
)
|
||||
}
|
||||
|
11
types/dynamic_fee.go
Normal file
11
types/dynamic_fee.go
Normal file
@ -0,0 +1,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
)
|
||||
|
||||
// HasDynamicFeeExtensionOption returns true if the tx implements the `ExtensionOptionDynamicFeeTx` extension option.
|
||||
func HasDynamicFeeExtensionOption(any *codectypes.Any) bool {
|
||||
_, ok := any.GetCachedValue().(*ExtensionOptionDynamicFeeTx)
|
||||
return ok
|
||||
}
|
321
types/dynamic_fee.pb.go
generated
Normal file
321
types/dynamic_fee.pb.go
generated
Normal file
@ -0,0 +1,321 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: ethermint/types/v1/dynamic_fee.proto
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
|
||||
_ "github.com/gogo/protobuf/gogoproto"
|
||||
proto "github.com/gogo/protobuf/proto"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// ExtensionOptionDynamicFeeTx is an extension option that specify the maxPrioPrice for cosmos tx
|
||||
type ExtensionOptionDynamicFeeTx struct {
|
||||
// the same as `max_priority_fee_per_gas` in eip-1559 spec
|
||||
MaxPriorityPrice github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=max_priority_price,json=maxPriorityPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"max_priority_price"`
|
||||
}
|
||||
|
||||
func (m *ExtensionOptionDynamicFeeTx) Reset() { *m = ExtensionOptionDynamicFeeTx{} }
|
||||
func (m *ExtensionOptionDynamicFeeTx) String() string { return proto.CompactTextString(m) }
|
||||
func (*ExtensionOptionDynamicFeeTx) ProtoMessage() {}
|
||||
func (*ExtensionOptionDynamicFeeTx) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_9d7cf05c9992c480, []int{0}
|
||||
}
|
||||
func (m *ExtensionOptionDynamicFeeTx) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ExtensionOptionDynamicFeeTx) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ExtensionOptionDynamicFeeTx.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *ExtensionOptionDynamicFeeTx) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ExtensionOptionDynamicFeeTx.Merge(m, src)
|
||||
}
|
||||
func (m *ExtensionOptionDynamicFeeTx) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ExtensionOptionDynamicFeeTx) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ExtensionOptionDynamicFeeTx.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ExtensionOptionDynamicFeeTx proto.InternalMessageInfo
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*ExtensionOptionDynamicFeeTx)(nil), "ethermint.types.v1.ExtensionOptionDynamicFeeTx")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("ethermint/types/v1/dynamic_fee.proto", fileDescriptor_9d7cf05c9992c480)
|
||||
}
|
||||
|
||||
var fileDescriptor_9d7cf05c9992c480 = []byte{
|
||||
// 232 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x49, 0x2d, 0xc9, 0x48,
|
||||
0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2f, 0x33, 0xd4, 0x4f,
|
||||
0xa9, 0xcc, 0x4b, 0xcc, 0xcd, 0x4c, 0x8e, 0x4f, 0x4b, 0x4d, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9,
|
||||
0x17, 0x12, 0x82, 0xab, 0xd2, 0x03, 0xab, 0xd2, 0x2b, 0x33, 0x94, 0x12, 0x49, 0xcf, 0x4f, 0xcf,
|
||||
0x07, 0x4b, 0xeb, 0x83, 0x58, 0x10, 0x95, 0x4a, 0xd5, 0x5c, 0xd2, 0xae, 0x15, 0x25, 0xa9, 0x79,
|
||||
0xc5, 0x99, 0xf9, 0x79, 0xfe, 0x05, 0x25, 0x99, 0xf9, 0x79, 0x2e, 0x10, 0xd3, 0xdc, 0x52, 0x53,
|
||||
0x43, 0x2a, 0x84, 0x62, 0xb8, 0x84, 0x72, 0x13, 0x2b, 0xe2, 0x0b, 0x8a, 0x32, 0xf3, 0x8b, 0x32,
|
||||
0x4b, 0x2a, 0x41, 0x8c, 0xe4, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xbd, 0x13, 0xf7,
|
||||
0xe4, 0x19, 0x6e, 0xdd, 0x93, 0x57, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf,
|
||||
0xd5, 0x4f, 0xce, 0x2f, 0xce, 0xcd, 0x2f, 0x86, 0x52, 0xba, 0xc5, 0x29, 0xd9, 0x10, 0x57, 0xea,
|
||||
0x79, 0xe6, 0x95, 0x04, 0x09, 0xe4, 0x26, 0x56, 0x04, 0x40, 0x0d, 0x0a, 0x00, 0x99, 0xe3, 0x64,
|
||||
0x75, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c,
|
||||
0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, 0x0a, 0x48, 0x66, 0xa6, 0x96,
|
||||
0x81, 0x8c, 0x44, 0xf3, 0x77, 0x12, 0x1b, 0xd8, 0xfd, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||
0x5c, 0xa9, 0x04, 0x48, 0x11, 0x01, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *ExtensionOptionDynamicFeeTx) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *ExtensionOptionDynamicFeeTx) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *ExtensionOptionDynamicFeeTx) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
{
|
||||
size := m.MaxPriorityPrice.Size()
|
||||
i -= size
|
||||
if _, err := m.MaxPriorityPrice.MarshalTo(dAtA[i:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i = encodeVarintDynamicFee(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintDynamicFee(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovDynamicFee(v)
|
||||
base := offset
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return base
|
||||
}
|
||||
func (m *ExtensionOptionDynamicFeeTx) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = m.MaxPriorityPrice.Size()
|
||||
n += 1 + l + sovDynamicFee(uint64(l))
|
||||
return n
|
||||
}
|
||||
|
||||
func sovDynamicFee(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
func sozDynamicFee(x uint64) (n int) {
|
||||
return sovDynamicFee(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *ExtensionOptionDynamicFeeTx) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowDynamicFee
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: ExtensionOptionDynamicFeeTx: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ExtensionOptionDynamicFeeTx: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field MaxPriorityPrice", wireType)
|
||||
}
|
||||
var stringLen uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowDynamicFee
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
stringLen |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
intStringLen := int(stringLen)
|
||||
if intStringLen < 0 {
|
||||
return ErrInvalidLengthDynamicFee
|
||||
}
|
||||
postIndex := iNdEx + intStringLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthDynamicFee
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
if err := m.MaxPriorityPrice.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipDynamicFee(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (skippy < 0) || (iNdEx+skippy) < 0 {
|
||||
return ErrInvalidLengthDynamicFee
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipDynamicFee(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
depth := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowDynamicFee
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowDynamicFee
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowDynamicFee
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthDynamicFee
|
||||
}
|
||||
iNdEx += length
|
||||
case 3:
|
||||
depth++
|
||||
case 4:
|
||||
if depth == 0 {
|
||||
return 0, ErrUnexpectedEndOfGroupDynamicFee
|
||||
}
|
||||
depth--
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
if iNdEx < 0 {
|
||||
return 0, ErrInvalidLengthDynamicFee
|
||||
}
|
||||
if depth == 0 {
|
||||
return iNdEx, nil
|
||||
}
|
||||
}
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthDynamicFee = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowDynamicFee = fmt.Errorf("proto: integer overflow")
|
||||
ErrUnexpectedEndOfGroupDynamicFee = fmt.Errorf("proto: unexpected end of group")
|
||||
)
|
@ -15,11 +15,6 @@ import (
|
||||
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
|
||||
// returns (effectiveFee, priority, error)
|
||||
func (k Keeper) DeductTxCostsFromUserBalance(
|
||||
@ -91,7 +86,7 @@ func (k Keeper) DeductTxCostsFromUserBalance(
|
||||
if baseFee != nil {
|
||||
tipPrice = new(big.Int).Sub(tipPrice, baseFee)
|
||||
}
|
||||
priorityBig := new(big.Int).Quo(tipPrice, DefaultPriorityReduction.BigInt())
|
||||
priorityBig := new(big.Int).Quo(tipPrice, evmtypes.DefaultPriorityReduction.BigInt())
|
||||
if !priorityBig.IsInt64() {
|
||||
priority = math.MaxInt64
|
||||
} else {
|
||||
|
@ -7,7 +7,6 @@ import (
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
|
||||
"github.com/evmos/ethermint/types"
|
||||
@ -267,7 +266,7 @@ func (tx DynamicFeeTx) Cost() *big.Int {
|
||||
|
||||
// EffectiveGasPrice returns the effective gas price
|
||||
func (tx *DynamicFeeTx) EffectiveGasPrice(baseFee *big.Int) *big.Int {
|
||||
return math.BigMin(new(big.Int).Add(tx.GasTipCap.BigInt(), baseFee), tx.GasFeeCap.BigInt())
|
||||
return EffectiveGasPrice(baseFee, tx.GasFeeCap.BigInt(), tx.GasTipCap.BigInt())
|
||||
}
|
||||
|
||||
// EffectiveFee returns effective_gasprice * gaslimit.
|
||||
|
2
x/evm/types/evm.pb.go
generated
2
x/evm/types/evm.pb.go
generated
@ -117,7 +117,7 @@ func (m *Params) GetAllowUnprotectedTxs() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ChainConfig defines the Ethereum ChainConfig parameters using *sdkmath.Int values
|
||||
// ChainConfig defines the Ethereum ChainConfig parameters using *sdk.Int values
|
||||
// instead of *big.Int.
|
||||
type ChainConfig struct {
|
||||
// Homestead switch block (nil no fork, 0 = already homestead)
|
||||
|
@ -2,6 +2,7 @@ package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
@ -9,9 +10,15 @@ import (
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
var EmptyCodeHash = crypto.Keccak256(nil)
|
||||
|
||||
// DecodeTxResponse decodes an protobuf-encoded byte slice into TxResponse
|
||||
@ -88,3 +95,9 @@ func BinSearch(lo, hi uint64, executable func(uint64) (bool, *MsgEthereumTxRespo
|
||||
}
|
||||
return hi, nil
|
||||
}
|
||||
|
||||
// EffectiveGasPrice compute the effective gas price based on eip-1159 rules
|
||||
// `effectiveGasPrice = min(baseFee + tipCap, feeCap)`
|
||||
func EffectiveGasPrice(baseFee *big.Int, feeCap *big.Int, tipCap *big.Int) *big.Int {
|
||||
return math.BigMin(new(big.Int).Add(tipCap, baseFee), feeCap)
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ var _ = Describe("Feemarket", func() {
|
||||
|
||||
Context("with MinGasPrices (feemarket param) < min-gas-prices (local)", func() {
|
||||
BeforeEach(func() {
|
||||
privKey, msg = setupTestWithContext("5", sdk.NewDec(3), sdk.ZeroInt())
|
||||
privKey, msg = setupTestWithContext("5", sdk.NewDec(3), sdk.NewInt(5))
|
||||
})
|
||||
Context("during CheckTx", func() {
|
||||
It("should reject transactions with gasPrice < MinGasPrices", func() {
|
||||
@ -139,7 +139,7 @@ var _ = Describe("Feemarket", func() {
|
||||
).To(BeTrue(), res.GetLog())
|
||||
})
|
||||
|
||||
It("should reject transactions with MinGasPrices < gasPrice < min-gas-prices", func() {
|
||||
It("should reject transactions with MinGasPrices < gasPrice < baseFee", func() {
|
||||
gasPrice := sdkmath.NewInt(4)
|
||||
res := checkTx(privKey, &gasPrice, &msg)
|
||||
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
||||
@ -149,7 +149,7 @@ var _ = Describe("Feemarket", func() {
|
||||
).To(BeTrue(), res.GetLog())
|
||||
})
|
||||
|
||||
It("should accept transactions with gasPrice > min-gas-prices", func() {
|
||||
It("should accept transactions with gasPrice >= baseFee", func() {
|
||||
gasPrice := sdkmath.NewInt(5)
|
||||
res := checkTx(privKey, &gasPrice, &msg)
|
||||
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
||||
@ -167,13 +167,16 @@ var _ = Describe("Feemarket", func() {
|
||||
).To(BeTrue(), res.GetLog())
|
||||
})
|
||||
|
||||
It("should accept transactions with MinGasPrices < gasPrice < than min-gas-prices", func() {
|
||||
It("should reject transactions with MinGasPrices < gasPrice < baseFee", func() {
|
||||
gasPrice := sdkmath.NewInt(4)
|
||||
res := deliverTx(privKey, &gasPrice, &msg)
|
||||
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
||||
res := checkTx(privKey, &gasPrice, &msg)
|
||||
Expect(res.IsOK()).To(Equal(false), "transaction should have failed")
|
||||
Expect(
|
||||
strings.Contains(res.GetLog(),
|
||||
"insufficient fee"),
|
||||
).To(BeTrue(), res.GetLog())
|
||||
})
|
||||
|
||||
It("should accept transactions with gasPrice >= min-gas-prices", func() {
|
||||
It("should accept transactions with gasPrice >= baseFee", func() {
|
||||
gasPrice := sdkmath.NewInt(5)
|
||||
res := deliverTx(privKey, &gasPrice, &msg)
|
||||
Expect(res.IsOK()).To(Equal(true), "transaction should have succeeded", res.GetLog())
|
||||
|
Loading…
Reference in New Issue
Block a user