diff --git a/x/wasm/keeper/keeper.go b/x/wasm/keeper/keeper.go index 41cb398ca3..a1edbbfea3 100644 --- a/x/wasm/keeper/keeper.go +++ b/x/wasm/keeper/keeper.go @@ -583,8 +583,15 @@ func (k Keeper) setContractAdmin(ctx sdk.Context, contractAddress, caller, newAd if !authZ.CanModifyContract(contractInfo.AdminAddr(), caller) { return sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not modify contract") } - contractInfo.Admin = newAdmin.String() + newAdminStr := newAdmin.String() + contractInfo.Admin = newAdminStr k.storeContractInfo(ctx, contractAddress, contractInfo) + ctx.EventManager().EmitEvent(sdk.NewEvent( + types.EventTypeUpdateContractAdmin, + sdk.NewAttribute(types.AttributeKeyContractAddr, contractAddress.String()), + sdk.NewAttribute(types.AttributeKeyNewAdmin, newAdminStr), + )) + return nil } @@ -922,6 +929,16 @@ func (k Keeper) setAccessConfig(ctx sdk.Context, codeID uint64, caller sdk.AccAd info.InstantiateConfig = newConfig k.storeCodeInfo(ctx, codeID, *info) + evt := sdk.NewEvent( + types.EventTypeUpdateCodeAccessConfig, + sdk.NewAttribute(types.AttributeKeyCodePermission, newConfig.Permission.String()), + sdk.NewAttribute(types.AttributeKeyCodeID, strconv.FormatUint(codeID, 10)), + ) + if addrs := newConfig.AllAuthorizedAddresses(); len(addrs) != 0 { + attr := sdk.NewAttribute(types.AttributeKeyAuthorizedAddresses, strings.Join(addrs, ",")) + evt.Attributes = append(evt.Attributes, attr.ToKVPair()) + } + ctx.EventManager().EmitEvent(evt) return nil } diff --git a/x/wasm/keeper/keeper_test.go b/x/wasm/keeper/keeper_test.go index d891dc52e3..a508aea841 100644 --- a/x/wasm/keeper/keeper_test.go +++ b/x/wasm/keeper/keeper_test.go @@ -11,6 +11,8 @@ import ( "testing" "time" + abci "github.com/tendermint/tendermint/abci/types" + wasmvm "github.com/CosmWasm/wasmvm" wasmvmtypes "github.com/CosmWasm/wasmvm/types" "github.com/cosmos/cosmos-sdk/baseapp" @@ -2041,6 +2043,7 @@ func TestSetAccessConfig(t *testing.T) { k := keepers.WasmKeeper creatorAddr := RandomAccountAddress(t) nonCreatorAddr := RandomAccountAddress(t) + const codeID = 1 specs := map[string]struct { authz AuthorizationPolicy @@ -2048,18 +2051,27 @@ func TestSetAccessConfig(t *testing.T) { newConfig types.AccessConfig caller sdk.AccAddress expErr bool + expEvts map[string]string }{ "user with new permissions == chain permissions": { authz: DefaultAuthorizationPolicy{}, chainPermission: types.AccessTypeEverybody, newConfig: types.AllowEverybody, caller: creatorAddr, + expEvts: map[string]string{ + "code_id": "1", + "code_permission": "Everybody", + }, }, "user with new permissions < chain permissions": { authz: DefaultAuthorizationPolicy{}, chainPermission: types.AccessTypeEverybody, newConfig: types.AllowNobody, caller: creatorAddr, + expEvts: map[string]string{ + "code_id": "1", + "code_permission": "Nobody", + }, }, "user with new permissions > chain permissions": { authz: DefaultAuthorizationPolicy{}, @@ -2080,29 +2092,59 @@ func TestSetAccessConfig(t *testing.T) { chainPermission: types.AccessTypeEverybody, newConfig: types.AllowEverybody, caller: creatorAddr, + expEvts: map[string]string{ + "code_id": "1", + "code_permission": "Everybody", + }, }, "gov with new permissions < chain permissions": { authz: GovAuthorizationPolicy{}, chainPermission: types.AccessTypeEverybody, newConfig: types.AllowNobody, caller: creatorAddr, + expEvts: map[string]string{ + "code_id": "1", + "code_permission": "Nobody", + }, }, "gov with new permissions > chain permissions": { authz: GovAuthorizationPolicy{}, chainPermission: types.AccessTypeNobody, newConfig: types.AccessTypeOnlyAddress.With(creatorAddr), caller: creatorAddr, + expEvts: map[string]string{ + "code_id": "1", + "code_permission": "OnlyAddress", + "authorized_addresses": creatorAddr.String(), + }, + }, + "gov with new permissions > chain permissions - multiple addresses": { + authz: GovAuthorizationPolicy{}, + chainPermission: types.AccessTypeNobody, + newConfig: types.AccessTypeAnyOfAddresses.With(creatorAddr, nonCreatorAddr), + caller: creatorAddr, + expEvts: map[string]string{ + "code_id": "1", + "code_permission": "AnyOfAddresses", + "authorized_addresses": creatorAddr.String() + "," + nonCreatorAddr.String(), + }, }, "gov without actor": { authz: GovAuthorizationPolicy{}, chainPermission: types.AccessTypeEverybody, newConfig: types.AllowEverybody, + expEvts: map[string]string{ + "code_id": "1", + "code_permission": "Everybody", + }, }, } - const codeID = 1 for name, spec := range specs { t.Run(name, func(t *testing.T) { ctx, _ := parentCtx.CacheContext() + em := sdk.NewEventManager() + ctx = ctx.WithEventManager(em) + newParams := types.DefaultParams() newParams.InstantiateDefaultPermission = spec.chainPermission k.SetParams(ctx, newParams) @@ -2115,6 +2157,10 @@ func TestSetAccessConfig(t *testing.T) { return } require.NoError(t, gotErr) + // and event emitted + require.Len(t, em.Events(), 1) + assert.Equal(t, "update_code_access_config", em.Events()[0].Type) + assert.Equal(t, spec.expEvts, attrsToStringMap(em.Events()[0].Attributes)) }) } } @@ -2288,3 +2334,77 @@ func TestIteratorContractByCreator(t *testing.T) { }) } } + +func TestSetContractAdmin(t *testing.T) { + parentCtx, keepers := CreateTestInput(t, false, AvailableCapabilities) + k := keepers.WasmKeeper + myAddr := RandomAccountAddress(t) + example := InstantiateReflectExampleContract(t, parentCtx, keepers) + specs := map[string]struct { + newAdmin sdk.AccAddress + caller sdk.AccAddress + policy AuthorizationPolicy + expAdmin string + expErr bool + }{ + "update admin": { + newAdmin: myAddr, + caller: example.CreatorAddr, + policy: DefaultAuthorizationPolicy{}, + expAdmin: myAddr.String(), + }, + "update admin - unauthorized": { + newAdmin: myAddr, + caller: RandomAccountAddress(t), + policy: DefaultAuthorizationPolicy{}, + expErr: true, + }, + "clear admin - default policy": { + caller: example.CreatorAddr, + policy: DefaultAuthorizationPolicy{}, + expAdmin: "", + }, + "clear admin - unauthorized": { + expAdmin: "", + policy: DefaultAuthorizationPolicy{}, + caller: RandomAccountAddress(t), + expErr: true, + }, + "clear admin - gov policy": { + newAdmin: nil, + policy: GovAuthorizationPolicy{}, + caller: example.CreatorAddr, + expAdmin: "", + }, + } + for name, spec := range specs { + t.Run(name, func(t *testing.T) { + ctx, _ := parentCtx.CacheContext() + em := sdk.NewEventManager() + ctx = ctx.WithEventManager(em) + gotErr := k.setContractAdmin(ctx, example.Contract, spec.caller, spec.newAdmin, spec.policy) + if spec.expErr { + require.Error(t, gotErr) + return + } + require.NoError(t, gotErr) + assert.Equal(t, spec.expAdmin, k.GetContractInfo(ctx, example.Contract).Admin) + // and event emitted + require.Len(t, em.Events(), 1) + assert.Equal(t, "update_contract_admin", em.Events()[0].Type) + exp := map[string]string{ + "_contract_address": example.Contract.String(), + "new_admin_address": spec.expAdmin, + } + assert.Equal(t, exp, attrsToStringMap(em.Events()[0].Attributes)) + }) + } +} + +func attrsToStringMap(attrs []abci.EventAttribute) map[string]string { + r := make(map[string]string, len(attrs)) + for _, v := range attrs { + r[string(v.Key)] = string(v.Value) + } + return r +} diff --git a/x/wasm/keeper/msg_server.go b/x/wasm/keeper/msg_server.go index e7d35c3398..d0383db699 100644 --- a/x/wasm/keeper/msg_server.go +++ b/x/wasm/keeper/msg_server.go @@ -246,6 +246,11 @@ func (m msgServer) UpdateInstantiateConfig(goCtx context.Context, msg *types.Msg if err := m.keeper.SetAccessConfig(ctx, msg.CodeID, sdk.AccAddress(msg.Sender), *msg.NewInstantiatePermission); err != nil { return nil, err } + ctx.EventManager().EmitEvent(sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender), + )) return &types.MsgUpdateInstantiateConfigResponse{}, nil } diff --git a/x/wasm/keeper/test_common.go b/x/wasm/keeper/test_common.go index c9873342fc..152e5fc766 100644 --- a/x/wasm/keeper/test_common.go +++ b/x/wasm/keeper/test_common.go @@ -676,7 +676,7 @@ func InstantiateReflectExampleContract(t testing.TB, ctx sdk.Context, keepers Te example := StoreReflectContract(t, ctx, keepers) initialAmount := sdk.NewCoins(sdk.NewInt64Coin("denom", 100)) label := "demo contract to query" - contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, example.CodeID, example.CreatorAddr, nil, []byte("{}"), label, initialAmount) + contractAddr, _, err := keepers.ContractKeeper.Instantiate(ctx, example.CodeID, example.CreatorAddr, example.CreatorAddr, []byte("{}"), label, initialAmount) require.NoError(t, err) return ExampleInstance{ diff --git a/x/wasm/types/events.go b/x/wasm/types/events.go index 579024ed0c..442c3ed369 100644 --- a/x/wasm/types/events.go +++ b/x/wasm/types/events.go @@ -6,24 +6,29 @@ const ( // CustomContractEventPrefix contracts can create custom events. To not mix them with other system events they got the `wasm-` prefix. CustomContractEventPrefix = "wasm-" - EventTypeStoreCode = "store_code" - EventTypeInstantiate = "instantiate" - EventTypeExecute = "execute" - EventTypeMigrate = "migrate" - EventTypePinCode = "pin_code" - EventTypeUnpinCode = "unpin_code" - EventTypeSudo = "sudo" - EventTypeReply = "reply" - EventTypeGovContractResult = "gov_contract_result" + EventTypeStoreCode = "store_code" + EventTypeInstantiate = "instantiate" + EventTypeExecute = "execute" + EventTypeMigrate = "migrate" + EventTypePinCode = "pin_code" + EventTypeUnpinCode = "unpin_code" + EventTypeSudo = "sudo" + EventTypeReply = "reply" + EventTypeGovContractResult = "gov_contract_result" + EventTypeUpdateContractAdmin = "update_contract_admin" + EventTypeUpdateCodeAccessConfig = "update_code_access_config" ) // event attributes returned from contract execution const ( AttributeReservedPrefix = "_" - AttributeKeyContractAddr = "_contract_address" - AttributeKeyCodeID = "code_id" - AttributeKeyChecksum = "code_checksum" - AttributeKeyResultDataHex = "result" - AttributeKeyRequiredCapability = "required_capability" + AttributeKeyContractAddr = "_contract_address" + AttributeKeyCodeID = "code_id" + AttributeKeyChecksum = "code_checksum" + AttributeKeyResultDataHex = "result" + AttributeKeyRequiredCapability = "required_capability" + AttributeKeyNewAdmin = "new_admin_address" + AttributeKeyCodePermission = "code_permission" + AttributeKeyAuthorizedAddresses = "authorized_addresses" ) diff --git a/x/wasm/types/types.pb.go b/x/wasm/types/types.pb.go index 9854da9dac..738c287682 100644 --- a/x/wasm/types/types.pb.go +++ b/x/wasm/types/types.pb.go @@ -1569,6 +1569,17 @@ func (m *AccessConfig) Unmarshal(dAtA []byte) error { return nil } +// si +func (a AccessConfig) AllAuthorizedAddresses() []string { + switch a.Permission { + case AccessTypeAnyOfAddresses: + return a.Addresses + case AccessTypeOnlyAddress: + return []string{a.Address} + } + return []string{} +} + func (m *Params) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0