Skip to content

Commit

Permalink
feat(distr): add distribution authorization (backport #35) (#43)
Browse files Browse the repository at this point in the history
Co-authored-by: Vladislav Varadinov <[email protected]>
Co-authored-by: tom <[email protected]>
  • Loading branch information
3 people authored Jul 20, 2023
1 parent c2d194b commit e1cdf73
Show file tree
Hide file tree
Showing 6 changed files with 619 additions and 0 deletions.
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

0 comments on commit e1cdf73

Please sign in to comment.