Merge PR #2405: Unbonding and Redelegations Queue

This commit is contained in:
Sunny Aggarwal 2018-10-07 21:43:47 -07:00 committed by Christopher Goes
parent d661ccb30e
commit cd21427a7b
23 changed files with 376 additions and 546 deletions

View File

@ -41,6 +41,7 @@ BREAKING CHANGES
* [x/gov] [#2195] Governance uses BFT Time
* [x/gov] \#2256 Removed slashing for governance non-voting validators
* [simulation] \#2162 Added back correct supply invariants
* [x/stake] \#2393 Removed `CompleteUnbonding` and `CompleteRedelegation` Msg types, and instead added unbonding/redelegation queues to endblocker
* SDK
* [core] \#2219 Update to Tendermint 0.24.0

View File

@ -97,9 +97,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation {
{5, stakesim.SimulateMsgEditValidator(app.stakeKeeper)},
{100, stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper)},
{100, stakesim.SimulateMsgBeginUnbonding(app.accountMapper, app.stakeKeeper)},
{100, stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper)},
{100, stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper)},
{100, stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper)},
{100, slashingsim.SimulateMsgUnjail(app.slashingKeeper)},
}
}

View File

@ -16,3 +16,34 @@ EndBlock() ValidatorSetChanges
ClearTendermintUpdates()
return vsc
```
## CompleteUnbonding
Complete the unbonding and transfer the coins to the delegate. Realize any
slashing that occurred during the unbonding period.
```golang
unbondingQueue(currTime time.Time):
// unbondings are in ordered queue from oldest to newest
for all unbondings whose CompleteTime < currTime:
validator = GetValidator(unbonding.ValidatorAddr)
AddCoins(unbonding.DelegatorAddr, unbonding.Balance)
removeUnbondingDelegation(unbonding)
return
```
## CompleteRedelegation
Note that unlike CompleteUnbonding slashing of redelegating shares does not
take place during completion. Slashing on redelegated shares takes place
actively as a slashing occurs. The redelegation completion queue serves simply to
clean up state, as redelegations older than an unbonding period need not be kept,
as that is the max time that their old validator's evidence can be used to slash them.
```golang
redelegationQueue(currTime time.Time):
// redelegations are in ordered queue from oldest to newest
for all redelegations whose CompleteTime < currTime:
removeRedelegation(redelegation)
return
```

View File

@ -7,9 +7,7 @@ corresponding updates to the state. Transactions:
* TxEditValidator
* TxDelegation
* TxStartUnbonding
* TxCompleteUnbonding
* TxRedelegate
* TxCompleteRedelegation
Other important state changes:
@ -188,27 +186,6 @@ startUnbonding(tx TxStartUnbonding):
return
```
### TxCompleteUnbonding
Complete the unbonding and transfer the coins to the delegate. Perform any
slashing that occurred during the unbonding period.
```golang
type TxUnbondingComplete struct {
DelegatorAddr sdk.Address
ValidatorAddr sdk.Address
}
redelegationComplete(tx TxRedelegate):
unbonding = getUnbondingDelegation(tx.DelegatorAddr, tx.Validator)
if unbonding.CompleteTime >= CurrentBlockTime && unbonding.CompleteHeight >= CurrentBlockHeight
validator = GetValidator(tx.ValidatorAddr)
returnTokens = ExpectedTokens * tx.startSlashRatio/validator.SlashRatio
AddCoins(unbonding.DelegatorAddr, returnTokens)
removeUnbondingDelegation(unbonding)
return
```
### TxRedelegation
The redelegation command allows delegators to instantly switch validators. Once
@ -243,26 +220,6 @@ redelegate(tx TxRedelegate):
return
```
### TxCompleteRedelegation
Note that unlike TxCompleteUnbonding slashing of redelegating shares does not
take place during completion. Slashing on redelegated shares takes place
actively as a slashing occurs.
```golang
type TxRedelegationComplete struct {
DelegatorAddr Address
ValidatorFrom Validator
ValidatorTo Validator
}
redelegationComplete(tx TxRedelegate):
redelegation = getRedelegation(tx.DelegatorAddr, tx.validatorFrom, tx.validatorTo)
if redelegation.CompleteTime >= CurrentBlockTime && redelegation.CompleteHeight >= CurrentBlockHeight
removeRedelegation(redelegation)
return
```
### Update Validators
Within many transactions the validator set must be updated based on changes in

View File

@ -4,6 +4,7 @@ package types
import (
"context"
"sync"
"time"
"github.com/golang/protobuf/proto"
@ -181,6 +182,12 @@ func (c Context) WithBlockHeader(header abci.Header) Context {
return c.withValue(contextKeyBlockHeader, header)
}
func (c Context) WithBlockTime(newTime time.Time) Context {
newHeader := c.BlockHeader()
newHeader.Time = newTime
return c.WithBlockHeader(newHeader)
}
func (c Context) WithBlockHeight(height int64) Context {
return c.withValue(contextKeyBlockHeight, height)
}

View File

@ -306,6 +306,13 @@ func PrefixEndBytes(prefix []byte) []byte {
return end
}
// InclusiveEndBytes returns the []byte that would end a
// range query such that the input would be included
func InclusiveEndBytes(inclusiveBytes []byte) (exclusiveBytes []byte) {
exclusiveBytes = append(inclusiveBytes, byte(0x00))
return exclusiveBytes
}
// TransientStoreKey is used for indexing transient stores in a MultiStore
type TransientStoreKey struct {
name string

View File

@ -70,9 +70,8 @@ func TestJailedValidatorDelegations(t *testing.T) {
got = stake.NewHandler(stakeKeeper)(ctx, msgBeginUnbonding)
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got: %v", got)
msgCompleteUnbonding := stake.NewMsgCompleteUnbonding(sdk.AccAddress(valAddr), valAddr)
got = stake.NewHandler(stakeKeeper)(ctx, msgCompleteUnbonding)
require.True(t, got.IsOK(), "expected complete unbonding validator msg to be ok, got: %v", got)
err := stakeKeeper.CompleteUnbonding(ctx, sdk.AccAddress(valAddr), valAddr)
require.Nil(t, err, "expected complete unbonding validator to be ok, got: %v", err)
// verify validator still exists and is jailed
validator, found := stakeKeeper.GetValidator(ctx, valAddr)

View File

@ -209,7 +209,6 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command {
cmd.AddCommand(
client.PostCommands(
GetCmdBeginRedelegate(storeName, cdc),
GetCmdCompleteRedelegate(cdc),
)...)
return cmd
@ -270,47 +269,6 @@ func GetCmdBeginRedelegate(storeName string, cdc *codec.Codec) *cobra.Command {
return cmd
}
// GetCmdCompleteRedelegate implements the complete redelegation command.
func GetCmdCompleteRedelegate(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete redelegation",
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
delAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
valSrcAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidatorSrc))
if err != nil {
return err
}
valDstAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidatorDst))
if err != nil {
return err
}
msg := stake.NewMsgCompleteRedelegate(delAddr, valSrcAddr, valDstAddr)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().AddFlagSet(fsRedelegation)
return cmd
}
// GetCmdUnbond implements the unbond validator command.
func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
@ -321,7 +279,6 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command {
cmd.AddCommand(
client.PostCommands(
GetCmdBeginUnbonding(storeName, cdc),
GetCmdCompleteUnbonding(cdc),
)...)
return cmd
@ -374,39 +331,3 @@ func GetCmdBeginUnbonding(storeName string, cdc *codec.Codec) *cobra.Command {
return cmd
}
// GetCmdCompleteUnbonding implements the complete unbonding validator command.
func GetCmdCompleteUnbonding(cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "complete",
Short: "complete unbonding",
RunE: func(cmd *cobra.Command, args []string) error {
txBldr := authtxb.NewTxBuilderFromCLI().WithCodec(cdc)
cliCtx := context.NewCLIContext().
WithCodec(cdc).
WithAccountDecoder(authcmd.GetAccountDecoder(cdc))
delAddr, err := cliCtx.GetFromAddress()
if err != nil {
return err
}
valAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidator))
if err != nil {
return err
}
msg := stake.NewMsgCompleteUnbonding(delAddr, valAddr)
if cliCtx.GenerateOnly {
return utils.PrintUnsignedStdTx(txBldr, cliCtx, []sdk.Msg{msg})
}
// build and sign the transaction, then broadcast to Tendermint
return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg})
},
}
cmd.Flags().AddFlagSet(fsValidator)
return cmd
}

View File

@ -41,31 +41,18 @@ type (
SharesAmount string `json:"shares"`
}
msgCompleteRedelegateInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorSrcAddr string `json:"validator_src_addr"` // in bech32
ValidatorDstAddr string `json:"validator_dst_addr"` // in bech32
}
msgBeginUnbondingInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
SharesAmount string `json:"shares"`
}
msgCompleteUnbondingInput struct {
DelegatorAddr string `json:"delegator_addr"` // in bech32
ValidatorAddr string `json:"validator_addr"` // in bech32
}
// the request body for edit delegations
EditDelegationsReq struct {
BaseReq utils.BaseReq `json:"base_req"`
Delegations []msgDelegationsInput `json:"delegations"`
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
CompleteUnbondings []msgCompleteUnbondingInput `json:"complete_unbondings"`
BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"`
CompleteRedelegates []msgCompleteRedelegateInput `json:"complete_redelegates"`
BaseReq utils.BaseReq `json:"base_req"`
Delegations []msgDelegationsInput `json:"delegations"`
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
BeginRedelegates []msgBeginRedelegateInput `json:"begin_redelegates"`
}
)
@ -106,9 +93,7 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte
// build messages
messages := make([]sdk.Msg, len(req.Delegations)+
len(req.BeginRedelegates)+
len(req.CompleteRedelegates)+
len(req.BeginUnbondings)+
len(req.CompleteUnbondings))
len(req.BeginUnbondings))
i := 0
for _, msg := range req.Delegations {
@ -177,39 +162,6 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte
i++
}
for _, msg := range req.CompleteRedelegates {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}
valSrcAddr, err := sdk.ValAddressFromBech32(msg.ValidatorSrcAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
valDstAddr, err := sdk.ValAddressFromBech32(msg.ValidatorDstAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}
messages[i] = stake.MsgCompleteRedelegate{
DelegatorAddr: delAddr,
ValidatorSrcAddr: valSrcAddr,
ValidatorDstAddr: valDstAddr,
}
i++
}
for _, msg := range req.BeginUnbondings {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
@ -243,32 +195,6 @@ func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx conte
i++
}
for _, msg := range req.CompleteUnbondings {
delAddr, err := sdk.AccAddressFromBech32(msg.DelegatorAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode delegator. Error: %s", err.Error()))
return
}
valAddr, err := sdk.ValAddressFromBech32(msg.ValidatorAddr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("Couldn't decode validator. Error: %s", err.Error()))
return
}
if !bytes.Equal(info.GetPubKey().Address(), delAddr) {
utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address")
return
}
messages[i] = stake.MsgCompleteUnbonding{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
}
i++
}
simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())

View File

@ -23,12 +23,8 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
return handleMsgDelegate(ctx, msg, k)
case types.MsgBeginRedelegate:
return handleMsgBeginRedelegate(ctx, msg, k)
case types.MsgCompleteRedelegate:
return handleMsgCompleteRedelegate(ctx, msg, k)
case types.MsgBeginUnbonding:
return handleMsgBeginUnbonding(ctx, msg, k)
case types.MsgCompleteUnbonding:
return handleMsgCompleteUnbonding(ctx, msg, k)
default:
return sdk.ErrTxDecode("invalid message parse in staking module").Result()
}
@ -37,6 +33,35 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
// Called every block, process inflation, update validator set
func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.ValidatorUpdate) {
endBlockerTags := sdk.EmptyTags()
matureUnbonds := k.DequeueAllMatureUnbondingQueue(ctx, ctx.BlockHeader().Time)
for _, dvPair := range matureUnbonds {
err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddr, dvPair.ValidatorAddr)
if err != nil {
continue
}
endBlockerTags.AppendTags(sdk.NewTags(
tags.Action, ActionCompleteUnbonding,
tags.Delegator, []byte(dvPair.DelegatorAddr.String()),
tags.SrcValidator, []byte(dvPair.ValidatorAddr.String()),
))
}
matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time)
for _, dvvTriplet := range matureRedelegations {
err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr, dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr)
if err != nil {
continue
}
endBlockerTags.AppendTags(sdk.NewTags(
tags.Action, tags.ActionCompleteRedelegation,
tags.Delegator, []byte(dvvTriplet.DelegatorAddr.String()),
tags.SrcValidator, []byte(dvvTriplet.ValidatorSrcAddr.String()),
tags.DstValidator, []byte(dvvTriplet.ValidatorDstAddr.String()),
))
}
pool := k.GetPool(ctx)
// Process provision inflation
@ -185,62 +210,37 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
}
func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k keeper.Keeper) sdk.Result {
err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
ubd, err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
if err != nil {
return err.Result()
}
finishTime := types.MsgCdc.MustMarshalBinary(ubd.MinTime)
tags := sdk.NewTags(
tags.Action, tags.ActionBeginUnbonding,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
tags.EndTime, finishTime,
)
return sdk.Result{Tags: tags}
}
func handleMsgCompleteUnbonding(ctx sdk.Context, msg types.MsgCompleteUnbonding, k keeper.Keeper) sdk.Result {
err := k.CompleteUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr)
if err != nil {
return err.Result()
}
tags := sdk.NewTags(
tags.Action, ActionCompleteUnbonding,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
)
return sdk.Result{Tags: tags}
return sdk.Result{Data: finishTime, Tags: tags}
}
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {
err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
red, err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
msg.ValidatorDstAddr, msg.SharesAmount)
if err != nil {
return err.Result()
}
finishTime := types.MsgCdc.MustMarshalBinary(red.MinTime)
tags := sdk.NewTags(
tags.Action, tags.ActionBeginRedelegation,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
tags.EndTime, finishTime,
)
return sdk.Result{Tags: tags}
}
func handleMsgCompleteRedelegate(ctx sdk.Context, msg types.MsgCompleteRedelegate, k keeper.Keeper) sdk.Result {
err := k.CompleteRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr, msg.ValidatorDstAddr)
if err != nil {
return err.Result()
}
tags := sdk.NewTags(
tags.Action, tags.ActionCompleteRedelegation,
tags.Delegator, []byte(msg.DelegatorAddr.String()),
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
)
return sdk.Result{Tags: tags}
return sdk.Result{Data: finishTime, Tags: tags}
}

View File

@ -125,11 +125,12 @@ func TestValidatorByPowerIndex(t *testing.T) {
// unbond self-delegation
msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(1000000))
msgCompleteUnbonding := NewMsgCompleteUnbonding(sdk.AccAddress(validatorAddr), validatorAddr)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg to be ok, got %v", got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
EndBlocker(ctx, keeper)
@ -260,13 +261,14 @@ func TestLegacyValidatorDelegations(t *testing.T) {
// unbond validator total self-delegations (which should jail the validator)
unbondShares := sdk.NewDec(10)
msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(valAddr), valAddr, unbondShares)
msgCompleteUnbonding := NewMsgCompleteUnbonding(sdk.AccAddress(valAddr), valAddr)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got %v", got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected complete unbonding validator msg to be ok, got %v", got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
// verify the validator record still exists, is jailed, and has correct tokens
validator, found = keeper.GetValidator(ctx, valAddr)
@ -427,13 +429,14 @@ func TestIncrementsMsgUnbond(t *testing.T) {
// TODO use decimals here
unbondShares := sdk.NewDec(10)
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
numUnbonds := 5
for i := 0; i < numUnbonds; i++ {
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
//Check that the accounts and the bond account have the appropriate values
validator, found = keeper.GetValidator(ctx, validatorAddr)
@ -522,11 +525,12 @@ func TestMultipleMsgCreateValidator(t *testing.T) {
_, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddrs[i], validatorAddr, sdk.NewDec(10)) // remove delegation
msgCompleteUnbonding := NewMsgCompleteUnbonding(delegatorAddrs[i], validatorAddr)
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
//Check that the account is unbonded
validators := keeper.GetValidators(ctx, 100)
@ -569,6 +573,10 @@ func TestMultipleMsgDelegate(t *testing.T) {
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10))
got := handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
//Check that the account is unbonded
_, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
@ -591,12 +599,14 @@ func TestJailValidator(t *testing.T) {
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected ok, got %v", got)
validator, _ := keeper.GetValidator(ctx, validatorAddr)
// unbond the validators bond portion
msgBeginUnbondingValidator := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
require.True(t, got.IsOK(), "expected no error: %v", got)
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
validator, found := keeper.GetValidator(ctx, validatorAddr)
require.True(t, found)
@ -608,11 +618,11 @@ func TestJailValidator(t *testing.T) {
// test that the delegator can still withdraw their bonds
msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10))
msgCompleteUnbondingDelegator := NewMsgCompleteUnbonding(delegatorAddr, validatorAddr)
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper)
require.True(t, got.IsOK(), "expected no error")
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbondingDelegator, keeper)
require.True(t, got.IsOK(), "expected no error")
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime)
EndBlocker(ctx, keeper)
// verify that the pubkey can now be reused
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
@ -633,30 +643,74 @@ func TestUnbondingPeriod(t *testing.T) {
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
EndBlocker(ctx, keeper)
// begin unbonding
msgBeginUnbonding := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
require.True(t, got.IsOK(), "expected no error")
origHeader := ctx.BlockHeader()
_, found := keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
require.True(t, found, "should not have unbonded")
// cannot complete unbonding at same time
msgCompleteUnbonding := NewMsgCompleteUnbonding(sdk.AccAddress(validatorAddr), validatorAddr)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, !got.IsOK(), "expected an error")
EndBlocker(ctx, keeper)
_, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
require.True(t, found, "should not have unbonded")
// cannot complete unbonding at time 6 seconds later
origHeader := ctx.BlockHeader()
headerTime6 := origHeader
headerTime6.Time = headerTime6.Time.Add(time.Second * 6)
ctx = ctx.WithBlockHeader(headerTime6)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
require.True(t, !got.IsOK(), "expected an error")
ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6))
EndBlocker(ctx, keeper)
_, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
require.True(t, found, "should not have unbonded")
// can complete unbonding at time 7 seconds later
headerTime7 := origHeader
headerTime7.Time = headerTime7.Time.Add(time.Second * 7)
ctx = ctx.WithBlockHeader(headerTime7)
got = handleMsgCompleteUnbonding(ctx, msgCompleteUnbonding, keeper)
ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7))
EndBlocker(ctx, keeper)
_, found = keeper.GetUnbondingDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr)
require.False(t, found, "should have unbonded")
}
func TestUnbondingFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1]
// create the validator
msgCreateValidator := newTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
// bond a delegator
msgDelegate := newTestMsgDelegate(delegatorAddr, validatorAddr, 10)
got = handleMsgDelegate(ctx, msgDelegate, keeper)
require.True(t, got.IsOK(), "expected ok, got %v", got)
// unbond the validators bond portion
msgBeginUnbondingValidator := NewMsgBeginUnbonding(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingValidator, keeper)
require.True(t, got.IsOK(), "expected no error")
// change the ctx to Block Time one second before the validator would have unbonded
var finishTime time.Time
types.MsgCdc.MustUnmarshalBinary(got.Data, &finishTime)
ctx = ctx.WithBlockTime(finishTime.Add(time.Second * -1))
// unbond the delegator from the validator
msgBeginUnbondingDelegator := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, sdk.NewDec(10))
got = handleMsgBeginUnbonding(ctx, msgBeginUnbondingDelegator, keeper)
require.True(t, got.IsOK(), "expected no error")
// move the Block time forward by one second
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(time.Second * 1))
// Run the EndBlocker
EndBlocker(ctx, keeper)
// Check to make sure that the unbonding delegation is no longer in state
// (meaning it was deleted in the above EndBlocker)
_, found := keeper.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
require.False(t, found, "should be removed from state")
}
func TestRedelegationPeriod(t *testing.T) {
@ -697,25 +751,24 @@ func TestRedelegationPeriod(t *testing.T) {
bal2 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins()
require.Equal(t, bal1, bal2)
origHeader := ctx.BlockHeader()
// cannot complete redelegation at same time
msgCompleteRedelegate := NewMsgCompleteRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error")
EndBlocker(ctx, keeper)
_, found := keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2)
require.True(t, found, "should not have unbonded")
// cannot complete redelegation at time 6 seconds later
origHeader := ctx.BlockHeader()
headerTime6 := origHeader
headerTime6.Time = headerTime6.Time.Add(time.Second * 6)
ctx = ctx.WithBlockHeader(headerTime6)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, !got.IsOK(), "expected an error")
ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 6))
EndBlocker(ctx, keeper)
_, found = keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2)
require.True(t, found, "should not have unbonded")
// can complete redelegation at time 7 seconds later
headerTime7 := origHeader
headerTime7.Time = headerTime7.Time.Add(time.Second * 7)
ctx = ctx.WithBlockHeader(headerTime7)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
ctx = ctx.WithBlockTime(origHeader.Time.Add(time.Second * 7))
EndBlocker(ctx, keeper)
_, found = keeper.GetRedelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2)
require.False(t, found, "should have unbonded")
}
func TestTransitiveRedelegation(t *testing.T) {
@ -753,9 +806,7 @@ func TestTransitiveRedelegation(t *testing.T) {
require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate)
// complete first redelegation
msgCompleteRedelegate := NewMsgCompleteRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2)
got = handleMsgCompleteRedelegate(ctx, msgCompleteRedelegate, keeper)
require.True(t, got.IsOK(), "expected no error")
EndBlocker(ctx, keeper)
// now should be able to redelegate from the second validator to the third
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)

View File

@ -155,6 +155,57 @@ func (k Keeper) RemoveUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDe
store.Delete(GetUBDByValIndexKey(ubd.DelegatorAddr, ubd.ValidatorAddr))
}
// gets a specific unbonding queue timeslice. A timeslice is a slice of DVPairs corresponding to unbonding delegations
// that expire at a certain time.
func (k Keeper) GetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvPairs []types.DVPair) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetUnbondingDelegationTimeKey(timestamp))
if bz == nil {
return []types.DVPair{}
}
k.cdc.MustUnmarshalBinary(bz, &dvPairs)
return dvPairs
}
// Sets a specific unbonding queue timeslice.
func (k Keeper) SetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(keys)
store.Set(GetUnbondingDelegationTimeKey(timestamp), bz)
}
// Insert an unbonding delegation to the appropriate timeslice in the unbonding queue
func (k Keeper) InsertUnbondingQueue(ctx sdk.Context, ubd types.UnbondingDelegation) {
timeSlice := k.GetUnbondingQueueTimeSlice(ctx, ubd.MinTime)
dvPair := types.DVPair{ubd.DelegatorAddr, ubd.ValidatorAddr}
if len(timeSlice) == 0 {
k.SetUnbondingQueueTimeSlice(ctx, ubd.MinTime, []types.DVPair{dvPair})
} else {
timeSlice = append(timeSlice, dvPair)
k.SetUnbondingQueueTimeSlice(ctx, ubd.MinTime, timeSlice)
}
}
// Returns all the unbonding queue timeslices from time 0 until endTime
func (k Keeper) UnbondingQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
store := ctx.KVStore(k.storeKey)
return store.Iterator(UnbondingQueueKey, sdk.InclusiveEndBytes(GetUnbondingDelegationTimeKey(endTime)))
}
// Returns a concatenated list of all the timeslices before currTime, and deletes the timeslices from the queue
func (k Keeper) DequeueAllMatureUnbondingQueue(ctx sdk.Context, currTime time.Time) (matureUnbonds []types.DVPair) {
store := ctx.KVStore(k.storeKey)
// gets an iterator for all timeslices from time 0 until the current Blockheader time
unbondingTimesliceIterator := k.UnbondingQueueIterator(ctx, ctx.BlockHeader().Time)
for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() {
timeslice := []types.DVPair{}
k.cdc.MustUnmarshalBinary(unbondingTimesliceIterator.Value(), &timeslice)
matureUnbonds = append(matureUnbonds, timeslice...)
store.Delete(unbondingTimesliceIterator.Key())
}
return matureUnbonds
}
//_____________________________________________________________________________________
// return a given amount of all the delegator redelegations
@ -241,6 +292,57 @@ func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
store.Delete(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr))
}
// Gets a specific redelegation queue timeslice. A timeslice is a slice of DVVTriplets corresponding to redelegations
// that expire at a certain time.
func (k Keeper) GetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvvTriplets []types.DVVTriplet) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(GetRedelegationTimeKey(timestamp))
if bz == nil {
return []types.DVVTriplet{}
}
k.cdc.MustUnmarshalBinary(bz, &dvvTriplets)
return dvvTriplets
}
// Sets a specific redelegation queue timeslice.
func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVVTriplet) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshalBinary(keys)
store.Set(GetRedelegationTimeKey(timestamp), bz)
}
// Insert an redelegation delegation to the appropriate timeslice in the redelegation queue
func (k Keeper) InsertRedelegationQueue(ctx sdk.Context, red types.Redelegation) {
timeSlice := k.GetRedelegationQueueTimeSlice(ctx, red.MinTime)
dvvTriplet := types.DVVTriplet{red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr}
if len(timeSlice) == 0 {
k.SetRedelegationQueueTimeSlice(ctx, red.MinTime, []types.DVVTriplet{dvvTriplet})
} else {
timeSlice = append(timeSlice, dvvTriplet)
k.SetRedelegationQueueTimeSlice(ctx, red.MinTime, timeSlice)
}
}
// Returns all the redelegation queue timeslices from time 0 until endTime
func (k Keeper) RedelegationQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
store := ctx.KVStore(k.storeKey)
return store.Iterator(RedelegationQueueKey, sdk.InclusiveEndBytes(GetRedelegationTimeKey(endTime)))
}
// Returns a concatenated list of all the timeslices before currTime, and deletes the timeslices from the queue
func (k Keeper) DequeueAllMatureRedelegationQueue(ctx sdk.Context, currTime time.Time) (matureRedelegations []types.DVVTriplet) {
store := ctx.KVStore(k.storeKey)
// gets an iterator for all timeslices from time 0 until the current Blockheader time
redelegationTimesliceIterator := k.RedelegationQueueIterator(ctx, ctx.BlockHeader().Time)
for ; redelegationTimesliceIterator.Valid(); redelegationTimesliceIterator.Next() {
timeslice := []types.DVVTriplet{}
k.cdc.MustUnmarshalBinary(redelegationTimesliceIterator.Value(), &timeslice)
matureRedelegations = append(matureRedelegations, timeslice...)
store.Delete(redelegationTimesliceIterator.Key())
}
return matureRedelegations
}
//_____________________________________________________________________________________
// Perform a delegation, set/update everything necessary within the store.
@ -339,6 +441,7 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sd
minTime time.Time, height int64, completeNow bool) {
validator, found := k.GetValidator(ctx, valSrcAddr)
switch {
case !found || validator.Status == sdk.Bonded:
@ -362,31 +465,32 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sd
// begin unbonding an unbonding record
func (k Keeper) BeginUnbonding(ctx sdk.Context,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.UnbondingDelegation, sdk.Error) {
// TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402
_, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr)
if found {
return types.ErrExistingUnbondingDelegation(k.Codespace())
}
returnAmount, err := k.unbond(ctx, delAddr, valAddr, sharesAmount)
if err != nil {
return err
return types.UnbondingDelegation{}, types.ErrExistingUnbondingDelegation(k.Codespace())
}
// create the unbonding delegation
params := k.GetParams(ctx)
minTime, height, completeNow := k.getBeginInfo(ctx, params, valAddr)
returnAmount, err := k.unbond(ctx, delAddr, valAddr, sharesAmount)
if err != nil {
return types.UnbondingDelegation{}, err
}
balance := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt())
// no need to create the ubd object just complete now
if completeNow {
_, _, err := k.bankKeeper.AddCoins(ctx, delAddr, sdk.Coins{balance})
if err != nil {
return err
return types.UnbondingDelegation{}, err
}
return nil
return types.UnbondingDelegation{MinTime: minTime}, nil
}
ubd := types.UnbondingDelegation{
@ -398,10 +502,12 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context,
InitialBalance: balance,
}
k.SetUnbondingDelegation(ctx, ubd)
return nil
k.InsertUnbondingQueue(ctx, ubd)
return ubd, nil
}
// complete unbonding an unbonding record
// CONTRACT: Expects unbonding passed in has finished the unbonding period
func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) sdk.Error {
ubd, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr)
@ -409,12 +515,6 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAd
return types.ErrNoUnbondingDelegation(k.Codespace())
}
// ensure that enough time has passed
ctxTime := ctx.BlockHeader().Time
if ubd.MinTime.After(ctxTime) {
return types.ErrNotMature(k.Codespace(), "unbonding", "unit-time", ubd.MinTime, ctxTime)
}
_, _, err := k.bankKeeper.AddCoins(ctx, ubd.DelegatorAddr, sdk.Coins{ubd.Balance})
if err != nil {
return err
@ -425,34 +525,34 @@ func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress, valAd
// complete unbonding an unbonding record
func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {
valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.Redelegation, sdk.Error) {
// check if this is a transitive redelegation
if k.HasReceivingRedelegation(ctx, delAddr, valSrcAddr) {
return types.ErrTransitiveRedelegation(k.Codespace())
return types.Redelegation{}, types.ErrTransitiveRedelegation(k.Codespace())
}
returnAmount, err := k.unbond(ctx, delAddr, valSrcAddr, sharesAmount)
if err != nil {
return err
return types.Redelegation{}, err
}
params := k.GetParams(ctx)
returnCoin := sdk.NewCoin(params.BondDenom, returnAmount.RoundInt())
dstValidator, found := k.GetValidator(ctx, valDstAddr)
if !found {
return types.ErrBadRedelegationDst(k.Codespace())
return types.Redelegation{}, types.ErrBadRedelegationDst(k.Codespace())
}
sharesCreated, err := k.Delegate(ctx, delAddr, returnCoin, dstValidator, false)
if err != nil {
return err
return types.Redelegation{}, err
}
// create the unbonding delegation
minTime, height, completeNow := k.getBeginInfo(ctx, params, valSrcAddr)
if completeNow { // no need to create the redelegation object
return nil
return types.Redelegation{MinTime: minTime}, nil
}
red := types.Redelegation{
@ -467,7 +567,8 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
InitialBalance: returnCoin,
}
k.SetRedelegation(ctx, red)
return nil
k.InsertRedelegationQueue(ctx, red)
return red, nil
}
// complete unbonding an ongoing redelegation

View File

@ -250,7 +250,7 @@ func TestUndelegateSelfDelegation(t *testing.T) {
keeper.SetDelegation(ctx, delegation)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
_, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
@ -306,7 +306,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
// unbond the all self-delegation to put validator in unbonding state
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
_, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
@ -328,7 +328,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
_, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
require.NoError(t, err)
// retrieve the unbonding delegation
@ -382,7 +382,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
_, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
@ -404,7 +404,7 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
_, err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
require.NoError(t, err)
// no ubd should have been found, coins should have been returned direcly to account
@ -553,7 +553,7 @@ func TestRedelegateSelfDelegation(t *testing.T) {
}
keeper.SetDelegation(ctx, delegation)
err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
_, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
require.NoError(t, err)
// end block
@ -618,7 +618,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
_, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
@ -640,7 +640,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
_, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
require.NoError(t, err)
// retrieve the unbonding delegation
@ -704,7 +704,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
_, err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
// end block
@ -726,7 +726,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) {
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
_, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
require.NoError(t, err)
// no ubd should have been found, coins should have been returned direcly to account

View File

@ -2,6 +2,7 @@ package keeper
import (
"encoding/binary"
"time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
@ -25,6 +26,8 @@ var (
RedelegationKey = []byte{0x0A} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0B} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x0C} // prefix for each key for an redelegation, by destination validator operator
UnbondingQueueKey = []byte{0x0D} // prefix for the timestamps in unbonding queue
RedelegationQueueKey = []byte{0x0E} // prefix for the timestamps in redelegations queue
)
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
@ -134,6 +137,12 @@ func GetUBDsByValIndexKey(valAddr sdk.ValAddress) []byte {
return append(UnbondingDelegationByValIndexKey, valAddr.Bytes()...)
}
// gets the prefix for all unbonding delegations from a delegator
func GetUnbondingDelegationTimeKey(timestamp time.Time) []byte {
bz := types.MsgCdc.MustMarshalBinary(timestamp)
return append(UnbondingQueueKey, bz...)
}
//________________________________________________________________________________
// gets the key for a redelegation
@ -202,6 +211,12 @@ func GetREDKeyFromValDstIndexKey(indexKey []byte) []byte {
return GetREDKey(delAddr, valSrcAddr, valDstAddr)
}
// gets the prefix for all unbonding delegations from a delegator
func GetRedelegationTimeKey(timestamp time.Time) []byte {
bz, _ := timestamp.MarshalBinary()
return append(RedelegationQueueKey, bz...)
}
//______________
// gets the prefix keyspace for redelegations from a delegator

View File

@ -62,9 +62,7 @@ func MakeTestCodec() *codec.Codec {
cdc.RegisterConcrete(types.MsgCreateValidator{}, "test/stake/CreateValidator", nil)
cdc.RegisterConcrete(types.MsgEditValidator{}, "test/stake/EditValidator", nil)
cdc.RegisterConcrete(types.MsgBeginUnbonding{}, "test/stake/BeginUnbonding", nil)
cdc.RegisterConcrete(types.MsgCompleteUnbonding{}, "test/stake/CompleteUnbonding", nil)
cdc.RegisterConcrete(types.MsgBeginRedelegate{}, "test/stake/BeginRedelegate", nil)
cdc.RegisterConcrete(types.MsgCompleteRedelegate{}, "test/stake/CompleteRedelegate", nil)
// Register AppAccount
cdc.RegisterInterface((*auth.Account)(nil), nil)

View File

@ -178,33 +178,6 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.
}
}
// SimulateMsgCompleteUnbonding
func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
validatorAcc := simulation.RandomAcc(r, accs)
validatorAddress := sdk.ValAddress(validatorAcc.Address)
delegatorAcc := simulation.RandomAcc(r, accs)
delegatorAddress := delegatorAcc.Address
msg := stake.MsgCompleteUnbonding{
DelegatorAddr: delegatorAddress,
ValidatorAddr: validatorAddress,
}
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}
ctx, write := ctx.CacheContext()
result := handler(ctx, msg)
if result.IsOK() {
write()
}
event(fmt.Sprintf("stake/MsgCompleteUnbonding/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgCompleteUnbonding: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil, nil
}
}
// SimulateMsgBeginRedelegate
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
@ -245,36 +218,6 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation
}
}
// SimulateMsgCompleteRedelegate
func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation {
handler := stake.NewHandler(k)
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) {
validatorSrcAcc := simulation.RandomAcc(r, accs)
validatorSrcAddress := sdk.ValAddress(validatorSrcAcc.Address)
validatorDstAcc := simulation.RandomAcc(r, accs)
validatorDstAddress := sdk.ValAddress(validatorDstAcc.Address)
delegatorAcc := simulation.RandomAcc(r, accs)
delegatorAddress := delegatorAcc.Address
msg := stake.MsgCompleteRedelegate{
DelegatorAddr: delegatorAddress,
ValidatorSrcAddr: validatorSrcAddress,
ValidatorDstAddr: validatorDstAddress,
}
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}
ctx, write := ctx.CacheContext()
result := handler(ctx, msg)
if result.IsOK() {
write()
}
event(fmt.Sprintf("stake/MsgCompleteRedelegate/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgCompleteRedelegate: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil, nil
}
}
// Setup
// nolint: errcheck
func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup {

View File

@ -49,9 +49,7 @@ func TestStakeWithRandomMessages(t *testing.T) {
{5, SimulateMsgEditValidator(stakeKeeper)},
{15, SimulateMsgDelegate(mapper, stakeKeeper)},
{10, SimulateMsgBeginUnbonding(mapper, stakeKeeper)},
{3, SimulateMsgCompleteUnbonding(stakeKeeper)},
{10, SimulateMsgBeginRedelegate(mapper, stakeKeeper)},
{3, SimulateMsgCompleteRedelegate(stakeKeeper)},
}, []simulation.RandSetup{
Setup(mapp, stakeKeeper),
}, []simulation.Invariant{

View File

@ -9,27 +9,25 @@ import (
)
type (
Keeper = keeper.Keeper
Validator = types.Validator
Description = types.Description
Commission = types.Commission
Delegation = types.Delegation
DelegationSummary = types.DelegationSummary
UnbondingDelegation = types.UnbondingDelegation
Redelegation = types.Redelegation
Params = types.Params
Pool = types.Pool
MsgCreateValidator = types.MsgCreateValidator
MsgEditValidator = types.MsgEditValidator
MsgDelegate = types.MsgDelegate
MsgBeginUnbonding = types.MsgBeginUnbonding
MsgCompleteUnbonding = types.MsgCompleteUnbonding
MsgBeginRedelegate = types.MsgBeginRedelegate
MsgCompleteRedelegate = types.MsgCompleteRedelegate
GenesisState = types.GenesisState
QueryDelegatorParams = querier.QueryDelegatorParams
QueryValidatorParams = querier.QueryValidatorParams
QueryBondsParams = querier.QueryBondsParams
Keeper = keeper.Keeper
Validator = types.Validator
Description = types.Description
Commission = types.Commission
Delegation = types.Delegation
DelegationSummary = types.DelegationSummary
UnbondingDelegation = types.UnbondingDelegation
Redelegation = types.Redelegation
Params = types.Params
Pool = types.Pool
MsgCreateValidator = types.MsgCreateValidator
MsgEditValidator = types.MsgEditValidator
MsgDelegate = types.MsgDelegate
MsgBeginUnbonding = types.MsgBeginUnbonding
MsgBeginRedelegate = types.MsgBeginRedelegate
GenesisState = types.GenesisState
QueryDelegatorParams = querier.QueryDelegatorParams
QueryValidatorParams = querier.QueryValidatorParams
QueryBondsParams = querier.QueryBondsParams
)
var (
@ -76,9 +74,7 @@ var (
NewMsgEditValidator = types.NewMsgEditValidator
NewMsgDelegate = types.NewMsgDelegate
NewMsgBeginUnbonding = types.NewMsgBeginUnbonding
NewMsgCompleteUnbonding = types.NewMsgCompleteUnbonding
NewMsgBeginRedelegate = types.NewMsgBeginRedelegate
NewMsgCompleteRedelegate = types.NewMsgCompleteRedelegate
NewQuerier = querier.NewQuerier
)

View File

@ -20,4 +20,5 @@ var (
Delegator = sdk.TagDelegator
Moniker = "moniker"
Identity = "identity"
EndTime = "end-time"
)

View File

@ -10,9 +10,7 @@ func RegisterCodec(cdc *codec.Codec) {
cdc.RegisterConcrete(MsgEditValidator{}, "cosmos-sdk/MsgEditValidator", nil)
cdc.RegisterConcrete(MsgDelegate{}, "cosmos-sdk/MsgDelegate", nil)
cdc.RegisterConcrete(MsgBeginUnbonding{}, "cosmos-sdk/BeginUnbonding", nil)
cdc.RegisterConcrete(MsgCompleteUnbonding{}, "cosmos-sdk/CompleteUnbonding", nil)
cdc.RegisterConcrete(MsgBeginRedelegate{}, "cosmos-sdk/BeginRedelegate", nil)
cdc.RegisterConcrete(MsgCompleteRedelegate{}, "cosmos-sdk/CompleteRedelegate", nil)
}
// generic sealed codec to be used throughout sdk

View File

@ -9,6 +9,23 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// DVPair is struct that just has a delegator-validator pair with no other data.
// It is intended to be used as a marshalable pointer. For example, a DVPair can be used to construct the
// key to getting an UnbondingDelegation from state.
type DVPair struct {
DelegatorAddr sdk.AccAddress
ValidatorAddr sdk.ValAddress
}
// DVVTriplet is struct that just has a delegator-validator-validator triplet with no other data.
// It is intended to be used as a marshalable pointer. For example, a DVVTriplet can be used to construct the
// key to getting a Redelegation from state.
type DVVTriplet struct {
DelegatorAddr sdk.AccAddress
ValidatorSrcAddr sdk.ValAddress
ValidatorDstAddr sdk.ValAddress
}
// Delegation represents the bond with tokens held by an account. It is
// owned by one delegator, and is associated with the voting power of one
// pubKey.

View File

@ -12,8 +12,6 @@ const MsgType = "stake"
// Verify interface at compile time
var _, _, _ sdk.Msg = &MsgCreateValidator{}, &MsgEditValidator{}, &MsgDelegate{}
var _, _ sdk.Msg = &MsgBeginUnbonding{}, &MsgCompleteUnbonding{}
var _, _ sdk.Msg = &MsgBeginRedelegate{}, &MsgCompleteRedelegate{}
//______________________________________________________________________
@ -276,51 +274,6 @@ func (msg MsgBeginRedelegate) ValidateBasic() sdk.Error {
return nil
}
// MsgDelegate - struct for bonding transactions
type MsgCompleteRedelegate struct {
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
ValidatorSrcAddr sdk.ValAddress `json:"validator_source_addr"`
ValidatorDstAddr sdk.ValAddress `json:"validator_destination_addr"`
}
func NewMsgCompleteRedelegate(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress) MsgCompleteRedelegate {
return MsgCompleteRedelegate{
DelegatorAddr: delAddr,
ValidatorSrcAddr: valSrcAddr,
ValidatorDstAddr: valDstAddr,
}
}
//nolint
func (msg MsgCompleteRedelegate) Type() string { return MsgType }
func (msg MsgCompleteRedelegate) Name() string { return "complete_redelegate" }
func (msg MsgCompleteRedelegate) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.DelegatorAddr}
}
// get the bytes for the message signer to sign on
func (msg MsgCompleteRedelegate) GetSignBytes() []byte {
b, err := MsgCdc.MarshalJSON(msg)
if err != nil {
panic(err)
}
return sdk.MustSortJSON(b)
}
// quick validity check
func (msg MsgCompleteRedelegate) ValidateBasic() sdk.Error {
if msg.DelegatorAddr == nil {
return ErrNilDelegatorAddr(DefaultCodespace)
}
if msg.ValidatorSrcAddr == nil {
return ErrNilValidatorAddr(DefaultCodespace)
}
if msg.ValidatorDstAddr == nil {
return ErrNilValidatorAddr(DefaultCodespace)
}
return nil
}
//______________________________________________________________________
// MsgBeginUnbonding - struct for unbonding transactions
@ -373,43 +326,3 @@ func (msg MsgBeginUnbonding) ValidateBasic() sdk.Error {
}
return nil
}
// MsgCompleteUnbonding - struct for unbonding transactions
type MsgCompleteUnbonding struct {
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
ValidatorAddr sdk.ValAddress `json:"validator_addr"`
}
func NewMsgCompleteUnbonding(delAddr sdk.AccAddress, valAddr sdk.ValAddress) MsgCompleteUnbonding {
return MsgCompleteUnbonding{
DelegatorAddr: delAddr,
ValidatorAddr: valAddr,
}
}
//nolint
func (msg MsgCompleteUnbonding) Type() string { return MsgType }
func (msg MsgCompleteUnbonding) Name() string { return "complete_unbonding" }
func (msg MsgCompleteUnbonding) GetSigners() []sdk.AccAddress {
return []sdk.AccAddress{msg.DelegatorAddr}
}
// get the bytes for the message signer to sign on
func (msg MsgCompleteUnbonding) GetSignBytes() []byte {
b, err := MsgCdc.MarshalJSON(msg)
if err != nil {
panic(err)
}
return sdk.MustSortJSON(b)
}
// quick validity check
func (msg MsgCompleteUnbonding) ValidateBasic() sdk.Error {
if msg.DelegatorAddr == nil {
return ErrNilDelegatorAddr(DefaultCodespace)
}
if msg.ValidatorAddr == nil {
return ErrNilValidatorAddr(DefaultCodespace)
}
return nil
}

View File

@ -177,31 +177,6 @@ func TestMsgBeginRedelegate(t *testing.T) {
}
}
// test ValidateBasic for MsgUnbond
func TestMsgCompleteRedelegate(t *testing.T) {
tests := []struct {
name string
delegatorAddr sdk.AccAddress
validatorSrcAddr sdk.ValAddress
validatorDstAddr sdk.ValAddress
expectPass bool
}{
{"regular", sdk.AccAddress(addr1), addr2, addr3, true},
{"empty delegator", sdk.AccAddress(emptyAddr), addr1, addr3, false},
{"empty source validator", sdk.AccAddress(addr1), emptyAddr, addr3, false},
{"empty destination validator", sdk.AccAddress(addr1), addr2, emptyAddr, false},
}
for _, tc := range tests {
msg := NewMsgCompleteRedelegate(tc.delegatorAddr, tc.validatorSrcAddr, tc.validatorDstAddr)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
}
}
}
// test ValidateBasic for MsgUnbond
func TestMsgBeginUnbonding(t *testing.T) {
tests := []struct {
@ -227,26 +202,3 @@ func TestMsgBeginUnbonding(t *testing.T) {
}
}
}
// test ValidateBasic for MsgUnbond
func TestMsgCompleteUnbonding(t *testing.T) {
tests := []struct {
name string
delegatorAddr sdk.AccAddress
validatorAddr sdk.ValAddress
expectPass bool
}{
{"regular", sdk.AccAddress(addr1), addr2, true},
{"empty delegator", sdk.AccAddress(emptyAddr), addr1, false},
{"empty validator", sdk.AccAddress(addr1), emptyAddr, false},
}
for _, tc := range tests {
msg := NewMsgCompleteUnbonding(tc.delegatorAddr, tc.validatorAddr)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
} else {
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
}
}
}