github.com/Finschia/finschia-sdk@v0.48.1/x/staking/types/authz.go (about)

     1  package types
     2  
     3  import (
     4  	sdk "github.com/Finschia/finschia-sdk/types"
     5  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
     6  	"github.com/Finschia/finschia-sdk/x/authz"
     7  )
     8  
     9  // TODO: Revisit this once we have propoer gas fee framework.
    10  // Tracking issues https://github.com/cosmos/cosmos-sdk/issues/9054, https://github.com/cosmos/cosmos-sdk/discussions/9072
    11  const gasCostPerIteration = uint64(10)
    12  
    13  // Normalized Msg type URLs
    14  var (
    15  	_ authz.Authorization = &StakeAuthorization{}
    16  )
    17  
    18  // NewStakeAuthorization creates a new StakeAuthorization object.
    19  func NewStakeAuthorization(allowed []sdk.ValAddress, denied []sdk.ValAddress, authzType AuthorizationType, amount *sdk.Coin) (*StakeAuthorization, error) {
    20  	allowedValidators, deniedValidators, err := validateAndBech32fy(allowed, denied)
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  
    25  	a := StakeAuthorization{}
    26  	if allowedValidators != nil {
    27  		a.Validators = &StakeAuthorization_AllowList{AllowList: &StakeAuthorization_Validators{Address: allowedValidators}}
    28  	} else {
    29  		a.Validators = &StakeAuthorization_DenyList{DenyList: &StakeAuthorization_Validators{Address: deniedValidators}}
    30  	}
    31  
    32  	if amount != nil {
    33  		a.MaxTokens = amount
    34  	}
    35  	a.AuthorizationType = authzType
    36  
    37  	return &a, nil
    38  }
    39  
    40  // MsgTypeURL implements Authorization.MsgTypeURL.
    41  func (a StakeAuthorization) MsgTypeURL() string {
    42  	authzType, err := normalizeAuthzType(a.AuthorizationType)
    43  	if err != nil {
    44  		panic(err)
    45  	}
    46  	return authzType
    47  }
    48  
    49  func (a StakeAuthorization) ValidateBasic() error {
    50  	if a.MaxTokens != nil && a.MaxTokens.IsNegative() {
    51  		return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "negative coin amount: %v", a.MaxTokens)
    52  	}
    53  	if a.AuthorizationType == AuthorizationType_AUTHORIZATION_TYPE_UNSPECIFIED {
    54  		return sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "unknown authorization type")
    55  	}
    56  
    57  	return nil
    58  }
    59  
    60  // Accept implements Authorization.Accept.
    61  func (a StakeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) {
    62  	var validatorAddress string
    63  	var amount sdk.Coin
    64  
    65  	switch msg := msg.(type) {
    66  	case *MsgDelegate:
    67  		validatorAddress = msg.ValidatorAddress
    68  		amount = msg.Amount
    69  	case *MsgUndelegate:
    70  		validatorAddress = msg.ValidatorAddress
    71  		amount = msg.Amount
    72  	case *MsgBeginRedelegate:
    73  		validatorAddress = msg.ValidatorDstAddress
    74  		amount = msg.Amount
    75  	default:
    76  		return authz.AcceptResponse{}, sdkerrors.ErrInvalidRequest.Wrap("unknown msg type")
    77  	}
    78  
    79  	isValidatorExists := false
    80  	allowedList := a.GetAllowList().GetAddress()
    81  	for _, validator := range allowedList {
    82  		ctx.GasMeter().ConsumeGas(gasCostPerIteration, "stake authorization")
    83  		if validator == validatorAddress {
    84  			isValidatorExists = true
    85  			break
    86  		}
    87  	}
    88  
    89  	denyList := a.GetDenyList().GetAddress()
    90  	for _, validator := range denyList {
    91  		ctx.GasMeter().ConsumeGas(gasCostPerIteration, "stake authorization")
    92  		if validator == validatorAddress {
    93  			return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrapf(" cannot delegate/undelegate to %s validator", validator)
    94  		}
    95  	}
    96  
    97  	if len(allowedList) > 0 && !isValidatorExists {
    98  		return authz.AcceptResponse{}, sdkerrors.ErrUnauthorized.Wrapf("cannot delegate/undelegate to %s validator", validatorAddress)
    99  	}
   100  
   101  	if a.MaxTokens == nil {
   102  		return authz.AcceptResponse{
   103  			Accept: true, Delete: false,
   104  			Updated: &StakeAuthorization{Validators: a.GetValidators(), AuthorizationType: a.GetAuthorizationType()},
   105  		}, nil
   106  	}
   107  
   108  	limitLeft := a.MaxTokens.Sub(amount)
   109  	if limitLeft.IsZero() {
   110  		return authz.AcceptResponse{Accept: true, Delete: true}, nil
   111  	}
   112  	return authz.AcceptResponse{
   113  		Accept: true, Delete: false,
   114  		Updated: &StakeAuthorization{Validators: a.GetValidators(), AuthorizationType: a.GetAuthorizationType(), MaxTokens: &limitLeft},
   115  	}, nil
   116  }
   117  
   118  func validateAndBech32fy(allowed []sdk.ValAddress, denied []sdk.ValAddress) ([]string, []string, error) {
   119  	if len(allowed) == 0 && len(denied) == 0 {
   120  		return nil, nil, sdkerrors.ErrInvalidRequest.Wrap("both allowed & deny list cannot be empty")
   121  	}
   122  
   123  	if len(allowed) > 0 && len(denied) > 0 {
   124  		return nil, nil, sdkerrors.ErrInvalidRequest.Wrap("cannot set both allowed & deny list")
   125  	}
   126  
   127  	allowedValidators := make([]string, len(allowed))
   128  	if len(allowed) > 0 {
   129  		for i, validator := range allowed {
   130  			allowedValidators[i] = validator.String()
   131  		}
   132  		return allowedValidators, nil, nil
   133  	}
   134  
   135  	deniedValidators := make([]string, len(denied))
   136  	for i, validator := range denied {
   137  		deniedValidators[i] = validator.String()
   138  	}
   139  
   140  	return nil, deniedValidators, nil
   141  }
   142  
   143  func normalizeAuthzType(authzType AuthorizationType) (string, error) {
   144  	switch authzType {
   145  	case AuthorizationType_AUTHORIZATION_TYPE_DELEGATE:
   146  		return sdk.MsgTypeURL(&MsgDelegate{}), nil
   147  	case AuthorizationType_AUTHORIZATION_TYPE_UNDELEGATE:
   148  		return sdk.MsgTypeURL(&MsgUndelegate{}), nil
   149  	case AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE:
   150  		return sdk.MsgTypeURL(&MsgBeginRedelegate{}), nil
   151  	default:
   152  		return "", sdkerrors.ErrInvalidType.Wrapf("unknown authorization type %T", authzType)
   153  	}
   154  }