|
| 1 | +package app |
| 2 | + |
| 3 | +import ( |
| 4 | + "math/big" |
| 5 | + |
| 6 | + sdk "github.com/cosmos/cosmos-sdk/types" |
| 7 | + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" |
| 8 | + "github.com/cosmos/cosmos-sdk/x/auth/ante" |
| 9 | + paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" |
| 10 | + upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" |
| 11 | + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" |
| 12 | + ethtypes "github.com/ethereum/go-ethereum/core/types" |
| 13 | + "github.com/sei-protocol/sei-chain/app/antedecorators" |
| 14 | + "github.com/sei-protocol/sei-chain/utils" |
| 15 | + "github.com/sei-protocol/sei-chain/x/evm/derived" |
| 16 | + evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper" |
| 17 | + evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" |
| 18 | + oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" |
| 19 | +) |
| 20 | + |
| 21 | +var _ sdk.TxPrioritizer = (*SeiTxPrioritizer)(nil).GetTxPriority |
| 22 | + |
| 23 | +type SeiTxPrioritizer struct { |
| 24 | + evmKeeper *evmkeeper.Keeper |
| 25 | + upgradeKeeper *upgradekeeper.Keeper |
| 26 | + paramsKeeper *paramskeeper.Keeper |
| 27 | +} |
| 28 | + |
| 29 | +func NewSeiTxPrioritizer(ek *evmkeeper.Keeper, uk *upgradekeeper.Keeper, pk *paramskeeper.Keeper) *SeiTxPrioritizer { |
| 30 | + return &SeiTxPrioritizer{ |
| 31 | + evmKeeper: ek, |
| 32 | + upgradeKeeper: uk, |
| 33 | + paramsKeeper: pk, |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +func (s *SeiTxPrioritizer) GetTxPriority(ctx sdk.Context, tx sdk.Tx) (int64, error) { |
| 38 | + if ctx.HasPriority() { |
| 39 | + // The context already has a priority set, return it. |
| 40 | + return ctx.Priority(), nil |
| 41 | + } |
| 42 | + if evmTx := evmtypes.GetEVMTransactionMessage(tx); evmTx != nil { |
| 43 | + return s.getEvmTxPriority(ctx, evmTx) |
| 44 | + } |
| 45 | + if feeTx, ok := tx.(sdk.FeeTx); ok { |
| 46 | + return s.getCosmosTxPriority(ctx, feeTx) |
| 47 | + } |
| 48 | + return 0, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must either be EVM or Fee") |
| 49 | +} |
| 50 | + |
| 51 | +func (s *SeiTxPrioritizer) getEvmTxPriority(ctx sdk.Context, evmTx *evmtypes.MsgEVMTransaction) (int64, error) { |
| 52 | + |
| 53 | + if s.isUnassociatedAssociate(ctx, evmTx) { |
| 54 | + return antedecorators.EVMAssociatePriority, nil |
| 55 | + } |
| 56 | + |
| 57 | + // Check txData for sanity. |
| 58 | + txData, err := evmtypes.UnpackTxData(evmTx.Data) |
| 59 | + if err != nil { |
| 60 | + return 0, err |
| 61 | + } |
| 62 | + if txData.GetGasFeeCap().Cmp(s.getEvmBaseFee(ctx)) < 0 { |
| 63 | + return 0, sdkerrors.ErrInsufficientFee |
| 64 | + } |
| 65 | + minimumFee := s.evmKeeper.GetMinimumFeePerGas(ctx).TruncateInt().BigInt() |
| 66 | + if txData.GetGasFeeCap().Cmp(minimumFee) < 0 { |
| 67 | + return 0, sdkerrors.ErrInsufficientFee |
| 68 | + } |
| 69 | + if txData.GetGasTipCap().Sign() < 0 { |
| 70 | + return 0, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "gas fee cap cannot be negative") |
| 71 | + } |
| 72 | + // Check blob hashes for sanity. |
| 73 | + // If EVM version is Cancun or later, and the transaction contains at least one blob, we need to |
| 74 | + // make sure the transaction carries a non-zero blob fee cap. |
| 75 | + if evmTx.Derived.Version >= derived.Cancun && len(txData.GetBlobHashes()) > 0 { |
| 76 | + // For now we are simply assuming excessive blob gas is 0. In the future we might change it to be |
| 77 | + // dynamic based on prior block usage. |
| 78 | + chainConfig := evmtypes.DefaultChainConfig().EthereumConfig(s.evmKeeper.ChainID(ctx)) |
| 79 | + if txData.GetBlobFeeCap().Cmp(eip4844.CalcBlobFee(chainConfig, ðtypes.Header{Time: uint64(ctx.BlockTime().Unix())})) < 0 { |
| 80 | + return 0, sdkerrors.ErrInsufficientFee |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + gp := txData.EffectiveGasPrice(utils.Big0) |
| 85 | + priority := sdk.NewDecFromBigInt(gp).Quo(s.evmKeeper.GetPriorityNormalizer(ctx)).TruncateInt().BigInt() |
| 86 | + if priority.Cmp(big.NewInt(antedecorators.MaxPriority)) > 0 { |
| 87 | + priority = big.NewInt(antedecorators.MaxPriority) |
| 88 | + } |
| 89 | + return priority.Int64(), nil |
| 90 | +} |
| 91 | + |
| 92 | +func (s *SeiTxPrioritizer) isUnassociatedAssociate(ctx sdk.Context, evmTx *evmtypes.MsgEVMTransaction) bool { |
| 93 | + // TODO: when is derived populated? Check that it is reasonable to use it here. |
| 94 | + if evmTx.Derived == nil { |
| 95 | + return false |
| 96 | + } |
| 97 | + |
| 98 | + // TODO: this potentially looks up entries from KVstore. Do we want to? |
| 99 | + ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)) |
| 100 | + _, isAssociated := s.evmKeeper.GetEVMAddress(ctx, evmTx.Derived.SenderSeiAddr) |
| 101 | + return evmTx.Derived.IsAssociate && !isAssociated |
| 102 | +} |
| 103 | + |
| 104 | +func (s *SeiTxPrioritizer) getEvmBaseFee(ctx sdk.Context) *big.Int { |
| 105 | + const ( |
| 106 | + pacific1 = "pacific-1" |
| 107 | + historicalBlockHeight = 114945913 |
| 108 | + doneHeightName = "6.2.0" |
| 109 | + ) |
| 110 | + if ctx.ChainID() == pacific1 { |
| 111 | + height := ctx.BlockHeight() |
| 112 | + if height < historicalBlockHeight { |
| 113 | + return s.evmKeeper.GetBaseFeePerGas(ctx).TruncateInt().BigInt() |
| 114 | + } |
| 115 | + |
| 116 | + doneHeight := s.upgradeKeeper.GetDoneHeight( |
| 117 | + ctx.WithGasMeter(sdk.NewInfiniteGasMeter(1, 1)), doneHeightName) |
| 118 | + if height < doneHeight { |
| 119 | + return s.evmKeeper.GetCurrBaseFeePerGas(ctx).TruncateInt().BigInt() |
| 120 | + } |
| 121 | + } |
| 122 | + return s.evmKeeper.GetNextBaseFeePerGas(ctx).TruncateInt().BigInt() |
| 123 | +} |
| 124 | + |
| 125 | +func (s *SeiTxPrioritizer) getCosmosTxPriority(ctx sdk.Context, feeTx sdk.FeeTx) (int64, error) { |
| 126 | + if isOracleTx(feeTx) { |
| 127 | + return antedecorators.OraclePriority, nil |
| 128 | + } |
| 129 | + |
| 130 | + feeCoins := feeTx.GetFee() |
| 131 | + feeParams := s.paramsKeeper.GetFeesParams(ctx) |
| 132 | + feeCoins = feeCoins.NonZeroAmountsOf(append([]string{sdk.DefaultBondDenom}, feeParams.GetAllowedFeeDenoms()...)) |
| 133 | + gas := feeTx.GetGas() |
| 134 | + // skip checking that fees meet a minimum threshold for the validator. |
| 135 | + var priority int64 |
| 136 | + if gas > 0 { |
| 137 | + priority = ante.GetTxPriority(feeCoins, int64(gas)) |
| 138 | + } |
| 139 | + return min(antedecorators.MaxPriority, priority), nil |
| 140 | +} |
| 141 | + |
| 142 | +func isOracleTx(tx sdk.FeeTx) bool { |
| 143 | + if len(tx.GetMsgs()) == 0 { |
| 144 | + return false |
| 145 | + } |
| 146 | + for _, msg := range tx.GetMsgs() { |
| 147 | + switch msg.(type) { |
| 148 | + case *oracletypes.MsgAggregateExchangeRateVote: |
| 149 | + continue |
| 150 | + default: |
| 151 | + return false |
| 152 | + } |
| 153 | + } |
| 154 | + return true |
| 155 | +} |
0 commit comments