Refactor x/staking according to ADR 031 (#7556)
* Refactor x/staking according to ADR 031 * lint * review changes * review changes * review changes Co-authored-by: Aaron Craelius <aaron@regen.network>
This commit is contained in:
parent
8384a5a180
commit
18ef33caff
@ -3,11 +3,33 @@ package cosmos.staking.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "cosmos/staking/v1beta1/staking.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/staking/types";
|
||||
|
||||
// MsgCreateValidator defines an SDK message for creating a new validator.
|
||||
// Msg defines the staking Msg service.
|
||||
service Msg {
|
||||
// CreateValidator defines a method for creating a new validator.
|
||||
rpc CreateValidator(MsgCreateValidator) returns (MsgCreateValidatorResponse);
|
||||
|
||||
// EditValidator defines a method for editing an existing validator.
|
||||
rpc EditValidator(MsgEditValidator) returns (MsgEditValidatorResponse);
|
||||
|
||||
// Delegate defines a method for performing a delegation of coins
|
||||
// from a delegator to a validator.
|
||||
rpc Delegate(MsgDelegate) returns (MsgDelegateResponse);
|
||||
|
||||
// BeginRedelegate defines a method for performing a redelegation
|
||||
// of coins from a delegator and source validator to a destination validator.
|
||||
rpc BeginRedelegate(MsgBeginRedelegate) returns (MsgBeginRedelegateResponse);
|
||||
|
||||
// Undelegate defines a method for performing an undelegation from a
|
||||
// delegate and a validator.
|
||||
rpc Undelegate(MsgUndelegate) returns (MsgUndelegateResponse);
|
||||
}
|
||||
|
||||
// MsgCreateValidator defines a SDK message for creating a new validator.
|
||||
message MsgCreateValidator {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
@ -25,7 +47,10 @@ message MsgCreateValidator {
|
||||
cosmos.base.v1beta1.Coin value = 7 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// MsgEditValidator defines an SDK message for editing an existing validator.
|
||||
// MsgCreateValidatorResponse defines the Msg/CreateValidator response type.
|
||||
message MsgCreateValidatorResponse { }
|
||||
|
||||
// MsgEditValidator defines a SDK message for editing an existing validator.
|
||||
message MsgEditValidator {
|
||||
option (gogoproto.equal) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
@ -48,7 +73,10 @@ message MsgEditValidator {
|
||||
];
|
||||
}
|
||||
|
||||
// MsgDelegate defines an SDK message for performing a delegation of coins
|
||||
// MsgEditValidatorResponse defines the Msg/EditValidator response type.
|
||||
message MsgEditValidatorResponse { }
|
||||
|
||||
// MsgDelegate defines a SDK message for performing a delegation of coins
|
||||
// from a delegator to a validator.
|
||||
message MsgDelegate {
|
||||
option (gogoproto.equal) = false;
|
||||
@ -59,7 +87,10 @@ message MsgDelegate {
|
||||
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// MsgBeginRedelegate defines an SDK message for performing a redelegation
|
||||
// MsgDelegateResponse defines the Msg/Delegate response type.
|
||||
message MsgDelegateResponse { }
|
||||
|
||||
// MsgBeginRedelegate defines a SDK message for performing a redelegation
|
||||
// of coins from a delegator and source validator to a destination validator.
|
||||
message MsgBeginRedelegate {
|
||||
option (gogoproto.equal) = false;
|
||||
@ -71,7 +102,12 @@ message MsgBeginRedelegate {
|
||||
cosmos.base.v1beta1.Coin amount = 4 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// MsgUndelegate defines an SDK message for performing an undelegation from a
|
||||
// MsgBeginRedelegateResponse defines the Msg/BeginRedelegate response type.
|
||||
message MsgBeginRedelegateResponse {
|
||||
google.protobuf.Timestamp completion_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
}
|
||||
|
||||
// MsgUndelegate defines a SDK message for performing an undelegation from a
|
||||
// delegate and a validator.
|
||||
message MsgUndelegate {
|
||||
option (gogoproto.equal) = false;
|
||||
@ -80,4 +116,9 @@ message MsgUndelegate {
|
||||
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
|
||||
string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""];
|
||||
cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
|
||||
}
|
||||
}
|
||||
|
||||
// MsgUndelegateResponse defines the Msg/Undelegate response type.
|
||||
message MsgUndelegateResponse {
|
||||
google.protobuf.Timestamp completion_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
}
|
||||
|
||||
@ -9,11 +9,11 @@ import (
|
||||
)
|
||||
|
||||
func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||
msgServer := keeper.NewMsgServerImpl(k)
|
||||
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
|
||||
msgServer := keeper.NewMsgServerImpl(k)
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *types.MsgSetWithdrawAddress:
|
||||
res, err := msgServer.SetWithdrawAddress(sdk.WrapSDKContext(ctx), msg)
|
||||
|
||||
@ -13,7 +13,7 @@ type msgServer struct {
|
||||
Keeper
|
||||
}
|
||||
|
||||
// NewMsgServerImpl returns an implementation of the bank MsgServer interface
|
||||
// NewMsgServerImpl returns an implementation of the distribution MsgServer interface
|
||||
// for the provided Keeper.
|
||||
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
|
||||
return &msgServer{Keeper: keeper}
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
package staking
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
tmstrings "github.com/tendermint/tendermint/libs/strings"
|
||||
|
||||
"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/keeper"
|
||||
@ -15,356 +8,34 @@ import (
|
||||
)
|
||||
|
||||
func NewHandler(k keeper.Keeper) sdk.Handler {
|
||||
msgServer := keeper.NewMsgServerImpl(k)
|
||||
|
||||
return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) {
|
||||
ctx = ctx.WithEventManager(sdk.NewEventManager())
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *types.MsgCreateValidator:
|
||||
return handleMsgCreateValidator(ctx, msg, k)
|
||||
res, err := msgServer.CreateValidator(sdk.WrapSDKContext(ctx), msg)
|
||||
return sdk.WrapServiceResult(ctx, res, err)
|
||||
|
||||
case *types.MsgEditValidator:
|
||||
return handleMsgEditValidator(ctx, msg, k)
|
||||
res, err := msgServer.EditValidator(sdk.WrapSDKContext(ctx), msg)
|
||||
return sdk.WrapServiceResult(ctx, res, err)
|
||||
|
||||
case *types.MsgDelegate:
|
||||
return handleMsgDelegate(ctx, msg, k)
|
||||
res, err := msgServer.Delegate(sdk.WrapSDKContext(ctx), msg)
|
||||
return sdk.WrapServiceResult(ctx, res, err)
|
||||
|
||||
case *types.MsgBeginRedelegate:
|
||||
return handleMsgBeginRedelegate(ctx, msg, k)
|
||||
res, err := msgServer.BeginRedelegate(sdk.WrapSDKContext(ctx), msg)
|
||||
return sdk.WrapServiceResult(ctx, res, err)
|
||||
|
||||
case *types.MsgUndelegate:
|
||||
return handleMsgUndelegate(ctx, msg, k)
|
||||
res, err := msgServer.Undelegate(sdk.WrapSDKContext(ctx), msg)
|
||||
return sdk.WrapServiceResult(ctx, res, err)
|
||||
|
||||
default:
|
||||
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These functions assume everything has been authenticated,
|
||||
// now we just perform action and save
|
||||
|
||||
func handleMsgCreateValidator(ctx sdk.Context, msg *types.MsgCreateValidator, k keeper.Keeper) (*sdk.Result, error) {
|
||||
|
||||
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check to see if the pubkey or sender has been registered before
|
||||
if _, found := k.GetValidator(ctx, valAddr); found {
|
||||
return nil, types.ErrValidatorOwnerExists
|
||||
}
|
||||
|
||||
pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, msg.Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); found {
|
||||
return nil, types.ErrValidatorPubKeyExists
|
||||
}
|
||||
|
||||
bondDenom := k.BondDenom(ctx)
|
||||
if msg.Value.Denom != bondDenom {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Value.Denom, bondDenom)
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
validator := types.NewValidator(valAddr, pk, msg.Description)
|
||||
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
|
||||
k.AfterValidatorCreated(ctx, validator.GetOperator())
|
||||
|
||||
// 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.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),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.Amount.String()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
||||
),
|
||||
})
|
||||
|
||||
return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
|
||||
}
|
||||
|
||||
func handleMsgEditValidator(ctx sdk.Context, msg *types.MsgEditValidator, k keeper.Keeper) (*sdk.Result, error) {
|
||||
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
|
||||
k.BeforeValidatorModified(ctx, valAddr)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
validator.MinSelfDelegation = (*msg.MinSelfDelegation)
|
||||
}
|
||||
|
||||
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 &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
|
||||
}
|
||||
|
||||
func handleMsgDelegate(ctx sdk.Context, msg *types.MsgDelegate, k keeper.Keeper) (*sdk.Result, error) {
|
||||
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 {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom)
|
||||
}
|
||||
|
||||
// NOTE: source funds are always unbonded
|
||||
_, err = k.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonded, validator, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)},
|
||||
)
|
||||
}()
|
||||
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeDelegate,
|
||||
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
||||
),
|
||||
})
|
||||
|
||||
return &sdk.Result{Events: ctx.EventManager().ABCIEvents()}, nil
|
||||
}
|
||||
|
||||
func handleMsgUndelegate(ctx sdk.Context, msg *types.MsgUndelegate, k keeper.Keeper) (*sdk.Result, error) {
|
||||
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 {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom)
|
||||
}
|
||||
|
||||
completionTime, err := k.Undelegate(ctx, delegatorAddress, addr, shares)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ts, err := gogotypes.TimestampProto(completionTime)
|
||||
if err != nil {
|
||||
return nil, types.ErrBadRedelegationAddr
|
||||
}
|
||||
|
||||
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)},
|
||||
)
|
||||
}()
|
||||
|
||||
completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(ts)
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeUnbond,
|
||||
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
||||
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 &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().ABCIEvents()}, nil
|
||||
}
|
||||
|
||||
func handleMsgBeginRedelegate(ctx sdk.Context, msg *types.MsgBeginRedelegate, k keeper.Keeper) (*sdk.Result, error) {
|
||||
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 {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
ts, err := gogotypes.TimestampProto(completionTime)
|
||||
if err != nil {
|
||||
return nil, types.ErrBadRedelegationAddr
|
||||
}
|
||||
|
||||
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)},
|
||||
)
|
||||
}()
|
||||
|
||||
completionTimeBz := types.ModuleCdc.MustMarshalBinaryLengthPrefixed(ts)
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeRedelegate,
|
||||
sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress),
|
||||
sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
||||
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 &sdk.Result{Data: completionTimeBz, Events: ctx.EventManager().ABCIEvents()}, nil
|
||||
}
|
||||
|
||||
@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gogotypes "github.com/gogo/protobuf/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
@ -21,6 +20,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/staking"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/keeper"
|
||||
"github.com/cosmos/cosmos-sdk/x/staking/types"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
func bootstrapHandlerGenesisTest(t *testing.T, power int64, numAddrs int, accAmount int64) (*simapp.SimApp, sdk.Context, []sdk.AccAddress, []sdk.ValAddress) {
|
||||
@ -117,13 +117,11 @@ func TestValidatorByPowerIndex(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err := gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
@ -255,13 +253,11 @@ func TestLegacyValidatorDelegations(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err := gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
// verify the validator record still exists, is jailed, and has correct tokens
|
||||
@ -500,13 +496,11 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err := gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
// check that the accounts and the bond account have the appropriate values
|
||||
@ -618,10 +612,8 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
_, err = gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
// adds validator into unbonding queue
|
||||
@ -676,13 +668,11 @@ func TestMultipleMsgDelegate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err := gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
// check that the account is unbonded
|
||||
@ -715,13 +705,11 @@ func TestJailValidator(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err := gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
validator, found := app.StakingKeeper.GetValidator(ctx, validatorAddr)
|
||||
@ -735,13 +723,10 @@ func TestJailValidator(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts = &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err = gogotypes.TimestampFromProto(ts)
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
// verify that the pubkey can now be reused
|
||||
@ -783,13 +768,11 @@ func TestValidatorQueue(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, res)
|
||||
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err := gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime)
|
||||
staking.EndBlocker(ctx, app.StakingKeeper)
|
||||
|
||||
origHeader := ctx.BlockHeader()
|
||||
@ -889,13 +872,11 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) {
|
||||
require.NotNil(t, res)
|
||||
|
||||
// change the ctx to Block Time one second before the validator would have unbonded
|
||||
ts := &gogotypes.Timestamp{}
|
||||
types.ModuleCdc.MustUnmarshalBinaryLengthPrefixed(res.Data, ts)
|
||||
|
||||
finishTime, err := gogotypes.TimestampFromProto(ts)
|
||||
var resData types.MsgUndelegateResponse
|
||||
err = proto.Unmarshal(res.Data, &resData)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx = ctx.WithBlockTime(finishTime.Add(time.Second * -1))
|
||||
ctx = ctx.WithBlockTime(resData.CompletionTime.Add(time.Second * -1))
|
||||
|
||||
// unbond the delegator from the validator
|
||||
msgUndelegateDelegator := types.NewMsgUndelegate(delegatorAddr, validatorAddr, unbondAmt)
|
||||
|
||||
350
x/staking/keeper/msg_server.go
Normal file
350
x/staking/keeper/msg_server.go
Normal file
@ -0,0 +1,350 @@
|
||||
package keeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
metrics "github.com/armon/go-metrics"
|
||||
tmstrings "github.com/tendermint/tendermint/libs/strings"
|
||||
|
||||
"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{}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// check to see if the pubkey or sender has been registered before
|
||||
if _, found := k.GetValidator(ctx, valAddr); found {
|
||||
return nil, types.ErrValidatorOwnerExists
|
||||
}
|
||||
|
||||
pk, err := sdk.GetPubKeyFromBech32(sdk.Bech32PubKeyTypeConsPub, msg.Pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, found := k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)); found {
|
||||
return nil, types.ErrValidatorPubKeyExists
|
||||
}
|
||||
|
||||
bondDenom := k.BondDenom(ctx)
|
||||
if msg.Value.Denom != bondDenom {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Value.Denom, bondDenom)
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
validator := types.NewValidator(valAddr, pk, msg.Description)
|
||||
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
|
||||
k.AfterValidatorCreated(ctx, validator.GetOperator())
|
||||
|
||||
// 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),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Value.Amount.String()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
||||
),
|
||||
})
|
||||
|
||||
return &types.MsgCreateValidatorResponse{}, nil
|
||||
}
|
||||
|
||||
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
|
||||
k.BeforeValidatorModified(ctx, valAddr)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
validator.MinSelfDelegation = (*msg.MinSelfDelegation)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom)
|
||||
}
|
||||
|
||||
// NOTE: source funds are always unbonded
|
||||
_, err = k.Keeper.Delegate(ctx, delegatorAddress, msg.Amount.Amount, types.Unbonded, validator, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)},
|
||||
)
|
||||
}()
|
||||
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeDelegate,
|
||||
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
||||
),
|
||||
sdk.NewEvent(
|
||||
sdk.EventTypeMessage,
|
||||
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
|
||||
sdk.NewAttribute(sdk.AttributeKeySender, msg.DelegatorAddress),
|
||||
),
|
||||
})
|
||||
|
||||
return &types.MsgDelegateResponse{}, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)},
|
||||
)
|
||||
}()
|
||||
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeRedelegate,
|
||||
sdk.NewAttribute(types.AttributeKeySrcValidator, msg.ValidatorSrcAddress),
|
||||
sdk.NewAttribute(types.AttributeKeyDstValidator, msg.ValidatorDstAddress),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, sdkerrors.Wrapf(types.ErrBadDenom, "got %s, expected %s", msg.Amount.Denom, bondDenom)
|
||||
}
|
||||
|
||||
completionTime, err := k.Keeper.Undelegate(ctx, delegatorAddress, addr, shares)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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)},
|
||||
)
|
||||
}()
|
||||
|
||||
ctx.EventManager().EmitEvents(sdk.Events{
|
||||
sdk.NewEvent(
|
||||
types.EventTypeUnbond,
|
||||
sdk.NewAttribute(types.AttributeKeyValidator, msg.ValidatorAddress),
|
||||
sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.Amount.String()),
|
||||
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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user