Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(distr): add distribution authorization (backport #35) #43

Merged
merged 5 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions proto/cosmos/distribution/v1beta1/authz.proto
Original file line number Diff line number Diff line change
@@ -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"];
}

78 changes: 78 additions & 0 deletions x/distribution/types/authz.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading
Loading