From eb95a0874f7ffbe2c0ba2a9bc65466a43bad2c12 Mon Sep 17 00:00:00 2001 From: Matt Witkowski Date: Wed, 24 Jan 2024 14:50:15 -0500 Subject: [PATCH 1/3] Add nav unit conversion to upgrade handler. (#1816) * Add CHANGELOG entry. * Add function to get marker addr from key. * Add conversion func to upgrade handler, and an iterate all function to marker keeper. * Update changelog entry. * Add tests for upgrade. * Add test for upgrade. * Add test for GetMarkerFromNetAssetValue and fix bug. * Add additional test case for ConvertNAVUnits. * Add test for IterateAllNetAssetMarkers. * Add error checks. --- CHANGELOG.md | 1 + app/upgrades.go | 26 ++++++++ app/upgrades_test.go | 115 ++++++++++++++++++++++++++++++++- x/marker/keeper/keeper.go | 18 ++++++ x/marker/keeper/keeper_test.go | 94 +++++++++++++++++++++++++++ x/marker/types/key.go | 7 ++ x/marker/types/key_test.go | 8 +++ 7 files changed, 267 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9dbf74a2b..74689cb160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features * Add the ibcratelimit module [#1498](https://github.com/provenance-io/provenance/issues/1498). +* Add fix for NAV units to tourmaline upgrade handler [#1815](https://github.com/provenance-io/provenance/issues/1815). ### Improvements diff --git a/app/upgrades.go b/app/upgrades.go index 0568160020..204c9891c1 100644 --- a/app/upgrades.go +++ b/app/upgrades.go @@ -127,6 +127,7 @@ var upgrades = map[string]appUpgrade{ } removeInactiveValidatorDelegations(ctx, app) + convertNavUnits(ctx, app) return vm, nil }, @@ -141,6 +142,7 @@ var upgrades = map[string]appUpgrade{ } removeInactiveValidatorDelegations(ctx, app) + convertNavUnits(ctx, app) return vm, nil }, @@ -349,3 +351,27 @@ func updateIbcMarkerDenomMetadata(ctx sdk.Context, app *App) { }) ctx.Logger().Info("Done updating ibc marker denom metadata") } + +// convertNavUnits iterates all the net asset values and updates their units if they are using usd. +func convertNavUnits(ctx sdk.Context, app *App) { + ctx.Logger().Info("Converting NAV units") + err := app.MarkerKeeper.IterateAllNetAssetValues(ctx, func(markerAddr sdk.AccAddress, nav markertypes.NetAssetValue) (stop bool) { + if nav.Price.Denom == markertypes.UsdDenom { + nav.Price.Amount = nav.Price.Amount.Mul(math.NewInt(10)) + marker, err := app.MarkerKeeper.GetMarker(ctx, markerAddr) + if err != nil { + ctx.Logger().Error(fmt.Sprintf("Unable to get marker for address: %s, error: %s", markerAddr, err)) + return false + } + err = app.MarkerKeeper.SetNetAssetValue(ctx, marker, nav, "upgrade") + if err != nil { + ctx.Logger().Error(fmt.Sprintf("Unable to set net asset value for marker: %s, error: %s", markerAddr, err)) + return false + } + } + return false + }) + if err != nil { + ctx.Logger().Error(fmt.Sprintf("Unable to iterate all net asset values error: %s", err)) + } +} diff --git a/app/upgrades_test.go b/app/upgrades_test.go index a431946155..643f408fda 100644 --- a/app/upgrades_test.go +++ b/app/upgrades_test.go @@ -425,13 +425,17 @@ func (s *UpgradeTestSuite) TestSaffron() { } func (s *UpgradeTestSuite) TestTourmalineRC1() { - expInLog := []string{} + expInLog := []string{ + "INF Converting NAV units", + } s.AssertUpgradeHandlerLogs("tourmaline-rc1", expInLog, nil) } func (s *UpgradeTestSuite) TestTourmaline() { - expInLog := []string{} + expInLog := []string{ + "INF Converting NAV units", + } s.AssertUpgradeHandlerLogs("tourmaline", expInLog, nil) } @@ -780,3 +784,110 @@ func (s *UpgradeTestSuite) TestSetExchangeParams() { }) } } + +func (s *UpgradeTestSuite) TestConvertNAVUnits() { + tests := []struct { + name string + markerNavs []sdk.Coins + expected []sdk.Coins + }{ + { + name: "should work with no markers", + markerNavs: []sdk.Coins{}, + expected: []sdk.Coins{}, + }, + { + name: "should work with one marker no usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1)), + }, + expected: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1)), + }, + }, + { + name: "should work with multiple markers no usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1)), + sdk.NewCoins(sdk.NewInt64Coin("georgethedog", 2)), + }, + expected: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1)), + sdk.NewCoins(sdk.NewInt64Coin("georgethedog", 2)), + }, + }, + { + name: "should work with one marker with usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1), sdk.NewInt64Coin(markertypes.UsdDenom, 2)), + }, + expected: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1), sdk.NewInt64Coin(markertypes.UsdDenom, 20)), + }, + }, + { + name: "should work with multiple markers with usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1), sdk.NewInt64Coin(markertypes.UsdDenom, 3)), + sdk.NewCoins(sdk.NewInt64Coin("georgethedog", 2), sdk.NewInt64Coin(markertypes.UsdDenom, 4)), + }, + expected: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1), sdk.NewInt64Coin(markertypes.UsdDenom, 30)), + sdk.NewCoins(sdk.NewInt64Coin("georgethedog", 2), sdk.NewInt64Coin(markertypes.UsdDenom, 40)), + }, + }, + } + + for _, tc := range tests { + s.Run(tc.name, func() { + // Create the marker + for i, prices := range tc.markerNavs { + address := sdk.AccAddress(fmt.Sprintf("marker%d", i)) + marker := markertypes.NewEmptyMarkerAccount(fmt.Sprintf("coin%d", i), address.String(), []markertypes.AccessGrant{}) + marker.Supply = sdk.OneInt() + s.Require().NoError(s.app.MarkerKeeper.AddMarkerAccount(s.ctx, marker), "AddMarkerAccount() error") + + var navs []markertypes.NetAssetValue + for _, price := range prices { + navs = append(navs, markertypes.NewNetAssetValue(price, uint64(1))) + navAddr := sdk.AccAddress(price.Denom) + if acc, _ := s.app.MarkerKeeper.GetMarkerByDenom(s.ctx, price.Denom); acc == nil { + navMarker := markertypes.NewEmptyMarkerAccount(price.Denom, navAddr.String(), []markertypes.AccessGrant{}) + navMarker.Supply = sdk.OneInt() + s.Require().NoError(s.app.MarkerKeeper.AddMarkerAccount(s.ctx, navMarker), "AddMarkerAccount() error") + } + } + s.Require().NoError(s.app.MarkerKeeper.AddSetNetAssetValues(s.ctx, marker, navs, "AddSetNetAssetValues() error")) + } + + // Test Logic + convertNavUnits(s.ctx, s.app) + for i := range tc.markerNavs { + marker, err := s.app.MarkerKeeper.GetMarkerByDenom(s.ctx, fmt.Sprintf("coin%d", i)) + s.Require().NoError(err, "GetMarkerByDenom() error") + var prices sdk.Coins + + s.app.MarkerKeeper.IterateNetAssetValues(s.ctx, marker.GetAddress(), func(state markertypes.NetAssetValue) (stop bool) { + prices = append(prices, state.Price) + return false + }) + s.Require().EqualValues(tc.expected[i], prices, "should update prices correctly for nav") + } + + // Destroy the marker + for i, prices := range tc.markerNavs { + coin := fmt.Sprintf("coin%d", i) + marker, err := s.app.MarkerKeeper.GetMarkerByDenom(s.ctx, coin) + s.Require().NoError(err, "GetMarkerByDenom() error") + s.app.MarkerKeeper.RemoveMarker(s.ctx, marker) + + // We need to remove the nav markers + for _, price := range prices { + if navMarker, _ := s.app.MarkerKeeper.GetMarkerByDenom(s.ctx, price.Denom); navMarker != nil { + s.app.MarkerKeeper.RemoveMarker(s.ctx, navMarker) + } + } + } + }) + } +} diff --git a/x/marker/keeper/keeper.go b/x/marker/keeper/keeper.go index 2b53938583..3b1b633d6b 100644 --- a/x/marker/keeper/keeper.go +++ b/x/marker/keeper/keeper.go @@ -324,6 +324,24 @@ func (k Keeper) IterateNetAssetValues(ctx sdk.Context, markerAddr sdk.AccAddress return nil } +// IterateAllNetAssetValues iterates all net asset values +func (k Keeper) IterateAllNetAssetValues(ctx sdk.Context, handler func(sdk.AccAddress, types.NetAssetValue) (stop bool)) error { + store := ctx.KVStore(k.storeKey) + it := sdk.KVStorePrefixIterator(store, types.NetAssetValuePrefix) + defer it.Close() + for ; it.Valid(); it.Next() { + markerAddr := types.GetMarkerFromNetAssetValueKey(it.Key()) + var markerNav types.NetAssetValue + err := k.cdc.Unmarshal(it.Value(), &markerNav) + if err != nil { + return err + } else if handler(markerAddr, markerNav) { + break + } + } + return nil +} + // RemoveNetAssetValues removes all net asset values for a marker func (k Keeper) RemoveNetAssetValues(ctx sdk.Context, markerAddr sdk.AccAddress) { store := ctx.KVStore(k.storeKey) diff --git a/x/marker/keeper/keeper_test.go b/x/marker/keeper/keeper_test.go index a24fc1d6a2..9573a388a9 100644 --- a/x/marker/keeper/keeper_test.go +++ b/x/marker/keeper/keeper_test.go @@ -1588,6 +1588,100 @@ func TestAddRemoveSendDeny(t *testing.T) { } } +func TestIterateAllNetAssetValues(t *testing.T) { + app := simapp.Setup(t) + ctx := app.BaseApp.NewContext(false, tmproto.Header{}) + + tests := []struct { + name string + markerNavs []sdk.Coins + expected int + }{ + { + name: "should work with no markers", + markerNavs: []sdk.Coins{}, + expected: 0, + }, + { + name: "should work with one marker no usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1)), + }, + expected: 1, + }, + { + name: "should work with multiple markers no usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1)), + sdk.NewCoins(sdk.NewInt64Coin("georgethedog", 2)), + }, + expected: 2, + }, + { + name: "should work with one marker with usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1), sdk.NewInt64Coin(types.UsdDenom, 2)), + }, + expected: 2, + }, + { + name: "should work with multiple markers with usd denom", + markerNavs: []sdk.Coins{ + sdk.NewCoins(sdk.NewInt64Coin("jackthecat", 1), sdk.NewInt64Coin(types.UsdDenom, 3)), + sdk.NewCoins(sdk.NewInt64Coin("georgethedog", 2), sdk.NewInt64Coin(types.UsdDenom, 4)), + }, + expected: 4, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Create the marker + for i, prices := range tc.markerNavs { + address := sdk.AccAddress(fmt.Sprintf("marker%d", i)) + marker := types.NewEmptyMarkerAccount(fmt.Sprintf("coin%d", i), address.String(), []types.AccessGrant{}) + marker.Supply = sdk.OneInt() + require.NoError(t, app.MarkerKeeper.AddMarkerAccount(ctx, marker), "AddMarkerAccount() error") + + var navs []types.NetAssetValue + for _, price := range prices { + navs = append(navs, types.NewNetAssetValue(price, uint64(1))) + navAddr := sdk.AccAddress(price.Denom) + if acc, _ := app.MarkerKeeper.GetMarkerByDenom(ctx, price.Denom); acc == nil { + navMarker := types.NewEmptyMarkerAccount(price.Denom, navAddr.String(), []types.AccessGrant{}) + navMarker.Supply = sdk.OneInt() + require.NoError(t, app.MarkerKeeper.AddMarkerAccount(ctx, navMarker), "AddMarkerAccount() error") + } + } + require.NoError(t, app.MarkerKeeper.AddSetNetAssetValues(ctx, marker, navs, "AddSetNetAssetValues() error")) + } + + // Test Logic + count := 0 + app.MarkerKeeper.IterateAllNetAssetValues(ctx, func(aa sdk.AccAddress, nav types.NetAssetValue) (stop bool) { + count += 1 + return false + }) + assert.Equal(t, tc.expected, count, "should iterate the correct number of times") + + // Destroy the marker + for i, prices := range tc.markerNavs { + coin := fmt.Sprintf("coin%d", i) + marker, err := app.MarkerKeeper.GetMarkerByDenom(ctx, coin) + require.NoError(t, err, "GetMarkerByDenom() error") + app.MarkerKeeper.RemoveMarker(ctx, marker) + + // We need to remove the nav markers + for _, price := range prices { + if navMarker, _ := app.MarkerKeeper.GetMarkerByDenom(ctx, price.Denom); navMarker != nil { + app.MarkerKeeper.RemoveMarker(ctx, navMarker) + } + } + } + }) + } +} + func TestReqAttrBypassAddrs(t *testing.T) { // Tests both GetReqAttrBypassAddrs and IsReqAttrBypassAddr. expectedNames := []string{ diff --git a/x/marker/types/key.go b/x/marker/types/key.go index 38f8d8a3d6..827b57bdb1 100644 --- a/x/marker/types/key.go +++ b/x/marker/types/key.go @@ -99,3 +99,10 @@ func NetAssetValueKeyPrefix(markerAddr sdk.AccAddress) []byte { func NetAssetValueKey(markerAddr sdk.AccAddress, denom string) []byte { return append(NetAssetValueKeyPrefix(markerAddr), denom...) } + +// GetMarkerFromNetAssetValueKey returns the marker address in the NetAssetValue key. +func GetMarkerFromNetAssetValueKey(key []byte) sdk.AccAddress { + markerKeyLen := key[1] + markerAddr := sdk.AccAddress(key[2 : markerKeyLen+2]) + return markerAddr +} diff --git a/x/marker/types/key_test.go b/x/marker/types/key_test.go index b82c8265fb..aa479d4ac9 100644 --- a/x/marker/types/key_test.go +++ b/x/marker/types/key_test.go @@ -55,6 +55,14 @@ func TestNetAssetValueKey(t *testing.T) { assert.Equal(t, "nhash", string(navKey[denomArrLen+2:])) } +func TestGetMarkerFromNetAssetValueKey(t *testing.T) { + addr, err := MarkerAddress("nhash") + require.NoError(t, err) + navKey := NetAssetValueKey(addr, "nhash") + addr2 := GetMarkerFromNetAssetValueKey(navKey) + assert.Equal(t, addr, addr2, "should match original marker address") +} + func TestDenySendMarkerPrefix(t *testing.T) { addr, err := MarkerAddress("nhash") require.NoError(t, err, "MarkerAddress(nhash)") From e715918cb61c1f222744867be0c5c835cabed3a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:48:25 +0000 Subject: [PATCH 2/3] Bump google.golang.org/grpc from 1.60.1 to 1.61.0 (#1820) * Bump google.golang.org/grpc from 1.60.1 to 1.61.0 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.60.1 to 1.61.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.60.1...v1.61.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Updated Changelog --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] --- CHANGELOG.md | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74689cb160..d66ccbd880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,7 +70,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ - Bump `github/codeql-action` from 2 to 3 ([#1784](https://github.com/provenance-io/provenance/pull/1784)) - Bump `actions/download-artifact` from 3 to 4 ([#1785](https://github.com/provenance-io/provenance/pull/1785)) - Bump `actions/upload-artifact` from 3 to 4 ([#1785](https://github.com/provenance-io/provenance/pull/1785)) -- Bump `google.golang.org/grpc` from 1.59.0 to 1.60.1 ([#1794](https://github.com/provenance-io/provenance/pull/1794)) +- Bump `google.golang.org/grpc` from 1.59.0 to 1.61.0 ([#1794](https://github.com/provenance-io/provenance/pull/1794), [#1820](https://github.com/provenance-io/provenance/pull/1820)) - Bump `golang.org/x/crypto` from 0.14.0 to 0.17.0 ([#1788](https://github.com/provenance-io/provenance/pull/1788)) - Bump `cosmossdk.io/errors` from 1.0.0 to 1.0.1 ([#1806](https://github.com/provenance-io/provenance/pull/1806)) - Bump `actions/cache` from 3 to 4 ([#1817](https://github.com/provenance-io/provenance/pull/1817)) diff --git a/go.mod b/go.mod index 799a00b375..3e67ebc012 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/text v0.14.0 google.golang.org/genproto/googleapis/api v0.0.0-20231120223509-83a465c0220f - google.golang.org/grpc v1.60.1 + google.golang.org/grpc v1.61.0 google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 19561fcd7d..afb2f4bc91 100644 --- a/go.sum +++ b/go.sum @@ -1790,8 +1790,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= From e7615d69fdc08d7545639f26dd297dec4836522f Mon Sep 17 00:00:00 2001 From: Carlton Hanna Date: Wed, 24 Jan 2024 22:07:32 -0700 Subject: [PATCH 3/3] Add filtering of nil events from begin blocker (#1823) * add filtering of nil events from begin blocker * Add tests and refactor function * add comments * fix lint --- CHANGELOG.md | 1 + app/app.go | 28 ++++++++++++- app/app_test.go | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d66ccbd880..a073255071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * Add upgrade handler for 1.18 [#1756](https://github.com/provenance-io/provenance/pull/1756). * Remove the rust upgrade handlers [PR 1774](https://github.com/provenance-io/provenance/pull/1774). * Allow bypassing the config warning wait using an environment variable [PR 1810](https://github.com/provenance-io/provenance/pull/1810). +* Filter out empty distribution events from begin blocker [#1822](https://github.com/provenance-io/provenance/pull/1822). ### Bug Fixes diff --git a/app/app.go b/app/app.go index 8c50610c23..604376f5d7 100644 --- a/app/app.go +++ b/app/app.go @@ -1124,7 +1124,33 @@ func (app *App) Name() string { return app.BaseApp.Name() } // BeginBlocker application updates every begin block func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock { - return app.mm.BeginBlock(ctx, req) + responseBeginBlock := app.mm.BeginBlock(ctx, req) + responseBeginBlock.Events = filterBeginBlockerEvents(responseBeginBlock) + return responseBeginBlock +} + +// filterBeginBlockerEvents filters out events from a given abci.ResponseBeginBlock according to the criteria defined in shouldFilterEvent. +func filterBeginBlockerEvents(responseBeginBlock abci.ResponseBeginBlock) []abci.Event { + filteredEvents := make([]abci.Event, 0) + for _, e := range responseBeginBlock.Events { + if shouldFilterEvent(e) { + continue + } + filteredEvents = append(filteredEvents, e) + } + return filteredEvents +} + +// shouldFilterEvent checks if an abci.Event should be filtered based on its type and attributes. +func shouldFilterEvent(e abci.Event) bool { + if e.Type == distrtypes.EventTypeCommission || e.Type == distrtypes.EventTypeRewards || e.Type == distrtypes.EventTypeProposerReward || e.Type == banktypes.EventTypeTransfer || e.Type == banktypes.EventTypeCoinSpent || e.Type == banktypes.EventTypeCoinReceived { + for _, a := range e.Attributes { + if string(a.Key) == sdk.AttributeKeyAmount && len(a.Value) == 0 { + return true + } + } + } + return false } // EndBlocker application updates every end block diff --git a/app/app_test.go b/app/app_test.go index cbf4126006..7f21e23171 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -14,6 +14,8 @@ import ( sdksim "github.com/cosmos/cosmos-sdk/simapp" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" @@ -322,3 +324,103 @@ func assertAddrNotInAccounts(t *testing.T, addr sdk.AccAddress, addrName string, } return true } + +func createEvent(eventType string, attributes []abci.EventAttribute) abci.Event { + return abci.Event{ + Type: eventType, + Attributes: attributes, + } +} + +func TestShouldFilterEvent(t *testing.T) { + tests := []struct { + name string + event abci.Event + expect bool + }{ + {"Empty commission event", createEvent(distrtypes.EventTypeCommission, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil commission event", createEvent(distrtypes.EventTypeCommission, []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty commission event", createEvent(distrtypes.EventTypeCommission, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty rewards event", createEvent(distrtypes.EventTypeRewards, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil rewards event", createEvent(distrtypes.EventTypeRewards, []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty rewards event", createEvent(distrtypes.EventTypeRewards, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty proposer_reward event", createEvent(distrtypes.EventTypeProposerReward, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil proposer_reward event", createEvent(distrtypes.EventTypeProposerReward, []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty proposer_reward event", createEvent(distrtypes.EventTypeProposerReward, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty transfer event", createEvent(banktypes.EventTypeTransfer, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil transfer event", createEvent(banktypes.EventTypeTransfer, []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty transfer event", createEvent(banktypes.EventTypeTransfer, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty coin_spent event", createEvent(banktypes.EventTypeCoinSpent, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil coin_spent event", createEvent(banktypes.EventTypeCoinSpent, []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty coin_spent event", createEvent(banktypes.EventTypeCoinSpent, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty coin_received event", createEvent(banktypes.EventTypeCoinReceived, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil coin_received event", createEvent(banktypes.EventTypeCoinReceived, []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty coin_received event", createEvent(banktypes.EventTypeCoinReceived, []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Unhandled event type with empty amount", createEvent("unhandled_type", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), false}, + {"Unhandled event type with nil amount", createEvent("unhandled_type", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), false}, + {"Unhandled event type with non-empty amount", createEvent("unhandled_type", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + {"Event with no attributes", createEvent(distrtypes.EventTypeCommission, []abci.EventAttribute{}), false}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result := shouldFilterEvent(tc.event) + assert.Equal(t, tc.expect, result, "Test %v failed: expected %v, got %v", tc.name, tc.expect, result) + }) + } +} + +func TestFilterBeginBlockerEvents(t *testing.T) { + tests := []struct { + name string + events []abci.Event + expected []abci.Event + }{ + { + name: "Filter out events with empty amounts", + events: []abci.Event{ + createEvent(distrtypes.EventTypeCommission, []abci.EventAttribute{{Key: []byte(sdk.AttributeKeyAmount), Value: []byte("")}}), + createEvent(distrtypes.EventTypeRewards, []abci.EventAttribute{{Key: []byte(sdk.AttributeKeyAmount), Value: []byte("100")}}), + }, + expected: []abci.Event{ + createEvent(distrtypes.EventTypeRewards, []abci.EventAttribute{{Key: []byte(sdk.AttributeKeyAmount), Value: []byte("100")}}), + }, + }, + { + name: "No filtering when all events are valid", + events: []abci.Event{ + createEvent(banktypes.EventTypeTransfer, []abci.EventAttribute{{Key: []byte(sdk.AttributeKeyAmount), Value: []byte("100")}}), + }, + expected: []abci.Event{ + createEvent(banktypes.EventTypeTransfer, []abci.EventAttribute{{Key: []byte(sdk.AttributeKeyAmount), Value: []byte("100")}}), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + responseBeginBlock := abci.ResponseBeginBlock{Events: tc.events} + actualEvents := filterBeginBlockerEvents(responseBeginBlock) + assert.Equal(t, len(tc.expected), len(actualEvents), "Number of events mismatch") + + for i, expectedEvent := range tc.expected { + actualEvent := actualEvents[i] + assert.Equal(t, expectedEvent.Type, actualEvent.Type, "Event types mismatch") + + assert.Equal(t, len(expectedEvent.Attributes), len(actualEvent.Attributes), "Number of attributes mismatch in event %v", expectedEvent.Type) + + for j, expectedAttribute := range expectedEvent.Attributes { + actualAttribute := actualEvent.Attributes[j] + assert.Equal(t, expectedAttribute.Key, actualAttribute.Key, "Attribute keys mismatch in event %v", expectedEvent.Type) + assert.Equal(t, expectedAttribute.Value, actualAttribute.Value, "Attribute values mismatch in event %v", expectedEvent.Type) + } + } + }) + } +}