diff --git a/CHANGELOG.md b/CHANGELOG.md index c9dbf74a2b..da5cfebb1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,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..7e4d1050ff 100644 --- a/app/app.go +++ b/app/app.go @@ -1124,7 +1124,28 @@ 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) + var filteredEvents []abci.Event + for _, e := range responseBeginBlock.Events { + if shouldFilterEvent(e) { + continue + } + filteredEvents = append(filteredEvents, e) + } + responseBeginBlock.Events = filteredEvents + return responseBeginBlock +} + +func shouldFilterEvent(e abci.Event) bool { + typeStr := string(e.Type) + if typeStr == "commission" || typeStr == "rewards" || typeStr == "proposer_reward" || typeStr == "transfer" || typeStr == "coin_spent" || typeStr == "coin_received" { + for _, a := range e.Attributes { + if string(a.Key) == "amount" && 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..ca61a5033f 100644 --- a/app/app_test.go +++ b/app/app_test.go @@ -322,3 +322,115 @@ 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("commission", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil commission event", createEvent("commission", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty commission event", createEvent("commission", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty rewards event", createEvent("rewards", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil rewards event", createEvent("rewards", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty rewards event", createEvent("rewards", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty proposer_reward event", createEvent("proposer_reward", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil proposer_reward event", createEvent("proposer_reward", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty proposer_reward event", createEvent("proposer_reward", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty transfer event", createEvent("transfer", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil transfer event", createEvent("transfer", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty transfer event", createEvent("transfer", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty coin_spent event", createEvent("coin_spent", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil coin_spent event", createEvent("coin_spent", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty coin_spent event", createEvent("coin_spent", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), false}, + + {"Empty coin_received event", createEvent("coin_received", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), true}, + {"Nil coin_received event", createEvent("coin_received", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), true}, + {"Non-empty coin_received event", createEvent("coin_received", []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("commission", []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 TestBeginBlocker(t *testing.T) { +// db := dbm.NewMemDB() +// encCdc := MakeEncodingConfig() +// app := New(log.NewNopLogger(), db, nil, true, map[int64]bool{}, "", 5, encCdc, sdksim.EmptyAppOptions{}) +// ctx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger()) +// req := abci.RequestBeginBlock{} + +// tests := []struct { +// name string +// events []abci.Event +// expected []abci.Event +// }{ +// { +// name: "Filter out events with nil or empty amounts", +// events: []abci.Event{ +// createEvent("commission", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("")}}), +// createEvent("transfer", []abci.EventAttribute{{Key: []byte("amount"), Value: nil}}), +// createEvent("rewards", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), +// }, +// expected: []abci.Event{ +// createEvent("rewards", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), +// }, +// }, +// { +// name: "No filtering when events are valid", +// events: []abci.Event{ +// createEvent("rewards", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), +// }, +// expected: []abci.Event{ +// createEvent("rewards", []abci.EventAttribute{{Key: []byte("amount"), Value: []byte("100")}}), +// }, +// }, +// } + +// for _, tc := range tests { +// t.Run(tc.name, func(t *testing.T) { +// responseBeginBlock := abci.ResponseBeginBlock{Events: tc.events} +// ctx = ctx.WithEventManager(sdk.NewEventManager()) +// converted := ConvertABCIEventsToSDKEvents(responseBeginBlock.Events) +// ctx.EventManager().EmitEvents(converted) +// result := app.BeginBlocker(ctx, req) +// assert.Equal(t, tc.expected, result.Events, "BeginBlocker returned events not expected.") +// }) +// } +// } + +// func ConvertABCIEventsToSDKEvents(abciEvents []abci.Event) []sdk.Event { +// sdkEvents := make([]sdk.Event, 0, len(abciEvents)) + +// for _, e := range abciEvents { +// attributes := make([]sdk.Attribute, 0, len(e.Attributes)) +// for _, a := range e.Attributes { +// attributes = append(attributes, sdk.NewAttribute(string(a.Key), string(a.Value))) +// } +// sdkEvent := types.NewEvent(e.Type, attributes...) +// sdkEvents = append(sdkEvents, sdkEvent) +// } + +// return sdkEvents +// }