Skip to content

Commit

Permalink
feat: add CancelUnbondingDelegation authz (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vvaradinov authored Mar 14, 2023
1 parent ff4eb6b commit 0f17a15
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 44 deletions.
2 changes: 2 additions & 0 deletions proto/cosmos/staking/v1beta1/authz.proto
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ enum AuthorizationType {
AUTHORIZATION_TYPE_UNDELEGATE = 2;
// AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for Msg/BeginRedelegate
AUTHORIZATION_TYPE_REDELEGATE = 3;
// AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION defines an authorization type for Msg/MsgCancelUnbondingDelegation
AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION = 4;
}
52 changes: 43 additions & 9 deletions x/staking/types/authz.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,23 @@ func NewStakeAuthorization(allowed []sdk.ValAddress, denied []sdk.ValAddress, au

a := StakeAuthorization{}
if allowedValidators != nil {
a.Validators = &StakeAuthorization_AllowList{AllowList: &StakeAuthorization_Validators{Address: allowedValidators}}
a.Validators = &StakeAuthorization_AllowList{
AllowList: &StakeAuthorization_Validators{
Address: allowedValidators,
},
}
} else {
a.Validators = &StakeAuthorization_DenyList{DenyList: &StakeAuthorization_Validators{Address: deniedValidators}}
a.Validators = &StakeAuthorization_DenyList{
DenyList: &StakeAuthorization_Validators{
Address: deniedValidators,
},
}
}

if amount != nil {
a.MaxTokens = amount
}

a.AuthorizationType = authzType

return &a, nil
Expand All @@ -43,21 +52,30 @@ func (a StakeAuthorization) MsgTypeURL() string {
return authzType
}

// ValidateBasic performs a stateless validation of the fields.
// It fails if MaxTokens is either undefined or negative or if the authorization
// is unspecified.
func (a StakeAuthorization) ValidateBasic() error {
if a.MaxTokens != nil && a.MaxTokens.IsNegative() {
return sdkerrors.Wrapf(authz.ErrNegativeMaxTokens, "negative coin amount: %v", a.MaxTokens)
}

if a.AuthorizationType == AuthorizationType_AUTHORIZATION_TYPE_UNSPECIFIED {
return authz.ErrUnknownAuthorizationType
}

return nil
}

// Accept implements Authorization.Accept.
// Accept implements Authorization.Accept. It checks, that the validator is not in the denied list,
// and, should the allowed list not be empty, if the validator is in the allowed list.
// If these conditions are met, the authorization amount is validated and if successful, the
// corresponding AcceptResponse is returned.
func (a StakeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptResponse, error) {
var validatorAddress string
var amount sdk.Coin
var (
validatorAddress string
amount sdk.Coin
)

switch msg := msg.(type) {
case *MsgDelegate:
Expand All @@ -69,6 +87,9 @@ func (a StakeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptRe
case *MsgBeginRedelegate:
validatorAddress = msg.ValidatorDstAddress
amount = msg.Amount
case *MsgCancelUnbondingDelegation:
validatorAddress = msg.ValidatorAddress
amount = msg.Amount
default:
return authz.AcceptResponse{}, sdkerrors.ErrInvalidRequest.Wrap("unknown msg type")
}
Expand Down Expand Up @@ -97,21 +118,32 @@ func (a StakeAuthorization) Accept(ctx sdk.Context, msg sdk.Msg) (authz.AcceptRe

if a.MaxTokens == nil {
return authz.AcceptResponse{
Accept: true, Delete: false,
Updated: &StakeAuthorization{Validators: a.GetValidators(), AuthorizationType: a.GetAuthorizationType()},
Accept: true,
Delete: false,
Updated: &StakeAuthorization{
Validators: a.GetValidators(),
AuthorizationType: a.GetAuthorizationType(),
},
}, nil
}

limitLeft, err := a.MaxTokens.SafeSub(amount)
if err != nil {
return authz.AcceptResponse{}, err
}

if limitLeft.IsZero() {
return authz.AcceptResponse{Accept: true, Delete: true}, nil
}

return authz.AcceptResponse{
Accept: true, Delete: false,
Updated: &StakeAuthorization{Validators: a.GetValidators(), AuthorizationType: a.GetAuthorizationType(), MaxTokens: &limitLeft},
Accept: true,
Delete: false,
Updated: &StakeAuthorization{
Validators: a.GetValidators(),
AuthorizationType: a.GetAuthorizationType(),
MaxTokens: &limitLeft,
},
}, nil
}

Expand Down Expand Up @@ -149,6 +181,8 @@ func normalizeAuthzType(authzType AuthorizationType) (string, error) {
return sdk.MsgTypeURL(&MsgUndelegate{}), nil
case AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE:
return sdk.MsgTypeURL(&MsgBeginRedelegate{}), nil
case AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION:
return sdk.MsgTypeURL(&MsgCancelUnbondingDelegation{}), nil
default:
return "", sdkerrors.Wrapf(authz.ErrUnknownAuthorizationType, "cannot normalize authz type with %T", authzType)
}
Expand Down
76 changes: 41 additions & 35 deletions x/staking/types/authz.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions x/staking/types/authz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ func TestAuthzAuthorizations(t *testing.T) {
beginRedelAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_REDELEGATE, &coin100)
require.Equal(t, beginRedelAuth.MsgTypeURL(), sdk.MsgTypeURL(&stakingtypes.MsgBeginRedelegate{}))

// verify MethodName for CancelUnbondingDelegation
cancelUnbondAuth, _ := stakingtypes.NewStakeAuthorization([]sdk.ValAddress{val1, val2}, []sdk.ValAddress{}, stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION, &coin100)
require.Equal(t, cancelUnbondAuth.MsgTypeURL(), sdk.MsgTypeURL(&stakingtypes.MsgCancelUnbondingDelegation{}))

validators1_2 := []string{val1.String(), val2.String()}

testCases := []struct {
Expand Down Expand Up @@ -276,6 +280,73 @@ func TestAuthzAuthorizations(t *testing.T) {
false,
nil,
},
{
"cancel unbonding delegation: expect 0 remaining coins",
[]sdk.ValAddress{val1},
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, val1, ctx.BlockHeight(), coin100),
false,
true,
nil,
},
{
"cancel unbonding delegation: verify remaining coins",
[]sdk.ValAddress{val1},
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, val1, ctx.BlockHeight(), coin50),
false,
false,
&stakingtypes.StakeAuthorization{
Validators: &stakingtypes.StakeAuthorization_AllowList{
AllowList: &stakingtypes.StakeAuthorization_Validators{Address: []string{val1.String()}},
},
MaxTokens: &coin50,
AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
},
},
{
"cancel unbonding delegation: testing with invalid validator",
[]sdk.ValAddress{val1, val2},
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, val3, ctx.BlockHeight(), coin50),
true,
false,
nil,
},
{
"cancel unbonding delegation: testing delegate without spent limit",
[]sdk.ValAddress{val1, val2},
[]sdk.ValAddress{},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
nil,
stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, val2, ctx.BlockHeight(), coin100),
false,
false,
&stakingtypes.StakeAuthorization{
Validators: &stakingtypes.StakeAuthorization_AllowList{
AllowList: &stakingtypes.StakeAuthorization_Validators{Address: validators1_2},
},
MaxTokens: nil,
AuthorizationType: stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
},
},
{
"cancel unbonding delegation: fail cannot undelegate, permission denied",
[]sdk.ValAddress{},
[]sdk.ValAddress{val1},
stakingtypes.AuthorizationType_AUTHORIZATION_TYPE_CANCEL_UNBONDING_DELEGATION,
&coin100,
stakingtypes.NewMsgCancelUnbondingDelegation(delAddr, val1, ctx.BlockHeight(), coin100),
true,
false,
nil,
},
}

for _, tc := range testCases {
Expand Down

0 comments on commit 0f17a15

Please sign in to comment.