diff --git a/EVENTS.md b/EVENTS.md new file mode 100644 index 00000000..402537c4 --- /dev/null +++ b/EVENTS.md @@ -0,0 +1,62 @@ +# Event System + +Events are an essential part of the Cosmos SDK. They are similar to "logs" in Ethereum and allow a blockchain +app to attach key-value pairs to a transaction that can later be used to search for it or extract some information +in human readable form. Events are not written to the application state, nor do they form part of the AppHash, +but mainly intended for client use (and become an essential API for any reactive app or app that searches for txs). + +See https://github.com/CosmWasm/wasmd/blob/master/EVENTS.md for more details + + +### Standard Events in x/poe +```go +// create new validator +sdk.NewEvent( + "create_validator", + sdk.NewAttribute("operator", msg.DelegatorAddress), + sdk.NewAttribute("moniker", msg.Description.Moniker), + sdk.NewAttribute("pubkey", hex.EncodeToString(pk.Bytes())), + sdk.NewAttribute("amount", msg.Value.Amount.String()), +) + +// update any validator data +sdk.NewEvent( + "update_validator", + sdk.NewAttribute("operator", msg.DelegatorAddress), + sdk.NewAttribute("moniker", msg.Description.Moniker), +), + +``` + +### Standard Events in x/twasm +In twasm we have the concept of (privileged](https://github.com/confio/tgrade/tree/main/x/twasm#privileged) contracts that +add a number of new events to the system: + +```go +// when a contract was set as privileged +sdk.NewEvent( + "set_privileged_contract", + sdk.NewAttribute("_contract_address", contractAddr.String()), +) + +// when a contract had the privileged flag removed +sdk.NewEvent( + "unset_privileged_contract", + sdk.NewAttribute("_contract_address", contractAddr.String()), +) + +// when a new privilege was given to a contract +sdk.NewEvent( + "register_privilege", + sdk.NewAttribute("_contract_address", contractAddr.String()), + sdk.NewAttribute("privilege_type", privilegeType.String()), +) + +// when a privilege was removed for a contract +event := sdk.NewEvent( + "release_privilege", + sdk.NewAttribute(wasmtypes.AttributeKeyContractAddr, contractAddr.String()), + sdk.NewAttribute("privilege_type", privilegeType.String()), +) +``` +We also emit the standard events from [wasmd/x/wasm](https://github.com/CosmWasm/wasmd/blob/master/EVENTS.md#standard-events-in-xwasm) \ No newline at end of file diff --git a/Makefile b/Makefile index 4b4f2080..5801b981 100644 --- a/Makefile +++ b/Makefile @@ -120,7 +120,7 @@ test-unit: @VERSION=$(VERSION) go test -mod=readonly -tags='ledger test_ledger_mock' ./... test-system: install - @VERSION=$(VERSION) go test -mod=readonly -tags='ledger test_ledger_mock system_test' ./testing --wait-time=30s --verbose + @VERSION=$(VERSION) go test -mod=readonly -tags='ledger test_ledger_mock system_test' ./testing --wait-time=45s --verbose test-race: @VERSION=$(VERSION) go test -mod=readonly -race -tags='ledger test_ledger_mock' ./... diff --git a/x/twasm/keeper/handler_plugin.go b/x/twasm/keeper/handler_plugin.go index d2e3c5c2..92052582 100644 --- a/x/twasm/keeper/handler_plugin.go +++ b/x/twasm/keeper/handler_plugin.go @@ -41,29 +41,32 @@ func NewTgradeHandler(cdc codec.Marshaler, keeper tgradeKeeper, bankKeeper minte } // DispatchMsg handles wasmVM message for privileged contracts -func (h TgradeHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) (events []sdk.Event, data [][]byte, err error) { +func (h TgradeHandler) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, _ string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) { if msg.Custom == nil { return nil, nil, wasmtypes.ErrUnknownMsg } if !h.keeper.IsPrivileged(ctx, contractAddr) { return nil, nil, wasmtypes.ErrUnknownMsg } + em := sdk.NewEventManager() + ctx = ctx.WithEventManager(em) var tMsg contract.TgradeMsg if err := tMsg.UnmarshalWithAny(msg.Custom, h.cdc); err != nil { return nil, nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } switch { case tMsg.Privilege != nil: - return nil, nil, h.handlePrivilege(ctx, contractAddr, tMsg.Privilege) + err := h.handlePrivilege(ctx, contractAddr, tMsg.Privilege) + return em.Events(), nil, err case tMsg.ExecuteGovProposal != nil: - return nil, nil, h.handleGovProposalExecution(ctx, contractAddr, tMsg.ExecuteGovProposal) + err := h.handleGovProposalExecution(ctx, contractAddr, tMsg.ExecuteGovProposal) + return em.Events(), nil, err case tMsg.MintTokens != nil: evts, err := h.handleMintToken(ctx, contractAddr, tMsg.MintTokens) - return evts, nil, err + return append(evts, em.Events()...), nil, err } ModuleLogger(ctx).Info("unhandled message", "msg", msg) - return nil, nil, wasmtypes.ErrUnknownMsg } @@ -160,10 +163,10 @@ func (h TgradeHandler) handleMintToken(ctx sdk.Context, contractAddr sdk.AccAddr } return sdk.Events{sdk.NewEvent( - types.EventTypeRewards, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + types.EventTypeMintTokens, + sdk.NewAttribute(wasmtypes.AttributeKeyContractAddr, contractAddr.String()), sdk.NewAttribute(sdk.AttributeKeyAmount, token.String()), - sdk.NewAttribute(types.AttributeKeyRewardRecipient, mint.RecipientAddr), + sdk.NewAttribute(types.AttributeKeyRecipient, mint.RecipientAddr), )}, nil } diff --git a/x/twasm/keeper/handler_plugin_test.go b/x/twasm/keeper/handler_plugin_test.go index a2019674..d647c6e0 100644 --- a/x/twasm/keeper/handler_plugin_test.go +++ b/x/twasm/keeper/handler_plugin_test.go @@ -25,6 +25,7 @@ func TestTgradeHandlesDispatchMsg(t *testing.T) { src wasmvmtypes.CosmosMsg expErr *sdkerrors.Error expCapturedGovContent []govtypes.Content + expEvents []sdk.Event }{ "handle privilege msg": { src: wasmvmtypes.CosmosMsg{ @@ -32,7 +33,9 @@ func TestTgradeHandlesDispatchMsg(t *testing.T) { }, setup: func(m *handlerTgradeKeeperMock) { noopRegisterHook(m) + m.GetContractInfoFn = emitCtxEventWithGetContractInfoFn(m.GetContractInfoFn, sdk.NewEvent("testing")) }, + expEvents: sdk.Events{sdk.NewEvent("testing")}, }, "handle execute gov proposal msg": { src: wasmvmtypes.CosmosMsg{ @@ -45,8 +48,10 @@ func TestTgradeHandlesDispatchMsg(t *testing.T) { details.AddRegisteredPrivilege(types.PrivilegeTypeGovProposalExecutor, 1) require.NoError(t, info.SetExtension(&details)) }) + m.GetContractInfoFn = emitCtxEventWithGetContractInfoFn(m.GetContractInfoFn, sdk.NewEvent("testing")) }, expCapturedGovContent: []govtypes.Content{&govtypes.TextProposal{Title: "foo", Description: "bar"}}, + expEvents: sdk.Events{sdk.NewEvent("testing")}, }, "handle minter msg": { src: wasmvmtypes.CosmosMsg{ @@ -60,6 +65,12 @@ func TestTgradeHandlesDispatchMsg(t *testing.T) { require.NoError(t, info.SetExtension(&details)) }) }, + expEvents: sdk.Events{sdk.NewEvent( + types.EventTypeMintTokens, + sdk.NewAttribute(wasmtypes.AttributeKeyContractAddr, contractAddr.String()), + sdk.NewAttribute(sdk.AttributeKeyAmount, "1utgd"), + sdk.NewAttribute(types.AttributeKeyRecipient, otherAddr.String()), + )}, }, "non custom msg rejected": { src: wasmvmtypes.CosmosMsg{}, @@ -93,14 +104,27 @@ func TestTgradeHandlesDispatchMsg(t *testing.T) { mock := handlerTgradeKeeperMock{} spec.setup(&mock) h := NewTgradeHandler(cdc, mock, minterMock, govRouter) - var ctx sdk.Context - _, _, gotErr := h.DispatchMsg(ctx, contractAddr, "", spec.src) + em := sdk.NewEventManager() + ctx := sdk.Context{}.WithEventManager(em) + + // when + gotEvents, _, gotErr := h.DispatchMsg(ctx, contractAddr, "", spec.src) + // then require.True(t, spec.expErr.Is(gotErr), "expected %v but got %#+v", spec.expErr, gotErr) assert.Equal(t, spec.expCapturedGovContent, govRouter.captured) + assert.Equal(t, spec.expEvents, gotEvents) + assert.Empty(t, em.Events()) }) } } +func emitCtxEventWithGetContractInfoFn(fn func(ctx sdk.Context, contractAddress sdk.AccAddress) *wasmtypes.ContractInfo, event sdk.Event) func(ctx sdk.Context, contractAddress sdk.AccAddress) *wasmtypes.ContractInfo { + return func(ctx sdk.Context, contractAddress sdk.AccAddress) *wasmtypes.ContractInfo { + ctx.EventManager().EmitEvent(event) + return fn(ctx, contractAddress) + } +} + type registration struct { cb types.PrivilegeType addr sdk.AccAddress @@ -551,12 +575,12 @@ func TestHandleMintToken(t *testing.T) { assert.Equal(t, (*capturedSentCoins)[0].coins, spec.expMintedCoins) assert.Equal(t, (*capturedSentCoins)[0].recipientAddr, spec.expRecipient) require.Len(t, gotEvts, 1) - assert.Equal(t, types.EventTypeRewards, gotEvts[0].Type) + assert.Equal(t, types.EventTypeMintTokens, gotEvts[0].Type) }) } } -// noopRegisterHook does nothing and but all methods for registration +// noopRegisterHook provided method stubs for all methods for registration func noopRegisterHook(m *handlerTgradeKeeperMock, mutators ...func(*wasmtypes.ContractInfo)) { m.IsPrivilegedFn = func(ctx sdk.Context, contract sdk.AccAddress) bool { return true diff --git a/x/twasm/keeper/privileged.go b/x/twasm/keeper/privileged.go index 1bd47170..77b20821 100644 --- a/x/twasm/keeper/privileged.go +++ b/x/twasm/keeper/privileged.go @@ -43,7 +43,6 @@ func (k Keeper) SetPrivileged(ctx sdk.Context, contractAddr sdk.AccAddress) erro k.Logger(ctx).Info("Set privileged", "contractAddr", contractAddr.String()) event := sdk.NewEvent( types.EventTypeSetPrivileged, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(wasmtypes.AttributeKeyContractAddr, contractAddr.String()), ) ctx.EventManager().EmitEvent(event) @@ -98,7 +97,6 @@ func (k Keeper) UnsetPrivileged(ctx sdk.Context, contractAddr sdk.AccAddress) er k.Logger(ctx).Info("Unset privileged", "contractAddr", contractAddr.String()) event := sdk.NewEvent( types.EventTypeUnsetPrivileged, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(wasmtypes.AttributeKeyContractAddr, contractAddr.String()), ) ctx.EventManager().EmitEvent(event) @@ -187,7 +185,6 @@ func (k Keeper) appendToPrivilegedContracts(ctx sdk.Context, privilegeType types k.Logger(ctx).Info("Add privilege", "contractAddr", contractAddr.String(), "type", privilegeType.String()) event := sdk.NewEvent( types.EventTypeRegisterPrivilege, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(wasmtypes.AttributeKeyContractAddr, contractAddr.String()), sdk.NewAttribute(types.AttributeKeyCallbackType, privilegeType.String()), ) @@ -212,7 +209,6 @@ func (k Keeper) removePrivilegeRegistration(ctx sdk.Context, privilegeType types k.Logger(ctx).Info("Remove privilege", "contractAddr", contractAddr.String(), "type", privilegeType.String()) event := sdk.NewEvent( types.EventTypeReleasePrivilege, - sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), sdk.NewAttribute(wasmtypes.AttributeKeyContractAddr, contractAddr.String()), sdk.NewAttribute(types.AttributeKeyCallbackType, privilegeType.String()), ) diff --git a/x/twasm/types/events.go b/x/twasm/types/events.go index 6bf5cc71..96b31198 100644 --- a/x/twasm/types/events.go +++ b/x/twasm/types/events.go @@ -5,10 +5,10 @@ const ( EventTypeUnsetPrivileged = "unset_privileged_contract" EventTypeRegisterPrivilege = "register_privilege" EventTypeReleasePrivilege = "release_privilege" - EventTypeRewards = "rewards" + EventTypeMintTokens = "mint" ) const ( // event attributes - AttributeKeyCallbackType = "privilege_type" - AttributeKeyRewardRecipient = "recipient" + AttributeKeyCallbackType = "privilege_type" + AttributeKeyRecipient = "recipient" )