Merge PR #3243: allow multiple simultaneous redelegations/ubds between same delegator/validator(s) addresses
* remove kv seperation for marshalling * pending * cleanup * cleanup x2 * pending * working * minor refactors * entry structs defined * uncompiled mechanism written * add many compile fixes * code compiles * fix test compile errors * test cover passes * ... * multiple entries fix * ... * more design fix * working * fix test cover bug * Update PENDING.md * update comment around queue completion for redelegations/ubds * basic spec updates * remove ErrConflictingRedelegation * @cwgoes comments are resolved * Update x/staking/keeper/slash.go Co-Authored-By: rigelrozanski <rigel.rozanski@gmail.com> * address @alexanderbez comments
This commit is contained in:
parent
916ea85630
commit
133934ae37
@ -27,9 +27,10 @@ BREAKING CHANGES
|
||||
* [\#3064](https://github.com/cosmos/cosmos-sdk/issues/3064) Sanitize `sdk.Coin` denom. Coins denoms are now case insensitive, i.e. 100fooToken equals to 100FOOTOKEN.
|
||||
* [\#3195](https://github.com/cosmos/cosmos-sdk/issues/3195) Allows custom configuration for syncable strategy
|
||||
* [\#3242](https://github.com/cosmos/cosmos-sdk/issues/3242) Fix infinite gas
|
||||
meter utilization during aborted ante handler executions.
|
||||
* [\#2222] [x/staking] `/stake` -> `/staking` module rename
|
||||
|
||||
meter utilization during aborted ante handler executions.
|
||||
* [staking] \#2222 `/stake` -> `/staking` module rename
|
||||
* [staking] \#1402 Redelegation and unbonding-delegation structs changed to include multiple an array of entries
|
||||
|
||||
* Tendermint
|
||||
* \#3279 Upgrade to Tendermint 0.28.0-dev0
|
||||
|
||||
@ -83,6 +84,7 @@ IMPROVEMENTS
|
||||
slashing, and staking modules.
|
||||
* [\#3093](https://github.com/cosmos/cosmos-sdk/issues/3093) Ante handler does no longer read all accounts in one go when processing signatures as signature
|
||||
verification may fail before last signature is checked.
|
||||
* [x/stake] \#1402 Add for multiple simultaneous redelegations or unbonding-delegations within an unbonding period
|
||||
|
||||
* Tendermint
|
||||
|
||||
|
||||
@ -467,8 +467,9 @@ func TestBonding(t *testing.T) {
|
||||
require.Len(t, txs, 1)
|
||||
require.Equal(t, resultTx.Height, txs[0].Height)
|
||||
|
||||
unbonding := getUndelegation(t, port, addr, operAddrs[0])
|
||||
require.Equal(t, int64(30), unbonding.Balance.Amount.Int64())
|
||||
ubd := getUnbondingDelegation(t, port, addr, operAddrs[0])
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
require.Equal(t, int64(30), ubd.Entries[0].Balance.Amount.Int64())
|
||||
|
||||
// test redelegation
|
||||
resultTx = doBeginRedelegation(t, port, name1, pw, addr, operAddrs[0], operAddrs[1], 30, fees)
|
||||
@ -502,23 +503,28 @@ func TestBonding(t *testing.T) {
|
||||
|
||||
redelegation := getRedelegations(t, port, addr, operAddrs[0], operAddrs[1])
|
||||
require.Len(t, redelegation, 1)
|
||||
require.Equal(t, "30", redelegation[0].Balance.Amount.String())
|
||||
require.Len(t, redelegation[0].Entries, 1)
|
||||
require.Equal(t, "30", redelegation[0].Entries[0].Balance.Amount.String())
|
||||
|
||||
delegatorUbds := getDelegatorUnbondingDelegations(t, port, addr)
|
||||
require.Len(t, delegatorUbds, 1)
|
||||
require.Equal(t, "30", delegatorUbds[0].Balance.Amount.String())
|
||||
require.Len(t, delegatorUbds[0].Entries, 1)
|
||||
require.Equal(t, "30", delegatorUbds[0].Entries[0].Balance.Amount.String())
|
||||
|
||||
delegatorReds := getRedelegations(t, port, addr, nil, nil)
|
||||
require.Len(t, delegatorReds, 1)
|
||||
require.Equal(t, "30", delegatorReds[0].Balance.Amount.String())
|
||||
require.Len(t, delegatorReds[0].Entries, 1)
|
||||
require.Equal(t, "30", delegatorReds[0].Entries[0].Balance.Amount.String())
|
||||
|
||||
validatorUbds := getValidatorUnbondingDelegations(t, port, operAddrs[0])
|
||||
require.Len(t, validatorUbds, 1)
|
||||
require.Equal(t, "30", validatorUbds[0].Balance.Amount.String())
|
||||
require.Len(t, validatorUbds[0].Entries, 1)
|
||||
require.Equal(t, "30", validatorUbds[0].Entries[0].Balance.Amount.String())
|
||||
|
||||
validatorReds := getRedelegations(t, port, nil, operAddrs[0], nil)
|
||||
require.Len(t, validatorReds, 1)
|
||||
require.Equal(t, "30", validatorReds[0].Balance.Amount.String())
|
||||
require.Len(t, validatorReds[0].Entries, 1)
|
||||
require.Equal(t, "30", validatorReds[0].Entries[0].Balance.Amount.String())
|
||||
|
||||
// TODO Undonding status not currently implemented
|
||||
// require.Equal(t, sdk.Unbonding, bondedValidators[0].Status)
|
||||
|
||||
@ -942,8 +942,13 @@ func getDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, vali
|
||||
}
|
||||
|
||||
// GET /staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr} Query all unbonding delegations between a delegator and a validator
|
||||
func getUndelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) staking.UnbondingDelegation {
|
||||
res, body := Request(t, port, "GET", fmt.Sprintf("/staking/delegators/%s/unbonding_delegations/%s", delegatorAddr, validatorAddr), nil)
|
||||
func getUnbondingDelegation(t *testing.T, port string, delegatorAddr sdk.AccAddress,
|
||||
validatorAddr sdk.ValAddress) staking.UnbondingDelegation {
|
||||
|
||||
res, body := Request(t, port, "GET",
|
||||
fmt.Sprintf("/staking/delegators/%s/unbonding_delegations/%s",
|
||||
delegatorAddr, validatorAddr), nil)
|
||||
|
||||
require.Equal(t, http.StatusOK, res.StatusCode, body)
|
||||
|
||||
var unbond staking.UnbondingDelegation
|
||||
|
||||
@ -116,14 +116,18 @@ func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context) {
|
||||
|
||||
// iterate through redelegations, reset creation height
|
||||
app.stakingKeeper.IterateRedelegations(ctx, func(_ int64, red staking.Redelegation) (stop bool) {
|
||||
red.CreationHeight = 0
|
||||
for i := range red.Entries {
|
||||
red.Entries[i].CreationHeight = 0
|
||||
}
|
||||
app.stakingKeeper.SetRedelegation(ctx, red)
|
||||
return false
|
||||
})
|
||||
|
||||
// iterate through unbonding delegations, reset creation height
|
||||
app.stakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) (stop bool) {
|
||||
ubd.CreationHeight = 0
|
||||
for i := range ubd.Entries {
|
||||
ubd.Entries[i].CreationHeight = 0
|
||||
}
|
||||
app.stakingKeeper.SetUnbondingDelegation(ctx, ubd)
|
||||
return false
|
||||
})
|
||||
|
||||
@ -303,7 +303,8 @@ func TestGaiaCLICreateValidator(t *testing.T) {
|
||||
// Get unbonding delegations from the validator
|
||||
validatorUbds := f.QueryStakingUnbondingDelegationsFrom(barVal)
|
||||
require.Len(t, validatorUbds, 1)
|
||||
require.Equal(t, "1", validatorUbds[0].Balance.Amount.String())
|
||||
require.Len(t, validatorUbds[0].Entries, 1)
|
||||
require.Equal(t, "1", validatorUbds[0].Entries[0].Balance.Amount.String())
|
||||
|
||||
// Query staking parameters
|
||||
params := f.QueryStakingParameters()
|
||||
|
||||
@ -96,9 +96,9 @@ type Description struct {
|
||||
### Delegation
|
||||
|
||||
Delegations are identified by combining `DelegatorAddr` (the address of the delegator)
|
||||
with the `OperatorAddr` Delegators are indexed in the store as follows:
|
||||
with the `ValidatorAddr` Delegators are indexed in the store as follows:
|
||||
|
||||
- Delegation: ` 0x0A | DelegatorAddr | OperatorAddr -> amino(delegation)`
|
||||
- Delegation: ` 0x0A | DelegatorAddr | ValidatorAddr -> amino(delegation)`
|
||||
|
||||
Atom holders may delegate coins to validators; under this circumstance their
|
||||
funds are held in a `Delegation` data structure. It is owned by one
|
||||
@ -107,26 +107,29 @@ the transaction is the owner of the bond.
|
||||
|
||||
```golang
|
||||
type Delegation struct {
|
||||
Shares sdk.Dec // delegation shares received
|
||||
Height int64 // last height bond updated
|
||||
DelegatorAddr sdk.AccAddress
|
||||
ValidatorAddr sdk.ValAddress
|
||||
Shares sdk.Dec // delegation shares received
|
||||
}
|
||||
```
|
||||
|
||||
### UnbondingDelegation
|
||||
|
||||
Shares in a `Delegation` can be unbonded, but they must for some time exist as an `UnbondingDelegation`, where shares can be reduced if Byzantine behavior is detected.
|
||||
Shares in a `Delegation` can be unbonded, but they must for some time exist as
|
||||
an `UnbondingDelegation`, where shares can be reduced if Byzantine behavior is
|
||||
detected.
|
||||
|
||||
`UnbondingDelegation` are indexed in the store as:
|
||||
|
||||
- UnbondingDelegationByDelegator: ` 0x0B | DelegatorAddr | OperatorAddr ->
|
||||
- UnbondingDelegationByDelegator: ` 0x0B | DelegatorAddr | ValidatorAddr ->
|
||||
amino(unbondingDelegation)`
|
||||
- UnbondingDelegationByValOwner: ` 0x0C | OperatorAddr | DelegatorAddr | OperatorAddr ->
|
||||
- UnbondingDelegationByValOwner: ` 0x0C | ValidatorAddr | DelegatorAddr | ValidatorAddr ->
|
||||
nil`
|
||||
|
||||
The first map here is used in queries, to lookup all unbonding delegations for
|
||||
a given delegator, while the second map is used in slashing, to lookup all
|
||||
unbonding delegations associated with a given validator that need to be
|
||||
slashed.
|
||||
The first map here is used in queries, to lookup all unbonding delegations for
|
||||
a given delegator, while the second map is used in slashing, to lookup all
|
||||
unbonding delegations associated with a given validator that need to be
|
||||
slashed.
|
||||
|
||||
A UnbondingDelegation object is created every time an unbonding is initiated.
|
||||
The unbond must be completed with a second transaction provided by the
|
||||
@ -134,8 +137,16 @@ delegation owner after the unbonding period has passed.
|
||||
|
||||
```golang
|
||||
type UnbondingDelegation struct {
|
||||
Tokens sdk.Coins // the value in Atoms of the amount of shares which are unbonding
|
||||
CompleteTime int64 // unix time to complete redelegation
|
||||
DelegatorAddr sdk.AccAddress // delegator
|
||||
ValidatorAddr sdk.ValAddress // validator unbonding from operator addr
|
||||
Entries []UnbondingDelegationEntry // unbonding delegation entries
|
||||
}
|
||||
|
||||
type UnbondingDelegationEntry struct {
|
||||
CreationHeight int64 // height which the unbonding took place
|
||||
CompletionTime time.Time // unix time for unbonding completion
|
||||
InitialBalance sdk.Coin // atoms initially scheduled to receive at completion
|
||||
Balance sdk.Coin // atoms to receive at completion
|
||||
}
|
||||
```
|
||||
|
||||
@ -143,20 +154,20 @@ type UnbondingDelegation struct {
|
||||
|
||||
Shares in a `Delegation` can be rebonded to a different validator, but they must
|
||||
for some time exist as a `Redelegation`, where shares can be reduced if Byzantine
|
||||
behavior is detected. This is tracked as moving a delegation from a `FromOperatorAddr`
|
||||
to a `ToOperatorAddr`.
|
||||
behavior is detected. This is tracked as moving a delegation from a `ValidatorSrcAddr`
|
||||
to a `ValidatorDstAddr`.
|
||||
|
||||
`Redelegation` are indexed in the store as:
|
||||
|
||||
- Redelegations: `0x0D | DelegatorAddr | FromOperatorAddr | ToOperatorAddr ->
|
||||
- Redelegations: `0x0D | DelegatorAddr | ValidatorSrcAddr | ValidatorDstAddr ->
|
||||
amino(redelegation)`
|
||||
- RedelegationsBySrc: `0x0E | FromOperatorAddr | ToOperatorAddr |
|
||||
- RedelegationsBySrc: `0x0E | ValidatorSrcAddr | ValidatorDstAddr |
|
||||
DelegatorAddr -> nil`
|
||||
- RedelegationsByDst: `0x0F | ToOperatorAddr | FromOperatorAddr | DelegatorAddr
|
||||
- RedelegationsByDst: `0x0F | ValidatorDstAddr | ValidatorSrcAddr | DelegatorAddr
|
||||
-> nil`
|
||||
|
||||
The first map here is used for queries, to lookup all redelegations for a given
|
||||
delegator. The second map is used for slashing based on the `FromOperatorAddr`,
|
||||
delegator. The second map is used for slashing based on the `ValidatorSrcAddr`,
|
||||
while the third map is for slashing based on the ToValOwnerAddr.
|
||||
|
||||
A redelegation object is created every time a redelegation occurs. The
|
||||
@ -167,8 +178,18 @@ the original redelegation has been completed.
|
||||
|
||||
```golang
|
||||
type Redelegation struct {
|
||||
SourceShares sdk.Dec // amount of source shares redelegating
|
||||
DestinationShares sdk.Dec // amount of destination shares created at redelegation
|
||||
CompleteTime int64 // unix time to complete redelegation
|
||||
DelegatorAddr sdk.AccAddress // delegator
|
||||
ValidatorSrcAddr sdk.ValAddress // validator redelegation source operator addr
|
||||
ValidatorDstAddr sdk.ValAddress // validator redelegation destination operator addr
|
||||
Entries []RedelegationEntry // redelegation entries
|
||||
}
|
||||
|
||||
type RedelegationEntry struct {
|
||||
CreationHeight int64 // height which the redelegation took place
|
||||
CompletionTime time.Time // unix time for redelegation completion
|
||||
InitialBalance sdk.Coin // initial balance when redelegation started
|
||||
Balance sdk.Coin // current balance (current value held in destination validator)
|
||||
SharesSrc sdk.Dec // amount of source-validator shares removed by redelegation
|
||||
SharesDst sdk.Dec // amount of destination-validator shares created by redelegation
|
||||
}
|
||||
```
|
||||
|
||||
@ -2,7 +2,6 @@ package staking
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
@ -18,9 +17,11 @@ import (
|
||||
// Returns final validator set after applying all declaration and delegations
|
||||
func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res []abci.ValidatorUpdate, err error) {
|
||||
|
||||
// We need to pretend to be "n blocks before genesis", where "n" is the validator update delay,
|
||||
// so that e.g. slashing periods are correctly initialized for the validator set
|
||||
// e.g. with a one-block offset - the first TM block is at height 1, so state updates applied from genesis.json are in block 0.
|
||||
// We need to pretend to be "n blocks before genesis", where "n" is the
|
||||
// validator update delay, so that e.g. slashing periods are correctly
|
||||
// initialized for the validator set e.g. with a one-block offset - the
|
||||
// first TM block is at height 1, so state updates applied from
|
||||
// genesis.json are in block 0.
|
||||
ctx = ctx.WithBlockHeight(1 - types.ValidatorUpdateDelay)
|
||||
|
||||
keeper.SetPool(ctx, data.Pool)
|
||||
@ -46,20 +47,18 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
|
||||
keeper.SetDelegation(ctx, delegation)
|
||||
}
|
||||
|
||||
sort.SliceStable(data.UnbondingDelegations[:], func(i, j int) bool {
|
||||
return data.UnbondingDelegations[i].CreationHeight < data.UnbondingDelegations[j].CreationHeight
|
||||
})
|
||||
for _, ubd := range data.UnbondingDelegations {
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
keeper.InsertUnbondingQueue(ctx, ubd)
|
||||
for _, entry := range ubd.Entries {
|
||||
keeper.InsertUBDQueue(ctx, ubd, entry.CompletionTime)
|
||||
}
|
||||
}
|
||||
|
||||
sort.SliceStable(data.Redelegations[:], func(i, j int) bool {
|
||||
return data.Redelegations[i].CreationHeight < data.Redelegations[j].CreationHeight
|
||||
})
|
||||
for _, red := range data.Redelegations {
|
||||
keeper.SetRedelegation(ctx, red)
|
||||
keeper.InsertRedelegationQueue(ctx, red)
|
||||
for _, entry := range red.Entries {
|
||||
keeper.InsertRedelegationQueue(ctx, red, entry.CompletionTime)
|
||||
}
|
||||
}
|
||||
|
||||
// don't need to run Tendermint updates if we exported
|
||||
|
||||
@ -53,7 +53,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, sdk.T
|
||||
k.UnbondAllMatureValidatorQueue(ctx)
|
||||
|
||||
// Remove all mature unbonding delegations from the ubd queue.
|
||||
matureUnbonds := k.DequeueAllMatureUnbondingQueue(ctx, ctx.BlockHeader().Time)
|
||||
matureUnbonds := k.DequeueAllMatureUBDQueue(ctx, ctx.BlockHeader().Time)
|
||||
for _, dvPair := range matureUnbonds {
|
||||
err := k.CompleteUnbonding(ctx, dvPair.DelegatorAddr, dvPair.ValidatorAddr)
|
||||
if err != nil {
|
||||
@ -70,7 +70,8 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, sdk.T
|
||||
// Remove all mature redelegations from the red queue.
|
||||
matureRedelegations := k.DequeueAllMatureRedelegationQueue(ctx, ctx.BlockHeader().Time)
|
||||
for _, dvvTriplet := range matureRedelegations {
|
||||
err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr, dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr)
|
||||
err := k.CompleteRedelegation(ctx, dvvTriplet.DelegatorAddr,
|
||||
dvvTriplet.ValidatorSrcAddr, dvvTriplet.ValidatorDstAddr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -216,34 +217,34 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper)
|
||||
}
|
||||
|
||||
func handleMsgBeginUnbonding(ctx sdk.Context, msg types.MsgBeginUnbonding, k keeper.Keeper) sdk.Result {
|
||||
ubd, err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
|
||||
completionTime, err := k.BeginUnbonding(ctx, msg.DelegatorAddr, msg.ValidatorAddr, msg.SharesAmount)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(ubd.MinTime)
|
||||
finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime)
|
||||
tags := sdk.NewTags(
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorAddr.String()),
|
||||
tags.EndTime, []byte(ubd.MinTime.Format(time.RFC3339)),
|
||||
tags.EndTime, []byte(completionTime.Format(time.RFC3339)),
|
||||
)
|
||||
|
||||
return sdk.Result{Data: finishTime, Tags: tags}
|
||||
}
|
||||
|
||||
func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k keeper.Keeper) sdk.Result {
|
||||
red, err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
|
||||
completionTime, err := k.BeginRedelegation(ctx, msg.DelegatorAddr, msg.ValidatorSrcAddr,
|
||||
msg.ValidatorDstAddr, msg.SharesAmount)
|
||||
if err != nil {
|
||||
return err.Result()
|
||||
}
|
||||
|
||||
finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(red.MinTime)
|
||||
finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime)
|
||||
resTags := sdk.NewTags(
|
||||
tags.Delegator, []byte(msg.DelegatorAddr.String()),
|
||||
tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()),
|
||||
tags.DstValidator, []byte(msg.ValidatorDstAddr.String()),
|
||||
tags.EndTime, []byte(red.MinTime.Format(time.RFC3339)),
|
||||
tags.EndTime, []byte(completionTime.Format(time.RFC3339)),
|
||||
)
|
||||
|
||||
return sdk.Result{Data: finishTime, Tags: resTags}
|
||||
|
||||
@ -416,6 +416,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(delegatorAddr, validatorAddr, unbondShares)
|
||||
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)
|
||||
var finishTime time.Time
|
||||
@ -423,7 +424,7 @@ func TestIncrementsMsgUnbond(t *testing.T) {
|
||||
ctx = ctx.WithBlockTime(finishTime)
|
||||
EndBlocker(ctx, keeper)
|
||||
|
||||
//Check that the accounts and the bond account have the appropriate values
|
||||
// check that the accounts and the bond account have the appropriate values
|
||||
validator, found = keeper.GetValidator(ctx, validatorAddr)
|
||||
require.True(t, found)
|
||||
bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
@ -849,46 +850,205 @@ func TestTransitiveRedelegation(t *testing.T) {
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
}
|
||||
|
||||
func TestConflictingRedelegation(t *testing.T) {
|
||||
func TestMultipleRedelegationAtSameTime(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
validatorAddr := sdk.ValAddress(keep.Addrs[0])
|
||||
validatorAddr2 := sdk.ValAddress(keep.Addrs[1])
|
||||
valAddr := sdk.ValAddress(keep.Addrs[0])
|
||||
valAddr2 := sdk.ValAddress(keep.Addrs[1])
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 1
|
||||
params.UnbondingTime = 1 * time.Second
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validators
|
||||
msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10)
|
||||
msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10)
|
||||
msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// end block to bond them
|
||||
EndBlocker(ctx, keeper)
|
||||
|
||||
// begin redelegate
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(sdk.AccAddress(validatorAddr), validatorAddr, validatorAddr2, sdk.NewDec(5))
|
||||
// begin a redelegate
|
||||
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr,
|
||||
valAddr, valAddr2, sdk.NewDec(5))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// cannot redelegate again while first redelegation still exists
|
||||
// there should only be one entry in the redelegation object
|
||||
rd, found := keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2)
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
|
||||
// start a second redelegation at this same time as the first
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, !got.IsOK(), "expected an error, msg: %v", msgBeginRedelegate)
|
||||
require.True(t, got.IsOK(), "expected no error, msg: %v", msgBeginRedelegate)
|
||||
|
||||
// progress forward in time
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(10 * time.Second))
|
||||
// now there should be two entries
|
||||
rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2)
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 2)
|
||||
|
||||
// complete first redelegation
|
||||
// move forward in time, should complete both redelegations
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(1 * time.Second))
|
||||
EndBlocker(ctx, keeper)
|
||||
|
||||
// now should be able to redelegate again
|
||||
rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2)
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
func TestMultipleRedelegationAtUniqueTimes(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
valAddr := sdk.ValAddress(keep.Addrs[0])
|
||||
valAddr2 := sdk.ValAddress(keep.Addrs[1])
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 10 * time.Second
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validators
|
||||
msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], 10)
|
||||
got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// end block to bond them
|
||||
EndBlocker(ctx, keeper)
|
||||
|
||||
// begin a redelegate
|
||||
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
|
||||
msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr,
|
||||
valAddr, valAddr2, sdk.NewDec(5))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error")
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// move forward in time and start a second redelegation
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second))
|
||||
got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, msg: %v", msgBeginRedelegate)
|
||||
|
||||
// now there should be two entries
|
||||
rd, found := keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2)
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 2)
|
||||
|
||||
// move forward in time, should complete the first redelegation, but not the second
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second))
|
||||
EndBlocker(ctx, keeper)
|
||||
rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2)
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
|
||||
// move forward in time, should complete the second redelegation
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second))
|
||||
EndBlocker(ctx, keeper)
|
||||
rd, found = keeper.GetRedelegation(ctx, selfDelAddr, valAddr, valAddr2)
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
valAddr := sdk.ValAddress(keep.Addrs[0])
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 1 * time.Second
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validator
|
||||
msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// end block to bond
|
||||
EndBlocker(ctx, keeper)
|
||||
|
||||
// begin an unbonding delegation
|
||||
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(selfDelAddr, valAddr, sdk.NewDec(5))
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// there should only be one entry in the ubd object
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr)
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// start a second ubd at this same time as the first
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, msg: %v", msgBeginUnbonding)
|
||||
|
||||
// now there should be two entries
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr)
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 2)
|
||||
|
||||
// move forwaubd in time, should complete both ubds
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(1 * time.Second))
|
||||
EndBlocker(ctx, keeper)
|
||||
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr)
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) {
|
||||
ctx, _, keeper := keep.CreateTestInput(t, false, 1000)
|
||||
valAddr := sdk.ValAddress(keep.Addrs[0])
|
||||
|
||||
// set the unbonding time
|
||||
params := keeper.GetParams(ctx)
|
||||
params.UnbondingTime = 10 * time.Second
|
||||
keeper.SetParams(ctx, params)
|
||||
|
||||
// create the validator
|
||||
msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10)
|
||||
got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator")
|
||||
|
||||
// end block to bond
|
||||
EndBlocker(ctx, keeper)
|
||||
|
||||
// begin an unbonding delegation
|
||||
selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator)
|
||||
msgBeginUnbonding := NewMsgBeginUnbonding(selfDelAddr, valAddr, sdk.NewDec(5))
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, %v", got)
|
||||
|
||||
// there should only be one entry in the ubd object
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr)
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// move forwaubd in time and start a second redelegation
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second))
|
||||
got = handleMsgBeginUnbonding(ctx, msgBeginUnbonding, keeper)
|
||||
require.True(t, got.IsOK(), "expected no error, msg: %v", msgBeginUnbonding)
|
||||
|
||||
// now there should be two entries
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr)
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 2)
|
||||
|
||||
// move forwaubd in time, should complete the first redelegation, but not the second
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second))
|
||||
EndBlocker(ctx, keeper)
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr)
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// move forwaubd in time, should complete the second redelegation
|
||||
ctx = ctx.WithBlockTime(ctx.BlockHeader().Time.Add(5 * time.Second))
|
||||
EndBlocker(ctx, keeper)
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, selfDelAddr, valAddr)
|
||||
require.False(t, found)
|
||||
}
|
||||
|
||||
func TestUnbondingWhenExcessValidators(t *testing.T) {
|
||||
@ -991,14 +1151,16 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
|
||||
keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1))
|
||||
|
||||
// unbonding delegation should have been slashed by half
|
||||
unbonding, found := keeper.GetUnbondingDelegation(ctx, del, valA)
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, del, valA)
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(2), unbonding.Balance.Amount.Int64())
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
require.Equal(t, int64(2), ubd.Entries[0].Balance.Amount.Int64())
|
||||
|
||||
// redelegation should have been slashed by half
|
||||
redelegation, found := keeper.GetRedelegation(ctx, del, valA, valB)
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(3), redelegation.Balance.Amount.Int64())
|
||||
require.Len(t, redelegation.Entries, 1)
|
||||
require.Equal(t, int64(3), redelegation.Entries[0].Balance.Amount.Int64())
|
||||
|
||||
// destination delegation should have been slashed by half
|
||||
delegation, found = keeper.GetDelegation(ctx, del, valB)
|
||||
@ -1015,14 +1177,16 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) {
|
||||
keeper.Slash(ctx, consAddr0, 2, 10, sdk.NewDecWithPrec(5, 1))
|
||||
|
||||
// unbonding delegation should be unchanged
|
||||
unbonding, found = keeper.GetUnbondingDelegation(ctx, del, valA)
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, del, valA)
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(2), unbonding.Balance.Amount.Int64())
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
require.Equal(t, int64(2), ubd.Entries[0].Balance.Amount.Int64())
|
||||
|
||||
// redelegation should be unchanged
|
||||
redelegation, found = keeper.GetRedelegation(ctx, del, valA, valB)
|
||||
require.True(t, found)
|
||||
require.Equal(t, int64(3), redelegation.Balance.Amount.Int64())
|
||||
require.Len(t, redelegation.Entries, 1)
|
||||
require.Equal(t, int64(3), redelegation.Entries[0].Balance.Amount.Int64())
|
||||
|
||||
// destination delegation should be unchanged
|
||||
delegation, found = keeper.GetDelegation(ctx, del, valB)
|
||||
|
||||
@ -170,9 +170,28 @@ 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) {
|
||||
// SetUnbondingDelegationEntry adds an entry to the unbonding delegation at
|
||||
// the given addresses. It creates the unbonding delegation if it does not exist
|
||||
func (k Keeper) SetUnbondingDelegationEntry(ctx sdk.Context,
|
||||
delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress,
|
||||
creationHeight int64, minTime time.Time, balance sdk.Coin) types.UnbondingDelegation {
|
||||
|
||||
ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr)
|
||||
if found {
|
||||
ubd.AddEntry(creationHeight, minTime, balance)
|
||||
} else {
|
||||
ubd = types.NewUnbondingDelegation(delegatorAddr, validatorAddr, creationHeight, minTime, balance)
|
||||
}
|
||||
k.SetUnbondingDelegation(ctx, ubd)
|
||||
return ubd
|
||||
}
|
||||
|
||||
//________________________________________________
|
||||
// unbonding delegation queue timeslice operations
|
||||
|
||||
// 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) GetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time) (dvPairs []types.DVPair) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := store.Get(GetUnbondingDelegationTimeKey(timestamp))
|
||||
if bz == nil {
|
||||
@ -183,38 +202,45 @@ func (k Keeper) GetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time)
|
||||
}
|
||||
|
||||
// Sets a specific unbonding queue timeslice.
|
||||
func (k Keeper) SetUnbondingQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) {
|
||||
func (k Keeper) SetUBDQueueTimeSlice(ctx sdk.Context, timestamp time.Time, keys []types.DVPair) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
bz := k.cdc.MustMarshalBinaryLengthPrefixed(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)
|
||||
func (k Keeper) InsertUBDQueue(ctx sdk.Context, ubd types.UnbondingDelegation,
|
||||
completionTime time.Time) {
|
||||
|
||||
timeSlice := k.GetUBDQueueTimeSlice(ctx, completionTime)
|
||||
dvPair := types.DVPair{ubd.DelegatorAddr, ubd.ValidatorAddr}
|
||||
if len(timeSlice) == 0 {
|
||||
k.SetUnbondingQueueTimeSlice(ctx, ubd.MinTime, []types.DVPair{dvPair})
|
||||
k.SetUBDQueueTimeSlice(ctx, completionTime, []types.DVPair{dvPair})
|
||||
} else {
|
||||
timeSlice = append(timeSlice, dvPair)
|
||||
k.SetUnbondingQueueTimeSlice(ctx, ubd.MinTime, timeSlice)
|
||||
k.SetUBDQueueTimeSlice(ctx, completionTime, timeSlice)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns all the unbonding queue timeslices from time 0 until endTime
|
||||
func (k Keeper) UnbondingQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
|
||||
func (k Keeper) UBDQueueIterator(ctx sdk.Context, endTime time.Time) sdk.Iterator {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
return store.Iterator(UnbondingQueueKey, sdk.InclusiveEndBytes(GetUnbondingDelegationTimeKey(endTime)))
|
||||
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) {
|
||||
// Returns a concatenated list of all the timeslices inclusively previous to
|
||||
// currTime, and deletes the timeslices from the queue
|
||||
func (k Keeper) DequeueAllMatureUBDQueue(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)
|
||||
unbondingTimesliceIterator := k.UBDQueueIterator(ctx, ctx.BlockHeader().Time)
|
||||
for ; unbondingTimesliceIterator.Valid(); unbondingTimesliceIterator.Next() {
|
||||
timeslice := []types.DVPair{}
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(unbondingTimesliceIterator.Value(), ×lice)
|
||||
value := unbondingTimesliceIterator.Value()
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice)
|
||||
matureUnbonds = append(matureUnbonds, timeslice...)
|
||||
store.Delete(unbondingTimesliceIterator.Key())
|
||||
}
|
||||
@ -298,6 +324,26 @@ func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) {
|
||||
store.Set(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr), []byte{})
|
||||
}
|
||||
|
||||
// SetUnbondingDelegationEntry adds an entry to the unbonding delegation at
|
||||
// the given addresses. It creates the unbonding delegation if it does not exist
|
||||
func (k Keeper) SetRedelegationEntry(ctx sdk.Context,
|
||||
delegatorAddr sdk.AccAddress, validatorSrcAddr,
|
||||
validatorDstAddr sdk.ValAddress, creationHeight int64,
|
||||
minTime time.Time, balance sdk.Coin,
|
||||
sharesSrc, sharesDst sdk.Dec) types.Redelegation {
|
||||
|
||||
red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr)
|
||||
if found {
|
||||
red.AddEntry(creationHeight, minTime, balance, sharesSrc, sharesDst)
|
||||
} else {
|
||||
red = types.NewRedelegation(delegatorAddr, validatorSrcAddr,
|
||||
validatorDstAddr, creationHeight, minTime, balance, sharesSrc,
|
||||
sharesDst)
|
||||
}
|
||||
k.SetRedelegation(ctx, red)
|
||||
return red
|
||||
}
|
||||
|
||||
// iterate through all redelegations
|
||||
func (k Keeper) IterateRedelegations(ctx sdk.Context, fn func(index int64, red types.Redelegation) (stop bool)) {
|
||||
store := ctx.KVStore(k.storeKey)
|
||||
@ -322,6 +368,9 @@ func (k Keeper) RemoveRedelegation(ctx sdk.Context, red types.Redelegation) {
|
||||
store.Delete(GetREDByValDstIndexKey(red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr))
|
||||
}
|
||||
|
||||
//________________________________________________
|
||||
// redelegation queue timeslice operations
|
||||
|
||||
// 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) {
|
||||
@ -342,14 +391,16 @@ func (k Keeper) SetRedelegationQueueTimeSlice(ctx sdk.Context, timestamp time.Ti
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (k Keeper) InsertRedelegationQueue(ctx sdk.Context, red types.Redelegation,
|
||||
completionTime time.Time) {
|
||||
|
||||
timeSlice := k.GetRedelegationQueueTimeSlice(ctx, completionTime)
|
||||
dvvTriplet := types.DVVTriplet{red.DelegatorAddr, red.ValidatorSrcAddr, red.ValidatorDstAddr}
|
||||
if len(timeSlice) == 0 {
|
||||
k.SetRedelegationQueueTimeSlice(ctx, red.MinTime, []types.DVVTriplet{dvvTriplet})
|
||||
k.SetRedelegationQueueTimeSlice(ctx, completionTime, []types.DVVTriplet{dvvTriplet})
|
||||
} else {
|
||||
timeSlice = append(timeSlice, dvvTriplet)
|
||||
k.SetRedelegationQueueTimeSlice(ctx, red.MinTime, timeSlice)
|
||||
k.SetRedelegationQueueTimeSlice(ctx, completionTime, timeSlice)
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,14 +410,16 @@ func (k Keeper) RedelegationQueueIterator(ctx sdk.Context, endTime time.Time) sd
|
||||
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
|
||||
// Returns a concatenated list of all the timeslices inclusively previous to
|
||||
// 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.MustUnmarshalBinaryLengthPrefixed(redelegationTimesliceIterator.Value(), ×lice)
|
||||
value := redelegationTimesliceIterator.Value()
|
||||
k.cdc.MustUnmarshalBinaryLengthPrefixed(value, ×lice)
|
||||
matureRedelegations = append(matureRedelegations, timeslice...)
|
||||
store.Delete(redelegationTimesliceIterator.Key())
|
||||
}
|
||||
@ -474,9 +527,9 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
|
||||
|
||||
//______________________________________________________________________________________________________
|
||||
|
||||
// get info for begin functions: MinTime and CreationHeight
|
||||
// get info for begin functions: completionTime and CreationHeight
|
||||
func (k Keeper) getBeginInfo(ctx sdk.Context, valSrcAddr sdk.ValAddress) (
|
||||
minTime time.Time, height int64, completeNow bool) {
|
||||
completionTime time.Time, height int64, completeNow bool) {
|
||||
|
||||
validator, found := k.GetValidator(ctx, valSrcAddr)
|
||||
|
||||
@ -484,17 +537,17 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, valSrcAddr sdk.ValAddress) (
|
||||
case !found || validator.Status == sdk.Bonded:
|
||||
|
||||
// the longest wait - just unbonding period from now
|
||||
minTime = ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx))
|
||||
completionTime = ctx.BlockHeader().Time.Add(k.UnbondingTime(ctx))
|
||||
height = ctx.BlockHeight()
|
||||
return minTime, height, false
|
||||
return completionTime, height, false
|
||||
|
||||
case validator.Status == sdk.Unbonded:
|
||||
return minTime, height, true
|
||||
return completionTime, height, true
|
||||
|
||||
case validator.Status == sdk.Unbonding:
|
||||
minTime = validator.UnbondingMinTime
|
||||
completionTime = validator.UnbondingMinTime
|
||||
height = validator.UnbondingHeight
|
||||
return minTime, height, false
|
||||
return completionTime, height, false
|
||||
|
||||
default:
|
||||
panic("unknown validator status")
|
||||
@ -502,21 +555,15 @@ func (k Keeper) getBeginInfo(ctx sdk.Context, valSrcAddr sdk.ValAddress) (
|
||||
}
|
||||
|
||||
// begin unbonding an unbonding record
|
||||
func (k Keeper) BeginUnbonding(ctx sdk.Context,
|
||||
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.UnbondingDelegation{}, types.ErrExistingUnbondingDelegation(k.Codespace())
|
||||
}
|
||||
func (k Keeper) BeginUnbonding(ctx sdk.Context, delAddr sdk.AccAddress,
|
||||
valAddr sdk.ValAddress, sharesAmount sdk.Dec) (completionTime time.Time, sdkErr sdk.Error) {
|
||||
|
||||
// create the unbonding delegation
|
||||
minTime, height, completeNow := k.getBeginInfo(ctx, valAddr)
|
||||
completionTime, height, completeNow := k.getBeginInfo(ctx, valAddr)
|
||||
|
||||
returnAmount, err := k.unbond(ctx, delAddr, valAddr, sharesAmount)
|
||||
if err != nil {
|
||||
return types.UnbondingDelegation{}, err
|
||||
return completionTime, err
|
||||
}
|
||||
balance := sdk.NewCoin(k.BondDenom(ctx), returnAmount)
|
||||
|
||||
@ -524,107 +571,103 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context,
|
||||
if completeNow {
|
||||
_, err := k.bankKeeper.UndelegateCoins(ctx, delAddr, sdk.Coins{balance})
|
||||
if err != nil {
|
||||
return types.UnbondingDelegation{}, err
|
||||
return completionTime, err
|
||||
}
|
||||
|
||||
return types.UnbondingDelegation{MinTime: minTime}, nil
|
||||
return completionTime, nil
|
||||
}
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: delAddr,
|
||||
ValidatorAddr: valAddr,
|
||||
CreationHeight: height,
|
||||
MinTime: minTime,
|
||||
Balance: balance,
|
||||
InitialBalance: balance,
|
||||
}
|
||||
k.SetUnbondingDelegation(ctx, ubd)
|
||||
k.InsertUnbondingQueue(ctx, ubd)
|
||||
ubd := k.SetUnbondingDelegationEntry(ctx, delAddr,
|
||||
valAddr, height, completionTime, balance)
|
||||
|
||||
return ubd, nil
|
||||
k.InsertUBDQueue(ctx, ubd, completionTime)
|
||||
return completionTime, 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 {
|
||||
// CompleteUnbonding completes the unbonding of all mature entries in the
|
||||
// retrieved unbonding delegation object.
|
||||
func (k Keeper) CompleteUnbonding(ctx sdk.Context, delAddr sdk.AccAddress,
|
||||
valAddr sdk.ValAddress) sdk.Error {
|
||||
|
||||
ubd, found := k.GetUnbondingDelegation(ctx, delAddr, valAddr)
|
||||
if !found {
|
||||
return types.ErrNoUnbondingDelegation(k.Codespace())
|
||||
}
|
||||
|
||||
_, _, err := k.bankKeeper.AddCoins(ctx, ubd.DelegatorAddr, sdk.Coins{ubd.Balance})
|
||||
if err != nil {
|
||||
return err
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
|
||||
// loop through all the entries and complete unbonding mature entries
|
||||
for i := 0; i < len(ubd.Entries); i++ {
|
||||
entry := ubd.Entries[i]
|
||||
if entry.IsMature(ctxTime) {
|
||||
ubd.RemoveEntry(int64(i))
|
||||
i--
|
||||
|
||||
_, _, err := k.bankKeeper.AddCoins(ctx, ubd.DelegatorAddr, sdk.Coins{entry.Balance})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
k.RemoveUnbondingDelegation(ctx, ubd)
|
||||
|
||||
// set the unbonding delegation or remove it if there are no more entries
|
||||
if len(ubd.Entries) == 0 {
|
||||
k.RemoveUnbondingDelegation(ctx, ubd)
|
||||
} else {
|
||||
k.SetUnbondingDelegation(ctx, ubd)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// begin unbonding / redelegation; create a redelegation record
|
||||
func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
|
||||
valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (types.Redelegation, sdk.Error) {
|
||||
valSrcAddr, valDstAddr sdk.ValAddress, sharesAmount sdk.Dec) (
|
||||
completionTime time.Time, errSdk sdk.Error) {
|
||||
|
||||
if bytes.Equal(valSrcAddr, valDstAddr) {
|
||||
return types.Redelegation{}, types.ErrSelfRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
// check if there is already a redelgation in progress from src to dst
|
||||
// TODO quick fix, instead we should use an index, see https://github.com/cosmos/cosmos-sdk/issues/1402
|
||||
_, found := k.GetRedelegation(ctx, delAddr, valSrcAddr, valDstAddr)
|
||||
if found {
|
||||
return types.Redelegation{}, types.ErrConflictingRedelegation(k.Codespace())
|
||||
return time.Time{}, types.ErrSelfRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
// check if this is a transitive redelegation
|
||||
if k.HasReceivingRedelegation(ctx, delAddr, valSrcAddr) {
|
||||
return types.Redelegation{}, types.ErrTransitiveRedelegation(k.Codespace())
|
||||
return time.Time{}, types.ErrTransitiveRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
returnAmount, err := k.unbond(ctx, delAddr, valSrcAddr, sharesAmount)
|
||||
if err != nil {
|
||||
return types.Redelegation{}, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
if returnAmount.IsZero() {
|
||||
return types.Redelegation{}, types.ErrVerySmallRedelegation(k.Codespace())
|
||||
return time.Time{}, types.ErrVerySmallRedelegation(k.Codespace())
|
||||
}
|
||||
returnCoin := sdk.NewCoin(k.BondDenom(ctx), returnAmount)
|
||||
|
||||
dstValidator, found := k.GetValidator(ctx, valDstAddr)
|
||||
if !found {
|
||||
return types.Redelegation{}, types.ErrBadRedelegationDst(k.Codespace())
|
||||
return time.Time{}, types.ErrBadRedelegationDst(k.Codespace())
|
||||
}
|
||||
|
||||
sharesCreated, err := k.Delegate(ctx, delAddr, returnCoin, dstValidator, false)
|
||||
if err != nil {
|
||||
return types.Redelegation{}, err
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
// create the unbonding delegation
|
||||
minTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr)
|
||||
completionTime, height, completeNow := k.getBeginInfo(ctx, valSrcAddr)
|
||||
|
||||
if completeNow { // no need to create the redelegation object
|
||||
return types.Redelegation{MinTime: minTime}, nil
|
||||
return completionTime, nil
|
||||
}
|
||||
|
||||
red := types.Redelegation{
|
||||
DelegatorAddr: delAddr,
|
||||
ValidatorSrcAddr: valSrcAddr,
|
||||
ValidatorDstAddr: valDstAddr,
|
||||
CreationHeight: height,
|
||||
MinTime: minTime,
|
||||
SharesDst: sharesCreated,
|
||||
SharesSrc: sharesAmount,
|
||||
Balance: returnCoin,
|
||||
InitialBalance: returnCoin,
|
||||
}
|
||||
k.SetRedelegation(ctx, red)
|
||||
k.InsertRedelegationQueue(ctx, red)
|
||||
return red, nil
|
||||
red := k.SetRedelegationEntry(ctx, delAddr, valSrcAddr, valDstAddr,
|
||||
height, completionTime, returnCoin, sharesAmount, sharesCreated)
|
||||
k.InsertRedelegationQueue(ctx, red, completionTime)
|
||||
return completionTime, nil
|
||||
}
|
||||
|
||||
// complete unbonding an ongoing redelegation
|
||||
// CompleteRedelegation completes the unbonding of all mature entries in the
|
||||
// retrieved unbonding delegation object.
|
||||
func (k Keeper) CompleteRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
|
||||
valSrcAddr, valDstAddr sdk.ValAddress) sdk.Error {
|
||||
|
||||
@ -633,12 +676,23 @@ func (k Keeper) CompleteRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
|
||||
return types.ErrNoRedelegation(k.Codespace())
|
||||
}
|
||||
|
||||
// ensure that enough time has passed
|
||||
ctxTime := ctx.BlockHeader().Time
|
||||
if red.MinTime.After(ctxTime) {
|
||||
return types.ErrNotMature(k.Codespace(), "redelegation", "unit-time", red.MinTime, ctxTime)
|
||||
|
||||
// loop through all the entries and complete mature redelegation entries
|
||||
for i := 0; i < len(red.Entries); i++ {
|
||||
entry := red.Entries[i]
|
||||
if entry.IsMature(ctxTime) {
|
||||
red.RemoveEntry(int64(i))
|
||||
i--
|
||||
}
|
||||
}
|
||||
|
||||
// set the redelegation or remove it if there are no more entries
|
||||
if len(red.Entries) == 0 {
|
||||
k.RemoveRedelegation(ctx, red)
|
||||
} else {
|
||||
k.SetRedelegation(ctx, red)
|
||||
}
|
||||
|
||||
k.RemoveRedelegation(ctx, red)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -137,13 +137,8 @@ func TestDelegation(t *testing.T) {
|
||||
func TestUnbondingDelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
CreationHeight: 0,
|
||||
MinTime: time.Unix(0, 0),
|
||||
Balance: sdk.NewInt64Coin(types.DefaultBondDenom, 5),
|
||||
}
|
||||
ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(types.DefaultBondDenom, 5))
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
@ -152,7 +147,7 @@ func TestUnbondingDelegation(t *testing.T) {
|
||||
require.True(t, ubd.Equal(resUnbond))
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
ubd.Balance = sdk.NewInt64Coin(types.DefaultBondDenom, 21)
|
||||
ubd.Entries[0].Balance = sdk.NewInt64Coin(types.DefaultBondDenom, 21)
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
|
||||
resUnbonds := keeper.GetUnbondingDelegations(ctx, addrDels[0], 5)
|
||||
@ -338,9 +333,10 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) {
|
||||
// retrieve the unbonding delegation
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.True(t, ubd.Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6)))
|
||||
assert.Equal(t, blockHeight, ubd.CreationHeight)
|
||||
assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.MinTime))
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
require.True(t, ubd.Entries[0].Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6)))
|
||||
assert.Equal(t, blockHeight, ubd.Entries[0].CreationHeight)
|
||||
assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime))
|
||||
}
|
||||
|
||||
func TestUndelegateFromUnbondedValidator(t *testing.T) {
|
||||
@ -490,15 +486,9 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) {
|
||||
func TestGetRedelegationsFromValidator(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
||||
rd := types.Redelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorSrcAddr: addrVals[0],
|
||||
ValidatorDstAddr: addrVals[1],
|
||||
CreationHeight: 0,
|
||||
MinTime: time.Unix(0, 0),
|
||||
SharesSrc: sdk.NewDec(5),
|
||||
SharesDst: sdk.NewDec(5),
|
||||
}
|
||||
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(types.DefaultBondDenom, 5),
|
||||
sdk.NewDec(5), sdk.NewDec(5))
|
||||
|
||||
// set and retrieve a record
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
@ -520,15 +510,9 @@ func TestGetRedelegationsFromValidator(t *testing.T) {
|
||||
func TestRedelegation(t *testing.T) {
|
||||
ctx, _, keeper := CreateTestInput(t, false, 0)
|
||||
|
||||
rd := types.Redelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorSrcAddr: addrVals[0],
|
||||
ValidatorDstAddr: addrVals[1],
|
||||
CreationHeight: 0,
|
||||
MinTime: time.Unix(0, 0),
|
||||
SharesSrc: sdk.NewDec(5),
|
||||
SharesDst: sdk.NewDec(5),
|
||||
}
|
||||
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(types.DefaultBondDenom, 5),
|
||||
sdk.NewDec(5), sdk.NewDec(5))
|
||||
|
||||
// test shouldn't have and redelegations
|
||||
has := keeper.HasReceivingRedelegation(ctx, addrDels[0], addrVals[1])
|
||||
@ -556,8 +540,8 @@ func TestRedelegation(t *testing.T) {
|
||||
require.True(t, has)
|
||||
|
||||
// modify a records, save, and retrieve
|
||||
rd.SharesSrc = sdk.NewDec(21)
|
||||
rd.SharesDst = sdk.NewDec(21)
|
||||
rd.Entries[0].SharesSrc = sdk.NewDec(21)
|
||||
rd.Entries[0].SharesDst = sdk.NewDec(21)
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
|
||||
resRed, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
@ -742,9 +726,10 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) {
|
||||
// retrieve the unbonding delegation
|
||||
ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.True(t, ubd.Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6)))
|
||||
assert.Equal(t, blockHeight, ubd.CreationHeight)
|
||||
assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.MinTime))
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
require.True(t, ubd.Entries[0].Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6)))
|
||||
assert.Equal(t, blockHeight, ubd.Entries[0].CreationHeight)
|
||||
assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime))
|
||||
}
|
||||
|
||||
func TestRedelegateFromUnbondedValidator(t *testing.T) {
|
||||
|
||||
@ -145,34 +145,41 @@ func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) {
|
||||
// (the amount actually slashed may be less if there's
|
||||
// insufficient stake remaining)
|
||||
func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation types.UnbondingDelegation,
|
||||
infractionHeight int64, slashFactor sdk.Dec) (slashAmount sdk.Int) {
|
||||
infractionHeight int64, slashFactor sdk.Dec) (totalSlashAmount sdk.Int) {
|
||||
|
||||
now := ctx.BlockHeader().Time
|
||||
totalSlashAmount = sdk.ZeroInt()
|
||||
|
||||
// If unbonding started before this height, stake didn't contribute to infraction
|
||||
if unbondingDelegation.CreationHeight < infractionHeight {
|
||||
return sdk.ZeroInt()
|
||||
}
|
||||
// perform slashing on all entries within the unbonding delegation
|
||||
for i, entry := range unbondingDelegation.Entries {
|
||||
|
||||
if unbondingDelegation.MinTime.Before(now) {
|
||||
// Unbonding delegation no longer eligible for slashing, skip it
|
||||
// TODO Settle and delete it automatically?
|
||||
return sdk.ZeroInt()
|
||||
}
|
||||
// If unbonding started before this height, stake didn't contribute to infraction
|
||||
if entry.CreationHeight < infractionHeight {
|
||||
continue
|
||||
}
|
||||
|
||||
// Calculate slash amount proportional to stake contributing to infraction
|
||||
slashAmountDec := slashFactor.MulInt(unbondingDelegation.InitialBalance.Amount)
|
||||
slashAmount = slashAmountDec.TruncateInt()
|
||||
if entry.IsMature(now) {
|
||||
// Unbonding delegation no longer eligible for slashing, skip it
|
||||
continue
|
||||
}
|
||||
|
||||
// Don't slash more tokens than held
|
||||
// Possible since the unbonding delegation may already
|
||||
// have been slashed, and slash amounts are calculated
|
||||
// according to stake held at time of infraction
|
||||
unbondingSlashAmount := sdk.MinInt(slashAmount, unbondingDelegation.Balance.Amount)
|
||||
// Calculate slash amount proportional to stake contributing to infraction
|
||||
slashAmountDec := slashFactor.MulInt(entry.InitialBalance.Amount)
|
||||
slashAmount := slashAmountDec.TruncateInt()
|
||||
totalSlashAmount = totalSlashAmount.Add(slashAmount)
|
||||
|
||||
// Update unbonding delegation if necessary
|
||||
if !unbondingSlashAmount.IsZero() {
|
||||
unbondingDelegation.Balance.Amount = unbondingDelegation.Balance.Amount.Sub(unbondingSlashAmount)
|
||||
// Don't slash more tokens than held
|
||||
// Possible since the unbonding delegation may already
|
||||
// have been slashed, and slash amounts are calculated
|
||||
// according to stake held at time of infraction
|
||||
unbondingSlashAmount := sdk.MinInt(slashAmount, entry.Balance.Amount)
|
||||
|
||||
// Update unbonding delegation if necessary
|
||||
if unbondingSlashAmount.IsZero() {
|
||||
continue
|
||||
}
|
||||
entry.Balance.Amount = entry.Balance.Amount.Sub(unbondingSlashAmount)
|
||||
unbondingDelegation.Entries[i] = entry
|
||||
k.SetUnbondingDelegation(ctx, unbondingDelegation)
|
||||
pool := k.GetPool(ctx)
|
||||
|
||||
@ -182,7 +189,7 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
|
||||
k.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
return slashAmount
|
||||
return totalSlashAmount
|
||||
}
|
||||
|
||||
// slash a redelegation and update the pool
|
||||
@ -192,44 +199,51 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
|
||||
// insufficient stake remaining)
|
||||
// nolint: unparam
|
||||
func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, redelegation types.Redelegation,
|
||||
infractionHeight int64, slashFactor sdk.Dec) (slashAmount sdk.Int) {
|
||||
infractionHeight int64, slashFactor sdk.Dec) (totalSlashAmount sdk.Int) {
|
||||
|
||||
now := ctx.BlockHeader().Time
|
||||
totalSlashAmount = sdk.ZeroInt()
|
||||
|
||||
// If redelegation started before this height, stake didn't contribute to infraction
|
||||
if redelegation.CreationHeight < infractionHeight {
|
||||
return sdk.ZeroInt()
|
||||
}
|
||||
// perform slashing on all entries within the redelegation
|
||||
for i, entry := range redelegation.Entries {
|
||||
|
||||
if redelegation.MinTime.Before(now) {
|
||||
// Redelegation no longer eligible for slashing, skip it
|
||||
// TODO Delete it automatically?
|
||||
return sdk.ZeroInt()
|
||||
}
|
||||
// If redelegation started before this height, stake didn't contribute to infraction
|
||||
if entry.CreationHeight < infractionHeight {
|
||||
continue
|
||||
}
|
||||
|
||||
// Calculate slash amount proportional to stake contributing to infraction
|
||||
slashAmountDec := slashFactor.MulInt(redelegation.InitialBalance.Amount)
|
||||
slashAmount = slashAmountDec.TruncateInt()
|
||||
if entry.IsMature(now) {
|
||||
// Redelegation no longer eligible for slashing, skip it
|
||||
continue
|
||||
}
|
||||
|
||||
// Don't slash more tokens than held
|
||||
// Possible since the redelegation may already
|
||||
// have been slashed, and slash amounts are calculated
|
||||
// according to stake held at time of infraction
|
||||
redelegationSlashAmount := sdk.MinInt(slashAmount, redelegation.Balance.Amount)
|
||||
// Calculate slash amount proportional to stake contributing to infraction
|
||||
slashAmountDec := slashFactor.MulInt(entry.InitialBalance.Amount)
|
||||
slashAmount := slashAmountDec.TruncateInt()
|
||||
totalSlashAmount = totalSlashAmount.Add(slashAmount)
|
||||
|
||||
// Update redelegation if necessary
|
||||
if !redelegationSlashAmount.IsZero() {
|
||||
redelegation.Balance.Amount = redelegation.Balance.Amount.Sub(redelegationSlashAmount)
|
||||
k.SetRedelegation(ctx, redelegation)
|
||||
}
|
||||
// Don't slash more tokens than held
|
||||
// Possible since the redelegation may already
|
||||
// have been slashed, and slash amounts are calculated
|
||||
// according to stake held at time of infraction
|
||||
redelegationSlashAmount := sdk.MinInt(slashAmount, entry.Balance.Amount)
|
||||
|
||||
// Unbond from target validator
|
||||
sharesToUnbond := slashFactor.Mul(redelegation.SharesDst)
|
||||
if !sharesToUnbond.IsZero() {
|
||||
// Update entry if necessary
|
||||
if !redelegationSlashAmount.IsZero() {
|
||||
entry.Balance.Amount = entry.Balance.Amount.Sub(redelegationSlashAmount)
|
||||
redelegation.Entries[i] = entry
|
||||
k.SetRedelegation(ctx, redelegation)
|
||||
}
|
||||
|
||||
// Unbond from target validator
|
||||
sharesToUnbond := slashFactor.Mul(entry.SharesDst)
|
||||
if sharesToUnbond.IsZero() {
|
||||
continue
|
||||
}
|
||||
delegation, found := k.GetDelegation(ctx, redelegation.DelegatorAddr, redelegation.ValidatorDstAddr)
|
||||
if !found {
|
||||
// If deleted, delegation has zero shares, and we can't unbond any more
|
||||
return slashAmount
|
||||
continue
|
||||
}
|
||||
if sharesToUnbond.GT(delegation.Shares) {
|
||||
sharesToUnbond = delegation.Shares
|
||||
@ -246,5 +260,5 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
|
||||
k.SetPool(ctx, pool)
|
||||
}
|
||||
|
||||
return slashAmount
|
||||
return totalSlashAmount
|
||||
}
|
||||
|
||||
@ -70,16 +70,11 @@ func TestSlashUnbondingDelegation(t *testing.T) {
|
||||
ctx, keeper, params := setupHelper(t, 10)
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// set an unbonding delegation
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
CreationHeight: 0,
|
||||
// expiration timestamp (beyond which the unbonding delegation shouldn't be slashed)
|
||||
MinTime: time.Unix(0, 0),
|
||||
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 10),
|
||||
Balance: sdk.NewInt64Coin(params.BondDenom, 10),
|
||||
}
|
||||
// set an unbonding delegation with expiration timestamp (beyond which the
|
||||
// unbonding delegation shouldn't be slashed)
|
||||
ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 0,
|
||||
time.Unix(5, 0), sdk.NewInt64Coin(params.BondDenom, 10))
|
||||
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
|
||||
// unbonding started prior to the infraction height, stakw didn't contribute
|
||||
@ -100,12 +95,13 @@ func TestSlashUnbondingDelegation(t *testing.T) {
|
||||
require.Equal(t, int64(5), slashAmount.Int64())
|
||||
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
|
||||
// initialbalance unchanged
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), ubd.InitialBalance)
|
||||
// initial balance unchanged
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), ubd.Entries[0].InitialBalance)
|
||||
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), ubd.Balance)
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), ubd.Entries[0].Balance)
|
||||
newPool := keeper.GetPool(ctx)
|
||||
require.Equal(t, int64(5), oldPool.LooseTokens.Sub(newPool.LooseTokens).Int64())
|
||||
}
|
||||
@ -115,19 +111,12 @@ func TestSlashRedelegation(t *testing.T) {
|
||||
ctx, keeper, params := setupHelper(t, 10)
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// set a redelegation
|
||||
rd := types.Redelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorSrcAddr: addrVals[0],
|
||||
ValidatorDstAddr: addrVals[1],
|
||||
CreationHeight: 0,
|
||||
// expiration timestamp (beyond which the redelegation shouldn't be slashed)
|
||||
MinTime: time.Unix(0, 0),
|
||||
SharesSrc: sdk.NewDec(10),
|
||||
SharesDst: sdk.NewDec(10),
|
||||
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 10),
|
||||
Balance: sdk.NewInt64Coin(params.BondDenom, 10),
|
||||
}
|
||||
// set a redelegation with an expiration timestamp beyond which the
|
||||
// redelegation shouldn't be slashed
|
||||
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 0,
|
||||
time.Unix(5, 0), sdk.NewInt64Coin(params.BondDenom, 10), sdk.NewDec(10),
|
||||
sdk.NewDec(10))
|
||||
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
|
||||
// set the associated delegation
|
||||
@ -162,16 +151,17 @@ func TestSlashRedelegation(t *testing.T) {
|
||||
require.Equal(t, int64(5), slashAmount.Int64())
|
||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
|
||||
// end block
|
||||
updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
require.Equal(t, 1, len(updates))
|
||||
|
||||
// initialbalance unchanged
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance)
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.Entries[0].InitialBalance)
|
||||
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), rd.Balance)
|
||||
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), rd.Entries[0].Balance)
|
||||
|
||||
// shares decreased
|
||||
del, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[1])
|
||||
@ -252,16 +242,10 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||
consAddr := sdk.ConsAddress(PKs[0].Address())
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// set an unbonding delegation
|
||||
ubd := types.UnbondingDelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
CreationHeight: 11,
|
||||
// expiration timestamp (beyond which the unbonding delegation shouldn't be slashed)
|
||||
MinTime: time.Unix(0, 0),
|
||||
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 4),
|
||||
Balance: sdk.NewInt64Coin(params.BondDenom, 4),
|
||||
}
|
||||
// set an unbonding delegation with expiration timestamp beyond which the
|
||||
// unbonding delegation shouldn't be slashed
|
||||
ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 4))
|
||||
keeper.SetUnbondingDelegation(ctx, ubd)
|
||||
|
||||
// slash validator for the first time
|
||||
@ -278,8 +262,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||
// read updating unbonding delegation
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt(2), ubd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(2), ubd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool := keeper.GetPool(ctx)
|
||||
// bonded tokens burned
|
||||
@ -298,8 +283,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||
keeper.Slash(ctx, consAddr, 9, 10, fraction)
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
// balance decreased again
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool = keeper.GetPool(ctx)
|
||||
// bonded tokens burned again
|
||||
@ -318,8 +304,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||
keeper.Slash(ctx, consAddr, 9, 10, fraction)
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
// balance unchanged
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool = keeper.GetPool(ctx)
|
||||
// bonded tokens burned again
|
||||
@ -338,8 +325,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) {
|
||||
keeper.Slash(ctx, consAddr, 9, 10, fraction)
|
||||
ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
|
||||
require.True(t, found)
|
||||
require.Len(t, ubd.Entries, 1)
|
||||
// balance unchanged
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(0), ubd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool = keeper.GetPool(ctx)
|
||||
// just 1 bonded token burned again since that's all the validator now has
|
||||
@ -360,17 +348,9 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// set a redelegation
|
||||
rd := types.Redelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorSrcAddr: addrVals[0],
|
||||
ValidatorDstAddr: addrVals[1],
|
||||
CreationHeight: 11,
|
||||
MinTime: time.Unix(0, 0),
|
||||
SharesSrc: sdk.NewDec(6),
|
||||
SharesDst: sdk.NewDec(6),
|
||||
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6),
|
||||
Balance: sdk.NewInt64Coin(params.BondDenom, 6),
|
||||
}
|
||||
rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 6), sdk.NewDec(6),
|
||||
sdk.NewDec(6))
|
||||
keeper.SetRedelegation(ctx, rd)
|
||||
|
||||
// set the associated delegation
|
||||
@ -396,8 +376,9 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||
// read updating redelegation
|
||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt(3), rd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(3), rd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool := keeper.GetPool(ctx)
|
||||
// bonded tokens burned
|
||||
@ -420,8 +401,9 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||
// read updating redelegation
|
||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// balance decreased, now zero
|
||||
require.Equal(t, sdk.NewInt(0), rd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(0), rd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool = keeper.GetPool(ctx)
|
||||
// seven bonded tokens burned
|
||||
@ -441,8 +423,9 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||
// read updating redelegation
|
||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// balance still zero
|
||||
require.Equal(t, sdk.NewInt(0), rd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(0), rd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool = keeper.GetPool(ctx)
|
||||
// four more bonded tokens burned
|
||||
@ -465,8 +448,9 @@ func TestSlashWithRedelegation(t *testing.T) {
|
||||
// read updating redelegation
|
||||
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rd.Entries, 1)
|
||||
// balance still zero
|
||||
require.Equal(t, sdk.NewInt(0), rd.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(0), rd.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool = keeper.GetPool(ctx)
|
||||
// no more bonded tokens burned
|
||||
@ -482,19 +466,11 @@ func TestSlashBoth(t *testing.T) {
|
||||
ctx, keeper, params := setupHelper(t, 10)
|
||||
fraction := sdk.NewDecWithPrec(5, 1)
|
||||
|
||||
// set a redelegation
|
||||
rdA := types.Redelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorSrcAddr: addrVals[0],
|
||||
ValidatorDstAddr: addrVals[1],
|
||||
CreationHeight: 11,
|
||||
// expiration timestamp (beyond which the redelegation shouldn't be slashed)
|
||||
MinTime: time.Unix(0, 0),
|
||||
SharesSrc: sdk.NewDec(6),
|
||||
SharesDst: sdk.NewDec(6),
|
||||
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 6),
|
||||
Balance: sdk.NewInt64Coin(params.BondDenom, 6),
|
||||
}
|
||||
// set a redelegation with expiration timestamp beyond which the
|
||||
// redelegation shouldn't be slashed
|
||||
rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 6), sdk.NewDec(6),
|
||||
sdk.NewDec(6))
|
||||
keeper.SetRedelegation(ctx, rdA)
|
||||
|
||||
// set the associated delegation
|
||||
@ -505,16 +481,10 @@ func TestSlashBoth(t *testing.T) {
|
||||
}
|
||||
keeper.SetDelegation(ctx, delA)
|
||||
|
||||
// set an unbonding delegation
|
||||
ubdA := types.UnbondingDelegation{
|
||||
DelegatorAddr: addrDels[0],
|
||||
ValidatorAddr: addrVals[0],
|
||||
CreationHeight: 11,
|
||||
// expiration timestamp (beyond which the unbonding delegation shouldn't be slashed)
|
||||
MinTime: time.Unix(0, 0),
|
||||
InitialBalance: sdk.NewInt64Coin(params.BondDenom, 4),
|
||||
Balance: sdk.NewInt64Coin(params.BondDenom, 4),
|
||||
}
|
||||
// set an unbonding delegation with expiration timestamp (beyond which the
|
||||
// unbonding delegation shouldn't be slashed)
|
||||
ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 4))
|
||||
keeper.SetUnbondingDelegation(ctx, ubdA)
|
||||
|
||||
// slash validator
|
||||
@ -528,8 +498,9 @@ func TestSlashBoth(t *testing.T) {
|
||||
// read updating redelegation
|
||||
rdA, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
|
||||
require.True(t, found)
|
||||
require.Len(t, rdA.Entries, 1)
|
||||
// balance decreased
|
||||
require.Equal(t, sdk.NewInt(3), rdA.Balance.Amount)
|
||||
require.Equal(t, sdk.NewInt(3), rdA.Entries[0].Balance.Amount)
|
||||
// read updated pool
|
||||
newPool := keeper.GetPool(ctx)
|
||||
// loose tokens burned
|
||||
|
||||
@ -94,7 +94,13 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
|
||||
require.Nil(t, err)
|
||||
|
||||
ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger())
|
||||
ctx = ctx.WithConsensusParams(&abci.ConsensusParams{Validator: &abci.ValidatorParams{PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519}}})
|
||||
ctx = ctx.WithConsensusParams(
|
||||
&abci.ConsensusParams{
|
||||
Validator: &abci.ValidatorParams{
|
||||
PubKeyTypes: []string{tmtypes.ABCIPubKeyTypeEd25519},
|
||||
},
|
||||
},
|
||||
)
|
||||
cdc := MakeTestCodec()
|
||||
|
||||
pk := params.NewKeeper(cdc, keyParams, tkeyParams)
|
||||
|
||||
@ -296,7 +296,8 @@ func TestQueryDelegation(t *testing.T) {
|
||||
require.Equal(t, delegationsRes[0], delegation)
|
||||
|
||||
// Query unbonging delegation
|
||||
keeper.BeginUnbonding(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(10))
|
||||
_, err = keeper.BeginUnbonding(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(10))
|
||||
require.Nil(t, err)
|
||||
|
||||
queryBondParams = NewQueryBondsParams(addrAcc2, addrVal1)
|
||||
bz, errRes = cdc.MarshalJSON(queryBondParams)
|
||||
@ -347,8 +348,10 @@ func TestQueryDelegation(t *testing.T) {
|
||||
require.NotNil(t, err)
|
||||
|
||||
// Query redelegation
|
||||
redel, err := keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddr, val2.OperatorAddr, sdk.NewDec(10))
|
||||
_, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddr, val2.OperatorAddr, sdk.NewDec(10))
|
||||
require.Nil(t, err)
|
||||
redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddr, val2.OperatorAddr)
|
||||
require.True(t, found)
|
||||
|
||||
bz, errRes = cdc.MarshalJSON(NewQueryRedelegationParams(addrAcc2, val1.OperatorAddr, val2.OperatorAddr))
|
||||
require.Nil(t, errRes)
|
||||
@ -379,7 +382,7 @@ func TestQueryRedelegations(t *testing.T) {
|
||||
keeper.SetValidator(ctx, val2)
|
||||
|
||||
keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(100)), val1, true)
|
||||
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
_ = keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
|
||||
keeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), sdk.NewDec(20))
|
||||
keeper.ApplyAndReturnValidatorSetUpdates(ctx)
|
||||
|
||||
@ -59,7 +59,9 @@ func SupplyInvariants(ck bank.Keeper, k staking.Keeper,
|
||||
return false
|
||||
})
|
||||
k.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) bool {
|
||||
loose = loose.Add(sdk.NewDecFromInt(ubd.Balance.Amount))
|
||||
for _, entry := range ubd.Entries {
|
||||
loose = loose.Add(sdk.NewDecFromInt(entry.Balance.Amount))
|
||||
}
|
||||
return false
|
||||
})
|
||||
k.IterateValidators(ctx, func(_ int64, validator sdk.Validator) bool {
|
||||
|
||||
@ -26,6 +26,8 @@ type DVVTriplet struct {
|
||||
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.
|
||||
@ -35,6 +37,17 @@ type Delegation struct {
|
||||
Shares sdk.Dec `json:"shares"`
|
||||
}
|
||||
|
||||
// NewDelegation creates a new delegation object
|
||||
func NewDelegation(delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress,
|
||||
shares sdk.Dec) Delegation {
|
||||
|
||||
return Delegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Shares: shares,
|
||||
}
|
||||
}
|
||||
|
||||
// return the delegation
|
||||
func MustMarshalDelegation(cdc *codec.Codec, delegation Delegation) []byte {
|
||||
return cdc.MustMarshalBinaryLengthPrefixed(delegation)
|
||||
@ -82,14 +95,65 @@ func (d Delegation) HumanReadableString() (string, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
//________________________________________________________________________
|
||||
|
||||
// UnbondingDelegation reflects a delegation's passive unbonding queue.
|
||||
// it may hold multiple entries between the same delegator/validator
|
||||
type UnbondingDelegation struct {
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // delegator
|
||||
ValidatorAddr sdk.ValAddress `json:"validator_addr"` // validator unbonding from operator addr
|
||||
CreationHeight int64 `json:"creation_height"` // height which the unbonding took place
|
||||
MinTime time.Time `json:"min_time"` // unix time for unbonding completion
|
||||
InitialBalance sdk.Coin `json:"initial_balance"` // atoms initially scheduled to receive at completion
|
||||
Balance sdk.Coin `json:"balance"` // atoms to receive at completion
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // delegator
|
||||
ValidatorAddr sdk.ValAddress `json:"validator_addr"` // validator unbonding from operator addr
|
||||
Entries []UnbondingDelegationEntry `json:"entries"` // unbonding delegation entries
|
||||
}
|
||||
|
||||
// UnbondingDelegationEntry - entry to an UnbondingDelegation
|
||||
type UnbondingDelegationEntry struct {
|
||||
CreationHeight int64 `json:"creation_height"` // height which the unbonding took place
|
||||
CompletionTime time.Time `json:"completion_time"` // unix time for unbonding completion
|
||||
InitialBalance sdk.Coin `json:"initial_balance"` // atoms initially scheduled to receive at completion
|
||||
Balance sdk.Coin `json:"balance"` // atoms to receive at completion
|
||||
}
|
||||
|
||||
// IsMature - is the current entry mature
|
||||
func (e UnbondingDelegationEntry) IsMature(currentTime time.Time) bool {
|
||||
return !e.CompletionTime.After(currentTime)
|
||||
}
|
||||
|
||||
// NewUnbondingDelegation - create a new unbonding delegation object
|
||||
func NewUnbondingDelegation(delegatorAddr sdk.AccAddress,
|
||||
validatorAddr sdk.ValAddress, creationHeight int64, minTime time.Time,
|
||||
balance sdk.Coin) UnbondingDelegation {
|
||||
|
||||
entry := NewUnbondingDelegationEntry(creationHeight, minTime, balance)
|
||||
return UnbondingDelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorAddr: validatorAddr,
|
||||
Entries: []UnbondingDelegationEntry{entry},
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnbondingDelegation - create a new unbonding delegation object
|
||||
func NewUnbondingDelegationEntry(creationHeight int64, completionTime time.Time,
|
||||
balance sdk.Coin) UnbondingDelegationEntry {
|
||||
|
||||
return UnbondingDelegationEntry{
|
||||
CreationHeight: creationHeight,
|
||||
CompletionTime: completionTime,
|
||||
InitialBalance: balance,
|
||||
Balance: balance,
|
||||
}
|
||||
}
|
||||
|
||||
// AddEntry - append entry to the unbonding delegation
|
||||
func (d *UnbondingDelegation) AddEntry(creationHeight int64,
|
||||
minTime time.Time, balance sdk.Coin) {
|
||||
|
||||
entry := NewUnbondingDelegationEntry(creationHeight, minTime, balance)
|
||||
d.Entries = append(d.Entries, entry)
|
||||
}
|
||||
|
||||
// RemoveEntry - remove entry at index i to the unbonding delegation
|
||||
func (d *UnbondingDelegation) RemoveEntry(i int64) {
|
||||
d.Entries = append(d.Entries[:i], d.Entries[i+1:]...)
|
||||
}
|
||||
|
||||
// return the unbonding delegation
|
||||
@ -126,25 +190,83 @@ func (d UnbondingDelegation) HumanReadableString() (string, error) {
|
||||
resp := "Unbonding Delegation \n"
|
||||
resp += fmt.Sprintf("Delegator: %s\n", d.DelegatorAddr)
|
||||
resp += fmt.Sprintf("Validator: %s\n", d.ValidatorAddr)
|
||||
resp += fmt.Sprintf("Creation height: %v\n", d.CreationHeight)
|
||||
resp += fmt.Sprintf("Min time to unbond (unix): %v\n", d.MinTime)
|
||||
resp += fmt.Sprintf("Expected balance: %s", d.Balance.String())
|
||||
for _, entry := range d.Entries {
|
||||
resp += "Unbonding Delegation Entry\n"
|
||||
resp += fmt.Sprintf("Creation height: %v\n", entry.CreationHeight)
|
||||
resp += fmt.Sprintf("Min time to unbond (unix): %v\n", entry.CompletionTime)
|
||||
resp += fmt.Sprintf("Expected balance: %s", entry.Balance.String())
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
// Redelegation reflects a delegation's passive re-delegation queue.
|
||||
type Redelegation struct {
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // delegator
|
||||
ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // validator redelegation source operator addr
|
||||
ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // validator redelegation destination operator addr
|
||||
CreationHeight int64 `json:"creation_height"` // height which the redelegation took place
|
||||
MinTime time.Time `json:"min_time"` // unix time for redelegation completion
|
||||
InitialBalance sdk.Coin `json:"initial_balance"` // initial balance when redelegation started
|
||||
Balance sdk.Coin `json:"balance"` // current balance
|
||||
SharesSrc sdk.Dec `json:"shares_src"` // amount of source shares redelegating
|
||||
SharesDst sdk.Dec `json:"shares_dst"` // amount of destination shares redelegating
|
||||
DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // delegator
|
||||
ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // validator redelegation source operator addr
|
||||
ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // validator redelegation destination operator addr
|
||||
Entries []RedelegationEntry `json:"entries"` // redelegation entries
|
||||
}
|
||||
|
||||
// RedelegationEntry - entry to a Redelegation
|
||||
type RedelegationEntry struct {
|
||||
CreationHeight int64 `json:"creation_height"` // height which the redelegation took place
|
||||
CompletionTime time.Time `json:"completion_time"` // unix time for redelegation completion
|
||||
InitialBalance sdk.Coin `json:"initial_balance"` // initial balance when redelegation started
|
||||
Balance sdk.Coin `json:"balance"` // current balance (current value held in destination validator)
|
||||
SharesSrc sdk.Dec `json:"shares_src"` // amount of source-validator shares removed by redelegation
|
||||
SharesDst sdk.Dec `json:"shares_dst"` // amount of destination-validator shares created by redelegation
|
||||
}
|
||||
|
||||
// NewRedelegation - create a new redelegation object
|
||||
func NewRedelegation(delegatorAddr sdk.AccAddress, validatorSrcAddr,
|
||||
validatorDstAddr sdk.ValAddress, creationHeight int64,
|
||||
minTime time.Time, balance sdk.Coin,
|
||||
sharesSrc, sharesDst sdk.Dec) Redelegation {
|
||||
|
||||
entry := NewRedelegationEntry(creationHeight,
|
||||
minTime, balance, sharesSrc, sharesDst)
|
||||
|
||||
return Redelegation{
|
||||
DelegatorAddr: delegatorAddr,
|
||||
ValidatorSrcAddr: validatorSrcAddr,
|
||||
ValidatorDstAddr: validatorDstAddr,
|
||||
Entries: []RedelegationEntry{entry},
|
||||
}
|
||||
}
|
||||
|
||||
// NewRedelegation - create a new redelegation object
|
||||
func NewRedelegationEntry(creationHeight int64,
|
||||
completionTime time.Time, balance sdk.Coin,
|
||||
sharesSrc, sharesDst sdk.Dec) RedelegationEntry {
|
||||
|
||||
return RedelegationEntry{
|
||||
CreationHeight: creationHeight,
|
||||
CompletionTime: completionTime,
|
||||
InitialBalance: balance,
|
||||
Balance: balance,
|
||||
SharesSrc: sharesSrc,
|
||||
SharesDst: sharesDst,
|
||||
}
|
||||
}
|
||||
|
||||
// IsMature - is the current entry mature
|
||||
func (e RedelegationEntry) IsMature(currentTime time.Time) bool {
|
||||
return !e.CompletionTime.After(currentTime)
|
||||
}
|
||||
|
||||
// AddEntry - append entry to the unbonding delegation
|
||||
func (d *Redelegation) AddEntry(creationHeight int64,
|
||||
minTime time.Time, balance sdk.Coin,
|
||||
sharesSrc, sharesDst sdk.Dec) {
|
||||
|
||||
entry := NewRedelegationEntry(creationHeight, minTime, balance, sharesSrc, sharesDst)
|
||||
d.Entries = append(d.Entries, entry)
|
||||
}
|
||||
|
||||
// RemoveEntry - remove entry at index i to the unbonding delegation
|
||||
func (d *Redelegation) RemoveEntry(i int64) {
|
||||
d.Entries = append(d.Entries[:i], d.Entries[i+1:]...)
|
||||
}
|
||||
|
||||
// return the redelegation
|
||||
@ -182,11 +304,11 @@ func (d Redelegation) HumanReadableString() (string, error) {
|
||||
resp += fmt.Sprintf("Delegator: %s\n", d.DelegatorAddr)
|
||||
resp += fmt.Sprintf("Source Validator: %s\n", d.ValidatorSrcAddr)
|
||||
resp += fmt.Sprintf("Destination Validator: %s\n", d.ValidatorDstAddr)
|
||||
resp += fmt.Sprintf("Creation height: %v\n", d.CreationHeight)
|
||||
resp += fmt.Sprintf("Min time to unbond (unix): %v\n", d.MinTime)
|
||||
resp += fmt.Sprintf("Source shares: %s\n", d.SharesSrc.String())
|
||||
resp += fmt.Sprintf("Destination shares: %s", d.SharesDst.String())
|
||||
|
||||
for _, entry := range d.Entries {
|
||||
resp += fmt.Sprintf("Creation height: %v\n", entry.CreationHeight)
|
||||
resp += fmt.Sprintf("Min time to unbond (unix): %v\n", entry.CompletionTime)
|
||||
resp += fmt.Sprintf("Source shares: %s\n", entry.SharesSrc.String())
|
||||
resp += fmt.Sprintf("Destination shares: %s", entry.SharesDst.String())
|
||||
}
|
||||
return resp, nil
|
||||
|
||||
}
|
||||
|
||||
@ -10,16 +10,8 @@ import (
|
||||
)
|
||||
|
||||
func TestDelegationEqual(t *testing.T) {
|
||||
d1 := Delegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorAddr: addr2,
|
||||
Shares: sdk.NewDec(100),
|
||||
}
|
||||
d2 := Delegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorAddr: addr2,
|
||||
Shares: sdk.NewDec(100),
|
||||
}
|
||||
d1 := NewDelegation(sdk.AccAddress(addr1), addr2, sdk.NewDec(100))
|
||||
d2 := d1
|
||||
|
||||
ok := d1.Equal(d2)
|
||||
require.True(t, ok)
|
||||
@ -32,11 +24,7 @@ func TestDelegationEqual(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDelegationHumanReadableString(t *testing.T) {
|
||||
d := Delegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorAddr: addr2,
|
||||
Shares: sdk.NewDec(100),
|
||||
}
|
||||
d := NewDelegation(sdk.AccAddress(addr1), addr2, sdk.NewDec(100))
|
||||
|
||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||
// actual contents of the string.
|
||||
@ -46,69 +34,54 @@ func TestDelegationHumanReadableString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUnbondingDelegationEqual(t *testing.T) {
|
||||
ud1 := UnbondingDelegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorAddr: addr2,
|
||||
}
|
||||
ud2 := UnbondingDelegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorAddr: addr2,
|
||||
}
|
||||
ubd1 := NewUnbondingDelegation(sdk.AccAddress(addr1), addr2, 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(DefaultBondDenom, 0))
|
||||
ubd2 := ubd1
|
||||
|
||||
ok := ud1.Equal(ud2)
|
||||
ok := ubd1.Equal(ubd2)
|
||||
require.True(t, ok)
|
||||
|
||||
ud2.ValidatorAddr = addr3
|
||||
ubd2.ValidatorAddr = addr3
|
||||
|
||||
ud2.MinTime = time.Unix(20*20*2, 0)
|
||||
ok = ud1.Equal(ud2)
|
||||
ubd2.Entries[0].CompletionTime = time.Unix(20*20*2, 0)
|
||||
ok = ubd1.Equal(ubd2)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestUnbondingDelegationHumanReadableString(t *testing.T) {
|
||||
ud := UnbondingDelegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorAddr: addr2,
|
||||
}
|
||||
ubd := NewUnbondingDelegation(sdk.AccAddress(addr1), addr2, 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(DefaultBondDenom, 0))
|
||||
|
||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||
// actual contents of the string.
|
||||
valStr, err := ud.HumanReadableString()
|
||||
valStr, err := ubd.HumanReadableString()
|
||||
require.Nil(t, err)
|
||||
require.NotEmpty(t, valStr)
|
||||
}
|
||||
|
||||
func TestRedelegationEqual(t *testing.T) {
|
||||
r1 := Redelegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorSrcAddr: addr2,
|
||||
ValidatorDstAddr: addr3,
|
||||
}
|
||||
r2 := Redelegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorSrcAddr: addr2,
|
||||
ValidatorDstAddr: addr3,
|
||||
}
|
||||
r1 := NewRedelegation(sdk.AccAddress(addr1), addr2, addr3, 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(DefaultBondDenom, 0),
|
||||
sdk.NewDec(0), sdk.NewDec(0))
|
||||
r2 := NewRedelegation(sdk.AccAddress(addr1), addr2, addr3, 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(DefaultBondDenom, 0),
|
||||
sdk.NewDec(0), sdk.NewDec(0))
|
||||
|
||||
ok := r1.Equal(r2)
|
||||
require.True(t, ok)
|
||||
|
||||
r2.SharesDst = sdk.NewDec(10)
|
||||
r2.SharesSrc = sdk.NewDec(20)
|
||||
r2.MinTime = time.Unix(20*20*2, 0)
|
||||
r2.Entries[0].SharesDst = sdk.NewDec(10)
|
||||
r2.Entries[0].SharesSrc = sdk.NewDec(20)
|
||||
r2.Entries[0].CompletionTime = time.Unix(20*20*2, 0)
|
||||
|
||||
ok = r1.Equal(r2)
|
||||
require.False(t, ok)
|
||||
}
|
||||
|
||||
func TestRedelegationHumanReadableString(t *testing.T) {
|
||||
r := Redelegation{
|
||||
DelegatorAddr: sdk.AccAddress(addr1),
|
||||
ValidatorSrcAddr: addr2,
|
||||
ValidatorDstAddr: addr3,
|
||||
SharesDst: sdk.NewDec(10),
|
||||
SharesSrc: sdk.NewDec(20),
|
||||
}
|
||||
r := NewRedelegation(sdk.AccAddress(addr1), addr2, addr3, 0,
|
||||
time.Unix(0, 0), sdk.NewInt64Coin(DefaultBondDenom, 0),
|
||||
sdk.NewDec(10), sdk.NewDec(20))
|
||||
|
||||
// NOTE: Being that the validator's keypair is random, we cannot test the
|
||||
// actual contents of the string.
|
||||
|
||||
@ -178,11 +178,6 @@ func ErrTransitiveRedelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
"redelegation to this validator already in progress, first redelegation to this validator must complete before next redelegation")
|
||||
}
|
||||
|
||||
func ErrConflictingRedelegation(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation,
|
||||
"conflicting redelegation from this source validator to this dest validator already exists, you must wait for it to finish")
|
||||
}
|
||||
|
||||
func ErrDelegatorShareExRateInvalid(codespace sdk.CodespaceType) sdk.Error {
|
||||
return sdk.NewError(codespace, CodeInvalidDelegation,
|
||||
"cannot delegate to validators with invalid (zero) ex-rate")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user