diff --git a/app/app.go b/app/app.go index 31f6c31ad5..6f441fc9d0 100644 --- a/app/app.go +++ b/app/app.go @@ -394,6 +394,8 @@ type App struct { wsServerStartSignal chan struct{} httpServerStartSignalSent bool wsServerStartSignalSent bool + + txPrioritizer sdk.TxPrioritizer } type AppOption func(*App) @@ -985,6 +987,8 @@ func New( app.RegisterDeliverTxHook(app.AddCosmosEventsToEVMReceiptIfApplicable) + app.txPrioritizer = NewSeiTxPrioritizer(logger, &app.EvmKeeper, &app.UpgradeKeeper, &app.ParamsKeeper).GetTxPriorityHint + app.SetTxPrioritizer(app.txPrioritizer) return app } diff --git a/app/prioritizer.go b/app/prioritizer.go new file mode 100644 index 0000000000..72e2388f58 --- /dev/null +++ b/app/prioritizer.go @@ -0,0 +1,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 +} diff --git a/app/prioritizer_test.go b/app/prioritizer_test.go new file mode 100644 index 0000000000..0e9d86cee4 --- /dev/null +++ b/app/prioritizer_test.go @@ -0,0 +1,144 @@ +package app_test + +import ( + "testing" + + cosmostypes "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" + xparamtypes "github.com/cosmos/cosmos-sdk/x/params/types" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/tendermint/tendermint/libs/log" + + "github.com/sei-protocol/sei-chain/app" + "github.com/sei-protocol/sei-chain/app/antedecorators" + "github.com/sei-protocol/sei-chain/app/apptesting" + oracletypes "github.com/sei-protocol/sei-chain/x/oracle/types" +) + +type PrioritizerTestSuite struct { + apptesting.KeeperTestHelper + prioritizer *app.SeiTxPrioritizer +} + +func TestPrioritizerTestSuite(t *testing.T) { + suite.Run(t, new(PrioritizerTestSuite)) +} + +func (s *PrioritizerTestSuite) SetupTest() { + s.KeeperTestHelper.Setup() + logger, err := log.NewDefaultLogger(log.LogFormatPlain, "info") + require.NoError(s.T(), err) + s.prioritizer = app.NewSeiTxPrioritizer(logger, &s.App.EvmKeeper, &s.App.UpgradeKeeper, &s.App.ParamsKeeper) +} + +var ( + _ sdk.FeeTx = (*mockFeeTx)(nil) + _ sdk.Tx = (*mockTx)(nil) +) + +type mockFeeTx struct { + sdk.Tx + fees sdk.Coins + gas uint64 + msgs []sdk.Msg +} + +func (tx *mockFeeTx) FeePayer() sdk.AccAddress { return nil } +func (tx *mockFeeTx) FeeGranter() sdk.AccAddress { return nil } +func (tx *mockFeeTx) GetFee() sdk.Coins { return tx.fees } +func (tx *mockFeeTx) GetGas() uint64 { return tx.gas } +func (tx *mockFeeTx) GetMsgs() []sdk.Msg { return tx.msgs } + +type mockTx struct { + msgs []sdk.Msg + gasEstimate uint64 +} + +func (tx *mockTx) GetGasEstimate() uint64 { return tx.gasEstimate } +func (tx *mockTx) GetMsgs() []sdk.Msg { return tx.msgs } +func (*mockTx) ValidateBasic() error { return nil } +func (*mockTx) GetSigners() []sdk.AccAddress { return nil } + +func (s *PrioritizerTestSuite) TestGetTxPriority() { + var ( + zeroValueTx = func(*PrioritizerTestSuite) sdk.Tx { return &mockTx{} } + zeroValueFeeTx = func(*PrioritizerTestSuite) sdk.Tx { return &mockFeeTx{} } + zeroGasFeeTx = func(*PrioritizerTestSuite) sdk.Tx { + return &mockFeeTx{ + gas: 0, + } + } + oracleVoteTx = func(s *PrioritizerTestSuite) sdk.Tx { + return &mockFeeTx{ + msgs: []sdk.Msg{&oracletypes.MsgAggregateExchangeRateVote{}}, + } + } + ) + + for _, tc := range []struct { + name string + givenTx func(s *PrioritizerTestSuite) sdk.Tx + givenContext func(sdk.Context) sdk.Context + wantPriority int64 + wantErr string + expectedErrAs interface{} + }{ + { + name: "unexpected Tx type is error", + givenTx: zeroValueTx, + wantErr: "must either be EVM or Fee", + }, + { + name: "context with priority present is context priority", + givenTx: zeroValueFeeTx, + givenContext: func(ctx sdk.Context) sdk.Context { + return ctx.WithPriority(123) + }, + wantPriority: 123, + }, + { + name: "oracle Tx type is oracle priority", + givenTx: oracleVoteTx, + wantPriority: antedecorators.OraclePriority, + }, + { + name: "zero gas FeeTx is zero priority", + givenTx: zeroGasFeeTx, + wantPriority: 0, + }, + { + name: "cosmos tx with denominators is has priority of smallest demon multiplier", + givenTx: func(s *PrioritizerTestSuite) sdk.Tx { + s.App.ParamsKeeper.SetFeesParams(s.Ctx, xparamtypes.FeesParams{ + AllowedFeeDenoms: []string{"fish", "lobster"}, + }) + return &mockFeeTx{ + gas: 4_200, + fees: []sdk.Coin{ + {Denom: "fish", Amount: sdk.NewInt(230_000_000)}, + {Denom: "lobster", Amount: sdk.NewInt(290_000_000_000)}, + }, + } + }, + wantPriority: cosmostypes.NewInt(230_000_000).QuoRaw(4_200).Int64(), + }, + } { + s.T().Run(tc.name, func(t *testing.T) { + s.SetupTest() + tx := tc.givenTx(s) + ctx := s.Ctx + if tc.givenContext != nil { + ctx = tc.givenContext(ctx) + } + gotPriority, gotErr := s.prioritizer.GetTxPriorityHint(ctx, tx) + if tc.wantErr != "" { + require.Error(t, gotErr) + require.ErrorContains(t, gotErr, tc.wantErr) + } else { + require.NoError(t, gotErr) + require.Equal(t, tc.wantPriority, gotPriority) + } + }) + } +} diff --git a/go.mod b/go.mod index 1b24e876f7..329bc201a6 100644 --- a/go.mod +++ b/go.mod @@ -360,7 +360,7 @@ replace ( github.com/sei-protocol/sei-db => github.com/sei-protocol/sei-db v0.0.51 // Latest goleveldb is broken, we have to stick to this version github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.6.4 + github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.6.5 github.com/tendermint/tm-db => github.com/sei-protocol/tm-db v0.0.4 golang.org/x/crypto => golang.org/x/crypto v0.31.0 google.golang.org/grpc => google.golang.org/grpc v1.33.2 diff --git a/go.sum b/go.sum index 4dfd0a0a57..9b8151305d 100644 --- a/go.sum +++ b/go.sum @@ -1997,8 +1997,8 @@ github.com/sei-protocol/sei-iavl v0.2.0 h1:OisPjXiDT+oe+aeckzDEFgkZCYuUjHgs/PP8D github.com/sei-protocol/sei-iavl v0.2.0/go.mod h1:qRf8QYUPfrAO7K6VDB2B2l/N7K5L76OorioGBcJBIbw= github.com/sei-protocol/sei-ibc-go/v3 v3.3.6 h1:HHWvrslBpkXBHUFs+azwl36NuFEJyMo6huvsNPG854c= github.com/sei-protocol/sei-ibc-go/v3 v3.3.6/go.mod h1:VwB/vWu4ysT5DN2aF78d17LYmx3omSAdq6gpKvM7XRA= -github.com/sei-protocol/sei-tendermint v0.6.4 h1:lMgzloTLo3ixNBHV+ETkUeh13fwOPqqdXZW3pZxQ8Bs= -github.com/sei-protocol/sei-tendermint v0.6.4/go.mod h1:SSZv0P1NBP/4uB3gZr5XJIan3ks3Ui8FJJzIap4r6uc= +github.com/sei-protocol/sei-tendermint v0.6.5 h1:6jJOw330mcK8Xu8PYiChByHpsl+yGujsl1WZXDW0G4Q= +github.com/sei-protocol/sei-tendermint v0.6.5/go.mod h1:SSZv0P1NBP/4uB3gZr5XJIan3ks3Ui8FJJzIap4r6uc= github.com/sei-protocol/sei-tm-db v0.0.5 h1:3WONKdSXEqdZZeLuWYfK5hP37TJpfaUa13vAyAlvaQY= github.com/sei-protocol/sei-tm-db v0.0.5/go.mod h1:Cpa6rGyczgthq7/0pI31jys2Fw0Nfrc+/jKdP1prVqY= github.com/sei-protocol/tm-db v0.0.4 h1:7Y4EU62Xzzg6wKAHEotm7SXQR0aPLcGhKHkh3qd0tnk= diff --git a/sei-cosmos/baseapp/abci.go b/sei-cosmos/baseapp/abci.go index f45a904765..0486df9cfd 100644 --- a/sei-cosmos/baseapp/abci.go +++ b/sei-cosmos/baseapp/abci.go @@ -1214,3 +1214,47 @@ func (app *BaseApp) LoadLatest(ctx context.Context, req *abci.RequestLoadLatest) app.initialHeight = app.cms.LastCommitID().Version return &abci.ResponseLoadLatest{}, nil } + +func (app *BaseApp) GetTxPriorityHint(_ context.Context, req *abci.RequestGetTxPriorityHint) (_resp *abci.ResponseGetTxPriorityHint, _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. + app.logger.Error("tx prioritizer base app panicked. Falling back on no priority", "error", r) + if _err == nil { + _resp = &abci.ResponseGetTxPriorityHint{Priority: 0} + } + // Do not overwrite an existing error if one was already set to keep panics a + // non-event at this stage but safeguard against them. + } + }() + + defer telemetry.MeasureSince(time.Now(), "abci", "get_tx_priority_hint") + + tx, err := app.txDecoder(req.Tx) + if err != nil { + return nil, err + } + if tx == nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "nil tx") + } + + // TODO: should we bother validating the messages here? + msgs := tx.GetMsgs() + if err := validateBasicTxMsgs(msgs); err != nil { + return nil, err + } + var priority int64 + if app.txPrioritizer != nil { + sdkCtx := app.getContextForTx(runTxModeCheck, req.Tx) + priority, err = app.txPrioritizer(sdkCtx, tx) + if err != nil { + return nil, sdkerrors.Wrap(sdkerrors.ErrLogic, fmt.Sprintf("error getting tx priority: %s", err.Error())) + } + } + return &abci.ResponseGetTxPriorityHint{ + Priority: priority, + }, nil +} diff --git a/sei-cosmos/baseapp/baseapp.go b/sei-cosmos/baseapp/baseapp.go index c0a6798591..fad6e2f4c8 100644 --- a/sei-cosmos/baseapp/baseapp.go +++ b/sei-cosmos/baseapp/baseapp.go @@ -97,6 +97,7 @@ type BaseApp struct { //nolint: maligned preCommitHandler sdk.PreCommitHandler closeHandler sdk.CloseHandler inplaceTestnetInitializer sdk.InplaceTestnetInitializer + txPrioritizer sdk.TxPrioritizer appStore baseappVersions diff --git a/sei-cosmos/baseapp/options.go b/sei-cosmos/baseapp/options.go index 4d2747fd3d..988a712987 100644 --- a/sei-cosmos/baseapp/options.go +++ b/sei-cosmos/baseapp/options.go @@ -379,3 +379,9 @@ func (app *BaseApp) SetQueryMultiStore(ms sdk.CommitMultiStore) { func (app *BaseApp) SetMigrationHeight(height int64) { app.migrationHeight = height } + +// SetTxPrioritizer sets the transaction prioritizer for the BaseApp. If unset, +// calls to GetTxPriorityHint for all valid transactions will return 0. +func (app *BaseApp) SetTxPrioritizer(prioritizer sdk.TxPrioritizer) { + app.txPrioritizer = prioritizer +} diff --git a/sei-cosmos/go.mod b/sei-cosmos/go.mod index dd43018e2f..8307ff6436 100644 --- a/sei-cosmos/go.mod +++ b/sei-cosmos/go.mod @@ -134,6 +134,7 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20210609091139-0a56a4bca00b // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect @@ -196,7 +197,7 @@ replace ( github.com/sei-protocol/sei-db => github.com/sei-protocol/sei-db v0.0.51 // Latest goleveldb is broken, we have to stick to this version github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.6.0 + github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.6.5 // latest grpc doesn't work with with our modified proto compiler, so we need to enforce // the following version across all dependencies. google.golang.org/grpc => google.golang.org/grpc v1.33.2 diff --git a/sei-cosmos/go.sum b/sei-cosmos/go.sum index 4e499bef29..3f4fe2b33e 100644 --- a/sei-cosmos/go.sum +++ b/sei-cosmos/go.sum @@ -843,6 +843,8 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= @@ -966,8 +968,8 @@ github.com/sei-protocol/sei-db v0.0.51 h1:jK6Ps+jDbGdWIPZttaWk7VIsq8aLWWlkTp9axI github.com/sei-protocol/sei-db v0.0.51/go.mod h1:m5g7p0QeAS3dNJHIl28zQpzOgxQmvYqPb7t4hwgIOCA= github.com/sei-protocol/sei-iavl v0.1.9 h1:y4mVYftxLNRs6533zl7N0/Ch+CzRQc04JDfHolIxgBE= github.com/sei-protocol/sei-iavl v0.1.9/go.mod h1:7PfkEVT5dcoQE+s/9KWdoXJ8VVVP1QpYYPLdxlkSXFk= -github.com/sei-protocol/sei-tendermint v0.6.0 h1:H/qN54IUUnqMKbqL9rVt61ViIgPzpHixtKd43LV+C6I= -github.com/sei-protocol/sei-tendermint v0.6.0/go.mod h1:hLgRpS2d6VM8XzlhEtFeosCYkpuviU2ztqmOairIivc= +github.com/sei-protocol/sei-tendermint v0.6.5 h1:6jJOw330mcK8Xu8PYiChByHpsl+yGujsl1WZXDW0G4Q= +github.com/sei-protocol/sei-tendermint v0.6.5/go.mod h1:SSZv0P1NBP/4uB3gZr5XJIan3ks3Ui8FJJzIap4r6uc= github.com/sei-protocol/sei-tm-db v0.0.5 h1:3WONKdSXEqdZZeLuWYfK5hP37TJpfaUa13vAyAlvaQY= github.com/sei-protocol/sei-tm-db v0.0.5/go.mod h1:Cpa6rGyczgthq7/0pI31jys2Fw0Nfrc+/jKdP1prVqY= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= diff --git a/sei-cosmos/types/abci.go b/sei-cosmos/types/abci.go index 6921d45eac..3d056cd2a3 100644 --- a/sei-cosmos/types/abci.go +++ b/sei-cosmos/types/abci.go @@ -37,3 +37,5 @@ type LoadVersionHandler func() error type PreCommitHandler func(ctx Context) error type CloseHandler func() error type InplaceTestnetInitializer func(cryptotypes.PubKey) error + +type TxPrioritizer func(Context, Tx) (int64, error) diff --git a/sei-cosmos/types/context.go b/sei-cosmos/types/context.go index 930994f2e6..d0bb009bea 100644 --- a/sei-cosmos/types/context.go +++ b/sei-cosmos/types/context.go @@ -46,6 +46,7 @@ type Context struct { eventManager *EventManager evmEventManager *EVMEventManager priority int64 // The tx priority, only relevant in CheckTx + hasPriority bool // Whether the tx has a priority set pendingTxChecker abci.PendingTxChecker // Checker for pending transaction, only relevant in CheckTx checkTxCallback func(Context, error) // callback to make at the end of CheckTx. Input param is the error (nil-able) of `runMsgs` deliverTxCallback func(Context) // callback to make at the end of DeliverTx. @@ -241,12 +242,19 @@ func (c Context) StoreTracer() gaskv.IStoreTracer { return c.storeTracer } -// WithEventManager returns a Context with an updated tx priority +// WithPriority returns a Context with an updated tx priority. func (c Context) WithPriority(p int64) Context { c.priority = p + c.hasPriority = true return c } +// HasPriority returns true iff the priority is set for this Context even if it +// was set to zero. +func (c Context) HasPriority() bool { + return c.hasPriority +} + // HeaderHash returns a copy of the header hash obtained during abci.RequestBeginBlock func (c Context) HeaderHash() tmbytes.HexBytes { hash := make([]byte, len(c.headerHash)) diff --git a/sei-cosmos/types/context_test.go b/sei-cosmos/types/context_test.go index 1dc062ac4e..ad6156f895 100644 --- a/sei-cosmos/types/context_test.go +++ b/sei-cosmos/types/context_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" abci "github.com/tendermint/tendermint/abci/types" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" @@ -226,3 +227,32 @@ func (s *contextTestSuite) TestUnwrapSDKContext() { ctx = context.Background() s.Require().Panics(func() { types.UnwrapSDKContext(ctx) }) } + +func TestContext_Priority(t *testing.T) { + var ( + requireNoPriority = func(t *testing.T, ctx types.Context) { + require.Zero(t, ctx.Priority()) + require.False(t, ctx.HasPriority()) + } + requirePriority = func(t *testing.T, ctx types.Context, priority int64) { + require.Equal(t, priority, ctx.Priority()) + require.True(t, ctx.HasPriority()) + } + ) + + // Assert that a new context has no priority set and does not have priority. + var subject types.Context + requireNoPriority(t, subject) + + // Assert that setting a priority sets the priority and marks the context as + // having priority set. But does not change the original context. + prioritisedSubject := subject.WithPriority(100) + requirePriority(t, prioritisedSubject, 100) + requireNoPriority(t, subject) + + // Assert that setting priority to 0 updates the priority but still marks the + // context as having priority set. But does not change the original context. + deprioritisedSubject := subject.WithPriority(0) + requirePriority(t, deprioritisedSubject, 0) + requireNoPriority(t, subject) +} diff --git a/sei-wasmd/go.mod b/sei-wasmd/go.mod index fb94647984..a4d709dfec 100644 --- a/sei-wasmd/go.mod +++ b/sei-wasmd/go.mod @@ -40,7 +40,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect - github.com/btcsuite/btcd v0.22.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -78,6 +77,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/flatbuffers v1.12.1 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/orderedcode v0.0.1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect @@ -107,6 +107,7 @@ require ( github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20210609091139-0a56a4bca00b // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect @@ -165,7 +166,7 @@ replace ( // latest grpc doesn't work with with our modified proto compiler, so we need to enforce // the following version across all dependencies. github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 - github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.5.9 + github.com/tendermint/tendermint => github.com/sei-protocol/sei-tendermint v0.6.5 github.com/tendermint/tm-db => github.com/sei-protocol/tm-db v0.0.4 google.golang.org/grpc => google.golang.org/grpc v1.33.2 ) diff --git a/sei-wasmd/go.sum b/sei-wasmd/go.sum index 175435626f..b51d890443 100644 --- a/sei-wasmd/go.sum +++ b/sei-wasmd/go.sum @@ -114,7 +114,6 @@ github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dm github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= -github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= @@ -681,6 +680,8 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -776,8 +777,8 @@ github.com/sei-protocol/sei-iavl v0.1.9 h1:y4mVYftxLNRs6533zl7N0/Ch+CzRQc04JDfHo github.com/sei-protocol/sei-iavl v0.1.9/go.mod h1:7PfkEVT5dcoQE+s/9KWdoXJ8VVVP1QpYYPLdxlkSXFk= github.com/sei-protocol/sei-ibc-go/v3 v3.3.6 h1:HHWvrslBpkXBHUFs+azwl36NuFEJyMo6huvsNPG854c= github.com/sei-protocol/sei-ibc-go/v3 v3.3.6/go.mod h1:VwB/vWu4ysT5DN2aF78d17LYmx3omSAdq6gpKvM7XRA= -github.com/sei-protocol/sei-tendermint v0.5.9 h1:GLAuHGuCd1eDLfoU4uFMbqQo7z47XSV66Ln4/V8hsOY= -github.com/sei-protocol/sei-tendermint v0.5.9/go.mod h1:ip8M5kkQf8JC05np1xd4KR33ttJoseJi28Ea28ui85E= +github.com/sei-protocol/sei-tendermint v0.6.5 h1:6jJOw330mcK8Xu8PYiChByHpsl+yGujsl1WZXDW0G4Q= +github.com/sei-protocol/sei-tendermint v0.6.5/go.mod h1:SSZv0P1NBP/4uB3gZr5XJIan3ks3Ui8FJJzIap4r6uc= github.com/sei-protocol/sei-tm-db v0.0.5 h1:3WONKdSXEqdZZeLuWYfK5hP37TJpfaUa13vAyAlvaQY= github.com/sei-protocol/sei-tm-db v0.0.5/go.mod h1:Cpa6rGyczgthq7/0pI31jys2Fw0Nfrc+/jKdP1prVqY= github.com/sei-protocol/tm-db v0.0.4 h1:7Y4EU62Xzzg6wKAHEotm7SXQR0aPLcGhKHkh3qd0tnk= diff --git a/x/evm/ante/preprocess.go b/x/evm/ante/preprocess.go index ebf1a678cf..0a2d97e088 100644 --- a/x/evm/ante/preprocess.go +++ b/x/evm/ante/preprocess.go @@ -120,6 +120,12 @@ func (p *EVMPreprocessDecorator) IsAccountBalancePositive(ctx sdk.Context, seiAd // stateless func Preprocess(ctx sdk.Context, msgEVMTransaction *evmtypes.MsgEVMTransaction, chainID *big.Int, isBlockTest bool) error { + return PreprocessUnpacked(ctx, msgEVMTransaction, chainID, isBlockTest, nil) +} + +// PreprocessUnpacked does the same thing as Preprocess but accepts already unpacked txData to save computation +// if txData is nil, it will unpack from msgEVMTransaction.Data. +func PreprocessUnpacked(ctx sdk.Context, msgEVMTransaction *evmtypes.MsgEVMTransaction, chainID *big.Int, isBlockTest bool, txData ethtx.TxData) error { if msgEVMTransaction.Derived != nil { if msgEVMTransaction.Derived.PubKey == nil { // this means the message has `Derived` set from the outside, in which case we should reject @@ -128,9 +134,14 @@ func Preprocess(ctx sdk.Context, msgEVMTransaction *evmtypes.MsgEVMTransaction, // already preprocessed return nil } - txData, err := evmtypes.UnpackTxData(msgEVMTransaction.Data) - if err != nil { - return err + + if txData == nil { + // TxData not passed in, unpack it. + var err error + txData, err = evmtypes.UnpackTxData(msgEVMTransaction.Data) + if err != nil { + return err + } } if atx, ok := txData.(*ethtx.AssociateTx); ok {