2020-10-16 07:53:49 -07:00
|
|
|
package keeper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
feat: Cancel unbonding delegation entry (#10885)
## Description
Closes: #577
This pull request contains `Canceling unbonding delegation entry` and `delegate back to previous validator`
### `Msg` Service
```protobuf=
package cosmos.staking.v1beta1;
service Msg {
// CancelUnbondingDelegation
rpc CancelUnbondingDelegation(MsgCancelUnbondingDelegation) returns (MsgCancelUnbondingDelegationResponse);
}
// MsgCancelUnbondingDelegation
message MsgCancelUnbondingDelegation {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
// creation_height is the height which the unbonding took place.
int64 creation_height = 4;
}
// MsgCancelUnbondingDelegationResponse
message MsgCancelUnbondingDelegationResponse{
}
```
### `Msg` Method Implementation
```go=
func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *types.MsgCancelUnbondingDelegation) (*types.MsgCancelUnbondingDelegationResponse, error) {
/*
// checking the unbonding delegation at creation_height
// get the unbonding delegations of delegatorAddress
if ubdEntry balance is equal to msg.Amount
remove the entry from ubd
else
update the specific entry with new balance
if len(ubd.Entries) == 0 {
k.RemoveUnbondingDelegation(ctx, ubd)
} else {
k.SetUnbondingDelegation(ctx, ubd)
}
*/
// update the delegation back to validator
// get validator
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return nil, types.ErrNoValidatorFound
}
// delegate the unbonding amount to validator back
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonding, validator, false)
if err != nil {
return nil, err
}
_, err := ms.Keeper.CancelUnbondingDelegation(ctx,delegatorAddress,validatorAddress,amount,creation_height)
if err != nil {
return nil, err
}
return &types.MsgCancelUnbondingDelegationResponse{}, nil
}
```
#### `cli tx` Method
```bash=
simd tx staking cancel-unbond [validator-address] [amount] [creation_height] --from [user] --chain-id [chain-id]
Example:
simd tx staking cancel-unbond cosmosvaloper1mqtyv4qux68r26mql2hjhgrvz72snjwpulq22m 100000stake 10280 --from test1 --chain-id test-chain
```
---
### Author Checklist
*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*
I have...
- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed
### Reviewers Checklist
*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*
I have...
- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-04-05 03:01:13 -07:00
|
|
|
"strconv"
|
2020-10-16 07:53:49 -07:00
|
|
|
"time"
|
|
|
|
|
feat: Cancel unbonding delegation entry (#10885)
## Description
Closes: #577
This pull request contains `Canceling unbonding delegation entry` and `delegate back to previous validator`
### `Msg` Service
```protobuf=
package cosmos.staking.v1beta1;
service Msg {
// CancelUnbondingDelegation
rpc CancelUnbondingDelegation(MsgCancelUnbondingDelegation) returns (MsgCancelUnbondingDelegationResponse);
}
// MsgCancelUnbondingDelegation
message MsgCancelUnbondingDelegation {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
// creation_height is the height which the unbonding took place.
int64 creation_height = 4;
}
// MsgCancelUnbondingDelegationResponse
message MsgCancelUnbondingDelegationResponse{
}
```
### `Msg` Method Implementation
```go=
func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *types.MsgCancelUnbondingDelegation) (*types.MsgCancelUnbondingDelegationResponse, error) {
/*
// checking the unbonding delegation at creation_height
// get the unbonding delegations of delegatorAddress
if ubdEntry balance is equal to msg.Amount
remove the entry from ubd
else
update the specific entry with new balance
if len(ubd.Entries) == 0 {
k.RemoveUnbondingDelegation(ctx, ubd)
} else {
k.SetUnbondingDelegation(ctx, ubd)
}
*/
// update the delegation back to validator
// get validator
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return nil, types.ErrNoValidatorFound
}
// delegate the unbonding amount to validator back
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonding, validator, false)
if err != nil {
return nil, err
}
_, err := ms.Keeper.CancelUnbondingDelegation(ctx,delegatorAddress,validatorAddress,amount,creation_height)
if err != nil {
return nil, err
}
return &types.MsgCancelUnbondingDelegationResponse{}, nil
}
```
#### `cli tx` Method
```bash=
simd tx staking cancel-unbond [validator-address] [amount] [creation_height] --from [user] --chain-id [chain-id]
Example:
simd tx staking cancel-unbond cosmosvaloper1mqtyv4qux68r26mql2hjhgrvz72snjwpulq22m 100000stake 10280 --from test1 --chain-id test-chain
```
---
### Author Checklist
*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*
I have...
- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed
### Reviewers Checklist
*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*
I have...
- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-04-05 03:01:13 -07:00
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
|
|
|
|
2021-09-20 05:02:15 -07:00
|
|
|
"github.com/armon/go-metrics"
|
2020-10-16 07:53:49 -07:00
|
|
|
tmstrings "github.com/tendermint/tendermint/libs/strings"
|
|
|
|
|
2020-10-19 06:04:55 -07:00
|
|
|
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
2020-10-16 07:53:49 -07:00
|
|
|
"github.com/cosmos/cosmos-sdk/telemetry"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
|
|
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type msgServer struct {
|
|
|
|
Keeper
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewMsgServerImpl returns an implementation of the bank MsgServer interface
|
|
|
|
// for the provided Keeper.
|
|
|
|
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
|
|
|
|
return &msgServer{Keeper: keeper}
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ types.MsgServer = msgServer{}
|
|
|
|
|
2021-05-10 05:41:24 -07:00
|
|
|
// CreateValidator defines a method for creating a new validator
|
2020-10-16 07:53:49 -07:00
|
|
|
func (k msgServer) CreateValidator(goCtx context.Context, msg *types.MsgCreateValidator) (*types.MsgCreateValidatorResponse, error) {
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
|
|
|
|
|
|
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-11-12 03:55:55 -08:00
|
|
|
if msg.Commission.Rate.LT(k.MinCommissionRate(ctx)) {
|
|
|
|
return nil, sdkerrors.Wrapf(types.ErrCommissionLTMinRate, "cannot set validator commission to less than minimum rate of %s", k.MinCommissionRate(ctx))
|
|
|
|
}
|
|
|
|
|
2020-10-16 07:53:49 -07:00
|
|
|
// check to see if the pubkey or sender has been registered before
|
|
|
|
if _, found := k.GetValidator(ctx, valAddr); found {
|
|
|
|
return nil, types.ErrValidatorOwnerExists
|
|
|
|
}
|
|
|
|
|
2020-10-19 06:04:55 -07:00
|
|
|
pk, ok := msg.Pubkey.GetCachedValue().(cryptotypes.PubKey)
|
|
|
|
if !ok {
|
2020-11-09 08:01:43 -08:00
|
|
|
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidType, "Expecting cryptotypes.PubKey, got %T", pk)
|
2020-10-16 07:53:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); found {
|
|
|
|
return nil, types.ErrValidatorPubKeyExists
|
|
|
|
}
|
|
|
|
|
|
|
|
bondDenom := k.BondDenom(ctx)
|
|
|
|
if msg.Value.Denom != bondDenom {
|
2021-05-19 05:20:17 -07:00
|
|
|
return nil, sdkerrors.Wrapf(
|
|
|
|
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Value.Denom, bondDenom,
|
|
|
|
)
|
2020-10-16 07:53:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := msg.Description.EnsureLength(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
cp := ctx.ConsensusParams()
|
|
|
|
if cp != nil && cp.Validator != nil {
|
|
|
|
if !tmstrings.StringInSlice(pk.Type(), cp.Validator.PubKeyTypes) {
|
|
|
|
return nil, sdkerrors.Wrapf(
|
|
|
|
types.ErrValidatorPubKeyTypeNotSupported,
|
|
|
|
"got: %s, expected: %s", pk.Type(), cp.Validator.PubKeyTypes,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 05:07:52 -07:00
|
|
|
validator, err := types.NewValidator(valAddr, pk, msg.Description)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-11-12 03:55:55 -08:00
|
|
|
|
2020-10-16 07:53:49 -07:00
|
|
|
commission := types.NewCommissionWithTime(
|
|
|
|
msg.Commission.Rate, msg.Commission.MaxRate,
|
|
|
|
msg.Commission.MaxChangeRate, ctx.BlockHeader().Time,
|
|
|
|
)
|
|
|
|
|
|
|
|
validator, err = validator.SetInitialCommission(commission)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
validator.MinSelfDelegation = msg.MinSelfDelegation
|
|
|
|
|
|
|
|
k.SetValidator(ctx, validator)
|
|
|
|
k.SetValidatorByConsAddr(ctx, validator)
|
|
|
|
k.SetNewValidatorByPowerIndex(ctx, validator)
|
|
|
|
|
|
|
|
// call the after-creation hook
|
2021-06-30 03:13:07 -07:00
|
|
|
if err := k.AfterValidatorCreated(ctx, validator.GetOperator()); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-16 07:53:49 -07:00
|
|
|
|
|
|
|
// move coins from the msg.Address account to a (self-delegation) delegator account
|
|
|
|
// the validator account and global shares are updated within here
|
|
|
|
// NOTE source will always be from a wallet which are unbonded
|
|
|
|
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Value.Amount, types.Unbonded, validator, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeCreateValidator,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
2021-08-04 02:05:55 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.String()),
|
2020-10-16 07:53:49 -07:00
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
|
|
|
return &types.MsgCreateValidatorResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2021-05-10 05:41:24 -07:00
|
|
|
// EditValidator defines a method for editing an existing validator
|
2020-10-16 07:53:49 -07:00
|
|
|
func (k msgServer) EditValidator(goCtx context.Context, msg *types.MsgEditValidator) (*types.MsgEditValidatorResponse, error) {
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
|
|
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
// validator must already be registered
|
|
|
|
validator, found := k.GetValidator(ctx, valAddr)
|
|
|
|
if !found {
|
|
|
|
return nil, types.ErrNoValidatorFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace all editable fields (clients should autofill existing values)
|
|
|
|
description, err := validator.Description.UpdateDescription(msg.Description)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
validator.Description = description
|
|
|
|
|
|
|
|
if msg.CommissionRate != nil {
|
|
|
|
commission, err := k.UpdateValidatorCommission(ctx, validator, *msg.CommissionRate)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// call the before-modification hook since we're about to update the commission
|
2021-06-30 03:13:07 -07:00
|
|
|
if err := k.BeforeValidatorModified(ctx, valAddr); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-10-16 07:53:49 -07:00
|
|
|
|
|
|
|
validator.Commission = commission
|
|
|
|
}
|
|
|
|
|
|
|
|
if msg.MinSelfDelegation != nil {
|
|
|
|
if !msg.MinSelfDelegation.GT(validator.MinSelfDelegation) {
|
|
|
|
return nil, types.ErrMinSelfDelegationDecreased
|
|
|
|
}
|
|
|
|
|
|
|
|
if msg.MinSelfDelegation.GT(validator.Tokens) {
|
|
|
|
return nil, types.ErrSelfDelegationBelowMinimum
|
|
|
|
}
|
|
|
|
|
feat: Cancel unbonding delegation entry (#10885)
## Description
Closes: #577
This pull request contains `Canceling unbonding delegation entry` and `delegate back to previous validator`
### `Msg` Service
```protobuf=
package cosmos.staking.v1beta1;
service Msg {
// CancelUnbondingDelegation
rpc CancelUnbondingDelegation(MsgCancelUnbondingDelegation) returns (MsgCancelUnbondingDelegationResponse);
}
// MsgCancelUnbondingDelegation
message MsgCancelUnbondingDelegation {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
// creation_height is the height which the unbonding took place.
int64 creation_height = 4;
}
// MsgCancelUnbondingDelegationResponse
message MsgCancelUnbondingDelegationResponse{
}
```
### `Msg` Method Implementation
```go=
func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *types.MsgCancelUnbondingDelegation) (*types.MsgCancelUnbondingDelegationResponse, error) {
/*
// checking the unbonding delegation at creation_height
// get the unbonding delegations of delegatorAddress
if ubdEntry balance is equal to msg.Amount
remove the entry from ubd
else
update the specific entry with new balance
if len(ubd.Entries) == 0 {
k.RemoveUnbondingDelegation(ctx, ubd)
} else {
k.SetUnbondingDelegation(ctx, ubd)
}
*/
// update the delegation back to validator
// get validator
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return nil, types.ErrNoValidatorFound
}
// delegate the unbonding amount to validator back
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonding, validator, false)
if err != nil {
return nil, err
}
_, err := ms.Keeper.CancelUnbondingDelegation(ctx,delegatorAddress,validatorAddress,amount,creation_height)
if err != nil {
return nil, err
}
return &types.MsgCancelUnbondingDelegationResponse{}, nil
}
```
#### `cli tx` Method
```bash=
simd tx staking cancel-unbond [validator-address] [amount] [creation_height] --from [user] --chain-id [chain-id]
Example:
simd tx staking cancel-unbond cosmosvaloper1mqtyv4qux68r26mql2hjhgrvz72snjwpulq22m 100000stake 10280 --from test1 --chain-id test-chain
```
---
### Author Checklist
*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*
I have...
- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed
### Reviewers Checklist
*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*
I have...
- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-04-05 03:01:13 -07:00
|
|
|
validator.MinSelfDelegation = *msg.MinSelfDelegation
|
2020-10-16 07:53:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
k.SetValidator(ctx, validator)
|
|
|
|
|
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeEditValidator,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyCommissionRate, validator.Commission.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyMinSelfDelegation, validator.MinSelfDelegation.String()),
|
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.ValidatorAddress),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
|
|
|
return &types.MsgEditValidatorResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2021-05-10 05:41:24 -07:00
|
|
|
// Delegate defines a method for performing a delegation of coins from a delegator to a validator
|
2020-10-16 07:53:49 -07:00
|
|
|
func (k msgServer) Delegate(goCtx context.Context, msg *types.MsgDelegate) (*types.MsgDelegateResponse, error) {
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
|
|
valAddr, valErr := sdk.ValAddressFromBech32(msg.ValidatorAddress)
|
|
|
|
if valErr != nil {
|
|
|
|
return nil, valErr
|
|
|
|
}
|
|
|
|
|
|
|
|
validator, found := k.GetValidator(ctx, valAddr)
|
|
|
|
if !found {
|
|
|
|
return nil, types.ErrNoValidatorFound
|
|
|
|
}
|
|
|
|
|
|
|
|
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
bondDenom := k.BondDenom(ctx)
|
|
|
|
if msg.Amount.Denom != bondDenom {
|
2021-05-19 05:20:17 -07:00
|
|
|
return nil, sdkerrors.Wrapf(
|
|
|
|
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
|
|
|
|
)
|
2020-10-16 07:53:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: source funds are always unbonded
|
2021-04-28 23:57:46 -07:00
|
|
|
newShares, err := k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonded, validator, true)
|
2020-10-16 07:53:49 -07:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-11-10 05:05:15 -08:00
|
|
|
if msg.Amount.Amount.IsInt64() {
|
|
|
|
defer func() {
|
|
|
|
telemetry.IncrCounter(1, types.ModuleName, "delegate")
|
|
|
|
telemetry.SetGaugeWithLabels(
|
|
|
|
[]string{"tx", "msg", msg.Type()},
|
|
|
|
float32(msg.Amount.Amount.Int64()),
|
|
|
|
[]metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)},
|
|
|
|
)
|
|
|
|
}()
|
|
|
|
}
|
2020-10-16 07:53:49 -07:00
|
|
|
|
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeDelegate,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
2021-08-04 02:05:55 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
|
2021-04-28 23:57:46 -07:00
|
|
|
sdk.NewAttribute(types.AttributeKeyNewShares, newShares.String()),
|
2020-10-16 07:53:49 -07:00
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
|
|
|
return &types.MsgDelegateResponse{}, nil
|
|
|
|
}
|
|
|
|
|
2021-05-10 05:41:24 -07:00
|
|
|
// BeginRedelegate defines a method for performing a redelegation of coins from a delegator and source validator to a destination validator
|
2020-10-16 07:53:49 -07:00
|
|
|
func (k msgServer) BeginRedelegate(goCtx context.Context, msg *types.MsgBeginRedelegate) (*types.MsgBeginRedelegateResponse, error) {
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
|
|
valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
shares, err := k.ValidateUnbondAmount(
|
|
|
|
ctx, delegatorAddress, valSrcAddr, msg.Amount.Amount,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
bondDenom := k.BondDenom(ctx)
|
|
|
|
if msg.Amount.Denom != bondDenom {
|
2021-05-19 05:20:17 -07:00
|
|
|
return nil, sdkerrors.Wrapf(
|
|
|
|
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
|
|
|
|
)
|
2020-10-16 07:53:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
completionTime, err := k.BeginRedelegation(
|
|
|
|
ctx, delegatorAddress, valSrcAddr, valDstAddr, shares,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-11-10 08:07:40 -08:00
|
|
|
if msg.Amount.Amount.IsInt64() {
|
|
|
|
defer func() {
|
|
|
|
telemetry.IncrCounter(1, types.ModuleName, "redelegate")
|
|
|
|
telemetry.SetGaugeWithLabels(
|
|
|
|
[]string{"tx", "msg", msg.Type()},
|
|
|
|
float32(msg.Amount.Amount.Int64()),
|
|
|
|
[]metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)},
|
|
|
|
)
|
|
|
|
}()
|
|
|
|
}
|
2020-10-16 07:53:49 -07:00
|
|
|
|
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeRedelegate,
|
|
|
|
sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress),
|
2021-08-04 02:05:55 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
|
2020-10-16 07:53:49 -07:00
|
|
|
sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
|
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
|
|
|
return &types.MsgBeginRedelegateResponse{
|
|
|
|
CompletionTime: completionTime,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2021-05-10 05:41:24 -07:00
|
|
|
// Undelegate defines a method for performing an undelegation from a delegate and a validator
|
2020-10-16 07:53:49 -07:00
|
|
|
func (k msgServer) Undelegate(goCtx context.Context, msg *types.MsgUndelegate) (*types.MsgUndelegateResponse, error) {
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
|
|
|
|
|
|
addr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
shares, err := k.ValidateUnbondAmount(
|
|
|
|
ctx, delegatorAddress, addr, msg.Amount.Amount,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
bondDenom := k.BondDenom(ctx)
|
|
|
|
if msg.Amount.Denom != bondDenom {
|
2021-05-19 05:20:17 -07:00
|
|
|
return nil, sdkerrors.Wrapf(
|
|
|
|
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
|
|
|
|
)
|
2020-10-16 07:53:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
completionTime, err := k.Keeper.Undelegate(ctx, delegatorAddress, addr, shares)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-11-10 05:05:15 -08:00
|
|
|
if msg.Amount.Amount.IsInt64() {
|
|
|
|
defer func() {
|
|
|
|
telemetry.IncrCounter(1, types.ModuleName, "undelegate")
|
|
|
|
telemetry.SetGaugeWithLabels(
|
|
|
|
[]string{"tx", "msg", msg.Type()},
|
|
|
|
float32(msg.Amount.Amount.Int64()),
|
|
|
|
[]metrics.Label{telemetry.NewLabel("denom", msg.Amount.Denom)},
|
|
|
|
)
|
|
|
|
}()
|
|
|
|
}
|
2020-10-16 07:53:49 -07:00
|
|
|
|
|
|
|
ctx.EventManager().EmitEvents(sdk.Events{
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeUnbond,
|
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
2021-08-04 02:05:55 -07:00
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
|
2020-10-16 07:53:49 -07:00
|
|
|
sdk.NewAttribute(types.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
|
|
|
|
),
|
|
|
|
sdk.NewEvent(
|
|
|
|
sdk.EventTypeMessage,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
|
|
|
),
|
|
|
|
})
|
|
|
|
|
|
|
|
return &types.MsgUndelegateResponse{
|
|
|
|
CompletionTime: completionTime,
|
|
|
|
}, nil
|
|
|
|
}
|
feat: Cancel unbonding delegation entry (#10885)
## Description
Closes: #577
This pull request contains `Canceling unbonding delegation entry` and `delegate back to previous validator`
### `Msg` Service
```protobuf=
package cosmos.staking.v1beta1;
service Msg {
// CancelUnbondingDelegation
rpc CancelUnbondingDelegation(MsgCancelUnbondingDelegation) returns (MsgCancelUnbondingDelegationResponse);
}
// MsgCancelUnbondingDelegation
message MsgCancelUnbondingDelegation {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
// creation_height is the height which the unbonding took place.
int64 creation_height = 4;
}
// MsgCancelUnbondingDelegationResponse
message MsgCancelUnbondingDelegationResponse{
}
```
### `Msg` Method Implementation
```go=
func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *types.MsgCancelUnbondingDelegation) (*types.MsgCancelUnbondingDelegationResponse, error) {
/*
// checking the unbonding delegation at creation_height
// get the unbonding delegations of delegatorAddress
if ubdEntry balance is equal to msg.Amount
remove the entry from ubd
else
update the specific entry with new balance
if len(ubd.Entries) == 0 {
k.RemoveUnbondingDelegation(ctx, ubd)
} else {
k.SetUnbondingDelegation(ctx, ubd)
}
*/
// update the delegation back to validator
// get validator
validator, found := k.GetValidator(ctx, valAddr)
if !found {
return nil, types.ErrNoValidatorFound
}
// delegate the unbonding amount to validator back
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonding, validator, false)
if err != nil {
return nil, err
}
_, err := ms.Keeper.CancelUnbondingDelegation(ctx,delegatorAddress,validatorAddress,amount,creation_height)
if err != nil {
return nil, err
}
return &types.MsgCancelUnbondingDelegationResponse{}, nil
}
```
#### `cli tx` Method
```bash=
simd tx staking cancel-unbond [validator-address] [amount] [creation_height] --from [user] --chain-id [chain-id]
Example:
simd tx staking cancel-unbond cosmosvaloper1mqtyv4qux68r26mql2hjhgrvz72snjwpulq22m 100000stake 10280 --from test1 --chain-id test-chain
```
---
### Author Checklist
*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*
I have...
- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed
### Reviewers Checklist
*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*
I have...
- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
2022-04-05 03:01:13 -07:00
|
|
|
|
|
|
|
// CancelUnbondingDelegation defines a method for canceling the unbonding delegation
|
|
|
|
// and delegate back to the validator.
|
|
|
|
func (k msgServer) CancelUnbondingDelegation(goCtx context.Context, msg *types.MsgCancelUnbondingDelegation) (*types.MsgCancelUnbondingDelegationResponse, error) {
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
|
|
|
|
|
|
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
delegatorAddress, err := sdk.AccAddressFromBech32(msg.DelegatorAddress)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
bondDenom := k.BondDenom(ctx)
|
|
|
|
if msg.Amount.Denom != bondDenom {
|
|
|
|
return nil, sdkerrors.Wrapf(
|
|
|
|
sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
validator, found := k.GetValidator(ctx, valAddr)
|
|
|
|
if !found {
|
|
|
|
return nil, types.ErrNoValidatorFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// In some situations, the exchange rate becomes invalid, e.g. if
|
|
|
|
// Validator loses all tokens due to slashing. In this case,
|
|
|
|
// make all future delegations invalid.
|
|
|
|
if validator.InvalidExRate() {
|
|
|
|
return nil, types.ErrDelegatorShareExRateInvalid
|
|
|
|
}
|
|
|
|
|
|
|
|
if validator.IsJailed() {
|
|
|
|
return nil, types.ErrValidatorJailed
|
|
|
|
}
|
|
|
|
|
|
|
|
ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddress, valAddr)
|
|
|
|
if !found {
|
|
|
|
return nil, status.Errorf(
|
|
|
|
codes.NotFound,
|
|
|
|
"unbonding delegation with delegator %s not found for validator %s",
|
|
|
|
msg.DelegatorAddress, msg.ValidatorAddress,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
unbondEntry types.UnbondingDelegationEntry
|
|
|
|
unbondEntryIndex int64 = -1
|
|
|
|
)
|
|
|
|
|
|
|
|
for i, entry := range ubd.Entries {
|
|
|
|
if entry.CreationHeight == msg.CreationHeight {
|
|
|
|
unbondEntry = entry
|
|
|
|
unbondEntryIndex = int64(i)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if unbondEntryIndex == -1 {
|
|
|
|
return nil, sdkerrors.ErrNotFound.Wrapf("unbonding delegation entry is not found at block height %d", msg.CreationHeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
if unbondEntry.Balance.LT(msg.Amount.Amount) {
|
|
|
|
return nil, sdkerrors.ErrInvalidRequest.Wrap("amount is greater than the unbonding delegation entry balance")
|
|
|
|
}
|
|
|
|
|
|
|
|
if unbondEntry.CompletionTime.Before(ctx.BlockTime()) {
|
|
|
|
return nil, sdkerrors.ErrInvalidRequest.Wrap("unbonding delegation is already processed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// delegate back the unbonding delegation amount to the validator
|
|
|
|
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonding, validator, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
amount := unbondEntry.Balance.Sub(msg.Amount.Amount)
|
|
|
|
if amount.IsZero() {
|
|
|
|
ubd.RemoveEntry(unbondEntryIndex)
|
|
|
|
} else {
|
|
|
|
// update the unbondingDelegationEntryBalance and InitialBalance for ubd entry
|
|
|
|
unbondEntry.Balance = amount
|
|
|
|
unbondEntry.InitialBalance = unbondEntry.InitialBalance.Sub(msg.Amount.Amount)
|
|
|
|
ubd.Entries[unbondEntryIndex] = unbondEntry
|
|
|
|
}
|
|
|
|
|
|
|
|
// set the unbonding delegation or remove it if there are no more entries
|
|
|
|
if len(ubd.Entries) == 0 {
|
|
|
|
k.RemoveUnbondingDelegation(ctx, ubd)
|
|
|
|
} else {
|
|
|
|
k.SetUnbondingDelegation(ctx, ubd)
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.EventManager().EmitEvent(
|
|
|
|
sdk.NewEvent(
|
|
|
|
types.EventTypeCancelUnbondingDelegation,
|
|
|
|
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyDelegator, msg.DelegatorAddress),
|
|
|
|
sdk.NewAttribute(types.AttributeKeyCreationHeight, strconv.FormatInt(msg.CreationHeight, 10)),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
return &types.MsgCancelUnbondingDelegationResponse{}, nil
|
|
|
|
}
|