Skip to content

Commit

Permalink
Merge pull request #27 from 0glabs/handle_ws_new_txn
Browse files Browse the repository at this point in the history
do finding suggestion gas price in fee market module
  • Loading branch information
Solovyov1796 authored Feb 12, 2025
2 parents 41e4bf2 + 14aba72 commit c133a46
Show file tree
Hide file tree
Showing 8 changed files with 227 additions and 19 deletions.
9 changes: 6 additions & 3 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ type EthermintApp struct {

// Ethermint keepers
EvmKeeper *evmkeeper.Keeper
FeeMarketKeeper *feemarketkeeper.Keeper
FeeMarketKeeper feemarketkeeper.Keeper

// the module manager
mm *module.Manager
Expand Down Expand Up @@ -425,8 +425,11 @@ func NewEthermintApp(
// Create Ethermint keepers
feeMarketSs := app.GetSubspace(feemarkettypes.ModuleName)
app.FeeMarketKeeper = feemarketkeeper.NewKeeper(
appCodec, authtypes.NewModuleAddress(govtypes.ModuleName),
keys[feemarkettypes.StoreKey], tkeys[feemarkettypes.TransientKey], feeMarketSs,
appCodec,
authtypes.NewModuleAddress(govtypes.ModuleName),
keys[feemarkettypes.StoreKey], tkeys[feemarkettypes.TransientKey],
feeMarketSs,
bApp.Mempool(),
)

// Set authority to x/gov module account to only expect the module account to update params
Expand Down
4 changes: 2 additions & 2 deletions x/feemarket/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
// InitGenesis initializes genesis state based on exported genesis
func InitGenesis(
ctx sdk.Context,
k *keeper.Keeper,
k keeper.Keeper,
data types.GenesisState,
) []abci.ValidatorUpdate {
err := k.SetParams(ctx, data.Params)
Expand All @@ -41,7 +41,7 @@ func InitGenesis(
}

// ExportGenesis exports genesis state of the fee market module
func ExportGenesis(ctx sdk.Context, k *keeper.Keeper) *types.GenesisState {
func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
return &types.GenesisState{
Params: k.GetParams(ctx),
BlockGas: k.GetBlockGasWanted(ctx),
Expand Down
184 changes: 184 additions & 0 deletions x/feemarket/keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,33 @@ package keeper

import (
"fmt"
"math/big"
"reflect"
"sort"

abci "github.com/cometbft/cometbft/abci/types"
"github.com/evmos/ethermint/x/feemarket/types"

"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/signing"
gethtypes "github.com/ethereum/go-ethereum/core/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
)

const (
gasPriceSuggestionBlockNum int64 = 5
GasDenom = "ua0gi"
GasDenomConversionMultiplier = 1e12
)

type txnInfo struct {
gasPrice *big.Int
gasLimit uint64
nonce uint64
sender string
}

// BeginBlock updates base fee
func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { //nolint: revive
baseFee := k.CalculateBaseFee(ctx)
Expand Down Expand Up @@ -79,4 +98,169 @@ func (k *Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) { //nolint:
sdk.NewAttribute("height", fmt.Sprintf("%d", ctx.BlockHeight())),
sdk.NewAttribute("amount", fmt.Sprintf("%d", gasWanted)),
))

k.foundSuggestionGasPrice(ctx)
}

func (k Keeper) foundSuggestionGasPrice(ctx sdk.Context) {
logger := k.Logger(ctx)
var maxBlockGas uint64
if b := ctx.ConsensusParams().Block; b != nil {
maxBlockGas = uint64(b.MaxGas)

txnInfoMap := make(map[string][]*txnInfo, k.mempool.CountTx())
iterator := k.mempool.Select(ctx, nil)

for iterator != nil {
memTx := iterator.Tx()

sigVerifiableTx, ok := memTx.(signing.SigVerifiableTx)
if !ok {
logger.Error("memTx is not a SigVerifiableTx: ", "type", reflect.TypeOf(memTx))
iterator = iterator.Next()
continue
}

sigs, err := sigVerifiableTx.GetSignaturesV2()
if err != nil {
logger.Error("failed to get signatures:", "error=", err)
iterator = iterator.Next()
continue
}

if len(sigs) == 0 {
msgs := memTx.GetMsgs()
if len(msgs) == 1 {
msgEthTx, ok := msgs[0].(*evmtypes.MsgEthereumTx)
if ok {
ethTx := msgEthTx.AsTransaction()
signer := gethtypes.NewEIP2930Signer(ethTx.ChainId())
ethSender, err := signer.Sender(ethTx)
if err == nil {
signer := sdk.AccAddress(ethSender.Bytes()).String()
nonce := ethTx.Nonce()

if _, exists := txnInfoMap[signer]; !exists {
txnInfoMap[signer] = make([]*txnInfo, 0, 128)
}

txnInfoMap[signer] = append(txnInfoMap[signer], &txnInfo{
gasPrice: ethTx.GasPrice(),
gasLimit: ethTx.Gas(),
nonce: nonce,
sender: signer,
})
}
}
}
} else {
// ignore multisig case now
if fee, ok := memTx.(sdk.FeeTx); ok {
if len(sigs) == 1 {
signer := sdk.AccAddress(sigs[0].PubKey.Address()).String()

if _, exists := txnInfoMap[signer]; !exists {
txnInfoMap[signer] = make([]*txnInfo, 0, 16)
}

evmGasPrice, err := utilCosmosDemonGasPriceToEvmDemonGasPrice(fee.GetFee())

if err == nil {
txnInfoMap[signer] = append(txnInfoMap[signer], &txnInfo{
gasPrice: evmGasPrice,
gasLimit: utilCosmosDemonGasLimitToEvmDemonGasLimit(fee.GetGas()),
nonce: sigs[0].Sequence,
sender: signer,
})
}
}
} else {
logger.Error("unknown type of memTx: ", "type", reflect.TypeOf(memTx))
}
}

iterator = iterator.Next()
}

logger.Debug("mempool size: ", "size", k.mempool.CountTx())
if len(txnInfoMap) == 0 {
logger.Debug("not found suggestion gas price!")
k.SetSuggestionGasPrice(ctx, big.NewInt(0))
} else {
senderCnt := 0
txnCnt := 0
for sender := range txnInfoMap {
sort.Slice(txnInfoMap[sender], func(i, j int) bool {
return txnInfoMap[sender][i].nonce < txnInfoMap[sender][j].nonce
})
txnCnt += len(txnInfoMap[sender])
senderCnt++
}

remaing := gasPriceSuggestionBlockNum * int64(maxBlockGas)
var lastProcessedTx *txnInfo

for remaing > 0 && len(txnInfoMap) > 0 {
// Find the highest gas price among the first transaction of each account
var highestGasPrice *big.Int
var selectedSender string

// Compare first transaction (lowest nonce) from each account
for sender, txns := range txnInfoMap {
if len(txns) == 0 {
delete(txnInfoMap, sender)
continue
}

// First tx has lowest nonce due to earlier sorting
if highestGasPrice == nil || txns[0].gasPrice.Cmp(highestGasPrice) > 0 {
highestGasPrice = txns[0].gasPrice
selectedSender = sender
}
}

if selectedSender == "" {
break
}

// Process the selected transaction
selectedTx := txnInfoMap[selectedSender][0]
remaing -= int64(selectedTx.gasLimit)
lastProcessedTx = selectedTx

// Remove processed transaction
txnInfoMap[selectedSender] = txnInfoMap[selectedSender][1:]
if len(txnInfoMap[selectedSender]) == 0 {
delete(txnInfoMap, selectedSender)
}
}

if lastProcessedTx != nil && remaing <= 0 {
logger.Debug("found suggestion gas price: ", "value", lastProcessedTx.gasPrice.String())
k.SetSuggestionGasPrice(ctx, lastProcessedTx.gasPrice)
} else {
logger.Debug("not found suggestion gas price!")
k.SetSuggestionGasPrice(ctx, big.NewInt(0))
}
}
}
}

func utilCosmosDemonGasPriceToEvmDemonGasPrice(gasGroup sdk.Coins) (*big.Int, error) {
gasPrice := big.NewInt(0)
for _, coin := range gasGroup {
if coin.Denom == GasDenom {
thisGasPrice := big.NewInt(0).SetUint64(coin.Amount.Uint64())
thisGasPrice = thisGasPrice.Mul(thisGasPrice, big.NewInt(0).SetInt64(GasDenomConversionMultiplier))
gasPrice = gasPrice.Add(gasPrice, thisGasPrice)
} else {
return big.NewInt(0), fmt.Errorf("invalid denom: %s", coin.Denom)
}
}

return gasPrice, nil
}

func utilCosmosDemonGasLimitToEvmDemonGasLimit(gasLimit uint64) uint64 {
return gasLimit * GasDenomConversionMultiplier
}
28 changes: 22 additions & 6 deletions x/feemarket/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/mempool"
paramstypes "github.com/cosmos/cosmos-sdk/x/params/types"

"github.com/evmos/ethermint/x/feemarket/types"
Expand All @@ -43,12 +44,18 @@ type Keeper struct {
ss paramstypes.Subspace

suggestionGasPrice *big.Int

mempool mempool.Mempool
}

// NewKeeper generates new fee market module keeper
func NewKeeper(
cdc codec.BinaryCodec, authority sdk.AccAddress, storeKey, transientKey storetypes.StoreKey, ss paramstypes.Subspace,
) *Keeper {
cdc codec.BinaryCodec,
authority sdk.AccAddress,
storeKey, transientKey storetypes.StoreKey,
ss paramstypes.Subspace,
mempool mempool.Mempool,
) Keeper {
// ensure authority account is correctly formatted
if err := sdk.VerifyAddressFormat(authority); err != nil {
panic(err)
Expand All @@ -58,13 +65,14 @@ func NewKeeper(
ss = ss.WithKeyTable(types.ParamKeyTable())
}

return &Keeper{
return Keeper{
cdc: cdc,
storeKey: storeKey,
authority: authority,
transientKey: transientKey,
ss: ss,
suggestionGasPrice: big.NewInt(0),
mempool: mempool,
}
}

Expand Down Expand Up @@ -133,10 +141,18 @@ func (k Keeper) GetBaseFeeV1(ctx sdk.Context) *big.Int {
return new(big.Int).SetBytes(bz)
}

func (k *Keeper) SetSuggestionGasPrice(ctx sdk.Context, gas *big.Int) {
k.suggestionGasPrice = big.NewInt(0).Set(gas)
func (k Keeper) SetSuggestionGasPrice(ctx sdk.Context, gas *big.Int) {
store := ctx.KVStore(k.storeKey)
gasBz := gas.Bytes()
store.Set(types.KeyPrefixSuggestionGasPrice, gasBz)
}

func (k Keeper) GetSuggestionGasPrice(ctx sdk.Context) *big.Int {
return k.suggestionGasPrice
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyPrefixSuggestionGasPrice)
if len(bz) == 0 {
return big.NewInt(0)
}

return new(big.Int).SetBytes(bz)
}
4 changes: 2 additions & 2 deletions x/feemarket/keeper/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ import (

// Migrator is a struct for handling in-place store migrations.
type Migrator struct {
keeper *Keeper
keeper Keeper
legacySubspace types.Subspace
}

// NewMigrator returns a new Migrator.
func NewMigrator(keeper *Keeper, legacySubspace types.Subspace) Migrator {
func NewMigrator(keeper Keeper, legacySubspace types.Subspace) Migrator {
return Migrator{
keeper: keeper,
legacySubspace: legacySubspace,
Expand Down
7 changes: 5 additions & 2 deletions x/feemarket/keeper/params_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/mempool"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
"github.com/evmos/ethermint/app"
Expand Down Expand Up @@ -109,7 +110,7 @@ func (suite *KeeperTestSuite) TestLegacyParamsKeyTableRegistration() {
suite.Require().False(unregisteredSubspace.HasKeyTable())

// create a keeper, mimicking an app.go which has not registered the key table
k := keeper.NewKeeper(cdc, authtypes.NewModuleAddress("gov"), storeKey, tKey, unregisteredSubspace)
k := keeper.NewKeeper(cdc, authtypes.NewModuleAddress("gov"), storeKey, tKey, unregisteredSubspace, mempool.NoOpMempool{})

// the keeper must set the key table
var fetchedParams types.Params
Expand All @@ -120,5 +121,7 @@ func (suite *KeeperTestSuite) TestLegacyParamsKeyTableRegistration() {
suite.Require().Equal(params, fetchedParams)
// ensure we do not attempt to override any existing key tables to keep compatibility
// when passing a subpsace to the keeper that has already been used to work with parameters
suite.Require().NotPanics(func() { keeper.NewKeeper(cdc, authtypes.NewModuleAddress("gov"), storeKey, tKey, unregisteredSubspace) })
suite.Require().NotPanics(func() {
keeper.NewKeeper(cdc, authtypes.NewModuleAddress("gov"), storeKey, tKey, unregisteredSubspace, mempool.NoOpMempool{})
})
}
6 changes: 3 additions & 3 deletions x/feemarket/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,13 @@ func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry)
// AppModule implements an application module for the fee market module.
type AppModule struct {
AppModuleBasic
keeper *keeper.Keeper
keeper keeper.Keeper
// legacySubspace is used solely for migration of x/params managed parameters
legacySubspace types.Subspace
}

// NewAppModule creates a new AppModule object
func NewAppModule(k *keeper.Keeper, ss types.Subspace) AppModule {
func NewAppModule(k keeper.Keeper, ss types.Subspace) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: k,
Expand All @@ -138,7 +138,7 @@ func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// module-specific GRPC queries and handle the upgrade store migration for the module.
func (am AppModule) RegisterServices(cfg module.Configurator) {
types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
types.RegisterMsgServer(cfg.MsgServer(), am.keeper)
types.RegisterMsgServer(cfg.MsgServer(), &am.keeper)

m := keeper.NewMigrator(am.keeper, am.legacySubspace)
if err := cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2to3); err != nil {
Expand Down
4 changes: 3 additions & 1 deletion x/feemarket/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const (
const (
prefixBlockGasWanted = iota + 1
deprecatedPrefixBaseFee // unused
prefixSuggestionGasPrice
)

const (
Expand All @@ -43,7 +44,8 @@ const (

// KVStore key prefixes
var (
KeyPrefixBlockGasWanted = []byte{prefixBlockGasWanted}
KeyPrefixBlockGasWanted = []byte{prefixBlockGasWanted}
KeyPrefixSuggestionGasPrice = []byte{prefixSuggestionGasPrice}
)

// Transient Store key prefixes
Expand Down

0 comments on commit c133a46

Please sign in to comment.