Skip to content

Commit

Permalink
Merge pull request #29 from initia-labs/feat/gas-priority-lane
Browse files Browse the repository at this point in the history
feat: add priority lane
  • Loading branch information
beer-1 authored Dec 7, 2023
2 parents 768da2e + e15b063 commit d778922
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 39 deletions.
7 changes: 3 additions & 4 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ import (
signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
blockbase "github.com/skip-mev/block-sdk/block/base"
baselane "github.com/skip-mev/block-sdk/lanes/base"
freelane "github.com/skip-mev/block-sdk/lanes/free"
"github.com/skip-mev/block-sdk/lanes/mev"
"github.com/skip-mev/block-sdk/x/auction"
Expand Down Expand Up @@ -1008,17 +1007,17 @@ func NewInitiaApp(
applanes.FreeLaneMatchHandler(),
)

defaultLaneConfig := blockbase.LaneConfig{
priorityLaneConfig := blockbase.LaneConfig{
Logger: app.Logger(),
TxEncoder: app.txConfig.TxEncoder(),
TxDecoder: app.txConfig.TxDecoder(),
MaxBlockSpace: math.LegacyZeroDec(),
MaxTxs: 0,
SignerExtractor: signerExtractor,
}
defaultLane := baselane.NewDefaultLane(defaultLaneConfig)
priorityLane := applanes.NewPriorityLane(priorityLaneConfig)

lanes := []block.Lane{mevLane, freeLane, defaultLane}
lanes := []block.Lane{mevLane, freeLane, priorityLane}
mempool := block.NewLanedMempool(app.Logger(), true, lanes...)
app.SetMempool(mempool)

Expand Down
164 changes: 164 additions & 0 deletions app/lanes/priority.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package lanes

import (
"context"
"errors"
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkmempool "github.com/cosmos/cosmos-sdk/types/mempool"

signer_extraction "github.com/skip-mev/block-sdk/adapters/signer_extraction_adapter"
"github.com/skip-mev/block-sdk/block"
blockbase "github.com/skip-mev/block-sdk/block/base"
)

const (
// LaneName defines the name of the default lane.
LaneName = "priority"
)

var _ block.Lane = (*PriorityLane)(nil)

// DefaultLane defines a default lane implementation. The default lane orders
// transactions by the transaction fees. The default lane accepts any transaction
// that should not be ignored (as defined by the IgnoreList in the LaneConfig).
// The default lane builds and verifies blocks in a similar fashion to how the
// CometBFT/Tendermint consensus engine builds and verifies blocks pre SDK version
// 0.47.0.
type PriorityLane struct {
*blockbase.BaseLane
}

// NewPriorityLane returns a new default lane.
func NewPriorityLane(cfg blockbase.LaneConfig) *PriorityLane {
lane := blockbase.NewBaseLane(
cfg,
LaneName,
NewMempool(blockbase.NewDefaultTxPriority(), cfg.SignerExtractor, cfg.MaxTxs),
blockbase.DefaultMatchHandler(),
)

if err := lane.ValidateBasic(); err != nil {
panic(err)
}

return &PriorityLane{
BaseLane: lane,
}
}

type (
txKey struct {
nonce uint64
sender string
}

// Mempool defines a mempool that orders transactions based on the
// txPriority. The mempool is a wrapper on top of the SDK's Priority Nonce mempool.
// It include's additional helper functions that allow users to determine if a
// transaction is already in the mempool and to compare the priority of two
// transactions.
Mempool[C comparable] struct {
// index defines an index of transactions.
index sdkmempool.Mempool

// signerExtractor defines the signer extraction adapter that allows us to
// extract the signer from a transaction.
extractor signer_extraction.Adapter

// txCache is a map of all transactions in the mempool. It is used
// to quickly check if a transaction is already in the mempool.
txCache map[txKey]struct{}
}
)

// NewMempool returns a new Mempool.
func NewMempool[C comparable](txPriority blockbase.TxPriority[C], extractor signer_extraction.Adapter, maxTx int) *Mempool[C] {
return &Mempool[C]{
index: blockbase.NewPriorityMempool(
blockbase.PriorityNonceMempoolConfig[C]{
TxPriority: txPriority,
MaxTx: maxTx,
},
extractor,
),
extractor: extractor,
txCache: make(map[txKey]struct{}),
}
}

// CountTx returns the number of transactions in the mempool.
func (cm *Mempool[C]) CountTx() int {
return cm.index.CountTx()
}

// Select returns an iterator of all transactions in the mempool. NOTE: If you
// remove a transaction from the mempool while iterating over the transactions,
// the iterator will not be aware of the removal and will continue to iterate
// over the removed transaction. Be sure to reset the iterator if you remove a transaction.
func (cm *Mempool[C]) Select(ctx context.Context, txs [][]byte) sdkmempool.Iterator {
return cm.index.Select(ctx, txs)
}

// Compare return 0 to ignore priority check in ProcessLaneHandler.
func (cm *Mempool[C]) Compare(ctx sdk.Context, this sdk.Tx, other sdk.Tx) (int, error) {
return 0, nil
}

// Contains returns true if the transaction is contained in the mempool.
func (cm *Mempool[C]) Contains(tx sdk.Tx) bool {
if key, err := cm.getTxKey(tx); err != nil {
return false
} else {
if _, ok := cm.txCache[key]; ok {
return true
} else {
return false
}
}
}

// Insert inserts a transaction into the mempool.
func (cm *Mempool[C]) Insert(ctx context.Context, tx sdk.Tx) error {
if err := cm.index.Insert(ctx, tx); err != nil {
return fmt.Errorf("failed to insert tx into auction index: %w", err)
}

if key, err := cm.getTxKey(tx); err != nil {
return err
} else {
cm.txCache[key] = struct{}{}
}

return nil
}

// Remove removes a transaction from the mempool.
func (cm *Mempool[C]) Remove(tx sdk.Tx) error {
if err := cm.index.Remove(tx); err != nil && !errors.Is(err, sdkmempool.ErrTxNotFound) {
return fmt.Errorf("failed to remove transaction from the mempool: %w", err)
}

if key, err := cm.getTxKey(tx); err != nil {
return err
} else {
delete(cm.txCache, key)
}

return nil
}

func (cm *Mempool[C]) getTxKey(tx sdk.Tx) (txKey, error) {
signers, err := cm.extractor.GetSigners(tx)
if err != nil {
return txKey{}, err
}
if len(signers) == 0 {
return txKey{}, fmt.Errorf("attempted to remove a tx with no signatures")
}
sig := signers[0]
sender := sig.Signer.String()
nonce := sig.Sequence
return txKey{nonce, sender}, nil
}
59 changes: 24 additions & 35 deletions x/move/ante/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,28 @@ func (fc MempoolFeeChecker) CheckTxFeeWithMinGasPrices(ctx sdk.Context, tx sdk.T
feeCoins := feeTx.GetFee()
gas := feeTx.GetGas()

priority := int64(1)
if ctx.IsCheckTx() {
minGasPrices := ctx.MinGasPrices()
feeValueInBaseUnit := math.ZeroInt()
if fc.keeper != nil {
baseDenom := fc.keeper.BaseDenom(ctx)
baseMinGasPrice := fc.keeper.BaseMinGasPrice(ctx)

minGasPrices = combinedMinGasPrices(baseDenom, baseMinGasPrice, minGasPrices)

for _, coin := range feeTx.GetFee() {
quotePrice, err := fc.fetchPrice(ctx, baseDenom, coin.Denom)
if err != nil {
return nil, 1, err
}

quoteValueInBaseUnit := quotePrice.MulInt(coin.Amount).TruncateInt()
feeValueInBaseUnit = feeValueInBaseUnit.Add(quoteValueInBaseUnit)
}
if feeValueInBaseUnit.GT(math.OneInt()) {
priority = feeValueInBaseUnit.Int64()
}
}

if !minGasPrices.IsZero() {
Expand All @@ -59,30 +74,8 @@ func (fc MempoolFeeChecker) CheckTxFeeWithMinGasPrices(ctx sdk.Context, tx sdk.T
baseDenom := fc.keeper.BaseDenom(ctx)
requiredBaseAmount := requiredFees.AmountOfNoDenomValidation(baseDenom)

// If the requiredBaseAmount is zero, it means the operator
// do not want to receive base denom fee but want to get other
// denom fee.
if !requiredBaseAmount.IsZero() {
for _, coin := range feeCoins {
quotePrice, skip, err := fc.fetchOrSkipPrice(ctx, baseDenom, coin.Denom)
if err != nil {
return nil, 0, err
}
if skip {
continue
}

// sum the converted fee values
quoteValueInBaseUnit := quotePrice.MulInt(coin.Amount).TruncateInt()
sumInBaseUnit = sumInBaseUnit.Add(quoteValueInBaseUnit)

// check the sum is greater than the required.
if sumInBaseUnit.GTE(requiredBaseAmount) {
isSufficient = true
break
}
}
}
// converting to base token only works when the requiredBaseAmount is non-zero.
isSufficient = !requiredBaseAmount.IsZero() && feeValueInBaseUnit.GTE(requiredBaseAmount)
}

if !isSufficient {
Expand All @@ -98,27 +91,23 @@ func (fc MempoolFeeChecker) CheckTxFeeWithMinGasPrices(ctx sdk.Context, tx sdk.T
}
}

// TODO - if we want to use ethereum like priority system,
// then we need to compute all dex prices of all fee coins
return feeCoins, 1 /* FIFO */, nil
return feeCoins, priority, nil
}

func (fc MempoolFeeChecker) fetchOrSkipPrice(ctx sdk.Context, baseDenom, quoteDenom string) (price sdk.Dec, skip bool, err error) {
func (fc MempoolFeeChecker) fetchPrice(ctx sdk.Context, baseDenom, quoteDenom string) (price sdk.Dec, err error) {
if quoteDenom == baseDenom {
return sdk.OneDec(), false, nil
return sdk.OneDec(), nil
}

if found, err := fc.keeper.HasDexPair(ctx, quoteDenom); err != nil {
return sdk.ZeroDec(), false, err
return sdk.ZeroDec(), err
} else if !found {
return sdk.ZeroDec(), true, nil
return sdk.ZeroDec(), nil
}

if quotePrice, err := fc.keeper.GetPoolSpotPrice(ctx, quoteDenom); err != nil {
return sdk.ZeroDec(), false, err
} else if quotePrice.IsZero() {
return sdk.ZeroDec(), true, nil
return sdk.ZeroDec(), err
} else {
return quotePrice, false, nil
return quotePrice, nil
}
}

0 comments on commit d778922

Please sign in to comment.