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 }