diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d0f598591..59d1d02a30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## v0.2.3-alpha.5 +This is a maintenance release. + +Features: +* [#253](https://github.com/bnb-chain/greenfield-cosmos-sdk/pull/253) feat: add option for disabling event emit + +Fix: +* [#252](https://github.com/bnb-chain/greenfield-cosmos-sdk/pull/252) fix: limit pagination to protect the node would not be Query DoS + ## v0.2.3-alpha.4 This is a maintenance release. diff --git a/baseapp/options.go b/baseapp/options.go index 36ca4d1a0c..e57bee6a25 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -24,6 +24,11 @@ func SetPruning(opts pruningtypes.PruningOptions) func(*BaseApp) { return func(bapp *BaseApp) { bapp.cms.SetPruning(opts) } } +// SetEventing sets an eventing option on the event manager with the app +func SetEventing(eventingStr string) func(*BaseApp) { + return func(bapp *BaseApp) { sdk.SetEventingOption(eventingStr) } +} + // SetMinGasPrices returns an option that sets the minimum gas prices on the app. func SetMinGasPrices(gasPricesStr string) func(*BaseApp) { gasPrices, err := sdk.ParseDecCoins(gasPricesStr) diff --git a/server/config/config.go b/server/config/config.go index 28a7d4f4b1..732d785ec0 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -48,6 +48,8 @@ type BaseConfig struct { PruningKeepRecent string `mapstructure:"pruning-keep-recent"` PruningInterval string `mapstructure:"pruning-interval"` + Eventing string `mapstructure:"eventing"` + // HaltHeight contains a non-zero block height at which a node will gracefully // halt and shutdown that can be used to assist upgrades and testing. // @@ -270,6 +272,7 @@ func DefaultConfig() *Config { Pruning: pruningtypes.PruningOptionDefault, PruningKeepRecent: "0", PruningInterval: "0", + Eventing: sdk.EventingOptionEverything, MinRetainBlocks: 0, IndexEvents: make([]string, 0), IAVLCacheSize: 781250, diff --git a/server/config/toml.go b/server/config/toml.go index 728d8a735a..7636434c0b 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -31,6 +31,10 @@ pruning = "{{ .BaseConfig.Pruning }}" pruning-keep-recent = "{{ .BaseConfig.PruningKeepRecent }}" pruning-interval = "{{ .BaseConfig.PruningInterval }}" +# everything: all events will be emitted +# nothing: no events will be emitted. +eventing = "{{ .BaseConfig.Eventing }}" + # HaltHeight contains a non-zero block height at which a node will gracefully # halt and shutdown that can be used to assist upgrades and testing. # diff --git a/server/start.go b/server/start.go index 81d5f86ccd..2d387ba92e 100644 --- a/server/start.go +++ b/server/start.go @@ -32,6 +32,7 @@ import ( "github.com/cosmos/cosmos-sdk/server/types" pruningtypes "github.com/cosmos/cosmos-sdk/store/pruning/types" "github.com/cosmos/cosmos-sdk/telemetry" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/mempool" ) @@ -52,6 +53,7 @@ const ( FlagPruning = "pruning" FlagPruningKeepRecent = "pruning-keep-recent" FlagPruningInterval = "pruning-interval" + FlagEventing = "eventing" FlagIndexEvents = "index-events" FlagMinRetainBlocks = "min-retain-blocks" FlagIAVLCacheSize = "iavl-cache-size" @@ -183,6 +185,7 @@ is performed. Note, when enabled, gRPC will also be automatically enabled. cmd.Flags().Uint64(FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')") cmd.Flags().Uint(FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks") cmd.Flags().Uint64(FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks") + cmd.Flags().String(FlagEventing, sdk.EventingOptionEverything, "Eventing strategy (everything|nothing)") cmd.Flags().Bool(FlagAPIEnable, false, "Define if the API server should be enabled") cmd.Flags().Bool(FlagAPISwagger, false, "Define if swagger documentation should automatically be registered (Note: the API must also be enabled)") diff --git a/server/util.go b/server/util.go index 7237b27dc1..ed37b5784e 100644 --- a/server/util.go +++ b/server/util.go @@ -480,6 +480,7 @@ func DefaultBaseappOptions(appOpts types.AppOptions) []func(*baseapp.BaseApp) { return []func(*baseapp.BaseApp){ baseapp.SetPruning(pruningOpts), + baseapp.SetEventing(cast.ToString(appOpts.Get(FlagEventing))), baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(FlagMinGasPrices))), baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(FlagHaltHeight))), baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(FlagHaltTime))), diff --git a/types/events.go b/types/events.go index b74f3913c3..8a03c58192 100644 --- a/types/events.go +++ b/types/events.go @@ -16,6 +16,36 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) +// EventingOptionEverything will allow to emit all events. +const EventingOptionEverything = "everything" + +// EventingOptionNothing will emit none events. +const EventingOptionNothing = "nothing" + +type emittingStrategy int + +const ( + // emittingEverything strategy will emit everything. + emittingEverything emittingStrategy = iota + // emittingNothing strategy will emit nothing. + emittingNothing +) + +var strategy emittingStrategy + +func SetEventingOption(option string) { + switch option { + case "": + strategy = emittingEverything + case EventingOptionEverything: + strategy = emittingEverything + case EventingOptionNothing: + strategy = emittingNothing + default: + panic("invalid eventing option") + } +} + // ---------------------------------------------------------------------------- // Event Manager // ---------------------------------------------------------------------------- @@ -35,12 +65,18 @@ func (em *EventManager) Events() Events { return em.events } // EmitEvent stores a single Event object. // Deprecated: Use EmitTypedEvent func (em *EventManager) EmitEvent(event Event) { + if strategy == emittingNothing { + return + } em.events = em.events.AppendEvent(event) } // EmitEvents stores a series of Event objects. // Deprecated: Use EmitTypedEvents func (em *EventManager) EmitEvents(events Events) { + if strategy == emittingNothing { + return + } em.events = em.events.AppendEvents(events) } @@ -51,6 +87,9 @@ func (em EventManager) ABCIEvents() []abci.Event { // EmitTypedEvent takes typed event and emits converting it into Event func (em *EventManager) EmitTypedEvent(tev proto.Message) error { + if strategy == emittingNothing { + return nil + } event, err := TypedEventToEvent(tev) if err != nil { return err @@ -62,6 +101,9 @@ func (em *EventManager) EmitTypedEvent(tev proto.Message) error { // EmitTypedEvents takes series of typed events and emit func (em *EventManager) EmitTypedEvents(tevs ...proto.Message) error { + if strategy == emittingNothing { + return nil + } events := make(Events, len(tevs)) for i, tev := range tevs { res, err := TypedEventToEvent(tev) diff --git a/types/query/pagination.go b/types/query/pagination.go index 0c670b50e6..730741afc5 100644 --- a/types/query/pagination.go +++ b/types/query/pagination.go @@ -38,7 +38,8 @@ func ParsePagination(pageReq *PageRequest) (page, limit int, err error) { if limit < 0 { return 1, 0, status.Error(codes.InvalidArgument, "limit must greater than 0") - } else if limit == 0 { + } else if limit > DefaultLimit || limit == 0 { + // limit to protect the node would not be Query DoS limit = DefaultLimit } @@ -74,6 +75,9 @@ func Paginate( // count total results when the limit is zero/not supplied countTotal = true + } else if limit > DefaultLimit { + // limit to protect the node would not be Query DoS + limit = DefaultLimit } if len(key) != 0 { diff --git a/types/query/pagination_test.go b/types/query/pagination_test.go index 49a09619a4..4228b0cde9 100644 --- a/types/query/pagination_test.go +++ b/types/query/pagination_test.go @@ -234,11 +234,20 @@ func (s *paginationTestSuite) TestReversePagination() { s.Require().NotNil(res1.Pagination.NextKey) s.T().Log("verify paginate with custom limit and countTotal, Reverse false") - pageReq = &query.PageRequest{Limit: 150} + pageReq = &query.PageRequest{Limit: 100} request = types.NewQueryAllBalancesRequest(addr1, pageReq) res1, err = queryClient.AllBalances(gocontext.Background(), request) s.Require().NoError(err) - s.Require().Equal(res1.Balances.Len(), 150) + s.Require().Equal(res1.Balances.Len(), 100) + s.Require().NotNil(res1.Pagination.NextKey) + s.Require().Equal(res1.Pagination.Total, uint64(0)) + + s.T().Log("verify paginate with custom limit and countTotal, Reverse false") + pageReq = &query.PageRequest{Limit: 50, Offset: 100} + request = types.NewQueryAllBalancesRequest(addr1, pageReq) + res1, err = queryClient.AllBalances(gocontext.Background(), request) + s.Require().NoError(err) + s.Require().Equal(res1.Balances.Len(), 50) s.Require().NotNil(res1.Pagination.NextKey) s.Require().Equal(res1.Pagination.Total, uint64(0))