address review comments

This commit is contained in:
Kevin Davis 2019-10-04 13:55:49 -04:00
parent 54b9cf167f
commit ad82e971ae
9 changed files with 191 additions and 149 deletions

View File

@ -17,6 +17,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
"github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/x/crisis" "github.com/cosmos/cosmos-sdk/x/crisis"
distr "github.com/cosmos/cosmos-sdk/x/distribution" distr "github.com/cosmos/cosmos-sdk/x/distribution"
@ -293,6 +294,7 @@ func MakeCodec() *codec.Codec {
var cdc = codec.New() var cdc = codec.New()
ModuleBasics.RegisterCodec(cdc) ModuleBasics.RegisterCodec(cdc)
vesting.RegisterCodec(cdc)
sdk.RegisterCodec(cdc) sdk.RegisterCodec(cdc)
codec.RegisterCrypto(cdc) codec.RegisterCrypto(cdc)
codec.RegisterEvidences(cdc) codec.RegisterEvidences(cdc)

View File

@ -23,30 +23,25 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
voteInfos = req.LastCommitInfo.GetVotes() voteInfos = req.LastCommitInfo.GetVotes()
validatorVestingKeys := k.GetAllAccountKeys(ctx) validatorVestingKeys := k.GetAllAccountKeys(ctx)
for _, key := range validatorVestingKeys { for _, key := range validatorVestingKeys {
acc := k.GetAccountFromAuthKeeper(ctx, key[1:]) acc := k.GetAccountFromAuthKeeper(ctx, key)
if voteInfos.ContainsValidatorAddress(acc.ValidatorAddress) { vote, found := voteInfos.FilterByValidatorAddress(acc.ValidatorAddress)
vote := voteInfos.MustFilterByValidatorAddress(acc.ValidatorAddress) if !found || !vote.SignedLastBlock {
if !vote.SignedLastBlock { // if the validator was not found or explicitly didn't sign, increment the missing sign count
// if the validator explicitly missed signing the block, increment the missing sign count
k.UpdateMissingSignCount(ctx, acc.GetAddress(), true)
} else {
k.UpdateMissingSignCount(ctx, acc.GetAddress(), false)
}
} else {
// if the validator was not a voting member of the validator set, increment the missing sign count
k.UpdateMissingSignCount(ctx, acc.GetAddress(), true) k.UpdateMissingSignCount(ctx, acc.GetAddress(), true)
} else {
k.UpdateMissingSignCount(ctx, acc.GetAddress(), false)
} }
// check if a period ended in the last block // check if a period ended in the last block
endTimes := k.GetPeriodEndTimes(ctx, key[1:]) endTimes := k.GetPeriodEndTimes(ctx, key)
for i, t := range endTimes { for i, t := range endTimes {
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t { if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
k.UpdateVestedCoinsProgress(ctx, key[1:], i) k.UpdateVestedCoinsProgress(ctx, key, i)
} }
} }
// handle any new/remaining debt on the account // handle any new/remaining debt on the account
k.HandleVestingDebt(ctx, key[1:], currentBlockTime) k.HandleVestingDebt(ctx, key, currentBlockTime)
} }
k.SetPreviousBlockTime(ctx, currentBlockTime) k.SetPreviousBlockTime(ctx, currentBlockTime)
} }
@ -54,24 +49,14 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
// VoteInfos an array of abci.VoteInfo // VoteInfos an array of abci.VoteInfo
type VoteInfos []abci.VoteInfo type VoteInfos []abci.VoteInfo
// ContainsValidatorAddress returns true if the input validator address is found in the VoteInfos array // FilterByValidatorAddress returns the VoteInfo of the validator address matching the input validator address
func (vis VoteInfos) ContainsValidatorAddress(consAddress sdk.ConsAddress) bool { // and a boolean for if the address was found.
for _, vi := range vis { func (vis VoteInfos) FilterByValidatorAddress(consAddress sdk.ConsAddress) (abci.VoteInfo, bool) {
votingAddress := sdk.ConsAddress(vi.Validator.Address)
if bytes.Equal(consAddress, votingAddress) {
return true
}
}
return false
}
// MustFilterByValidatorAddress returns the VoteInfo that has a validator address matching the input validator address
func (vis VoteInfos) MustFilterByValidatorAddress(consAddress sdk.ConsAddress) abci.VoteInfo {
for i, vi := range vis { for i, vi := range vis {
votingAddress := sdk.ConsAddress(vi.Validator.Address) votingAddress := sdk.ConsAddress(vi.Validator.Address)
if bytes.Equal(consAddress, votingAddress) { if bytes.Equal(consAddress, votingAddress) {
return vis[i] return vis[i], true
} }
} }
panic("validator address not found") return abci.VoteInfo{}, false
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking"
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
"github.com/kava-labs/kava/x/validator-vesting/internal/keeper" "github.com/kava-labs/kava/x/validator-vesting/internal/keeper"
"github.com/kava-labs/kava/x/validator-vesting/internal/types"
) )
func TestBeginBlockerSignedBlock(t *testing.T) { func TestBeginBlockerSignedBlock(t *testing.T) {
@ -72,7 +73,7 @@ func TestBeginBlockerSignedBlock(t *testing.T) {
height++ height++
blockTime = addHour(blockTime) blockTime = addHour(blockTime)
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address) vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
require.Equal(t, []int64{0, 1}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{0, 1}, vva.CurrentPeriodProgress)
header = abci.Header{Height: height, Time: addHour(blockTime)} header = abci.Header{Height: height, Time: addHour(blockTime)}
@ -91,7 +92,7 @@ func TestBeginBlockerSignedBlock(t *testing.T) {
height++ height++
blockTime = addHour(blockTime) blockTime = addHour(blockTime)
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address) vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
require.Equal(t, []int64{1, 2}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{1, 2}, vva.CurrentPeriodProgress)
// mark the validator as being absent // mark the validator as being absent
req = abci.RequestBeginBlock{ req = abci.RequestBeginBlock{
@ -108,7 +109,7 @@ func TestBeginBlockerSignedBlock(t *testing.T) {
height++ height++
blockTime = addHour(blockTime) blockTime = addHour(blockTime)
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address) vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
require.Equal(t, []int64{2, 3}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{2, 3}, vva.CurrentPeriodProgress)
} }
func TestBeginBlockerSuccessfulPeriod(t *testing.T) { func TestBeginBlockerSuccessfulPeriod(t *testing.T) {
@ -158,14 +159,14 @@ func TestBeginBlockerSuccessfulPeriod(t *testing.T) {
if height == 11 { if height == 11 {
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address) vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
// require that missing sign count is set back to zero after the period increments. // require that missing sign count is set back to zero after the period increments.
require.Equal(t, []int64{0, 0}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{0, 0}, vva.CurrentPeriodProgress)
} }
} }
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address) vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
// t.Log(vva.MarshalYAML()) // t.Log(vva.MarshalYAML())
require.Equal(t, [][]int{[]int{1, 1}, []int{0, 0}, []int{0, 0}}, vva.VestingPeriodProgress) require.Equal(t, []types.VestingProgress{types.VestingProgress{true, true}, types.VestingProgress{false, false}, types.VestingProgress{false, false}}, vva.VestingPeriodProgress)
} }
func TestBeginBlockerUnsuccessfulPeriod(t *testing.T) { func TestBeginBlockerUnsuccessfulPeriod(t *testing.T) {
@ -175,12 +176,15 @@ func TestBeginBlockerUnsuccessfulPeriod(t *testing.T) {
numBlocks := int64(12) numBlocks := int64(12)
addHour := func(t time.Time) time.Time { return t.Add(1 * time.Hour) } addHour := func(t time.Time) time.Time { return t.Add(1 * time.Hour) }
ctx, ak, _, stakingKeeper, _, vvk := keeper.CreateTestInput(t, false, 1000) ctx, ak, _, stakingKeeper, supplyKeeper, vvk := keeper.CreateTestInput(t, false, 1000)
initialSupply := supplyKeeper.GetSupply(ctx).GetTotal()
keeper.CreateValidators(ctx, stakingKeeper, []int64{5, 5, 5}) keeper.CreateValidators(ctx, stakingKeeper, []int64{5, 5, 5})
vva := keeper.ValidatorVestingDelegatorTestAccount(now) vva := keeper.ValidatorVestingDelegatorTestAccount(now)
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
// delegate all coins
delTokens := sdk.TokensFromConsensusPower(60) delTokens := sdk.TokensFromConsensusPower(60)
vvk.SetValidatorVestingAccountKey(ctx, vva.Address) vvk.SetValidatorVestingAccountKey(ctx, vva.Address)
@ -223,7 +227,7 @@ func TestBeginBlockerUnsuccessfulPeriod(t *testing.T) {
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address) vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
// check that the period was unsucessful // check that the period was unsucessful
require.Equal(t, [][]int{[]int{1, 0}, []int{0, 0}, []int{0, 0}}, vva.VestingPeriodProgress) require.Equal(t, []types.VestingProgress{types.VestingProgress{true, false}, types.VestingProgress{false, false}, types.VestingProgress{false, false}}, vva.VestingPeriodProgress)
// check that there is debt after the period. // check that there is debt after the period.
require.Equal(t, sdk.Coins{sdk.NewInt64Coin("stake", 30000000)}, vva.DebtAfterFailedVesting) require.Equal(t, sdk.Coins{sdk.NewInt64Coin("stake", 30000000)}, vva.DebtAfterFailedVesting)
@ -234,4 +238,38 @@ func TestBeginBlockerUnsuccessfulPeriod(t *testing.T) {
}) })
// require that all delegations were unbonded // require that all delegations were unbonded
require.Equal(t, 0, delegations) require.Equal(t, 0, delegations)
// complete the unbonding period
header := abci.Header{Height: height, Time: blockTime.Add(time.Hour * 2)}
req := abci.RequestBeginBlock{
Header: header,
LastCommitInfo: abci.LastCommitInfo{
Votes: []abci.VoteInfo{{
Validator: val,
SignedLastBlock: false,
}},
},
}
ctx = ctx.WithBlockHeader(header)
BeginBlocker(ctx, req, vvk)
_ = staking.EndBlocker(ctx, stakingKeeper)
header = abci.Header{Height: height, Time: blockTime.Add(time.Hour * 2)}
req = abci.RequestBeginBlock{
Header: header,
LastCommitInfo: abci.LastCommitInfo{
Votes: []abci.VoteInfo{{
Validator: val,
SignedLastBlock: false,
}},
},
}
ctx = ctx.WithBlockHeader(header)
BeginBlocker(ctx, req, vvk)
vva = vvk.GetAccountFromAuthKeeper(ctx, vva.Address)
// require that debt has reset to zero and coins balance is reduced by period 1 amount.
require.Equal(t, vva.GetCoins(), sdk.Coins{sdk.NewInt64Coin("stake", 30000000)})
require.Equal(t, sdk.Coins(nil), vva.DebtAfterFailedVesting)
// require that the supply has decreased by period 1 amount
require.Equal(t, initialSupply.Sub(vva.VestingPeriods[0].Amount), supplyKeeper.GetSupply(ctx).GetTotal())
} }

View File

@ -83,7 +83,7 @@ func (k Keeper) IterateAccountKeys(ctx sdk.Context, cb func(accountKey []byte) (
func (k Keeper) GetAllAccountKeys(ctx sdk.Context) (keys [][]byte) { func (k Keeper) GetAllAccountKeys(ctx sdk.Context) (keys [][]byte) {
k.IterateAccountKeys(ctx, k.IterateAccountKeys(ctx,
func(key []byte) (stop bool) { func(key []byte) (stop bool) {
keys = append(keys, key) keys = append(keys, key[1:])
return false return false
}) })
return keys return keys
@ -103,39 +103,33 @@ func (k Keeper) GetAccountFromAuthKeeper(ctx sdk.Context, addr sdk.AccAddress) *
func (k Keeper) UpdateMissingSignCount(ctx sdk.Context, addr sdk.AccAddress, missedBlock bool) { func (k Keeper) UpdateMissingSignCount(ctx sdk.Context, addr sdk.AccAddress, missedBlock bool) {
vv := k.GetAccountFromAuthKeeper(ctx, addr) vv := k.GetAccountFromAuthKeeper(ctx, addr)
if missedBlock { if missedBlock {
vv.MissingSignCount[0]++ vv.CurrentPeriodProgress.MissedBlocks++
} }
vv.MissingSignCount[1]++ vv.CurrentPeriodProgress.TotalBlocks++
k.ak.SetAccount(ctx, vv) k.ak.SetAccount(ctx, vv)
} }
// UpdateVestedCoinsProgress sets the VestingPeriodProgress variable (0 = coins did not vest for the period, 1 = coins did vest for the period) for the given address and period. If coins did not vest, those coins are added to DebtAfterFailedVesting. Finally, MissingSignCount is reset to [0,0], representing that the next period has started and no blocks have been missed. // UpdateVestedCoinsProgress sets the VestingPeriodProgress variable (0 = coins did not vest for the period, 1 = coins did vest for the period) for the given address and period. If coins did not vest, those coins are added to DebtAfterFailedVesting. Finally, MissingSignCount is reset to [0,0], representing that the next period has started and no blocks have been missed.
func (k Keeper) UpdateVestedCoinsProgress(ctx sdk.Context, addr sdk.AccAddress, period int) { func (k Keeper) UpdateVestedCoinsProgress(ctx sdk.Context, addr sdk.AccAddress, period int) {
vv := k.GetAccountFromAuthKeeper(ctx, addr) vv := k.GetAccountFromAuthKeeper(ctx, addr)
threshold := sdk.NewDec(vv.SigningThreshold)
blocksMissed := sdk.NewDec(vv.MissingSignCount[0])
blockCount := sdk.NewDec(vv.MissingSignCount[1])
var successfulVest bool var successfulVest bool
if blockCount.IsZero() { if sdk.NewDec(vv.CurrentPeriodProgress.TotalBlocks).IsZero() {
successfulVest = true successfulVest = true
} else { } else {
blocksSigned := blockCount.Sub(blocksMissed) successfulVest = vv.CurrentPeriodProgress.SignedPercetageIsOverThreshold(vv.SigningThreshold)
percentageBlocksSigned := blocksSigned.Quo(blockCount).Mul(sdk.NewDec(100))
successfulVest = percentageBlocksSigned.GTE(threshold)
} }
if successfulVest { if successfulVest {
vv.VestingPeriodProgress[period][1] = 1 vv.VestingPeriodProgress[period].VestingSuccessful = true
} else { } else {
vv.VestingPeriodProgress[period][1] = 0 vv.VestingPeriodProgress[period].VestingSuccessful = false
notVestedTokens := vv.VestingPeriods[period].Amount notVestedTokens := vv.VestingPeriods[period].Amount
// add the tokens that did not vest to DebtAfterFailedVesting // add the tokens that did not vest to DebtAfterFailedVesting
vv.DebtAfterFailedVesting = vv.DebtAfterFailedVesting.Add(notVestedTokens) vv.DebtAfterFailedVesting = vv.DebtAfterFailedVesting.Add(notVestedTokens)
} }
vv.VestingPeriodProgress[period][0] = 1 vv.VestingPeriodProgress[period].PeriodComplete = true
// reset the number of missed blocks and total number of blocks in the period to zero // reset the number of missed blocks and total number of blocks in the period to zero
vv.MissingSignCount = []int64{0, 0} vv.CurrentPeriodProgress = types.CurrentPeriodProgress{0, 0}
k.ak.SetAccount(ctx, vv) k.ak.SetAccount(ctx, vv)
} }

View File

@ -11,6 +11,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking"
stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported" stakingexported "github.com/cosmos/cosmos-sdk/x/staking/exported"
"github.com/kava-labs/kava/x/validator-vesting/internal/types"
) )
func TestGetSetValidatorVestingAccounts(t *testing.T) { func TestGetSetValidatorVestingAccounts(t *testing.T) {
@ -36,7 +37,7 @@ func TestGetSetValidatorVestingAccounts(t *testing.T) {
keys := keeper.GetAllAccountKeys(ctx) keys := keeper.GetAllAccountKeys(ctx)
require.Equal(t, 1, len(keys)) require.Equal(t, 1, len(keys))
for _, k := range keys { for _, k := range keys {
require.NotPanics(t, func() { keeper.GetAccountFromAuthKeeper(ctx, k[1:]) }) require.NotPanics(t, func() { keeper.GetAccountFromAuthKeeper(ctx, k) })
} }
vvAccounts := ValidatorVestingTestAccounts(10) vvAccounts := ValidatorVestingTestAccounts(10)
@ -50,7 +51,7 @@ func TestGetSetValidatorVestingAccounts(t *testing.T) {
var ikeys [][]byte var ikeys [][]byte
keeper.IterateAccountKeys(ctx, func(accountKey []byte) bool { keeper.IterateAccountKeys(ctx, func(accountKey []byte) bool {
if bytes.Equal(accountKey, keys[0]) { if bytes.Equal(accountKey[1:], keys[0]) {
ikeys = append(ikeys, accountKey) ikeys = append(ikeys, accountKey)
return true return true
} }
@ -109,17 +110,17 @@ func TestSetMissingSignCount(t *testing.T) {
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
// require empty array after ValidatorVestingAccount is initialized // require empty array after ValidatorVestingAccount is initialized
require.Equal(t, []int64{0, 0}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{0, 0}, vva.CurrentPeriodProgress)
// validator signs a block // validator signs a block
keeper.UpdateMissingSignCount(ctx, vva.Address, false) keeper.UpdateMissingSignCount(ctx, vva.Address, false)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
require.Equal(t, []int64{0, 1}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{0, 1}, vva.CurrentPeriodProgress)
// validator misses a block // validator misses a block
keeper.UpdateMissingSignCount(ctx, vva.Address, true) keeper.UpdateMissingSignCount(ctx, vva.Address, true)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
require.Equal(t, []int64{1, 2}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{1, 2}, vva.CurrentPeriodProgress)
} }
@ -132,56 +133,56 @@ func TestUpdateVestedCoinsProgress(t *testing.T) {
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
// require all vesting period tracking variables to be zero after validator vesting account is initialized // require all vesting period tracking variables to be zero after validator vesting account is initialized
require.Equal(t, [][]int{{0, 0}, {0, 0}, {0, 0}}, vva.VestingPeriodProgress) require.Equal(t, []types.VestingProgress{types.VestingProgress{false, false}, types.VestingProgress{false, false}, types.VestingProgress{false, false}}, vva.VestingPeriodProgress)
// period 0 passes with all blocks signed // period 0 passes with all blocks signed
vva.MissingSignCount[0] = 0 vva.CurrentPeriodProgress.MissedBlocks = 0
vva.MissingSignCount[1] = 100 vva.CurrentPeriodProgress.TotalBlocks = 100
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0) keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
// require that debt is zero // require that debt is zero
require.Equal(t, sdk.Coins(nil), vva.DebtAfterFailedVesting) require.Equal(t, sdk.Coins(nil), vva.DebtAfterFailedVesting)
// require that the first vesting progress variable is 1 // require that the first vesting progress variable is successful
require.Equal(t, [][]int{{1, 1}, {0, 0}, {0, 0}}, vva.VestingPeriodProgress) require.Equal(t, []types.VestingProgress{types.VestingProgress{true, true}, types.VestingProgress{false, false}, types.VestingProgress{false, false}}, vva.VestingPeriodProgress)
// require that the missing block counter has reset // require that the missing block counter has reset
require.Equal(t, []int64{0, 0}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{0, 0}, vva.CurrentPeriodProgress)
vva = ValidatorVestingTestAccount() vva = ValidatorVestingTestAccount()
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
// period 0 passes with no blocks signed // period 0 passes with no blocks signed
// this is an edge case that shouldn't happen, // this is an edge case that shouldn't happen,
// the vest is considered successful in this case. // the vest is considered successful in this case.
vva.MissingSignCount[0] = 0 vva.CurrentPeriodProgress.MissedBlocks = 0
vva.MissingSignCount[1] = 0 vva.CurrentPeriodProgress.TotalBlocks = 0
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0) keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
// require that debt is zero // require that debt is zero
require.Equal(t, sdk.Coins(nil), vva.DebtAfterFailedVesting) require.Equal(t, sdk.Coins(nil), vva.DebtAfterFailedVesting)
// require that the first vesting progress variable is 1 // require that the first vesting progress variable is successful
require.Equal(t, [][]int{{1, 1}, {0, 0}, {0, 0}}, vva.VestingPeriodProgress) require.Equal(t, []types.VestingProgress{types.VestingProgress{true, true}, types.VestingProgress{false, false}, types.VestingProgress{false, false}}, vva.VestingPeriodProgress)
// require that the missing block counter has reset // require that the missing block counter has reset
require.Equal(t, []int64{0, 0}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{0, 0}, vva.CurrentPeriodProgress)
vva = ValidatorVestingTestAccount() vva = ValidatorVestingTestAccount()
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
// period 0 passes with 50% of blocks signed (below threshold) // period 0 passes with 50% of blocks signed (below threshold)
vva.MissingSignCount[0] = 50 vva.CurrentPeriodProgress.MissedBlocks = 50
vva.MissingSignCount[1] = 100 vva.CurrentPeriodProgress.TotalBlocks = 100
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0) keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
// require that period 1 coins have become debt // require that period 1 coins have become debt
require.Equal(t, sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)), vva.DebtAfterFailedVesting) require.Equal(t, sdk.NewCoins(sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)), vva.DebtAfterFailedVesting)
// require that the first vesting progress variable is {1,0} // require that the first vesting progress variable is {true, false}
require.Equal(t, [][]int{{1, 0}, {0, 0}, {0, 0}}, vva.VestingPeriodProgress) require.Equal(t, []types.VestingProgress{types.VestingProgress{true, false}, types.VestingProgress{false, false}, types.VestingProgress{false, false}}, vva.VestingPeriodProgress)
// require that the missing block counter has reset // require that the missing block counter has reset
require.Equal(t, []int64{0, 0}, vva.MissingSignCount) require.Equal(t, types.CurrentPeriodProgress{0, 0}, vva.CurrentPeriodProgress)
} }
func TestHandleVestingDebtNoDebt(t *testing.T) { func TestHandleVestingDebtNoDebt(t *testing.T) {
@ -240,8 +241,8 @@ func TestHandleVestingDebtForcedUnbond(t *testing.T) {
require.Equal(t, 1, delegations) require.Equal(t, 1, delegations)
// period 0 passes and the threshold is not met // period 0 passes and the threshold is not met
vva.MissingSignCount[0] = 50 vva.CurrentPeriodProgress.MissedBlocks = 50
vva.MissingSignCount[1] = 100 vva.CurrentPeriodProgress.TotalBlocks = 100
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0) keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
@ -277,8 +278,8 @@ func TestHandleVestingDebtBurn(t *testing.T) {
_ = staking.EndBlocker(ctx, stakingKeeper) _ = staking.EndBlocker(ctx, stakingKeeper)
// period 0 passes and the threshold is not met // period 0 passes and the threshold is not met
vva.MissingSignCount[0] = 50 vva.CurrentPeriodProgress.MissedBlocks = 50
vva.MissingSignCount[1] = 100 vva.CurrentPeriodProgress.TotalBlocks = 100
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0) keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)
@ -320,8 +321,8 @@ func TestHandleVestingDebtReturn(t *testing.T) {
_ = staking.EndBlocker(ctx, stakingKeeper) _ = staking.EndBlocker(ctx, stakingKeeper)
// period 0 passes and the threshold is not met // period 0 passes and the threshold is not met
vva.MissingSignCount[0] = 50 vva.CurrentPeriodProgress.MissedBlocks = 50
vva.MissingSignCount[1] = 100 vva.CurrentPeriodProgress.TotalBlocks = 100
ak.SetAccount(ctx, vva) ak.SetAccount(ctx, vva)
keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0) keeper.UpdateVestedCoinsProgress(ctx, vva.Address, 0)
vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address) vva = keeper.GetAccountFromAuthKeeper(ctx, vva.Address)

View File

@ -122,6 +122,8 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context
pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) pk := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace)
stakingParams := staking.NewParams(time.Hour, 100, uint16(7), sdk.DefaultBondDenom)
accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount)
bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs) bankKeeper := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace, blacklistedAddrs)
maccPerms := map[string][]string{ maccPerms := map[string][]string{
@ -133,7 +135,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context
supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms) supplyKeeper := supply.NewKeeper(cdc, keySupply, accountKeeper, bankKeeper, maccPerms)
stakingKeeper := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) stakingKeeper := staking.NewKeeper(cdc, keyStaking, supplyKeeper, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace)
stakingKeeper.SetParams(ctx, staking.DefaultParams()) stakingKeeper.SetParams(ctx, stakingParams)
keeper := NewKeeper(cdc, keyValidatorVesting, accountKeeper, bankKeeper, supplyKeeper, stakingKeeper) keeper := NewKeeper(cdc, keyValidatorVesting, accountKeeper, bankKeeper, supplyKeeper, stakingKeeper)

View File

@ -23,6 +23,30 @@ func init() {
authtypes.RegisterAccountTypeCodec(&ValidatorVestingAccount{}, "cosmos-sdk/ValidatorVestingAccount") authtypes.RegisterAccountTypeCodec(&ValidatorVestingAccount{}, "cosmos-sdk/ValidatorVestingAccount")
} }
type VestingProgress struct {
PeriodComplete bool `json:"period_complete" yaml:"period_complete"`
VestingSuccessful bool `json:"vesting_successful" yaml:"vesting_successful"`
}
type CurrentPeriodProgress struct {
MissedBlocks int64 `json:"missed_blocks" yaml:"missed_blocks"`
TotalBlocks int64 `json:"total_blocks" yaml:"total_blocks`
}
func (cpp CurrentPeriodProgress) GetSignedPercentage() sdk.Dec {
blocksSigned := cpp.TotalBlocks - cpp.MissedBlocks
// signed_percentage = blocksSigned/TotalBlocks * 100
signedPercentage := sdk.NewDec(blocksSigned).Quo(
sdk.NewDec(cpp.TotalBlocks)).Mul(
sdk.NewDec(100))
return signedPercentage
}
func (cpp CurrentPeriodProgress) SignedPercetageIsOverThreshold(threshold int64) bool {
signedPercentage := cpp.GetSignedPercentage()
return signedPercentage.GTE(sdk.NewDec(threshold))
}
// ValidatorVestingAccount implements the VestingAccount interface. It // ValidatorVestingAccount implements the VestingAccount interface. It
// conditionally vests by unlocking coins during each specified period, provided // conditionally vests by unlocking coins during each specified period, provided
// that the validator address has validated at least **SigningThreshold** blocks during // that the validator address has validated at least **SigningThreshold** blocks during
@ -32,12 +56,12 @@ func init() {
// the coins are returned to the return address, or burned if the return address is null. // the coins are returned to the return address, or burned if the return address is null.
type ValidatorVestingAccount struct { type ValidatorVestingAccount struct {
*vestingtypes.PeriodicVestingAccount *vestingtypes.PeriodicVestingAccount
ValidatorAddress sdk.ConsAddress `json:"validator_address" yaml:"validator_address"` ValidatorAddress sdk.ConsAddress `json:"validator_address" yaml:"validator_address"`
ReturnAddress sdk.AccAddress `json:"return_address" yaml:"return_address"` ReturnAddress sdk.AccAddress `json:"return_address" yaml:"return_address"`
SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"` SigningThreshold int64 `json:"signing_threshold" yaml:"signing_threshold"`
MissingSignCount []int64 `json:"missing_sign_count" yaml:"missing_sign_count"` CurrentPeriodProgress CurrentPeriodProgress `json:"missing_sign_count" yaml:"missing_sign_count"`
VestingPeriodProgress [][]int `json:"vesting_period_progress" yaml:"vesting_period_progress"` VestingPeriodProgress []VestingProgress `json:"vesting_period_progress" yaml:"vesting_period_progress"`
DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"` DebtAfterFailedVesting sdk.Coins `json:"debt_after_failed_vesting" yaml:"debt_after_failed_vesting"`
} }
// NewValidatorVestingAccountRaw creates a new ValidatorVestingAccount object from BaseVestingAccount // NewValidatorVestingAccountRaw creates a new ValidatorVestingAccount object from BaseVestingAccount
@ -49,9 +73,9 @@ func NewValidatorVestingAccountRaw(bva *vestingtypes.BaseVestingAccount,
StartTime: startTime, StartTime: startTime,
VestingPeriods: periods, VestingPeriods: periods,
} }
var vestingPeriodProgress = make([][]int, len(periods)) var vestingPeriodProgress []VestingProgress
for i := range vestingPeriodProgress { for i := 0; i < len(periods); i++ {
vestingPeriodProgress[i] = make([]int, 2) vestingPeriodProgress = append(vestingPeriodProgress, VestingProgress{false, false})
} }
return &ValidatorVestingAccount{ return &ValidatorVestingAccount{
@ -59,7 +83,7 @@ func NewValidatorVestingAccountRaw(bva *vestingtypes.BaseVestingAccount,
ValidatorAddress: validatorAddress, ValidatorAddress: validatorAddress,
ReturnAddress: returnAddress, ReturnAddress: returnAddress,
SigningThreshold: signingThreshold, SigningThreshold: signingThreshold,
MissingSignCount: []int64{0, 0}, CurrentPeriodProgress: CurrentPeriodProgress{0, 0},
VestingPeriodProgress: vestingPeriodProgress, VestingPeriodProgress: vestingPeriodProgress,
DebtAfterFailedVesting: sdk.NewCoins(), DebtAfterFailedVesting: sdk.NewCoins(),
} }
@ -82,21 +106,19 @@ func NewValidatorVestingAccount(baseAcc *authtypes.BaseAccount, startTime int64,
StartTime: startTime, StartTime: startTime,
VestingPeriods: periods, VestingPeriods: periods,
} }
var vestingPeriodProgress = make([][]int, len(periods)) var vestingPeriodProgress []VestingProgress
for i := range vestingPeriodProgress { for i := 0; i < len(periods); i++ {
vestingPeriodProgress[i] = make([]int, 2) vestingPeriodProgress = append(vestingPeriodProgress, VestingProgress{false, false})
} }
debt := sdk.NewCoins()
return &ValidatorVestingAccount{ return &ValidatorVestingAccount{
PeriodicVestingAccount: pva, PeriodicVestingAccount: pva,
ValidatorAddress: validatorAddress, ValidatorAddress: validatorAddress,
ReturnAddress: returnAddress, ReturnAddress: returnAddress,
SigningThreshold: signingThreshold, SigningThreshold: signingThreshold,
MissingSignCount: []int64{0, 0}, CurrentPeriodProgress: CurrentPeriodProgress{0, 0},
VestingPeriodProgress: vestingPeriodProgress, VestingPeriodProgress: vestingPeriodProgress,
DebtAfterFailedVesting: debt, DebtAfterFailedVesting: sdk.NewCoins(),
} }
} }
@ -111,8 +133,7 @@ func (vva ValidatorVestingAccount) GetVestedCoins(blockTime time.Time) sdk.Coins
for i := 0; i < numberPeriods; i++ { for i := 0; i < numberPeriods; i++ {
x := blockTime.Unix() - currentPeriodStartTime x := blockTime.Unix() - currentPeriodStartTime
if x >= vva.VestingPeriods[i].Length { if x >= vva.VestingPeriods[i].Length {
vestingComplete := vva.VestingPeriodProgress[i][0] == 1 if vva.VestingPeriodProgress[i].PeriodComplete {
if vestingComplete {
vestedCoins = vestedCoins.Add(vva.VestingPeriods[i].Amount) vestedCoins = vestedCoins.Add(vva.VestingPeriods[i].Amount)
} }
currentPeriodStartTime += vva.VestingPeriods[i].Length currentPeriodStartTime += vva.VestingPeriods[i].Length
@ -129,9 +150,8 @@ func (vva ValidatorVestingAccount) GetFailedVestedCoins() sdk.Coins {
var failedVestedCoins sdk.Coins var failedVestedCoins sdk.Coins
numberPeriods := len(vva.VestingPeriods) numberPeriods := len(vva.VestingPeriods)
for i := 0; i < numberPeriods; i++ { for i := 0; i < numberPeriods; i++ {
if vva.VestingPeriodProgress[i][0] == 1 { if vva.VestingPeriodProgress[i].PeriodComplete {
vestedFailure := vva.VestingPeriodProgress[i][1] == 0 if !vva.VestingPeriodProgress[i].VestingSuccessful {
if vestedFailure {
failedVestedCoins = failedVestedCoins.Add(vva.VestingPeriods[i].Amount) failedVestedCoins = failedVestedCoins.Add(vva.VestingPeriods[i].Amount)
} }
} else { } else {
@ -198,8 +218,8 @@ func (vva ValidatorVestingAccount) MarshalYAML() (interface{}, error) {
ValidatorAddress sdk.ConsAddress ValidatorAddress sdk.ConsAddress
ReturnAddress sdk.AccAddress ReturnAddress sdk.AccAddress
SigningThreshold int64 SigningThreshold int64
MissingSignCount []int64 CurrentPeriodProgress CurrentPeriodProgress
VestingPeriodProgress [][]int VestingPeriodProgress []VestingProgress
DebtAfterFailedVesting sdk.Coins DebtAfterFailedVesting sdk.Coins
}{ }{
Address: vva.Address, Address: vva.Address,
@ -216,7 +236,7 @@ func (vva ValidatorVestingAccount) MarshalYAML() (interface{}, error) {
ValidatorAddress: vva.ValidatorAddress, ValidatorAddress: vva.ValidatorAddress,
ReturnAddress: vva.ReturnAddress, ReturnAddress: vva.ReturnAddress,
SigningThreshold: vva.SigningThreshold, SigningThreshold: vva.SigningThreshold,
MissingSignCount: vva.MissingSignCount, CurrentPeriodProgress: vva.CurrentPeriodProgress,
VestingPeriodProgress: vva.VestingPeriodProgress, VestingPeriodProgress: vva.VestingPeriodProgress,
DebtAfterFailedVesting: vva.DebtAfterFailedVesting, DebtAfterFailedVesting: vva.DebtAfterFailedVesting,
}) })

View File

@ -68,51 +68,51 @@ func TestGetVestedCoinsValidatorVestingAcc(t *testing.T) {
require.Nil(t, vestedCoins) require.Nil(t, vestedCoins)
// require 50% of coins vested after successful period 1 vesting // require 50% of coins vested after successful period 1 vesting
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vestedCoins = vva.GetVestedCoins(now.Add(12 * time.Hour)) vestedCoins = vva.GetVestedCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins)
// require 50% of coins vested after unsuccessful period 1 vesting // require 50% of coins vested after unsuccessful period 1 vesting
// NOTE: There is a fairly important semantic distinction here. It seems tempting to say that a failed vesting period should mean that 'GetVestedCoins' should not return those coins. While the point of a validator vesting account is to 'seize' or 'burn' unsuccessfully vested coins, they do in fact vest and become spendable. The intuition is that they have to be spendable in order for the bank keeper to allow us to send/burn them. If they were not vested, then a validator vesting account that failed all of it's vesting periods would never return/burn the coins because it would never have a spendable balance by which to do so. They way we prevent them from being spent in a way other than return/burn is by sending them in the BeginBlock and thus beating any other transfers that would otherwise occur. // NOTE: There is a fairly important semantic distinction here. It seems tempting to say that a failed vesting period should mean that 'GetVestedCoins' should not return those coins. While the point of a validator vesting account is to 'seize' or 'burn' unsuccessfully vested coins, they do in fact vest and become spendable. The intuition is that they have to be spendable in order for the bank keeper to allow us to send/burn them. If they were not vested, then a validator vesting account that failed all of it's vesting periods would never return/burn the coins because it would never have a spendable balance by which to do so. They way we prevent them from being spent in a way other than return/burn is by sending them in the BeginBlock and thus beating any other transfers that would otherwise occur.
vva.VestingPeriodProgress[0] = []int{1, 0} vva.VestingPeriodProgress[0] = VestingProgress{true, false}
vestedCoins = vva.GetVestedCoins(now.Add(12 * time.Hour)) vestedCoins = vva.GetVestedCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins)
// require period 2 coins don't vest until period is over // require period 2 coins don't vest until period is over
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
// even if the vesting period was somehow successful, should still only return 50% of coins as vested, since the second vesting period hasn't completed. // even if the vesting period was somehow successful, should still only return 50% of coins as vested, since the second vesting period hasn't completed.
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vestedCoins = vva.GetVestedCoins(now.Add(15 * time.Hour)) vestedCoins = vva.GetVestedCoins(now.Add(15 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins)
// require 75% of coins vested after successful period 2 // require 75% of coins vested after successful period 2
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vestedCoins = vva.GetVestedCoins(now.Add(18 * time.Hour)) vestedCoins = vva.GetVestedCoins(now.Add(18 * time.Hour))
require.Equal(t, require.Equal(t,
sdk.Coins{ sdk.Coins{
sdk.NewInt64Coin(feeDenom, 750), sdk.NewInt64Coin(stakeDenom, 75)}, vestedCoins) sdk.NewInt64Coin(feeDenom, 750), sdk.NewInt64Coin(stakeDenom, 75)}, vestedCoins)
// require 75% of coins vested after successful period 1 and unsuccessful period 2. // require 75% of coins vested after successful period 1 and unsuccessful period 2.
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 0} vva.VestingPeriodProgress[1] = VestingProgress{true, false}
vestedCoins = vva.GetVestedCoins(now.Add(18 * time.Hour)) vestedCoins = vva.GetVestedCoins(now.Add(18 * time.Hour))
require.Equal(t, require.Equal(t,
sdk.Coins{ sdk.Coins{
sdk.NewInt64Coin(feeDenom, 750), sdk.NewInt64Coin(stakeDenom, 75)}, vestedCoins) sdk.NewInt64Coin(feeDenom, 750), sdk.NewInt64Coin(stakeDenom, 75)}, vestedCoins)
// require 100% of coins vested after all periods complete successfully // require 100% of coins vested after all periods complete successfully
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vva.VestingPeriodProgress[2] = []int{1, 1} vva.VestingPeriodProgress[2] = VestingProgress{true, true}
vestedCoins = vva.GetVestedCoins(now.Add(48 * time.Hour)) vestedCoins = vva.GetVestedCoins(now.Add(48 * time.Hour))
require.Equal(t, origCoins, vestedCoins) require.Equal(t, origCoins, vestedCoins)
// require 100% of coins vested after all periods complete unsuccessfully // require 100% of coins vested after all periods complete unsuccessfully
vva.VestingPeriodProgress[0] = []int{1, 0} vva.VestingPeriodProgress[0] = VestingProgress{true, false}
vva.VestingPeriodProgress[1] = []int{1, 0} vva.VestingPeriodProgress[1] = VestingProgress{true, false}
vva.VestingPeriodProgress[2] = []int{1, 0} vva.VestingPeriodProgress[2] = VestingProgress{true, false}
vestedCoins = vva.GetVestedCoins(now.Add(48 * time.Hour)) vestedCoins = vva.GetVestedCoins(now.Add(48 * time.Hour))
require.Equal(t, origCoins, vestedCoins) require.Equal(t, origCoins, vestedCoins)
@ -143,48 +143,48 @@ func TestGetVestingCoinsValidatorVestingAcc(t *testing.T) {
require.Equal(t, origCoins, vestingCoins) require.Equal(t, origCoins, vestingCoins)
// require 50% of coins vesting after successful period 1 vesting // require 50% of coins vesting after successful period 1 vesting
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vestingCoins = vva.GetVestingCoins(now.Add(12 * time.Hour)) vestingCoins = vva.GetVestingCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins)
// require 50% of coins vesting after unsuccessful period 1 vesting // require 50% of coins vesting after unsuccessful period 1 vesting
vva.VestingPeriodProgress[0] = []int{1, 0} vva.VestingPeriodProgress[0] = VestingProgress{true, false}
vestingCoins = vva.GetVestingCoins(now.Add(12 * time.Hour)) vestingCoins = vva.GetVestingCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins)
// require period 2 coins still vesting until period is over // require period 2 coins still vesting until period is over
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
// should never happen, but still won't affect vesting balance // should never happen, but still won't affect vesting balance
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vestingCoins = vva.GetVestingCoins(now.Add(15 * time.Hour)) vestingCoins = vva.GetVestingCoins(now.Add(15 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins)
// require 25% of coins vesting after successful period 2 // require 25% of coins vesting after successful period 2
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vestingCoins = vva.GetVestingCoins(now.Add(18 * time.Hour)) vestingCoins = vva.GetVestingCoins(now.Add(18 * time.Hour))
require.Equal(t, require.Equal(t,
sdk.Coins{ sdk.Coins{
sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}, vestingCoins) sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}, vestingCoins)
// require 25% of coins vesting after successful period 1 and unsuccessful period 2 // require 25% of coins vesting after successful period 1 and unsuccessful period 2
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, false}
vestingCoins = vva.GetVestingCoins(now.Add(18 * time.Hour)) vestingCoins = vva.GetVestingCoins(now.Add(18 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}, vestingCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 250), sdk.NewInt64Coin(stakeDenom, 25)}, vestingCoins)
// require no coins vesting after all periods complete successfully // require no coins vesting after all periods complete successfully
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vva.VestingPeriodProgress[2] = []int{1, 1} vva.VestingPeriodProgress[2] = VestingProgress{true, true}
vestingCoins = vva.GetVestingCoins(now.Add(48 * time.Hour)) vestingCoins = vva.GetVestingCoins(now.Add(48 * time.Hour))
require.Nil(t, vestingCoins) require.Nil(t, vestingCoins)
// require no coins vesting after all periods complete unsuccessfully // require no coins vesting after all periods complete unsuccessfully
vva.VestingPeriodProgress[0] = []int{1, 0} vva.VestingPeriodProgress[0] = VestingProgress{true, false}
vva.VestingPeriodProgress[1] = []int{1, 0} vva.VestingPeriodProgress[1] = VestingProgress{true, false}
vva.VestingPeriodProgress[2] = []int{1, 0} vva.VestingPeriodProgress[2] = VestingProgress{true, false}
vestingCoins = vva.GetVestingCoins(now.Add(48 * time.Hour)) vestingCoins = vva.GetVestingCoins(now.Add(48 * time.Hour))
require.Nil(t, vestingCoins) require.Nil(t, vestingCoins)
@ -211,12 +211,12 @@ func TestSpendableCoinsValidatorVestingAccount(t *testing.T) {
require.Nil(t, spendableCoins) require.Nil(t, spendableCoins)
// require that all vested coins (50%) are spendable when period 1 completes successfully // require that all vested coins (50%) are spendable when period 1 completes successfully
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
spendableCoins = vva.SpendableCoins(now.Add(12 * time.Hour)) spendableCoins = vva.SpendableCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins)
// require that 50% of coins are spendable after period 1 completes unsuccessfully. See note above. The reason the coins are still 'spendable' is that we need to be able to transfer the coins to the return address/burn them. Making them not spendable means that it would be impossible to recover the debt for a validator vesting account for which all periods failed. // require that 50% of coins are spendable after period 1 completes unsuccessfully. See note above. The reason the coins are still 'spendable' is that we need to be able to transfer the coins to the return address/burn them. Making them not spendable means that it would be impossible to recover the debt for a validator vesting account for which all periods failed.
vva.VestingPeriodProgress[0] = []int{1, 0} vva.VestingPeriodProgress[0] = VestingProgress{true, false}
spendableCoins = vva.SpendableCoins(now.Add(12 * time.Hour)) spendableCoins = vva.SpendableCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins)
@ -225,7 +225,7 @@ func TestSpendableCoinsValidatorVestingAccount(t *testing.T) {
vva.SetCoins(vva.GetCoins().Add(recvAmt)) vva.SetCoins(vva.GetCoins().Add(recvAmt))
// require that all vested coins (50%) are spendable plus any received after period 1 completes successfully // require that all vested coins (50%) are spendable plus any received after period 1 completes successfully
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
spendableCoins = vva.SpendableCoins(now.Add(12 * time.Hour)) spendableCoins = vva.SpendableCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 100)}, spendableCoins) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 100)}, spendableCoins)
@ -253,14 +253,14 @@ func TestGetFailedVestedCoins(t *testing.T) {
bacc.SetCoins(origCoins) bacc.SetCoins(origCoins)
vva := NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90) vva := NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90)
vva.VestingPeriodProgress[0] = []int{1, 0} vva.VestingPeriodProgress[0] = VestingProgress{true, false}
// require that period 1 coins are failed if the period completed unsucessfully. // require that period 1 coins are failed if the period completed unsucessfully.
require.Equal(t, require.Equal(t,
sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)},
vva.GetFailedVestedCoins(), vva.GetFailedVestedCoins(),
) )
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
require.Equal(t, require.Equal(t,
sdk.Coins(nil), sdk.Coins(nil),
vva.GetFailedVestedCoins(), vva.GetFailedVestedCoins(),
@ -290,9 +290,9 @@ func TestTrackDelegationValidatorVestingAcc(t *testing.T) {
// all periods pass successfully // all periods pass successfully
bacc.SetCoins(origCoins) bacc.SetCoins(origCoins)
vva = NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90) vva = NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90)
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vva.VestingPeriodProgress[2] = []int{1, 1} vva.VestingPeriodProgress[2] = VestingProgress{true, true}
vva.TrackDelegation(now.Add(48*time.Hour), origCoins) vva.TrackDelegation(now.Add(48*time.Hour), origCoins)
// require all delegated coins are free // require all delegated coins are free
require.Equal(t, origCoins, vva.DelegatedFree) require.Equal(t, origCoins, vva.DelegatedFree)
@ -305,7 +305,7 @@ func TestTrackDelegationValidatorVestingAcc(t *testing.T) {
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, vva.DelegatedVesting) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, vva.DelegatedVesting)
require.Nil(t, vva.DelegatedFree) require.Nil(t, vva.DelegatedFree)
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) vva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, vva.DelegatedVesting) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, vva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, vva.DelegatedFree) require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, vva.DelegatedFree)
@ -345,9 +345,9 @@ func TestTrackUndelegationPeriodicVestingAcc(t *testing.T) {
// require the ability to delegate all coins after they have successfully vested // require the ability to delegate all coins after they have successfully vested
bacc.SetCoins(origCoins) bacc.SetCoins(origCoins)
vva = NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90) vva = NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90)
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.VestingPeriodProgress[1] = []int{1, 1} vva.VestingPeriodProgress[1] = VestingProgress{true, true}
vva.VestingPeriodProgress[2] = []int{1, 1} vva.VestingPeriodProgress[2] = VestingProgress{true, true}
vva.TrackDelegation(now.Add(24*time.Hour), origCoins) vva.TrackDelegation(now.Add(24*time.Hour), origCoins)
vva.TrackUndelegation(origCoins) vva.TrackUndelegation(origCoins)
require.Nil(t, vva.DelegatedFree) require.Nil(t, vva.DelegatedFree)
@ -364,7 +364,7 @@ func TestTrackUndelegationPeriodicVestingAcc(t *testing.T) {
// successfuly vest period 1 and delegate to two validators // successfuly vest period 1 and delegate to two validators
vva = NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90) vva = NewValidatorVestingAccount(&bacc, now.Unix(), periods, testConsAddr, nil, 90)
vva.VestingPeriodProgress[0] = []int{1, 1} vva.VestingPeriodProgress[0] = VestingProgress{true, true}
vva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) vva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
vva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) vva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})

View File

@ -14,7 +14,7 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
voteInfos = req.LastCommitInfo.GetVotes() voteInfos = req.LastCommitInfo.GetVotes()
validatorVestingKeys := k.GetAllAccountKeys(ctx) validatorVestingKeys := k.GetAllAccountKeys(ctx)
for _, key := range validatorVestingKeys { for _, key := range validatorVestingKeys {
acc := k.GetAccountFromAuthKeeper(ctx, key[1:]) acc := k.GetAccountFromAuthKeeper(ctx, key)
if voteInfos.ContainsValidatorAddress(acc.ValidatorAddress) { if voteInfos.ContainsValidatorAddress(acc.ValidatorAddress) {
vote := voteInfos.MustFilterByValidatorAddress(acc.ValidatorAddress) vote := voteInfos.MustFilterByValidatorAddress(acc.ValidatorAddress)
if !vote.SignedLastBlock { if !vote.SignedLastBlock {
@ -29,15 +29,15 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
} }
// check if a period ended in the last block // check if a period ended in the last block
endTimes := k.GetPeriodEndTimes(ctx, key[1:]) endTimes := k.GetPeriodEndTimes(ctx, key)
for i, t := range endTimes { for i, t := range endTimes {
if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t { if currentBlockTime.Unix() >= t && previousBlockTime.Unix() < t {
k.UpdateVestedCoinsProgress(ctx, key[1:], i) k.UpdateVestedCoinsProgress(ctx, key, i)
} }
} }
// handle any new/remaining debt on the account // handle any new/remaining debt on the account
k.HandleVestingDebt(ctx, key[1:], currentBlockTime) k.HandleVestingDebt(ctx, key, currentBlockTime)
} }
k.SetPreviousBlockTime(ctx, currentBlockTime) k.SetPreviousBlockTime(ctx, currentBlockTime)
} }