From dd4a58613366a7a9a5b18bc89199b479b3ff4eca Mon Sep 17 00:00:00 2001 From: piux2 <90544084+piux2@users.noreply.github.com> Date: Sat, 14 Dec 2024 16:24:26 -0800 Subject: [PATCH] feat: min gas prices config --- gno.land/cmd/gnoland/start.go | 4 +++- gno.land/pkg/gnoland/app.go | 11 ++++++++-- gno.land/pkg/gnoland/app_test.go | 2 +- tm2/pkg/bft/config/config.go | 7 ++++++ tm2/pkg/sdk/auth/ante.go | 12 +++++++++-- tm2/pkg/sdk/config/config.go | 35 ++++++++++++++++++++++++++++++ tm2/pkg/sdk/config/config_test.go | 36 +++++++++++++++++++++++++++++++ tm2/pkg/std/gasprice.go | 5 +++++ 8 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 tm2/pkg/sdk/config/config.go create mode 100644 tm2/pkg/sdk/config/config_test.go diff --git a/gno.land/cmd/gnoland/start.go b/gno.land/cmd/gnoland/start.go index 77d7e20b8ef..d9651fba9e0 100644 --- a/gno.land/cmd/gnoland/start.go +++ b/gno.land/cmd/gnoland/start.go @@ -26,6 +26,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/crypto" "github.com/gnolang/gno/tm2/pkg/events" osm "github.com/gnolang/gno/tm2/pkg/os" + "github.com/gnolang/gno/tm2/pkg/std" "github.com/gnolang/gno/tm2/pkg/telemetry" "go.uber.org/zap" @@ -234,9 +235,10 @@ func execStart(ctx context.Context, c *startCfg, io commands.IO) error { // Create a top-level shared event switch evsw := events.NewEventSwitch() + minGasPrices := cfg.Application.MinGasPrices // Create application and node - cfg.LocalApp, err = gnoland.NewApp(nodeDir, c.skipFailingGenesisTxs, evsw, logger) + cfg.LocalApp, err = gnoland.NewApp(nodeDir, c.skipFailingGenesisTxs, evsw, logger, minGasPrices) if err != nil { return fmt.Errorf("unable to create the Gnoland app, %w", err) } diff --git a/gno.land/pkg/gnoland/app.go b/gno.land/pkg/gnoland/app.go index e0c93f6194f..370f1538324 100644 --- a/gno.land/pkg/gnoland/app.go +++ b/gno.land/pkg/gnoland/app.go @@ -39,6 +39,7 @@ type AppOptions struct { EventSwitch events.EventSwitch // required VMOutput io.Writer // optional InitChainerConfig // options related to InitChainer + MinGasPrices string // optional } // TestAppOptions provides a "ready" default [AppOptions] for use with @@ -79,9 +80,13 @@ func NewAppWithOptions(cfg *AppOptions) (abci.Application, error) { mainKey := store.NewStoreKey("main") baseKey := store.NewStoreKey("base") + // set sdk app options + var appOpts []func(*sdk.BaseApp) + if cfg.MinGasPrices != "" { + appOpts = append(appOpts, sdk.SetMinGasPrices(cfg.MinGasPrices)) + } // Create BaseApp. - // TODO: Add a consensus based min gas prices for the node, by default it does not check - baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey) + baseApp := sdk.NewBaseApp("gnoland", cfg.Logger, cfg.DB, baseKey, mainKey, appOpts...) baseApp.SetAppVersion("dev") // Set mounts for BaseApp's MultiStore. @@ -175,6 +180,7 @@ func NewApp( skipFailingGenesisTxs bool, evsw events.EventSwitch, logger *slog.Logger, + minGasPrices string, ) (abci.Application, error) { var err error @@ -185,6 +191,7 @@ func NewApp( GenesisTxResultHandler: PanicOnFailingTxResultHandler, StdlibDir: filepath.Join(gnoenv.RootDir(), "gnovm", "stdlibs"), }, + MinGasPrices: minGasPrices, } if skipFailingGenesisTxs { cfg.GenesisTxResultHandler = NoopGenesisTxResultHandler diff --git a/gno.land/pkg/gnoland/app_test.go b/gno.land/pkg/gnoland/app_test.go index 999e04b2c4b..cf0a5a2eb31 100644 --- a/gno.land/pkg/gnoland/app_test.go +++ b/gno.land/pkg/gnoland/app_test.go @@ -128,7 +128,7 @@ func TestNewApp(t *testing.T) { // NewApp should have good defaults and manage to run InitChain. td := t.TempDir() - app, err := NewApp(td, true, events.NewEventSwitch(), log.NewNoopLogger()) + app, err := NewApp(td, true, events.NewEventSwitch(), log.NewNoopLogger(), "") require.NoError(t, err, "NewApp should be successful") resp := app.InitChain(abci.RequestInitChain{ diff --git a/tm2/pkg/bft/config/config.go b/tm2/pkg/bft/config/config.go index f9e9a0cd899..5113493010b 100644 --- a/tm2/pkg/bft/config/config.go +++ b/tm2/pkg/bft/config/config.go @@ -17,6 +17,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/errors" osm "github.com/gnolang/gno/tm2/pkg/os" p2p "github.com/gnolang/gno/tm2/pkg/p2p/config" + sdk "github.com/gnolang/gno/tm2/pkg/sdk/config" telemetry "github.com/gnolang/gno/tm2/pkg/telemetry/config" ) @@ -54,6 +55,7 @@ type Config struct { Consensus *cns.ConsensusConfig `json:"consensus" toml:"consensus" comment:"##### consensus configuration options #####"` TxEventStore *eventstore.Config `json:"tx_event_store" toml:"tx_event_store" comment:"##### event store #####"` Telemetry *telemetry.Config `json:"telemetry" toml:"telemetry" comment:"##### node telemetry #####"` + Application *sdk.AppConfig `json:"application" toml:"application" comment:"##### app settings #####"` } // DefaultConfig returns a default configuration for a Tendermint node @@ -66,6 +68,7 @@ func DefaultConfig() *Config { Consensus: cns.DefaultConsensusConfig(), TxEventStore: eventstore.DefaultEventStoreConfig(), Telemetry: telemetry.DefaultTelemetryConfig(), + Application: sdk.DefaultAppConfig(), } } @@ -173,6 +176,7 @@ func TestConfig() *Config { Consensus: cns.TestConsensusConfig(), TxEventStore: eventstore.DefaultEventStoreConfig(), Telemetry: telemetry.DefaultTelemetryConfig(), + Application: sdk.DefaultAppConfig(), } } @@ -228,6 +232,9 @@ func (cfg *Config) ValidateBasic() error { if err := cfg.Consensus.ValidateBasic(); err != nil { return errors.Wrap(err, "Error in [consensus] section") } + if err := cfg.Application.ValidateBasic(); err != nil { + return errors.Wrap(err, "Error in [application] section") + } return nil } diff --git a/tm2/pkg/sdk/auth/ante.go b/tm2/pkg/sdk/auth/ante.go index d36b376aa8d..81959c56eb1 100644 --- a/tm2/pkg/sdk/auth/ante.go +++ b/tm2/pkg/sdk/auth/ante.go @@ -338,6 +338,13 @@ func DeductFees(bank BankKeeperI, ctx sdk.Context, acc std.Account, fees std.Coi // consensus. func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result { minGasPrices := ctx.MinGasPrices() + feeGasPrice := std.GasPrice{ + Gas: fee.GasWanted, + Price: std.Coin{ + Amount: fee.GasFee.Amount, + Denom: fee.GasFee.Denom, + }, + } if len(minGasPrices) == 0 { // no minimum gas price (not recommended) // TODO: allow for selective filtering of 0 fee txs. @@ -362,9 +369,10 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result { if prod1.Cmp(prod2) >= 0 { return sdk.Result{} } else { + fee := new(big.Int).Quo(prod2, gpg) return abciResult(std.ErrInsufficientFee( fmt.Sprintf( - "insufficient fees; got: %q required: %q", fee.GasFee, gp, + "insufficient fees; got: {Gas-Wanted: %d, Gas-Fee %s}, fee required: %d with %+v as minimum gas price set by the node", feeGasPrice.Gas, feeGasPrice.Price, fee, gp, ), )) } @@ -374,7 +382,7 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, fee std.Fee) sdk.Result { return abciResult(std.ErrInsufficientFee( fmt.Sprintf( - "insufficient fees; got: %q required (one of): %q", fee.GasFee, minGasPrices, + "insufficient fees; got: {Gas-Wanted: %d, Gas-Fee %s}, required (one of): %q", feeGasPrice.Gas, feeGasPrice.Price, minGasPrices, ), )) } diff --git a/tm2/pkg/sdk/config/config.go b/tm2/pkg/sdk/config/config.go new file mode 100644 index 00000000000..6e5ededf9a4 --- /dev/null +++ b/tm2/pkg/sdk/config/config.go @@ -0,0 +1,35 @@ +package config + +import ( + "github.com/gnolang/gno/tm2/pkg/errors" + "github.com/gnolang/gno/tm2/pkg/std" +) + +// ----------------------------------------------------------------------------- +// Application Config + +// AppConfig defines the configuration options for the Application +type AppConfig struct { + // Lowest gas prices accepted by a validator in the form of "100tokenA/3gas;10tokenB/5gas" separated by semicolons + MinGasPrices string `json:"min_gas_prices" toml:"min_gas_prices" comment:"Lowest gas prices accepted by a validator"` +} + +// DefaultAppConfig returns a default configuration for the application +func DefaultAppConfig() *AppConfig { + return &AppConfig{ + MinGasPrices: "", + } +} + +// ValidateBasic performs basic validation, checking format and param bounds, etc., and +// returns an error if any check fails. +func (cfg *AppConfig) ValidateBasic() error { + if cfg.MinGasPrices == "" { + return nil + } + if _, err := std.ParseGasPrices(cfg.MinGasPrices); err != nil { + return errors.Wrap(err, "invalid min gas prices") + } + + return nil +} diff --git a/tm2/pkg/sdk/config/config_test.go b/tm2/pkg/sdk/config/config_test.go new file mode 100644 index 00000000000..dd0c391849b --- /dev/null +++ b/tm2/pkg/sdk/config/config_test.go @@ -0,0 +1,36 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValidateAppConfig(t *testing.T) { + c := DefaultAppConfig() + c.MinGasPrices = "" // empty + + testCases := []struct { + testName string + minGasPrices string + expectErr bool + }{ + {"invalid min gas prices invalid gas", "10token/1", true}, + {"invalid min gas prices invalid gas denom", "9token/0gs", true}, + {"invalid min gas prices zero gas", "10token/0gas", true}, + {"invalid min gas prices no gas", "10token/gas", true}, + {"invalid min gas prices negtive gas", "10token/-1gas", true}, + {"invalid min gas prices invalid denom", "10$token/2gas", true}, + {"invalid min gas prices invalid second denom", "10token/2gas;10/3gas", true}, + {"valid min gas prices", "10foo/3gas;5bar/3gas", false}, + } + + cfg := DefaultAppConfig() + for _, tc := range testCases { + tc := tc + t.Run(tc.testName, func(t *testing.T) { + cfg.MinGasPrices = tc.minGasPrices + assert.Equal(t, tc.expectErr, cfg.ValidateBasic() != nil) + }) + } +} diff --git a/tm2/pkg/std/gasprice.go b/tm2/pkg/std/gasprice.go index f68ee190e41..06027ac3fb7 100644 --- a/tm2/pkg/std/gasprice.go +++ b/tm2/pkg/std/gasprice.go @@ -28,6 +28,11 @@ func ParseGasPrice(gasprice string) (GasPrice, error) { if gas.Denom != "gas" { return GasPrice{}, errors.New("invalid gas price: %s (invalid gas denom)", gasprice) } + + if gas.Amount <= 0 { + return GasPrice{}, errors.New("invalid gas price: %s (invalid gas amount)", gasprice) + } + return GasPrice{ Gas: gas.Amount, Price: price,