Merge PR #2163: Validator unbonding state

This commit is contained in:
Christopher Goes 2018-09-01 23:15:03 +02:00 committed by GitHub
commit d214952450
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 580 additions and 86 deletions

View File

@ -23,6 +23,8 @@ BREAKING CHANGES
* [x/stake] \#1901 Validator type's Owner field renamed to Operator; Validator's GetOwner() renamed accordingly to comply with the SDK's Validator interface. * [x/stake] \#1901 Validator type's Owner field renamed to Operator; Validator's GetOwner() renamed accordingly to comply with the SDK's Validator interface.
* [docs] [#2001](https://github.com/cosmos/cosmos-sdk/pull/2001) Update slashing spec for slashing period * [docs] [#2001](https://github.com/cosmos/cosmos-sdk/pull/2001) Update slashing spec for slashing period
* [x/stake, x/slashing] [#1305](https://github.com/cosmos/cosmos-sdk/issues/1305) - Rename "revoked" to "jailed" * [x/stake, x/slashing] [#1305](https://github.com/cosmos/cosmos-sdk/issues/1305) - Rename "revoked" to "jailed"
* [x/stake] [#1676] Revoked and jailed validators put into the unbonding state
* [x/stake] [#1877] Redelegations/unbonding-delegation from unbonding validator have reduced time
* [x/stake] \#2040 Validator operator type has now changed to `sdk.ValAddress` * [x/stake] \#2040 Validator operator type has now changed to `sdk.ValAddress`
* A new bech32 prefix has been introduced for Tendermint signing keys and * A new bech32 prefix has been introduced for Tendermint signing keys and
addresses, `cosmosconspub` and `cosmoscons` respectively. addresses, `cosmosconspub` and `cosmoscons` respectively.

View File

@ -177,7 +177,7 @@ func TestHandleAbsentValidator(t *testing.T) {
// validator should have been jailed // validator should have been jailed
validator, _ = sk.GetValidatorByPubKey(ctx, val) validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Unbonded, validator.GetStatus()) require.Equal(t, sdk.Unbonding, validator.GetStatus())
// unrevocation should fail prior to jail expiration // unrevocation should fail prior to jail expiration
got = slh(ctx, NewMsgUnjail(sdk.ValAddress(addr))) got = slh(ctx, NewMsgUnjail(sdk.ValAddress(addr)))
@ -224,7 +224,7 @@ func TestHandleAbsentValidator(t *testing.T) {
keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false)
} }
validator, _ = sk.GetValidatorByPubKey(ctx, val) validator, _ = sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Unbonded, validator.GetStatus()) require.Equal(t, sdk.Unbonding, validator.GetStatus())
} }
// Test a new validator entering the validator set // Test a new validator entering the validator set
@ -293,7 +293,7 @@ func TestHandleAlreadyJailed(t *testing.T) {
// validator should have been jailed and slashed // validator should have been jailed and slashed
validator, _ := sk.GetValidatorByPubKey(ctx, val) validator, _ := sk.GetValidatorByPubKey(ctx, val)
require.Equal(t, sdk.Unbonded, validator.GetStatus()) require.Equal(t, sdk.Unbonding, validator.GetStatus())
// validator should have been slashed // validator should have been slashed
require.Equal(t, int64(amtInt-1), validator.GetTokens().RoundInt64()) require.Equal(t, int64(amtInt-1), validator.GetTokens().RoundInt64())

View File

@ -80,5 +80,5 @@ func TestBeginBlocker(t *testing.T) {
// validator should be jailed // validator should be jailed
validator, found := sk.GetValidatorByPubKey(ctx, pk) validator, found := sk.GetValidatorByPubKey(ctx, pk)
require.True(t, found) require.True(t, found)
require.Equal(t, sdk.Unbonded, validator.GetStatus()) require.Equal(t, sdk.Unbonding, validator.GetStatus())
} }

View File

@ -85,8 +85,8 @@ func TestValidatorByPowerIndex(t *testing.T) {
keeper.Jail(ctx, keep.PKs[0]) keeper.Jail(ctx, keep.PKs[0])
validator, found = keeper.GetValidator(ctx, validatorAddr) validator, found = keeper.GetValidator(ctx, validatorAddr)
require.True(t, found) require.True(t, found)
require.Equal(t, sdk.Unbonded, validator.Status) // ensure is unbonded require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding
require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure is unbonded require.Equal(t, int64(500000), validator.Tokens.RoundInt64()) // ensure tokens slashed
// the old power record should have been deleted as the power changed // the old power record should have been deleted as the power changed
require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power)) require.False(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power))

View File

@ -2,6 +2,7 @@ package keeper
import ( import (
"bytes" "bytes"
"time"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/cosmos/cosmos-sdk/x/stake/types"
@ -313,6 +314,32 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
//______________________________________________________________________________________________________ //______________________________________________________________________________________________________
// get info for begin functions: MinTime and CreationHeight
func (k Keeper) getBeginInfo(ctx sdk.Context, params types.Params, valSrcAddr sdk.ValAddress) (
minTime time.Time, height int64, completeNow bool) {
validator, found := k.GetValidator(ctx, valSrcAddr)
switch {
case !found || validator.Status == sdk.Bonded:
// the longest wait - just unbonding period from now
minTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
height = ctx.BlockHeader().Height
return minTime, height, false
case validator.IsUnbonded(ctx):
return minTime, height, true
case validator.Status == sdk.Unbonding:
minTime = validator.UnbondingMinTime
height = validator.UnbondingHeight
return minTime, height, false
default:
panic("unknown validator status")
}
}
// complete unbonding an unbonding record // complete unbonding an unbonding record
func (k Keeper) BeginUnbonding(ctx sdk.Context, func (k Keeper) BeginUnbonding(ctx sdk.Context,
delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error { delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec) sdk.Error {
@ -330,12 +357,22 @@ func (k Keeper) BeginUnbonding(ctx sdk.Context,
// create the unbonding delegation // create the unbonding delegation
params := k.GetParams(ctx) params := k.GetParams(ctx)
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime) minTime, height, completeNow := k.getBeginInfo(ctx, params, valAddr)
balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()} balance := sdk.Coin{params.BondDenom, returnAmount.RoundInt()}
// no need to create the ubd object just complete now
if completeNow {
_, _, err := k.coinKeeper.AddCoins(ctx, delAddr, sdk.Coins{balance})
if err != nil {
return err
}
return nil
}
ubd := types.UnbondingDelegation{ ubd := types.UnbondingDelegation{
DelegatorAddr: delAddr, DelegatorAddr: delAddr,
ValidatorAddr: valAddr, ValidatorAddr: valAddr,
CreationHeight: height,
MinTime: minTime, MinTime: minTime,
Balance: balance, Balance: balance,
InitialBalance: balance, InitialBalance: balance,
@ -392,12 +429,17 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress,
} }
// create the unbonding delegation // create the unbonding delegation
minTime := ctx.BlockHeader().Time.Add(params.UnbondingTime) minTime, height, completeNow := k.getBeginInfo(ctx, params, valSrcAddr)
if completeNow { // no need to create the redelegation object
return nil
}
red := types.Redelegation{ red := types.Redelegation{
DelegatorAddr: delAddr, DelegatorAddr: delAddr,
ValidatorSrcAddr: valSrcAddr, ValidatorSrcAddr: valSrcAddr,
ValidatorDstAddr: valDstAddr, ValidatorDstAddr: valDstAddr,
CreationHeight: height,
MinTime: minTime, MinTime: minTime,
SharesDst: sharesCreated, SharesDst: sharesCreated,
SharesSrc: sharesAmount, SharesSrc: sharesAmount,

View File

@ -7,6 +7,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types" "github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -162,9 +163,7 @@ func TestUnbondDelegation(t *testing.T) {
} }
keeper.SetDelegation(ctx, delegation) keeper.SetDelegation(ctx, delegation)
var err error amount, err := keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
var amount sdk.Dec
amount, err = keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, int64(6), amount.RoundInt64()) // shares to be added to an unbonding delegation / redelegation require.Equal(t, int64(6), amount.RoundInt64()) // shares to be added to an unbonding delegation / redelegation
@ -180,6 +179,190 @@ func TestUnbondDelegation(t *testing.T) {
require.Equal(t, int64(4), pool.BondedTokens.RoundInt64()) require.Equal(t, int64(4), pool.BondedTokens.RoundInt64())
} }
// test removing all self delegation from a validator which should
// shift it from the bonded to unbonded state
func TestUndelegateSelfDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewDec(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
require.Equal(t, sdk.Unbonding, validator.Status)
}
func TestUndelegateFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewDec(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
selfDelegation := types.Delegation{
DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()),
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
header := ctx.BlockHeader()
blockHeight := int64(10)
header.Height = blockHeight
blockTime := time.Unix(333, 0)
header.Time = blockTime
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
params := keeper.GetParams(ctx)
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
//change the context
header = ctx.BlockHeader()
blockHeight2 := int64(20)
header.Height = blockHeight2
blockTime2 := time.Unix(444, 0)
header.Time = blockTime2
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
require.NoError(t, err)
// 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))
}
func TestUndelegateFromUnbondedValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewDec(20)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
DelegatorAddr: val0AccAddr,
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
header := ctx.BlockHeader()
blockHeight := int64(10)
header.Height = blockHeight
blockTime := time.Unix(333, 0)
header.Time = blockTime
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
params := keeper.GetParams(ctx)
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
// change the context to one which makes the validator considered unbonded
header = ctx.BlockHeader()
blockHeight2 := int64(20)
header.Height = blockHeight2
blockTime2 := time.Unix(444, 0).Add(params.UnbondingTime)
header.Time = blockTime2
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginUnbonding(ctx, addrDels[0], addrVals[0], sdk.NewDec(6))
require.NoError(t, err)
// no ubd should have been found, coins should have been returned direcly to account
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
require.False(t, found, "%v", ubd)
}
// Make sure that that the retrieving the delegations doesn't affect the state // Make sure that that the retrieving the delegations doesn't affect the state
func TestGetRedelegationsFromValidator(t *testing.T) { func TestGetRedelegationsFromValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0) ctx, _, keeper := CreateTestInput(t, false, 0)
@ -259,3 +442,206 @@ func TestRedelegation(t *testing.T) {
_, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) _, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
require.False(t, found) require.False(t, found)
} }
func TestRedelegateSelfDelegation(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewDec(30)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
DelegatorAddr: val0AccAddr,
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, selfDelegation)
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10))
require.NoError(t, err)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, int64(10), validator.Tokens.RoundInt64())
require.Equal(t, sdk.Unbonding, validator.Status)
}
func TestRedelegateFromUnbondingValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewDec(30)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
DelegatorAddr: val0AccAddr,
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
header := ctx.BlockHeader()
blockHeight := int64(10)
header.Height = blockHeight
blockTime := time.Unix(333, 0)
header.Time = blockTime
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
params := keeper.GetParams(ctx)
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
//change the context
header = ctx.BlockHeader()
blockHeight2 := int64(20)
header.Height = blockHeight2
blockTime2 := time.Unix(444, 0)
header.Time = blockTime2
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
require.NoError(t, err)
// 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))
}
func TestRedelegateFromUnbondedValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 0)
pool := keeper.GetPool(ctx)
pool.LooseTokens = sdk.NewDec(30)
//create a validator with a self-delegation
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
val0AccAddr := sdk.AccAddress(addrVals[0].Bytes())
selfDelegation := types.Delegation{
DelegatorAddr: val0AccAddr,
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, selfDelegation)
// create a second delegation to this validator
validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
pool = keeper.GetPool(ctx)
delegation := types.Delegation{
DelegatorAddr: addrDels[0],
ValidatorAddr: addrVals[0],
Shares: issuedShares,
}
keeper.SetDelegation(ctx, delegation)
// create a second validator
validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{})
validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, int64(10), issuedShares.RoundInt64())
keeper.SetPool(ctx, pool)
validator2 = keeper.UpdateValidator(ctx, validator2)
header := ctx.BlockHeader()
blockHeight := int64(10)
header.Height = blockHeight
blockTime := time.Unix(333, 0)
header.Time = blockTime
ctx = ctx.WithBlockHeader(header)
// unbond the all self-delegation to put validator in unbonding state
err := keeper.BeginUnbonding(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10))
require.NoError(t, err)
validator, found := keeper.GetValidator(ctx, addrVals[0])
require.True(t, found)
require.Equal(t, blockHeight, validator.UnbondingHeight)
params := keeper.GetParams(ctx)
require.True(t, blockTime.Add(params.UnbondingTime).Equal(validator.UnbondingMinTime))
// change the context to one which makes the validator considered unbonded
header = ctx.BlockHeader()
blockHeight2 := int64(20)
header.Height = blockHeight2
blockTime2 := time.Unix(444, 0).Add(params.UnbondingTime)
header.Time = blockTime2
ctx = ctx.WithBlockHeader(header)
// unbond some of the other delegation's shares
err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6))
require.NoError(t, err)
// no ubd should have been found, coins should have been returned direcly to account
ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
require.False(t, found, "%v", ubd)
}

View File

@ -18,8 +18,12 @@ import (
// Infraction committed equal to or less than an unbonding period in the past, // Infraction committed equal to or less than an unbonding period in the past,
// so all unbonding delegations and redelegations from that height are stored // so all unbonding delegations and redelegations from that height are stored
// CONTRACT: // CONTRACT:
// Slash will not slash unbonded validators (for the above reason)
// CONTRACT:
// Infraction committed at the current height or at a past height, // Infraction committed at the current height or at a past height,
// not at a height in the future // not at a height in the future
//
// nolint: gocyclo
func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Dec) { func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Dec) {
logger := ctx.Logger().With("module", "x/stake") logger := ctx.Logger().With("module", "x/stake")
@ -43,6 +47,12 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
pubkey.Address())) pubkey.Address()))
return return
} }
// should not be slashing unbonded
if validator.IsUnbonded(ctx) {
panic(fmt.Sprintf("should not be slashing unbonded validator: %v", validator))
}
operatorAddress := validator.GetOperator() operatorAddress := validator.GetOperator()
// Track remaining slash amount for the validator // Track remaining slash amount for the validator
@ -91,17 +101,16 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
// Cannot decrease balance below zero // Cannot decrease balance below zero
tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens) tokensToBurn := sdk.MinDec(remainingSlashAmount, validator.Tokens)
// Get the current pool // burn validator's tokens
pool := k.GetPool(ctx) pool := k.GetPool(ctx)
// remove tokens from the validator
validator, pool = validator.RemoveTokens(pool, tokensToBurn) validator, pool = validator.RemoveTokens(pool, tokensToBurn)
// burn tokens
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn) pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)
// update the pool
k.SetPool(ctx, pool) k.SetPool(ctx, pool)
// update the validator, possibly kicking it out // update the validator, possibly kicking it out
validator = k.UpdateValidator(ctx, validator) validator = k.UpdateValidator(ctx, validator)
// remove validator if it has been reduced to zero shares
// remove validator if it has no more tokens
if validator.Tokens.IsZero() { if validator.Tokens.IsZero() {
k.RemoveValidator(ctx, validator.Operator) k.RemoveValidator(ctx, validator.Operator)
} }
@ -134,12 +143,12 @@ func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) {
} }
// set the jailed flag on a validator // set the jailed flag on a validator
func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, jailed bool) { func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, isJailed bool) {
validator, found := k.GetValidatorByPubKey(ctx, pubkey) validator, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found { if !found {
panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, jailed)) panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed))
} }
validator.Jailed = jailed validator.Jailed = isJailed
k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it
return return
} }
@ -179,6 +188,7 @@ func (k Keeper) slashUnbondingDelegation(ctx sdk.Context, unbondingDelegation ty
unbondingDelegation.Balance.Amount = unbondingDelegation.Balance.Amount.Sub(unbondingSlashAmount) unbondingDelegation.Balance.Amount = unbondingDelegation.Balance.Amount.Sub(unbondingSlashAmount)
k.SetUnbondingDelegation(ctx, unbondingDelegation) k.SetUnbondingDelegation(ctx, unbondingDelegation)
pool := k.GetPool(ctx) pool := k.GetPool(ctx)
// Burn loose tokens // Burn loose tokens
// Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760 // Ref https://github.com/cosmos/cosmos-sdk/pull/1278#discussion_r198657760
pool.LooseTokens = pool.LooseTokens.Sub(slashAmount) pool.LooseTokens = pool.LooseTokens.Sub(slashAmount)
@ -239,6 +249,7 @@ func (k Keeper) slashRedelegation(ctx sdk.Context, validator types.Validator, re
if err != nil { if err != nil {
panic(fmt.Errorf("error unbonding delegator: %v", err)) panic(fmt.Errorf("error unbonding delegator: %v", err))
} }
// Burn loose tokens // Burn loose tokens
pool := k.GetPool(ctx) pool := k.GetPool(ctx)
pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn) pool.LooseTokens = pool.LooseTokens.Sub(tokensToBurn)

View File

@ -11,8 +11,8 @@ import (
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
) )
// setup helper function // TODO integrate with test_common.go helper (CreateTestInput)
// creates two validators // setup helper function - creates two validators
func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
// setup // setup
ctx, _, keeper := CreateTestInput(t, false, amt) ctx, _, keeper := CreateTestInput(t, false, amt)
@ -34,8 +34,11 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) {
return ctx, keeper, params return ctx, keeper, params
} }
//_________________________________________________________________________________
// tests Jail, Unjail // tests Jail, Unjail
func TestRevocation(t *testing.T) { func TestRevocation(t *testing.T) {
// setup // setup
ctx, keeper, _ := setupHelper(t, 10) ctx, keeper, _ := setupHelper(t, 10)
addr := addrVals[0] addr := addrVals[0]
@ -57,7 +60,6 @@ func TestRevocation(t *testing.T) {
val, found = keeper.GetValidator(ctx, addr) val, found = keeper.GetValidator(ctx, addr)
require.True(t, found) require.True(t, found)
require.False(t, val.GetJailed()) require.False(t, val.GetJailed())
} }
// tests slashUnbondingDelegation // tests slashUnbondingDelegation
@ -95,8 +97,10 @@ func TestSlashUnbondingDelegation(t *testing.T) {
require.Equal(t, int64(5), slashAmount.RoundInt64()) require.Equal(t, int64(5), slashAmount.RoundInt64())
ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) ubd, found := keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0])
require.True(t, found) require.True(t, found)
// initialbalance unchanged // initialbalance unchanged
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), ubd.InitialBalance) require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), ubd.InitialBalance)
// balance decreased // balance decreased
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), ubd.Balance) require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), ubd.Balance)
newPool := keeper.GetPool(ctx) newPool := keeper.GetPool(ctx)
@ -155,14 +159,18 @@ func TestSlashRedelegation(t *testing.T) {
require.Equal(t, int64(5), slashAmount.RoundInt64()) require.Equal(t, int64(5), slashAmount.RoundInt64())
rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1])
require.True(t, found) require.True(t, found)
// initialbalance unchanged // initialbalance unchanged
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance) require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 10), rd.InitialBalance)
// balance decreased // balance decreased
require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), rd.Balance) require.Equal(t, sdk.NewInt64Coin(params.BondDenom, 5), rd.Balance)
// shares decreased // shares decreased
del, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[1]) del, found = keeper.GetDelegation(ctx, addrDels[0], addrVals[1])
require.True(t, found) require.True(t, found)
require.Equal(t, int64(5), del.Shares.RoundInt64()) require.Equal(t, int64(5), del.Shares.RoundInt64())
// pool bonded tokens decreased // pool bonded tokens decreased
newPool := keeper.GetPool(ctx) newPool := keeper.GetPool(ctx)
require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64())
@ -177,7 +185,7 @@ func TestSlashAtFutureHeight(t *testing.T) {
} }
// tests Slash at the current height // tests Slash at the current height
func TestSlashAtCurrentHeight(t *testing.T) { func TestSlashValidatorAtCurrentHeight(t *testing.T) {
ctx, keeper, _ := setupHelper(t, 10) ctx, keeper, _ := setupHelper(t, 10)
pk := PKs[0] pk := PKs[0]
fraction := sdk.NewDecWithPrec(5, 1) fraction := sdk.NewDecWithPrec(5, 1)

View File

@ -212,6 +212,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
cliffPower := k.GetCliffValidatorPower(ctx) cliffPower := k.GetCliffValidatorPower(ctx)
switch { switch {
// if the validator is already bonded and the power is increasing, we need // if the validator is already bonded and the power is increasing, we need
// perform the following: // perform the following:
// a) update Tendermint // a) update Tendermint
@ -240,10 +241,11 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
// skip to completion // skip to completion
// default case - validator was either: default:
// default case - validator was either:
// a) not-bonded and now has power-rank greater than cliff validator // a) not-bonded and now has power-rank greater than cliff validator
// b) bonded and now has decreased in power // b) bonded and now has decreased in power
default:
// update the validator set for this validator // update the validator set for this validator
updatedVal, updated := k.UpdateBondedValidators(ctx, validator) updatedVal, updated := k.UpdateBondedValidators(ctx, validator)
if updated { if updated {
@ -307,10 +309,13 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool) newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool)
if bytes.Equal(affectedVal.Operator, newCliffVal.Operator) { if bytes.Equal(affectedVal.Operator, newCliffVal.Operator) {
// The affected validator remains the cliff validator, however, since // The affected validator remains the cliff validator, however, since
// the store does not contain the new power, update the new power rank. // the store does not contain the new power, update the new power rank.
store.Set(ValidatorPowerCliffKey, affectedValRank) store.Set(ValidatorPowerCliffKey, affectedValRank)
} else if bytes.Compare(affectedValRank, newCliffValRank) > 0 { } else if bytes.Compare(affectedValRank, newCliffValRank) > 0 {
// The affected validator no longer remains the cliff validator as it's // The affected validator no longer remains the cliff validator as it's
// power is greater than the new cliff validator. // power is greater than the new cliff validator.
k.setCliffValidator(ctx, newCliffVal, pool) k.setCliffValidator(ctx, newCliffVal, pool)
@ -321,7 +326,7 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator { func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator {
if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded { if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded {
newValidator = k.unbondValidator(ctx, newValidator) newValidator = k.beginUnbondingValidator(ctx, newValidator)
// need to also clear the cliff validator spot because the jail has // need to also clear the cliff validator spot because the jail has
// opened up a new spot which will be filled when // opened up a new spot which will be filled when
@ -416,20 +421,20 @@ func (k Keeper) UpdateBondedValidators(
} }
} }
// increment bondedValidatorsCount / get the validator to bond // if we've reached jailed validators no further bonded validators exist
if !validator.Jailed { if validator.Jailed {
if validator.Status != sdk.Bonded {
validatorToBond = validator
if newValidatorBonded {
panic("already decided to bond a validator, can't bond another!")
}
newValidatorBonded = true
}
} else {
// TODO: document why we must break here.
break break
} }
// increment bondedValidatorsCount / get the validator to bond
if validator.Status != sdk.Bonded {
validatorToBond = validator
if newValidatorBonded {
panic("already decided to bond a validator, can't bond another!")
}
newValidatorBonded = true
}
// increment the total number of bonded validators and potentially mark // increment the total number of bonded validators and potentially mark
// the validator to bond // the validator to bond
if validator.Status != sdk.Bonded { if validator.Status != sdk.Bonded {
@ -464,13 +469,15 @@ func (k Keeper) UpdateBondedValidators(
} }
if bytes.Equal(validatorToBond.Operator, affectedValidator.Operator) { if bytes.Equal(validatorToBond.Operator, affectedValidator.Operator) {
// unbond the old cliff validator iff the affected validator was
// newly bonded and has greater power // begin unbonding the old cliff validator iff the affected
k.unbondValidator(ctx, oldCliffVal) // validator was newly bonded and has greater power
k.beginUnbondingValidator(ctx, oldCliffVal)
} else { } else {
// otherwise unbond the affected validator, which must have been
// kicked out // otherwise begin unbonding the affected validator, which must
affectedValidator = k.unbondValidator(ctx, affectedValidator) // have been kicked out
affectedValidator = k.beginUnbondingValidator(ctx, affectedValidator)
} }
} }
@ -563,25 +570,30 @@ func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) {
if !found { if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
} }
k.unbondValidator(ctx, validator) k.beginUnbondingValidator(ctx, validator)
} }
} }
// perform all the store operations for when a validator status becomes unbonded // perform all the store operations for when a validator status becomes unbonded
func (k Keeper) unbondValidator(ctx sdk.Context, validator types.Validator) types.Validator { func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey) store := ctx.KVStore(k.storeKey)
pool := k.GetPool(ctx) pool := k.GetPool(ctx)
params := k.GetParams(ctx)
// sanity check // sanity check
if validator.Status == sdk.Unbonded { if validator.Status == sdk.Unbonded ||
panic(fmt.Sprintf("should not already be unbonded, validator: %v\n", validator)) validator.Status == sdk.Unbonding {
panic(fmt.Sprintf("should not already be unbonded or unbonding, validator: %v\n", validator))
} }
// set the status // set the status
validator, pool = validator.UpdateStatus(pool, sdk.Unbonded) validator, pool = validator.UpdateStatus(pool, sdk.Unbonding)
k.SetPool(ctx, pool) k.SetPool(ctx, pool)
validator.UnbondingMinTime = ctx.BlockHeader().Time.Add(params.UnbondingTime)
validator.UnbondingHeight = ctx.BlockHeader().Height
// save the now unbonded validator record // save the now unbonded validator record
k.SetValidator(ctx, validator) k.SetValidator(ctx, validator)

View File

@ -137,7 +137,7 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
expectedValStatus := map[int]sdk.BondStatus{ expectedValStatus := map[int]sdk.BondStatus{
9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded, 9: sdk.Bonded, 8: sdk.Bonded, 7: sdk.Bonded, 5: sdk.Bonded, 4: sdk.Bonded,
0: sdk.Unbonded, 1: sdk.Unbonded, 2: sdk.Unbonded, 3: sdk.Unbonded, 6: sdk.Unbonded, 0: sdk.Unbonding, 1: sdk.Unbonding, 2: sdk.Unbonding, 3: sdk.Unbonding, 6: sdk.Unbonding,
} }
// require all the validators have their respective statuses // require all the validators have their respective statuses
@ -145,9 +145,11 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) {
valAddr := validators[valIdx].Operator valAddr := validators[valIdx].Operator
val, _ := keeper.GetValidator(ctx, valAddr) val, _ := keeper.GetValidator(ctx, valAddr)
require.Equal( assert.Equal(
t, val.GetStatus(), status, t, status, val.GetStatus(),
fmt.Sprintf("expected validator to have status: %s", sdk.BondStatusToString(status))) fmt.Sprintf("expected validator at index %v to have status: %s",
valIdx,
sdk.BondStatusToString(status)))
} }
} }
@ -610,8 +612,8 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
validators[i], found = keeper.GetValidator(ctx, validators[i].Operator) validators[i], found = keeper.GetValidator(ctx, validators[i].Operator)
require.True(t, found) require.True(t, found)
} }
assert.Equal(t, sdk.Unbonded, validators[0].Status) assert.Equal(t, sdk.Unbonding, validators[0].Status)
assert.Equal(t, sdk.Unbonded, validators[1].Status) assert.Equal(t, sdk.Unbonding, validators[1].Status)
assert.Equal(t, sdk.Bonded, validators[2].Status) assert.Equal(t, sdk.Bonded, validators[2].Status)
assert.Equal(t, sdk.Bonded, validators[3].Status) assert.Equal(t, sdk.Bonded, validators[3].Status)
assert.Equal(t, sdk.Unbonded, validators[4].Status) assert.Equal(t, sdk.Unbonded, validators[4].Status)

View File

@ -45,6 +45,7 @@ func SupplyInvariants(ck bank.Keeper, k stake.Keeper, am auth.AccountMapper) sim
case sdk.Bonded: case sdk.Bonded:
bonded = bonded.Add(validator.GetPower()) bonded = bonded.Add(validator.GetPower())
case sdk.Unbonding: case sdk.Unbonding:
loose = loose.Add(validator.GetTokens().RoundInt())
case sdk.Unbonded: case sdk.Unbonded:
loose = loose.Add(validator.GetTokens().RoundInt()) loose = loose.Add(validator.GetTokens().RoundInt())
} }

View File

@ -19,7 +19,10 @@ import (
// SimulateMsgCreateValidator // SimulateMsgCreateValidator
func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.Operation { func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
denom := k.GetParams(ctx).BondDenom denom := k.GetParams(ctx).BondDenom
description := stake.Description{ description := stake.Description{
Moniker: simulation.RandStringOfLength(r, 10), Moniker: simulation.RandStringOfLength(r, 10),
@ -56,7 +59,10 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation
// SimulateMsgEditValidator // SimulateMsgEditValidator
func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
description := stake.Description{ description := stake.Description{
Moniker: simulation.RandStringOfLength(r, 10), Moniker: simulation.RandStringOfLength(r, 10),
Identity: simulation.RandStringOfLength(r, 10), Identity: simulation.RandStringOfLength(r, 10),
@ -84,7 +90,10 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation {
// SimulateMsgDelegate // SimulateMsgDelegate
func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation { func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
denom := k.GetParams(ctx).BondDenom denom := k.GetParams(ctx).BondDenom
validatorKey := simulation.RandomKey(r, keys) validatorKey := simulation.RandomKey(r, keys)
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
@ -116,7 +125,10 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operat
// SimulateMsgBeginUnbonding // SimulateMsgBeginUnbonding
func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.Operation { func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
denom := k.GetParams(ctx).BondDenom denom := k.GetParams(ctx).BondDenom
validatorKey := simulation.RandomKey(r, keys) validatorKey := simulation.RandomKey(r, keys)
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
@ -148,7 +160,10 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.
// SimulateMsgCompleteUnbonding // SimulateMsgCompleteUnbonding
func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation { func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
validatorKey := simulation.RandomKey(r, keys) validatorKey := simulation.RandomKey(r, keys)
validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address())
delegatorKey := simulation.RandomKey(r, keys) delegatorKey := simulation.RandomKey(r, keys)
@ -171,7 +186,10 @@ func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation {
// SimulateMsgBeginRedelegate // SimulateMsgBeginRedelegate
func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation { func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
denom := k.GetParams(ctx).BondDenom denom := k.GetParams(ctx).BondDenom
sourceValidatorKey := simulation.RandomKey(r, keys) sourceValidatorKey := simulation.RandomKey(r, keys)
sourceValidatorAddress := sdk.ValAddress(sourceValidatorKey.PubKey().Address()) sourceValidatorAddress := sdk.ValAddress(sourceValidatorKey.PubKey().Address())
@ -207,7 +225,10 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation
// SimulateMsgCompleteRedelegate // SimulateMsgCompleteRedelegate
func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation { func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
validatorSrcKey := simulation.RandomKey(r, keys) validatorSrcKey := simulation.RandomKey(r, keys)
validatorSrcAddress := sdk.ValAddress(validatorSrcKey.PubKey().Address()) validatorSrcAddress := sdk.ValAddress(validatorSrcKey.PubKey().Address())
validatorDstKey := simulation.RandomKey(r, keys) validatorDstKey := simulation.RandomKey(r, keys)

View File

@ -3,6 +3,7 @@ package types
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"time"
abci "github.com/tendermint/tendermint/abci/types" abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto"
@ -31,15 +32,14 @@ type Validator struct {
Description Description `json:"description"` // description terms for the validator Description Description `json:"description"` // description terms for the validator
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
// fee related
LastBondedTokens sdk.Dec `json:"prev_bonded_tokens"` // Previous bonded tokens held
} }
// NewValidator - initialize a new validator // NewValidator - initialize a new validator
@ -54,12 +54,12 @@ func NewValidator(operator sdk.ValAddress, pubKey crypto.PubKey, description Des
Description: description, Description: description,
BondHeight: int64(0), BondHeight: int64(0),
BondIntraTxCounter: int16(0), BondIntraTxCounter: int16(0),
ProposerRewardPool: sdk.Coins{}, UnbondingHeight: int64(0),
UnbondingMinTime: time.Unix(0, 0),
Commission: sdk.ZeroDec(), Commission: sdk.ZeroDec(),
CommissionMax: sdk.ZeroDec(), CommissionMax: sdk.ZeroDec(),
CommissionChangeRate: sdk.ZeroDec(), CommissionChangeRate: sdk.ZeroDec(),
CommissionChangeToday: sdk.ZeroDec(), CommissionChangeToday: sdk.ZeroDec(),
LastBondedTokens: sdk.ZeroDec(),
} }
} }
@ -73,12 +73,12 @@ type validatorValue struct {
Description Description Description Description
BondHeight int64 BondHeight int64
BondIntraTxCounter int16 BondIntraTxCounter int16
ProposerRewardPool sdk.Coins UnbondingHeight int64
UnbondingMinTime time.Time
Commission sdk.Dec Commission sdk.Dec
CommissionMax sdk.Dec CommissionMax sdk.Dec
CommissionChangeRate sdk.Dec CommissionChangeRate sdk.Dec
CommissionChangeToday sdk.Dec CommissionChangeToday sdk.Dec
LastBondedTokens sdk.Dec
} }
// return the redelegation without fields contained within the key for the store // return the redelegation without fields contained within the key for the store
@ -92,12 +92,12 @@ func MustMarshalValidator(cdc *wire.Codec, validator Validator) []byte {
Description: validator.Description, Description: validator.Description,
BondHeight: validator.BondHeight, BondHeight: validator.BondHeight,
BondIntraTxCounter: validator.BondIntraTxCounter, BondIntraTxCounter: validator.BondIntraTxCounter,
ProposerRewardPool: validator.ProposerRewardPool, UnbondingHeight: validator.UnbondingHeight,
UnbondingMinTime: validator.UnbondingMinTime,
Commission: validator.Commission, Commission: validator.Commission,
CommissionMax: validator.CommissionMax, CommissionMax: validator.CommissionMax,
CommissionChangeRate: validator.CommissionChangeRate, CommissionChangeRate: validator.CommissionChangeRate,
CommissionChangeToday: validator.CommissionChangeToday, CommissionChangeToday: validator.CommissionChangeToday,
LastBondedTokens: validator.LastBondedTokens,
} }
return cdc.MustMarshalBinary(val) return cdc.MustMarshalBinary(val)
} }
@ -108,7 +108,6 @@ func MustUnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) Validat
if err != nil { if err != nil {
panic(err) panic(err)
} }
return validator return validator
} }
@ -134,12 +133,12 @@ func UnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) (validator
Description: storeValue.Description, Description: storeValue.Description,
BondHeight: storeValue.BondHeight, BondHeight: storeValue.BondHeight,
BondIntraTxCounter: storeValue.BondIntraTxCounter, BondIntraTxCounter: storeValue.BondIntraTxCounter,
ProposerRewardPool: storeValue.ProposerRewardPool, UnbondingHeight: storeValue.UnbondingHeight,
UnbondingMinTime: storeValue.UnbondingMinTime,
Commission: storeValue.Commission, Commission: storeValue.Commission,
CommissionMax: storeValue.CommissionMax, CommissionMax: storeValue.CommissionMax,
CommissionChangeRate: storeValue.CommissionChangeRate, CommissionChangeRate: storeValue.CommissionChangeRate,
CommissionChangeToday: storeValue.CommissionChangeToday, CommissionChangeToday: storeValue.CommissionChangeToday,
LastBondedTokens: storeValue.LastBondedTokens,
}, nil }, nil
} }
@ -161,12 +160,12 @@ func (v Validator) HumanReadableString() (string, error) {
resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String()) resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String())
resp += fmt.Sprintf("Description: %s\n", v.Description) resp += fmt.Sprintf("Description: %s\n", v.Description)
resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight) resp += fmt.Sprintf("Bond Height: %d\n", v.BondHeight)
resp += fmt.Sprintf("Proposer Reward Pool: %s\n", v.ProposerRewardPool.String()) resp += fmt.Sprintf("Unbonding Height: %d\n", v.UnbondingHeight)
resp += fmt.Sprintf("Minimum Unbonding Time: %v\n", v.UnbondingMinTime)
resp += fmt.Sprintf("Commission: %s\n", v.Commission.String()) resp += fmt.Sprintf("Commission: %s\n", v.Commission.String())
resp += fmt.Sprintf("Max Commission Rate: %s\n", v.CommissionMax.String()) resp += fmt.Sprintf("Max Commission Rate: %s\n", v.CommissionMax.String())
resp += fmt.Sprintf("Commission Change Rate: %s\n", v.CommissionChangeRate.String()) resp += fmt.Sprintf("Commission Change Rate: %s\n", v.CommissionChangeRate.String())
resp += fmt.Sprintf("Commission Change Today: %s\n", v.CommissionChangeToday.String()) resp += fmt.Sprintf("Commission Change Today: %s\n", v.CommissionChangeToday.String())
resp += fmt.Sprintf("Previous Bonded Tokens: %s\n", v.LastBondedTokens.String())
return resp, nil return resp, nil
} }
@ -186,15 +185,14 @@ type BechValidator struct {
Description Description `json:"description"` // description terms for the validator Description Description `json:"description"` // description terms for the validator
BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator BondHeight int64 `json:"bond_height"` // earliest height as a bonded validator
BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change BondIntraTxCounter int16 `json:"bond_intra_tx_counter"` // block-local tx index of validator change
ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer
UnbondingHeight int64 `json:"unbonding_height"` // if unbonding, height at which this validator has begun unbonding
UnbondingMinTime time.Time `json:"unbonding_time"` // if unbonding, min time for the validator to complete unbonding
Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators Commission sdk.Dec `json:"commission"` // XXX the commission rate of fees charged to any delegators
CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge CommissionMax sdk.Dec `json:"commission_max"` // XXX maximum commission rate which this validator can ever charge
CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission CommissionChangeRate sdk.Dec `json:"commission_change_rate"` // XXX maximum daily increase of the validator commission
CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) CommissionChangeToday sdk.Dec `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time)
// fee related
LastBondedTokens sdk.Dec `json:"prev_bonded_shares"` // last bonded token amount
} }
// get the bech validator from the the regular validator // get the bech validator from the the regular validator
@ -216,14 +214,13 @@ func (v Validator) Bech32Validator() (BechValidator, error) {
Description: v.Description, Description: v.Description,
BondHeight: v.BondHeight, BondHeight: v.BondHeight,
BondIntraTxCounter: v.BondIntraTxCounter, BondIntraTxCounter: v.BondIntraTxCounter,
ProposerRewardPool: v.ProposerRewardPool, UnbondingHeight: v.UnbondingHeight,
UnbondingMinTime: v.UnbondingMinTime,
Commission: v.Commission, Commission: v.Commission,
CommissionMax: v.CommissionMax, CommissionMax: v.CommissionMax,
CommissionChangeRate: v.CommissionChangeRate, CommissionChangeRate: v.CommissionChangeRate,
CommissionChangeToday: v.CommissionChangeToday, CommissionChangeToday: v.CommissionChangeToday,
LastBondedTokens: v.LastBondedTokens,
}, nil }, nil
} }
@ -238,12 +235,10 @@ func (v Validator) Equal(c2 Validator) bool {
v.Tokens.Equal(c2.Tokens) && v.Tokens.Equal(c2.Tokens) &&
v.DelegatorShares.Equal(c2.DelegatorShares) && v.DelegatorShares.Equal(c2.DelegatorShares) &&
v.Description == c2.Description && v.Description == c2.Description &&
v.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) &&
v.Commission.Equal(c2.Commission) && v.Commission.Equal(c2.Commission) &&
v.CommissionMax.Equal(c2.CommissionMax) && v.CommissionMax.Equal(c2.CommissionMax) &&
v.CommissionChangeRate.Equal(c2.CommissionChangeRate) && v.CommissionChangeRate.Equal(c2.CommissionChangeRate) &&
v.CommissionChangeToday.Equal(c2.CommissionChangeToday) && v.CommissionChangeToday.Equal(c2.CommissionChangeToday)
v.LastBondedTokens.Equal(c2.LastBondedTokens)
} }
// return the TM validator address // return the TM validator address
@ -428,6 +423,20 @@ func (v Validator) BondedTokens() sdk.Dec {
return sdk.ZeroDec() return sdk.ZeroDec()
} }
// Returns if the validator should be considered unbonded
func (v Validator) IsUnbonded(ctx sdk.Context) bool {
switch v.Status {
case sdk.Unbonded:
return true
case sdk.Unbonding:
ctxTime := ctx.BlockHeader().Time
if ctxTime.After(v.UnbondingMinTime) {
return true
}
}
return false
}
//______________________________________________________________________ //______________________________________________________________________
// ensure fulfills the sdk validator types // ensure fulfills the sdk validator types