diff --git a/CHANGELOG.md b/CHANGELOG.md index 48832300cde7..773a11c084c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Features +* (autz) [#35](https://github.com/evmos/cosmos-sdk/pull/35) Add Distribution authorization * (autz) [#21](https://github.com/evmos/cosmos-sdk/pull/21) Add CancelUnbondingDelegation authz ## [v0.47.4](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.47.4) - 2023-07-17 diff --git a/proto/cosmos/distribution/v1beta1/authz.proto b/proto/cosmos/distribution/v1beta1/authz.proto new file mode 100644 index 000000000000..89b8009b0275 --- /dev/null +++ b/proto/cosmos/distribution/v1beta1/authz.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +import "cosmos_proto/cosmos.proto"; + +option go_package = "github.com/cosmos/cosmos-sdk/x/distribution/types"; + +// DistributionAuthorization defines a grant that can be given to an address to allow them to +// execute distribution messages on behalf of the granter. +message DistributionAuthorization { + option (cosmos_proto.implements_interface) = "Authorization"; + + // message_type represents the type of the message that is authorized by this DistributionAuthorization. + string message_type = 1; + // allowed_list specifies list of addresses that are allowed to execute the distribution messages. + repeated string allowed_list = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"]; +} + diff --git a/x/distribution/types/authz.go b/x/distribution/types/authz.go new file mode 100644 index 000000000000..0fdd2d47f96e --- /dev/null +++ b/x/distribution/types/authz.go @@ -0,0 +1,78 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/authz" + "golang.org/x/exp/slices" +) + +var _ authz.Authorization = &DistributionAuthorization{} + +var ( + SetWithdrawerAddressMsg = sdk.MsgTypeURL(&MsgSetWithdrawAddress{}) + WithdrawDelegatorRewardMsg = sdk.MsgTypeURL(&MsgWithdrawDelegatorReward{}) + WithdrawValidatorCommissionMsg = sdk.MsgTypeURL(&MsgWithdrawValidatorCommission{}) +) + +// NewDistributionAuthorization creates a new DistributionAuthorization. +func NewDistributionAuthorization(msgType string, allowed ...string) *DistributionAuthorization { + return &DistributionAuthorization{ + MessageType: msgType, + AllowedList: allowed, + } +} + +// MsgTypeURL implements Authorization.MsgTypeURL. +func (da *DistributionAuthorization) MsgTypeURL() string { + return da.MessageType +} + +// Accept implements Authorization.Accept. It checks, that the +// withdrawer for MsgSetWithdrawAddress, +// validator for MsgWithdrawValidatorCommission, +// the delegator address for MsgWithdrawDelegatorReward +// is in the allowed list. If these conditions are met, the AcceptResponse is returned. +func (da *DistributionAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) { + switch msg := msg.(type) { + case *MsgSetWithdrawAddress: + if !slices.Contains(da.AllowedList, msg.WithdrawAddress) { + return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrap("address is not in the allowed list") + } + case *MsgWithdrawValidatorCommission: + if !slices.Contains(da.AllowedList, msg.ValidatorAddress) { + return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrap("address is not in the allowed list") + } + case *MsgWithdrawDelegatorReward: + if !slices.Contains(da.AllowedList, msg.DelegatorAddress) { + return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrap("address is not in the allowed list") + } + default: + return authz.AcceptResponse{}, sdkerrors.ErrInvalidRequest.Wrap("unknown msg type") + } + + return authz.AcceptResponse{ + Accept: true, + Delete: false, + Updated: &DistributionAuthorization{ + AllowedList: da.AllowedList, + MessageType: da.MessageType, + }, + }, nil +} + +// ValidateBasic performs a stateless validation of the fields. +func (da *DistributionAuthorization) ValidateBasic() error { + if len(da.AllowedList) == 0 { + return sdkerrors.ErrInvalidRequest.Wrap("allowed list cannot be empty") + } + + // validate all the addresses are correct bech32 addresses + for _, addr := range da.AllowedList { + if _, err := sdk.AccAddressFromBech32(addr); err != nil { + return sdkerrors.ErrInvalidAddress.Wrapf("invalid address: %s", addr) + } + } + + return nil +} diff --git a/x/distribution/types/authz.pb.go b/x/distribution/types/authz.pb.go new file mode 100644 index 000000000000..4c98aba41fc1 --- /dev/null +++ b/x/distribution/types/authz.pb.go @@ -0,0 +1,382 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: cosmos/distribution/v1beta1/authz.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// DistributionAuthorization defines a grant that can be given to an address to allow them to +// execute distribution messages on behalf of the granter. +type DistributionAuthorization struct { + // message_type represents the type of the message that is authorized by this DistributionAuthorization. + MessageType string `protobuf:"bytes,1,opt,name=message_type,json=messageType,proto3" json:"message_type,omitempty"` + // allowed_list specifies list of addresses that are allowed to execute the distribution messages. + AllowedList []string `protobuf:"bytes,2,rep,name=allowed_list,json=allowedList,proto3" json:"allowed_list,omitempty"` +} + +func (da *DistributionAuthorization) Reset() { *da = DistributionAuthorization{} } +func (da *DistributionAuthorization) String() string { return proto.CompactTextString(da) } +func (*DistributionAuthorization) ProtoMessage() {} +func (*DistributionAuthorization) Descriptor() ([]byte, []int) { + return fileDescriptor_6f4334195c58df3b, []int{0} +} +func (da *DistributionAuthorization) XXX_Unmarshal(b []byte) error { + return da.Unmarshal(b) +} +func (da *DistributionAuthorization) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_DistributionAuthorization.Marshal(b, da, deterministic) + } else { + b = b[:cap(b)] + n, err := da.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (da *DistributionAuthorization) XXX_Merge(src proto.Message) { + xxx_messageInfo_DistributionAuthorization.Merge(da, src) +} +func (da *DistributionAuthorization) XXX_Size() int { + return da.Size() +} +func (da *DistributionAuthorization) XXX_DiscardUnknown() { + xxx_messageInfo_DistributionAuthorization.DiscardUnknown(da) +} + +var xxx_messageInfo_DistributionAuthorization proto.InternalMessageInfo + +func (da *DistributionAuthorization) GetMessageType() string { + if da != nil { + return da.MessageType + } + return "" +} + +func (da *DistributionAuthorization) GetAllowedList() []string { + if da != nil { + return da.AllowedList + } + return nil +} + +func init() { + proto.RegisterType((*DistributionAuthorization)(nil), "cosmos.distribution.v1beta1.DistributionAuthorization") +} + +func init() { + proto.RegisterFile("cosmos/distribution/v1beta1/authz.proto", fileDescriptor_6f4334195c58df3b) +} + +var fileDescriptor_6f4334195c58df3b = []byte{ + // 256 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4f, 0xce, 0x2f, 0xce, + 0xcd, 0x2f, 0xd6, 0x4f, 0xc9, 0x2c, 0x2e, 0x29, 0xca, 0x4c, 0x2a, 0x2d, 0xc9, 0xcc, 0xcf, 0xd3, + 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0x2c, 0x2d, 0xc9, 0xa8, 0xd2, 0x2b, 0x28, + 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x86, 0x28, 0xd4, 0x43, 0x56, 0xa8, 0x07, 0x55, 0x28, 0x25, 0x09, + 0x91, 0x8c, 0x07, 0x2b, 0xd5, 0x87, 0xaa, 0x04, 0x73, 0x94, 0xfa, 0x18, 0xb9, 0x24, 0x5d, 0x90, + 0xf4, 0x38, 0x96, 0x96, 0x64, 0xe4, 0x17, 0x65, 0x56, 0x25, 0x82, 0x38, 0x42, 0x8a, 0x5c, 0x3c, + 0xb9, 0xa9, 0xc5, 0xc5, 0x89, 0xe9, 0xa9, 0xf1, 0x25, 0x95, 0x05, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, + 0x1a, 0x9c, 0x41, 0xdc, 0x50, 0xb1, 0x90, 0xca, 0x82, 0x54, 0x21, 0x6b, 0x2e, 0x9e, 0xc4, 0x9c, + 0x9c, 0xfc, 0xf2, 0xd4, 0x94, 0xf8, 0x9c, 0xcc, 0xe2, 0x12, 0x09, 0x26, 0x05, 0x66, 0x0d, 0x4e, + 0x27, 0x89, 0x4b, 0x5b, 0x74, 0x45, 0xa0, 0x16, 0x39, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x07, + 0x97, 0x14, 0x65, 0xe6, 0xa5, 0x07, 0x71, 0x43, 0x55, 0xfb, 0x64, 0x16, 0x97, 0x58, 0x09, 0x9e, + 0xda, 0xa2, 0xcb, 0x8b, 0x62, 0xa5, 0x93, 0xf7, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, + 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, + 0x31, 0x44, 0x19, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0x42, 0xfd, 0x00, + 0xa5, 0x74, 0x8b, 0x53, 0xb2, 0xf5, 0x2b, 0x50, 0xc3, 0x08, 0xe4, 0xdc, 0xe2, 0x24, 0x36, 0xb0, + 0x27, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x27, 0x43, 0x33, 0x09, 0x47, 0x01, 0x00, 0x00, +} + +func (da *DistributionAuthorization) Marshal() (dAtA []byte, err error) { + size := da.Size() + dAtA = make([]byte, size) + n, err := da.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (da *DistributionAuthorization) MarshalTo(dAtA []byte) (int, error) { + size := da.Size() + return da.MarshalToSizedBuffer(dAtA[:size]) +} + +func (da *DistributionAuthorization) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(da.AllowedList) > 0 { + for iNdEx := len(da.AllowedList) - 1; iNdEx >= 0; iNdEx-- { + i -= len(da.AllowedList[iNdEx]) + copy(dAtA[i:], da.AllowedList[iNdEx]) + i = encodeVarintAuthz(dAtA, i, uint64(len(da.AllowedList[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(da.MessageType) > 0 { + i -= len(da.MessageType) + copy(dAtA[i:], da.MessageType) + i = encodeVarintAuthz(dAtA, i, uint64(len(da.MessageType))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAuthz(dAtA []byte, offset int, v uint64) int { + offset -= sovAuthz(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (da *DistributionAuthorization) Size() (n int) { + if da == nil { + return 0 + } + var l int + _ = l + l = len(da.MessageType) + if l > 0 { + n += 1 + l + sovAuthz(uint64(l)) + } + if len(da.AllowedList) > 0 { + for _, s := range da.AllowedList { + l = len(s) + n += 1 + l + sovAuthz(uint64(l)) + } + } + return n +} + +func sovAuthz(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAuthz(x uint64) (n int) { + return sovAuthz(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (da *DistributionAuthorization) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DistributionAuthorization: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DistributionAuthorization: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MessageType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + da.MessageType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedList", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAuthz + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAuthz + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAuthz + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + da.AllowedList = append(da.AllowedList, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAuthz(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAuthz + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAuthz(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAuthz + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAuthz + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAuthz + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAuthz + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAuthz = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAuthz = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAuthz = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/distribution/types/authz_test.go b/x/distribution/types/authz_test.go new file mode 100644 index 000000000000..6d5d7767f931 --- /dev/null +++ b/x/distribution/types/authz_test.go @@ -0,0 +1,133 @@ +package types_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + distributiontypes "github.com/cosmos/cosmos-sdk/x/distribution/types" +) + +var ( + validatorAddr = "cosmosvaloper1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47" + delegatorAddr = "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47" + randomAddr = "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf42" + withdrawerAddr = "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf48" + withdrawerAddr2 = "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf46" +) + +func TestAuthzAuthorizations(t *testing.T) { + key := sdk.NewKVStoreKey(distributiontypes.StoreKey) + testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) + ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{}) + + testCases := []struct { + name string + msgTypeUrl string + msg sdk.Msg + expUpdated distributiontypes.DistributionAuthorization + allowed []string + expectErr bool + }{ + { + "fail - set withdrawer address not in allowed list", + distributiontypes.SetWithdrawerAddressMsg, + &distributiontypes.MsgSetWithdrawAddress{ + DelegatorAddress: delegatorAddr, + WithdrawAddress: withdrawerAddr, + }, + distributiontypes.DistributionAuthorization{ + MessageType: distributiontypes.SetWithdrawerAddressMsg, + AllowedList: []string{delegatorAddr}, + }, + []string{delegatorAddr}, + true, + }, + { + "fail - withdraw validator commission address not in allowed list", + distributiontypes.WithdrawValidatorCommissionMsg, + &distributiontypes.MsgWithdrawValidatorCommission{ + ValidatorAddress: validatorAddr, + }, + distributiontypes.DistributionAuthorization{ + MessageType: distributiontypes.WithdrawValidatorCommissionMsg, + AllowedList: []string{delegatorAddr}, + }, + []string{delegatorAddr}, + true, + }, + { + "fail - withdraw delegator rewards address not in allowed list", + distributiontypes.WithdrawValidatorCommissionMsg, + &distributiontypes.MsgWithdrawDelegatorReward{ + DelegatorAddress: delegatorAddr, + ValidatorAddress: validatorAddr, + }, + distributiontypes.DistributionAuthorization{ + MessageType: distributiontypes.WithdrawValidatorCommissionMsg, + AllowedList: []string{randomAddr}, + }, + []string{randomAddr}, + true, + }, + { + "success - set withdrawer address in allowed list", + distributiontypes.WithdrawValidatorCommissionMsg, + &distributiontypes.MsgSetWithdrawAddress{ + DelegatorAddress: delegatorAddr, + WithdrawAddress: withdrawerAddr2, + }, + distributiontypes.DistributionAuthorization{ + MessageType: distributiontypes.WithdrawValidatorCommissionMsg, + AllowedList: []string{withdrawerAddr2}, + }, + []string{withdrawerAddr2}, + false, + }, + { + "success - withdraw delegator rewards address in allowed list", + distributiontypes.WithdrawDelegatorRewardMsg, + &distributiontypes.MsgWithdrawDelegatorReward{ + DelegatorAddress: delegatorAddr, + }, + distributiontypes.DistributionAuthorization{ + MessageType: distributiontypes.WithdrawDelegatorRewardMsg, + AllowedList: []string{delegatorAddr}, + }, + []string{delegatorAddr}, + false, + }, + { + "success - withdraw validator commission address in allowed list", + distributiontypes.WithdrawValidatorCommissionMsg, + &distributiontypes.MsgWithdrawValidatorCommission{ + ValidatorAddress: validatorAddr, + }, + distributiontypes.DistributionAuthorization{ + MessageType: distributiontypes.WithdrawValidatorCommissionMsg, + AllowedList: []string{validatorAddr}, + }, + []string{validatorAddr}, + false, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + distAuth := distributiontypes.NewDistributionAuthorization(tc.msgTypeUrl, tc.allowed...) + resp, err := distAuth.Accept(ctx, tc.msg) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + if resp.Updated != nil { + require.Equal(t, tc.expUpdated.String(), resp.Updated.String()) + } + } + }) + } +} diff --git a/x/distribution/types/codec.go b/x/distribution/types/codec.go index 9d5118a938f8..a58cc2eb8217 100644 --- a/x/distribution/types/codec.go +++ b/x/distribution/types/codec.go @@ -7,6 +7,7 @@ import ( cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" + "github.com/cosmos/cosmos-sdk/x/authz" authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" @@ -25,6 +26,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { legacy.RegisterAminoMsg(cdc, &MsgCommunityPoolSpend{}, "cosmos-sdk/distr/MsgCommunityPoolSpend") cdc.RegisterConcrete(Params{}, "cosmos-sdk/x/distribution/Params", nil) + cdc.RegisterConcrete(&DistributionAuthorization{}, "cosmos-sdk/DistributionAuthorization", nil) } func RegisterInterfaces(registry types.InterfaceRegistry) { @@ -42,6 +44,11 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { (*govtypes.Content)(nil), &CommunityPoolSpendProposal{}) + registry.RegisterImplementations( + (*authz.Authorization)(nil), + &DistributionAuthorization{}, + ) + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) }