feat(server/v2): add min gas price and check with tx fee (#21173)

Co-authored-by: Julien Robert <julien@rbrt.fr>
This commit is contained in:
Akhil Kumar P 2024-08-29 13:16:58 +05:30 committed by GitHub
parent 78de1a2549
commit 81a225e6a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 222 additions and 79 deletions

View File

@ -38,13 +38,14 @@ func AddCommands[T transaction.Tx](
rootCmd *cobra.Command,
newApp AppCreator[T],
logger log.Logger,
serverCfg ServerConfig,
components ...ServerComponent[T],
) error {
if len(components) == 0 {
return errors.New("no components provided")
}
server := NewServer(logger, components...)
server := NewServer(logger, serverCfg, components...)
originalPersistentPreRunE := rootCmd.PersistentPreRunE
rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
// set the default command outputs

View File

@ -7,6 +7,18 @@ import (
"github.com/spf13/viper"
)
// ServerConfig defines configuration for the server component.
type ServerConfig struct {
MinGasPrices string `mapstructure:"minimum-gas-prices" toml:"minimum-gas-prices" comment:"minimum-gas-prices defines the price which a validator is willing to accept for processing a transaction. A transaction's fees must meet the minimum of any denomination specified in this config (e.g. 0.25token1;0.0001token2)."`
}
// DefaultServerConfig returns the default config of server component
func DefaultServerConfig() ServerConfig {
return ServerConfig{
MinGasPrices: "0stake",
}
}
// ReadConfig returns a viper instance of the config file
func ReadConfig(configPath string) (*viper.Viper, error) {
v := viper.New()

View File

@ -1,6 +1,16 @@
// Package serverv2 defines constants for server configuration flags and output formats.
package serverv2
import "fmt"
// start flags are prefixed with the server name
// this allows viper to properly bind the flags
func prefix(f string) string {
return fmt.Sprintf("%s.%s", serverName, f)
}
var FlagMinGasPrices = prefix("minimum-gas-prices")
const (
// FlagHome specifies the home directory flag.
FlagHome = "home"

View File

@ -56,25 +56,32 @@ type CLIConfig struct {
Txs []*cobra.Command
}
const (
serverName = "server"
)
var _ ServerComponent[transaction.Tx] = (*Server[transaction.Tx])(nil)
type Server[T transaction.Tx] struct {
logger log.Logger
components []ServerComponent[T]
config ServerConfig
}
func NewServer[T transaction.Tx](
logger log.Logger,
config ServerConfig,
components ...ServerComponent[T],
) *Server[T] {
return &Server[T]{
logger: logger,
config: config,
components: components,
}
}
func (s *Server[T]) Name() string {
return "server"
return serverName
}
// Start starts all components concurrently.
@ -151,9 +158,19 @@ func (s *Server[T]) CLICommands() CLIConfig {
return commands
}
// Config returns config of the server component
func (s *Server[T]) Config() ServerConfig {
return s.config
}
// Configs returns all configs of all server components.
func (s *Server[T]) Configs() map[string]any {
cfgs := make(map[string]any)
// add server component config
cfgs[s.Name()] = s.config
// add other components' config
for _, mod := range s.components {
if configmod, ok := mod.(HasConfig); ok {
cfg := configmod.Config()
@ -164,9 +181,22 @@ func (s *Server[T]) Configs() map[string]any {
return cfgs
}
func (s *Server[T]) StartCmdFlags() *pflag.FlagSet {
flags := pflag.NewFlagSet(s.Name(), pflag.ExitOnError)
flags.String(FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)")
return flags
}
// Init initializes all server components with the provided application, configuration, and logger.
// It returns an error if any component fails to initialize.
func (s *Server[T]) Init(appI AppI[T], v *viper.Viper, logger log.Logger) error {
cfg := s.config
if v != nil {
if err := UnmarshalSubConfig(v, s.Name(), &cfg); err != nil {
return fmt.Errorf("failed to unmarshal config: %w", err)
}
}
var components []ServerComponent[T]
for _, mod := range s.components {
mod := mod
@ -177,6 +207,7 @@ func (s *Server[T]) Init(appI AppI[T], v *viper.Viper, logger log.Logger) error
components = append(components, mod)
}
s.config = cfg
s.components = components
return nil
}
@ -217,6 +248,11 @@ func (s *Server[T]) WriteConfig(configPath string) error {
// StartFlags returns all flags of all server components.
func (s *Server[T]) StartFlags() []*pflag.FlagSet {
flags := []*pflag.FlagSet{}
// add server component flags
flags = append(flags, s.StartCmdFlags())
// add other components' start cmd flags
for _, mod := range s.components {
if startmod, ok := mod.(HasStartFlags); ok {
flags = append(flags, startmod.StartCmdFlags())

View File

@ -65,6 +65,7 @@ func TestServer(t *testing.T) {
server := serverv2.NewServer(
logger,
serverv2.DefaultServerConfig(),
grpcServer,
mockServer,
)

View File

@ -10,6 +10,10 @@ max-recv-msg-size = 10485760
# The default value is math.MaxInt32.
max-send-msg-size = 2147483647
[server]
# minimum-gas-prices defines the price which a validator is willing to accept for processing a transaction. A transaction's fees must meet the minimum of any denomination specified in this config (e.g. 0.25token1;0.0001token2).
minimum-gas-prices = '0stake'
[store]
# The type of database for application and snapshots databases.
app-db-backend = 'goleveldb'

View File

@ -75,6 +75,7 @@ func initRootCmd[T transaction.Tx](
rootCmd,
newApp,
logger,
initServerConfig(),
cometbft.New(&genericTxDecoder[T]{txConfig}, cometbft.DefaultServerOptions[T]()),
grpc.New[T](),
store.New[T](newApp),

View File

@ -3,6 +3,8 @@ package cmd
import (
"strings"
serverv2 "cosmossdk.io/server/v2"
clientconfig "github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
)
@ -49,3 +51,20 @@ gas-adjustment = {{ .GasConfig.GasAdjustment }}
return customClientConfigTemplate, customClientConfig
}
// Allow the chain developer to overwrite the server default app toml config.
func initServerConfig() serverv2.ServerConfig {
serverCfg := serverv2.DefaultServerConfig()
// The server's default minimum gas price is set to "0stake" inside
// app.toml. However, the chain developer can set a default app.toml value for their
// validators here. Please update value based on chain denom.
//
// In summary:
// - if you set serverCfg.MinGasPrices value, validators CAN tweak their
// own app.toml to override, or use this default value.
//
// In simapp, we set the min gas prices to 0.
serverCfg.MinGasPrices = "0stake"
return serverCfg
}

View File

@ -41,7 +41,6 @@ import (
)
var (
flagMinGasPrices = "minimum-gas-prices"
flagNodeDirPrefix = "node-dir-prefix"
flagNumValidators = "validator-count"
flagOutputDir = "output-dir"
@ -72,7 +71,7 @@ func addTestnetFlagsToCmd(cmd *cobra.Command) {
cmd.Flags().IntP(flagNumValidators, "n", 4, "Number of validators to initialize the testnet with")
cmd.Flags().StringP(flagOutputDir, "o", "./.testnets", "Directory to store initialization data for the testnet")
cmd.Flags().String(flags.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created")
cmd.Flags().String(flagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)")
cmd.Flags().String(serverv2.FlagMinGasPrices, fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom), "Minimum gas prices to accept for transactions; All fees in a tx must meet this minimum (e.g. 0.01photino,0.001stake)")
cmd.Flags().String(flags.FlagKeyType, string(hd.Secp256k1Type), "Key signing algorithm to generate keys for")
// support old flags name for backwards compatibility
@ -129,7 +128,7 @@ Example:
args.outputDir, _ = cmd.Flags().GetString(flagOutputDir)
args.keyringBackend, _ = cmd.Flags().GetString(flags.FlagKeyringBackend)
args.chainID, _ = cmd.Flags().GetString(flags.FlagChainID)
args.minGasPrices, _ = cmd.Flags().GetString(flagMinGasPrices)
args.minGasPrices, _ = cmd.Flags().GetString(serverv2.FlagMinGasPrices)
args.nodeDirPrefix, _ = cmd.Flags().GetString(flagNodeDirPrefix)
args.nodeDaemonHome, _ = cmd.Flags().GetString(flagNodeDaemonHome)
args.startingIPAddress, _ = cmd.Flags().GetString(flagStartingIPAddress)
@ -337,6 +336,9 @@ func initTestnetFiles[T transaction.Tx](
return err
}
serverCfg := serverv2.DefaultServerConfig()
serverCfg.MinGasPrices = args.minGasPrices
// Write server config
cometServer := cometbft.New[T](
&genericTxDecoder[T]{clientCtx.TxConfig},
@ -345,7 +347,7 @@ func initTestnetFiles[T transaction.Tx](
)
storeServer := store.New[T](newApp)
grpcServer := grpc.New[T](grpc.OverwriteDefaultConfig(grpcConfig))
server := serverv2.NewServer(log.NewNopLogger(), cometServer, grpcServer, storeServer)
server := serverv2.NewServer(log.NewNopLogger(), serverCfg, cometServer, grpcServer, storeServer)
err = server.WriteConfig(filepath.Join(nodeDir, "config"))
if err != nil {
return err

View File

@ -10,7 +10,6 @@ import (
)
func TestStakeUnstake(t *testing.T) {
t.Skip("The fee deduction is not yet implemented in v2")
// Scenario:
// delegate tokens to validator
// undelegate some tokens

View File

@ -28,6 +28,10 @@ max-recv-msg-size = 10485760
# The default value is math.MaxInt32.
max-send-msg-size = 2147483647
[server]
# minimum-gas-prices defines the price which a validator is willing to accept for processing a transaction. A transaction's fees must meet the minimum of any denomination specified in this config (e.g. 0.25token1;0.0001token2).
minimum-gas-prices = '0stake'
[store]
# The type of database for application and snapshots databases.
app-db-backend = 'goleveldb'

View File

@ -39,11 +39,12 @@ type v2KeyChangesMap map[string][]string
// list all the keys which are need to be modified in v2
var v2KeyChanges = v2KeyChangesMap{
"min-retain-blocks": []string{"comet.min-retain-blocks"},
"index-events": []string{"comet.index-events"},
"halt-height": []string{"comet.halt-height"},
"halt-time": []string{"comet.halt-time"},
"app-db-backend": []string{"store.app-db-backend"},
"minimum-gas-prices": []string{"server.minimum-gas-prices"},
"min-retain-blocks": []string{"comet.min-retain-blocks"},
"index-events": []string{"comet.index-events"},
"halt-height": []string{"comet.halt-height"},
"halt-time": []string{"comet.halt-time"},
"app-db-backend": []string{"store.app-db-backend"},
"pruning-keep-recent": []string{
"store.options.ss-pruning-option.keep-recent",
"store.options.sc-pruning-option.keep-recent",

View File

@ -2,8 +2,10 @@ package ante
import (
"bytes"
"context"
"fmt"
"cosmossdk.io/core/event"
"cosmossdk.io/core/transaction"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/auth/types"
@ -14,7 +16,7 @@ import (
// TxFeeChecker checks if the provided fee is enough and returns the effective fee and tx priority.
// The effective fee should be deducted later, and the priority should be returned in the ABCI response.
type TxFeeChecker func(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error)
type TxFeeChecker func(ctx context.Context, tx transaction.Tx) (sdk.Coins, int64, error)
// DeductFeeDecorator deducts fees from the fee payer. The fee payer is the fee granter (if specified) or first signer of the tx.
// If the fee payer does not have the funds to pay for the fees, return an InsufficientFunds error.
@ -25,60 +27,78 @@ type DeductFeeDecorator struct {
bankKeeper types.BankKeeper
feegrantKeeper FeegrantKeeper
txFeeChecker TxFeeChecker
minGasPrices sdk.DecCoins
}
func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper, fk FeegrantKeeper, tfc TxFeeChecker) DeductFeeDecorator {
if tfc == nil {
tfc = checkTxFeeWithValidatorMinGasPrices
}
return DeductFeeDecorator{
func NewDeductFeeDecorator(ak AccountKeeper, bk types.BankKeeper, fk FeegrantKeeper, tfc TxFeeChecker) *DeductFeeDecorator {
dfd := &DeductFeeDecorator{
accountKeeper: ak,
bankKeeper: bk,
feegrantKeeper: fk,
txFeeChecker: tfc,
minGasPrices: sdk.NewDecCoins(),
}
if tfc == nil {
dfd.txFeeChecker = dfd.checkTxFeeWithValidatorMinGasPrices
}
return dfd
}
func (dfd DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ bool, next sdk.AnteHandler) (sdk.Context, error) {
// SetMinGasPrices sets the minimum-gas-prices value in the state of DeductFeeDecorator
func (dfd *DeductFeeDecorator) SetMinGasPrices(minGasPrices sdk.DecCoins) {
dfd.minGasPrices = minGasPrices
}
// AnteHandle implements an AnteHandler decorator for the DeductFeeDecorator
func (dfd *DeductFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, _ bool, next sdk.AnteHandler) (sdk.Context, error) {
dfd.minGasPrices = ctx.MinGasPrices()
txPriority, err := dfd.innerValidateTx(ctx, tx)
if err != nil {
return ctx, err
}
newCtx := ctx.WithPriority(txPriority)
return next(newCtx, tx, false)
}
func (dfd *DeductFeeDecorator) innerValidateTx(ctx context.Context, tx transaction.Tx) (priority int64, err error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must implement the FeeTx interface")
return 0, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must implement the FeeTx interface")
}
txService := dfd.accountKeeper.GetEnvironment().TransactionService
execMode := txService.ExecMode(ctx)
if execMode != transaction.ExecModeSimulate && ctx.BlockHeight() > 0 && feeTx.GetGas() == 0 {
return ctx, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas")
}
execMode := dfd.accountKeeper.GetEnvironment().TransactionService.ExecMode(ctx)
headerInfo := dfd.accountKeeper.GetEnvironment().HeaderService.HeaderInfo(ctx)
var (
priority int64
err error
)
if execMode != transaction.ExecModeSimulate && headerInfo.Height > 0 && feeTx.GetGas() == 0 {
return 0, errorsmod.Wrap(sdkerrors.ErrInvalidGasLimit, "must provide positive gas")
}
fee := feeTx.GetFee()
if execMode != transaction.ExecModeSimulate {
fee, priority, err = dfd.txFeeChecker(ctx, tx)
if err != nil {
return ctx, err
return 0, err
}
}
if err := dfd.checkDeductFee(ctx, tx, fee); err != nil {
return ctx, err
if err := dfd.checkDeductFee(ctx, feeTx, fee); err != nil {
return 0, err
}
newCtx := ctx.WithPriority(priority)
return next(newCtx, tx, false)
return priority, nil
}
func (dfd DeductFeeDecorator) checkDeductFee(ctx sdk.Context, sdkTx sdk.Tx, fee sdk.Coins) error {
feeTx, ok := sdkTx.(sdk.FeeTx)
if !ok {
return errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must implement the FeeTx interface")
}
// ValidateTx implements an TxValidator for DeductFeeDecorator
// Note: This method is applicable only for transactions that implement the sdk.FeeTx interface.
func (dfd *DeductFeeDecorator) ValidateTx(ctx context.Context, tx transaction.Tx) error {
_, err := dfd.innerValidateTx(ctx, tx)
return err
}
func (dfd *DeductFeeDecorator) checkDeductFee(ctx context.Context, feeTx sdk.FeeTx, fee sdk.Coins) error {
addr := dfd.accountKeeper.GetModuleAddress(types.FeeCollectorName)
if len(addr) == 0 {
return fmt.Errorf("fee collector module account (%s) has not been set", types.FeeCollectorName)
@ -91,12 +111,10 @@ func (dfd DeductFeeDecorator) checkDeductFee(ctx sdk.Context, sdkTx sdk.Tx, fee
// if feegranter set, deduct fee from feegranter account.
// this works only when feegrant is enabled.
if feeGranter != nil {
feeGranterAddr := sdk.AccAddress(feeGranter)
if dfd.feegrantKeeper == nil {
return sdkerrors.ErrInvalidRequest.Wrap("fee grants are not enabled")
} else if !bytes.Equal(feeGranterAddr, feePayer) {
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranterAddr, feePayer, fee, sdkTx.GetMsgs())
} else if !bytes.Equal(feeGranter, feePayer) {
err := dfd.feegrantKeeper.UseGrantedFees(ctx, feeGranter, feePayer, fee, feeTx.GetMsgs())
if err != nil {
granterAddr, acErr := dfd.accountKeeper.AddressCodec().BytesToString(feeGranter)
if acErr != nil {
@ -109,38 +127,34 @@ func (dfd DeductFeeDecorator) checkDeductFee(ctx sdk.Context, sdkTx sdk.Tx, fee
return errorsmod.Wrapf(err, "%s does not allow to pay fees for %s", granterAddr, payerAddr)
}
}
deductFeesFrom = feeGranterAddr
deductFeesFrom = feeGranter
}
// deduct the fees
if !fee.IsZero() {
err := DeductFees(dfd.bankKeeper, ctx, deductFeesFrom, fee)
if err != nil {
if err := DeductFees(dfd.bankKeeper, ctx, deductFeesFrom, fee); err != nil {
return err
}
}
events := sdk.Events{
sdk.NewEvent(
sdk.EventTypeTx,
sdk.NewAttribute(sdk.AttributeKeyFee, fee.String()),
sdk.NewAttribute(sdk.AttributeKeyFeePayer, sdk.AccAddress(deductFeesFrom).String()),
),
if err := dfd.accountKeeper.GetEnvironment().EventService.EventManager(ctx).EmitKV(
sdk.EventTypeTx,
event.NewAttribute(sdk.AttributeKeyFee, fee.String()),
event.NewAttribute(sdk.AttributeKeyFeePayer, sdk.AccAddress(deductFeesFrom).String()),
); err != nil {
return err
}
ctx.EventManager().EmitEvents(events)
return nil
}
// DeductFees deducts fees from the given account.
func DeductFees(bankKeeper types.BankKeeper, ctx sdk.Context, acc []byte, fees sdk.Coins) error {
func DeductFees(bankKeeper types.BankKeeper, ctx context.Context, acc []byte, fees sdk.Coins) error {
if !fees.IsValid() {
return errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "invalid fee amount: %s", fees)
}
err := bankKeeper.SendCoinsFromAccountToModule(ctx, sdk.AccAddress(acc), types.FeeCollectorName, fees)
if err != nil {
if err := bankKeeper.SendCoinsFromAccountToModule(ctx, acc, types.FeeCollectorName, fees); err != nil {
return fmt.Errorf("failed to deduct fees: %w", err)
}

View File

@ -41,6 +41,11 @@ func TestDeductFeeDecorator_ZeroGas(t *testing.T) {
// Set IsCheckTx to true
s.ctx = s.ctx.WithIsCheckTx(true)
// Set current block height in headerInfo
headerInfo := s.ctx.HeaderInfo()
headerInfo.Height = s.ctx.BlockHeight()
s.ctx = s.ctx.WithHeaderInfo(headerInfo)
_, err = antehandler(s.ctx, tx, false)
require.Error(t, err)

View File

@ -1,8 +1,10 @@
package ante
import (
"context"
"math"
"cosmossdk.io/core/transaction"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
@ -12,7 +14,7 @@ import (
// checkTxFeeWithValidatorMinGasPrices implements the default fee logic, where the minimum price per
// unit of gas is fixed and set by each validator, can the tx priority is computed from the gas price.
func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error) {
func (dfd *DeductFeeDecorator) checkTxFeeWithValidatorMinGasPrices(ctx context.Context, tx transaction.Tx) (sdk.Coins, int64, error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return nil, 0, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
@ -24,8 +26,8 @@ func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins,
// Ensure that the provided fees meet a minimum threshold for the validator,
// if this is a CheckTx. This is only for local mempool purposes, and thus
// is only ran on check tx.
if ctx.ExecMode() == sdk.ExecModeCheck { // NOTE: using environment here breaks the API of fee logic, an alternative must be found for server/v2. ref: https://github.com/cosmos/cosmos-sdk/issues/19640
minGasPrices := ctx.MinGasPrices()
if dfd.accountKeeper.GetEnvironment().TransactionService.ExecMode(ctx) == transaction.ExecModeCheck {
minGasPrices := dfd.minGasPrices
if !minGasPrices.IsZero() {
requiredFees := make(sdk.Coins, len(minGasPrices))

View File

@ -6,6 +6,7 @@ import (
"fmt"
gogoproto "github.com/cosmos/gogoproto/proto"
"github.com/spf13/viper"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
grpcstatus "google.golang.org/grpc/status"
@ -34,6 +35,9 @@ import (
signingtypes "github.com/cosmos/cosmos-sdk/types/tx/signing"
)
// flagMinGasPricesV2 is the flag name for the minimum gas prices in the main server v2 component.
const flagMinGasPricesV2 = "server.minimum-gas-prices"
func init() {
appconfig.RegisterModule(&txconfigv1.Config{},
appconfig.Provide(ProvideModule),
@ -50,7 +54,7 @@ type ModuleInputs struct {
Codec codec.Codec
ProtoFileResolver txsigning.ProtoFileResolver
Environment appmodule.Environment
// BankKeeper is the expected bank keeper to be passed to AnteHandlers
// BankKeeper is the expected bank keeper to be passed to AnteHandlers / Tx Validators
BankKeeper authtypes.BankKeeper `optional:"true"`
MetadataBankKeeper BankKeeper `optional:"true"`
AccountKeeper ante.AccountKeeper `optional:"true"`
@ -58,8 +62,10 @@ type ModuleInputs struct {
AccountAbstractionKeeper ante.AccountAbstractionKeeper `optional:"true"`
CustomSignModeHandlers func() []txsigning.SignModeHandler `optional:"true"`
CustomGetSigners []txsigning.CustomGetSigner `optional:"true"`
UnorderedTxManager *unorderedtx.Manager `optional:"true"`
ExtraTxValidators []appmodule.TxValidator[transaction.Tx] `optional:"true"`
UnorderedTxManager *unorderedtx.Manager `optional:"true"`
TxFeeChecker ante.TxFeeChecker `optional:"true"`
Viper *viper.Viper `optional:"true"` // server v2
}
type ModuleOutputs struct {
@ -107,7 +113,40 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
panic(err)
}
baseAppOption := func(app *baseapp.BaseApp) {
svd := ante.NewSigVerificationDecorator(
in.AccountKeeper,
txConfig.SignModeHandler(),
ante.DefaultSigVerificationGasConsumer,
in.AccountAbstractionKeeper,
)
var (
minGasPrices sdk.DecCoins
feeTxValidator *ante.DeductFeeDecorator
)
if in.AccountKeeper != nil && in.BankKeeper != nil && in.Viper != nil {
minGasPricesStr := in.Viper.GetString(flagMinGasPricesV2)
minGasPrices, err = sdk.ParseDecCoins(minGasPricesStr)
if err != nil {
panic(fmt.Sprintf("invalid minimum gas prices: %v", err))
}
feeTxValidator = ante.NewDeductFeeDecorator(in.AccountKeeper, in.BankKeeper, in.FeeGrantKeeper, in.TxFeeChecker)
feeTxValidator.SetMinGasPrices(minGasPrices) // set min gas price in deduct fee decorator
}
return ModuleOutputs{
Module: NewAppModule(svd, feeTxValidator, in.ExtraTxValidators...),
BaseAppOption: newBaseAppOption(txConfig, in),
TxConfig: txConfig,
TxConfigOptions: txConfigOptions,
}
}
// newBaseAppOption returns baseapp option that sets the ante handler and post handler
// and set the tx encoder and decoder on baseapp.
func newBaseAppOption(txConfig client.TxConfig, in ModuleInputs) func(app *baseapp.BaseApp) {
return func(app *baseapp.BaseApp) {
// AnteHandlers
if !in.Config.SkipAnteHandler {
anteHandler, err := newAnteHandler(txConfig, in)
@ -145,20 +184,6 @@ func ProvideModule(in ModuleInputs) ModuleOutputs {
app.SetTxDecoder(txConfig.TxDecoder())
app.SetTxEncoder(txConfig.TxEncoder())
}
svd := ante.NewSigVerificationDecorator(
in.AccountKeeper,
txConfig.SignModeHandler(),
ante.DefaultSigVerificationGasConsumer,
in.AccountAbstractionKeeper,
)
return ModuleOutputs{
Module: NewAppModule(svd, in.ExtraTxValidators...),
TxConfig: txConfig,
TxConfigOptions: txConfigOptions,
BaseAppOption: baseAppOption,
}
}
func newAnteHandler(txConfig client.TxConfig, in ModuleInputs) (sdk.AnteHandler, error) {

View File

@ -19,6 +19,7 @@ var (
// This module is only useful for chains using server/v2. Ante/Post handlers are setup via baseapp options in depinject.
type AppModule struct {
sigVerification ante.SigVerificationDecorator
feeTxValidator *ante.DeductFeeDecorator
// txValidators contains tx validator that can be injected into the module via depinject.
// tx validators should be module based, but it can happen that you do not want to create a new module
// and simply depinject-it.
@ -28,10 +29,12 @@ type AppModule struct {
// NewAppModule creates a new AppModule object.
func NewAppModule(
sigVerification ante.SigVerificationDecorator,
feeTxValidator *ante.DeductFeeDecorator,
txValidators ...appmodulev2.TxValidator[transaction.Tx],
) AppModule {
return AppModule{
sigVerification: sigVerification,
feeTxValidator: feeTxValidator,
txValidators: txValidators,
}
}
@ -50,5 +53,9 @@ func (a AppModule) TxValidator(ctx context.Context, tx transaction.Tx) error {
}
}
if err := a.feeTxValidator.ValidateTx(ctx, tx); err != nil {
return err
}
return a.sigVerification.ValidateTx(ctx, tx)
}