-
Notifications
You must be signed in to change notification settings - Fork 879
Expand file tree
/
Copy pathprioritizer.go
More file actions
196 lines (178 loc) · 6.73 KB
/
prioritizer.go
File metadata and controls
196 lines (178 loc) · 6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package app
import (
"math"
"math/big"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
cosmosante "github.com/cosmos/cosmos-sdk/x/auth/ante"
paramskeeper "github.com/cosmos/cosmos-sdk/x/params/keeper"
upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/sei-protocol/sei-chain/app/antedecorators"
"github.com/sei-protocol/sei-chain/utils"
evmante "github.com/sei-protocol/sei-chain/x/evm/ante"
"github.com/sei-protocol/sei-chain/x/evm/derived"
evmkeeper "github.com/sei-protocol/sei-chain/x/evm/keeper"
evmtypes "github.com/sei-protocol/sei-chain/x/evm/types"
oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types"
"github.com/tendermint/tendermint/libs/log"
)
var _ sdk.TxPrioritizer = (*SeiTxPrioritizer)(nil).GetTxPriorityHint
type SeiTxPrioritizer struct {
evmKeeper *evmkeeper.Keeper
upgradeKeeper *upgradekeeper.Keeper
paramsKeeper *paramskeeper.Keeper
logger log.Logger
}
func NewSeiTxPrioritizer(logger log.Logger, ek *evmkeeper.Keeper, uk *upgradekeeper.Keeper, pk *paramskeeper.Keeper) *SeiTxPrioritizer {
return &SeiTxPrioritizer{
logger: logger,
evmKeeper: ek,
upgradeKeeper: uk,
paramsKeeper: pk,
}
}
func (s *SeiTxPrioritizer) GetTxPriorityHint(ctx sdk.Context, tx sdk.Tx) (_priorityHint int64, _err error) {
defer func() {
if r := recover(); r != nil {
// Fall back to no-op priority if we panic for any reason. This is to avoid DoS
// vectors where a malicious actor crafts a transaction that panics the
// prioritizer. Since the prioritizer is used as a hint only, it's safe to fall
// back to zero priority in this case and log the panic for monitoring purposes.
s.logger.Error("tx prioritizer panicked. Falling back on no priority", "error", r)
_priorityHint = 0
_err = nil
}
}()
if ctx.HasPriority() {
// The context already has a priority set, return it.
return ctx.Priority(), nil
}
if ok, err := evmante.IsEVMMessage(tx); err != nil {
return 0, err
} else if ok {
evmTx := evmtypes.GetEVMTransactionMessage(tx)
if evmTx != nil {
return s.getEvmTxPriority(ctx, evmTx)
}
// This should never happen since IsEVMMessage returned true. But we defensively
// return zero priority to be safe.
return 0, nil
}
if feeTx, ok := tx.(sdk.FeeTx); ok {
return s.getCosmosTxPriority(ctx, feeTx)
}
return 0, sdkerrors.Wrap(sdkerrors.ErrTxDecode, "Tx must either be EVM or Fee")
}
func (s *SeiTxPrioritizer) getEvmTxPriority(ctx sdk.Context, evmTx *evmtypes.MsgEVMTransaction) (int64, error) {
// Unpack the transaction data first to avoid double unpacking as part of preprocessing.
txData, err := evmtypes.UnpackTxData(evmTx.Data)
if err != nil {
return 0, err
}
if err := evmante.PreprocessUnpacked(ctx, evmTx, s.evmKeeper.ChainID(ctx), s.evmKeeper.EthBlockTestConfig.Enabled, txData); err != nil {
return 0, err
}
if evmTx.Derived.IsAssociate {
_, isAssociated := s.evmKeeper.GetEVMAddress(
ctx.WithGasMeter(sdk.NewInfiniteGasMeterWithMultiplier(ctx)),
evmTx.Derived.SenderSeiAddr)
if !isAssociated {
// Unassociated associate transactions have the second-highest priority.
// This is to ensure that associate transactions are processed before
// regular transactions, but after oracle transactions.
//
// Note that we are not checking if sufficient funds are present here to keep the
// priority calculation fast. CheckTx should fully check the transaction.
return antedecorators.EVMAssociatePriority, nil
}
return 0, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "account already has association set")
}
// Check txData for sanity.
feeCap := txData.GetGasFeeCap()
fee := s.getEvmBaseFee(ctx)
if feeCap.Cmp(fee) < 0 {
return 0, sdkerrors.ErrInsufficientFee
}
minimumFee := s.evmKeeper.GetMinimumFeePerGas(ctx).TruncateInt().BigInt()
if feeCap.Cmp(minimumFee) < 0 {
return 0, sdkerrors.ErrInsufficientFee
}
if txData.GetGasTipCap().Sign() < 0 {
return 0, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "gas fee cap cannot be negative")
}
// Check blob hashes for sanity. If EVM version is Cancun or later, and the
// transaction contains at least one blob, we need to make sure the transaction
// carries a non-zero blob fee cap.
if evmTx.Derived != nil && evmTx.Derived.Version >= derived.Cancun && len(txData.GetBlobHashes()) > 0 {
// For now we are simply assuming excessive blob gas is 0. In the future we might change it to be
// dynamic based on prior block usage.
chainConfig := evmtypes.DefaultChainConfig().EthereumConfig(s.evmKeeper.ChainID(ctx))
if txData.GetBlobFeeCap().Cmp(eip4844.CalcBlobFee(chainConfig, ðtypes.Header{Time: uint64(ctx.BlockTime().Unix())})) < 0 { //nolint:gosec
return 0, sdkerrors.ErrInsufficientFee
}
}
gp := txData.EffectiveGasPrice(utils.Big0)
priority := sdk.NewDecFromBigInt(gp).Quo(s.evmKeeper.GetPriorityNormalizer(ctx)).TruncateInt().BigInt()
if priority.Cmp(big.NewInt(antedecorators.MaxPriority)) > 0 {
priority = big.NewInt(antedecorators.MaxPriority)
}
return priority.Int64(), nil
}
func (s *SeiTxPrioritizer) getEvmBaseFee(ctx sdk.Context) *big.Int {
const (
pacific1 = "pacific-1"
historicalBlockHeight = 114945913
doneHeightName = "6.2.0"
)
if ctx.ChainID() == pacific1 {
height := ctx.BlockHeight()
if height < historicalBlockHeight {
return s.evmKeeper.GetBaseFeePerGas(ctx).TruncateInt().BigInt()
}
doneHeight := s.upgradeKeeper.GetDoneHeight(
ctx.WithGasMeter(sdk.NewInfiniteGasMeter(1, 1)), doneHeightName)
if height < doneHeight {
return s.evmKeeper.GetCurrBaseFeePerGas(ctx).TruncateInt().BigInt()
}
}
return s.evmKeeper.GetNextBaseFeePerGas(ctx).TruncateInt().BigInt()
}
func (s *SeiTxPrioritizer) getCosmosTxPriority(ctx sdk.Context, feeTx sdk.FeeTx) (int64, error) {
if isOracleTx(feeTx) {
return antedecorators.OraclePriority, nil
}
gas := feeTx.GetGas()
if gas <= 0 {
return 0, nil
}
var igas int64
if gas > math.MaxInt64 {
igas = math.MaxInt64
} else {
igas = int64(gas) //nolint:gosec
}
feeParams := s.paramsKeeper.GetFeesParams(ctx)
allowedDenoms := feeParams.GetAllowedFeeDenoms()
denoms := make([]string, 0, len(allowedDenoms)+1)
denoms = append(denoms, sdk.DefaultBondDenom)
denoms = append(denoms, allowedDenoms...)
feeCoins := feeTx.GetFee().NonZeroAmountsOf(denoms)
priority := cosmosante.GetTxPriority(feeCoins, igas)
return min(antedecorators.MaxPriority, priority), nil
}
func isOracleTx(tx sdk.FeeTx) bool {
if len(tx.GetMsgs()) == 0 {
return false
}
for _, msg := range tx.GetMsgs() {
switch msg.(type) {
case *oracletypes.MsgAggregateExchangeRateVote:
continue
default:
return false
}
}
return true
}