Merge PR #3486: Checking Vesting Coins with AmountOf

This commit is contained in:
Zaki Manian 2019-02-04 13:10:06 -08:00 committed by Jack Zampolin
parent 9fb774832c
commit 3780b84290
3 changed files with 56 additions and 97 deletions

View File

@ -82,6 +82,8 @@ BUG FIXES
passed on the command line.
* Gaia
* [\#3486](https://github.com/cosmos/cosmos-sdk/pull/3486) Use AmountOf in
vesting accounts instead of zipping/aligning denominations.
* SDK

View File

@ -221,27 +221,11 @@ func (bva BaseVestingAccount) spendableCoins(vestingCoins sdk.Coins) sdk.Coins {
var spendableCoins sdk.Coins
bc := bva.GetCoins()
j, k := 0, 0
for _, coin := range bc {
// zip/lineup all coins by their denomination to provide O(n) time
for j < len(vestingCoins) && vestingCoins[j].Denom != coin.Denom {
j++
}
for k < len(bva.DelegatedVesting) && bva.DelegatedVesting[k].Denom != coin.Denom {
k++
}
baseAmt := coin.Amount
vestingAmt := sdk.ZeroInt()
if len(vestingCoins) > 0 {
vestingAmt = vestingCoins[j].Amount
}
delVestingAmt := sdk.ZeroInt()
if len(bva.DelegatedVesting) > 0 {
delVestingAmt = bva.DelegatedVesting[k].Amount
}
vestingAmt := vestingCoins.AmountOf(coin.Denom)
delVestingAmt := bva.DelegatedVesting.AmountOf(coin.Denom)
// compute min((BC + DV) - V, BC) per the specification
min := sdk.MinInt(baseAmt.Add(delVestingAmt).Sub(vestingAmt), baseAmt)
@ -264,33 +248,12 @@ func (bva BaseVestingAccount) spendableCoins(vestingCoins sdk.Coins) sdk.Coins {
func (bva *BaseVestingAccount) trackDelegation(vestingCoins, amount sdk.Coins) {
bc := bva.GetCoins()
i, j, k := 0, 0, 0
for _, coin := range amount {
// zip/lineup all coins by their denomination to provide O(n) time
for i < len(bc) && bc[i].Denom != coin.Denom {
i++
}
for j < len(vestingCoins) && vestingCoins[j].Denom != coin.Denom {
j++
}
for k < len(bva.DelegatedVesting) && bva.DelegatedVesting[k].Denom != coin.Denom {
k++
}
baseAmt := sdk.ZeroInt()
if len(bc) > 0 {
baseAmt = bc[i].Amount
}
vestingAmt := sdk.ZeroInt()
if len(vestingCoins) > 0 {
vestingAmt = vestingCoins[j].Amount
}
delVestingAmt := sdk.ZeroInt()
if len(bva.DelegatedVesting) > 0 {
delVestingAmt = bva.DelegatedVesting[k].Amount
}
baseAmt := bc.AmountOf(coin.Denom)
vestingAmt := vestingCoins.AmountOf(coin.Denom)
delVestingAmt := bva.DelegatedVesting.AmountOf(coin.Denom)
// Panic if the delegation amount is zero or if the base coins does not
// exceed the desired delegation amount.
@ -325,21 +288,12 @@ func (bva *BaseVestingAccount) trackDelegation(vestingCoins, amount sdk.Coins) {
//
// CONTRACT: The account's coins and undelegation coins must be sorted.
func (bva *BaseVestingAccount) TrackUndelegation(amount sdk.Coins) {
i := 0
for _, coin := range amount {
// panic if the undelegation amount is zero
if coin.Amount.IsZero() {
panic("undelegation attempt with zero coins")
}
for i < len(bva.DelegatedFree) && bva.DelegatedFree[i].Denom != coin.Denom {
i++
}
delegatedFree := sdk.ZeroInt()
if len(bva.DelegatedFree) > 0 {
delegatedFree = bva.DelegatedFree[i].Amount
}
delegatedFree := bva.DelegatedFree.AmountOf(coin.Denom)
// compute x and y per the specification, where:
// X := min(DF, D)

View File

@ -12,7 +12,10 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
var testDenom = "testdenom"
var (
stakeDenom = "stake"
feeDenom = "fee"
)
func TestBaseAddressPubKey(t *testing.T) {
_, pub1, addr1 := keyPubAddr()
@ -107,7 +110,7 @@ func TestGetVestedCoinsContVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix())
@ -122,7 +125,7 @@ func TestGetVestedCoinsContVestingAcc(t *testing.T) {
// require 50% of coins vested
vestedCoins = cva.GetVestedCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, vestedCoins)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins)
// require 100% of coins vested
vestedCoins = cva.GetVestedCoins(now.Add(48 * time.Hour))
@ -134,7 +137,7 @@ func TestGetVestingCoinsContVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix())
@ -149,7 +152,7 @@ func TestGetVestingCoinsContVestingAcc(t *testing.T) {
// require 50% of coins vesting
vestingCoins = cva.GetVestingCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, vestingCoins)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins)
}
func TestSpendableCoinsContVestingAcc(t *testing.T) {
@ -157,7 +160,7 @@ func TestSpendableCoinsContVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix())
@ -174,15 +177,15 @@ func TestSpendableCoinsContVestingAcc(t *testing.T) {
// require that all vested coins (50%) are spendable
spendableCoins = cva.SpendableCoins(now.Add(12 * time.Hour))
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, spendableCoins)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins)
// receive some coins
recvAmt := sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}
recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}
cva.SetCoins(cva.GetCoins().Plus(recvAmt))
// require that all vested coins (50%) are spendable plus any received
spendableCoins = cva.SpendableCoins(now.Add(12 * time.Hour))
require.Equal(t, origCoins, spendableCoins)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 100)}, spendableCoins)
// spend all spendable coins
cva.SetCoins(cva.GetCoins().Minus(spendableCoins))
@ -197,7 +200,7 @@ func TestTrackDelegationContVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
@ -219,20 +222,20 @@ func TestTrackDelegationContVestingAcc(t *testing.T) {
// require the ability to delegate all vesting coins (50%) and all vested coins (50%)
bacc.SetCoins(origCoins)
cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix())
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedVesting)
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting)
require.Nil(t, cva.DelegatedFree)
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedFree)
require.Nil(t, cva.GetCoins())
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedFree)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000)}, cva.GetCoins())
// require no modifications when delegation amount is zero or not enough funds
bacc.SetCoins(origCoins)
cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix())
require.Panics(t, func() {
cva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(testDenom, 1000000)})
cva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)})
})
require.Nil(t, cva.DelegatedVesting)
require.Nil(t, cva.DelegatedFree)
@ -244,7 +247,7 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
@ -271,7 +274,7 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) {
cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix())
require.Panics(t, func() {
cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 0)})
cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 0)})
})
require.Nil(t, cva.DelegatedFree)
require.Nil(t, cva.DelegatedVesting)
@ -279,20 +282,20 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) {
// vest 50% and delegate to two validators
cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix())
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
// undelegate from one validator that got slashed 50%
cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 25)})
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, cva.DelegatedFree)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, cva.GetCoins())
cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)})
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, cva.DelegatedFree)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 25)}, cva.GetCoins())
// undelegate from the other validator that did not get slashed
cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
require.Nil(t, cva.DelegatedFree)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, cva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 75)}, cva.GetCoins())
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, cva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 75)}, cva.GetCoins())
}
func TestGetVestedCoinsDelVestingAcc(t *testing.T) {
@ -300,7 +303,7 @@ func TestGetVestedCoinsDelVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
@ -319,7 +322,7 @@ func TestGetVestingCoinsDelVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
@ -338,7 +341,7 @@ func TestSpendableCoinsDelVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
@ -358,7 +361,7 @@ func TestSpendableCoinsDelVestingAcc(t *testing.T) {
require.Nil(t, spendableCoins)
// receive some coins
recvAmt := sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}
recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}
dva.SetCoins(dva.GetCoins().Plus(recvAmt))
// require that only received coins are spendable since the account is still
@ -379,7 +382,7 @@ func TestTrackDelegationDelVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
@ -413,7 +416,7 @@ func TestTrackDelegationDelVestingAcc(t *testing.T) {
dva = NewDelayedVestingAccount(&bacc, endTime.Unix())
require.Panics(t, func() {
dva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(testDenom, 1000000)})
dva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)})
})
require.Nil(t, dva.DelegatedVesting)
require.Nil(t, dva.DelegatedFree)
@ -425,7 +428,7 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) {
endTime := now.Add(24 * time.Hour)
_, _, addr := keyPubAddr()
origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)}
origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)}
bacc := NewBaseAccountWithAddress(addr)
bacc.SetCoins(origCoins)
@ -452,7 +455,7 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) {
dva = NewDelayedVestingAccount(&bacc, endTime.Unix())
require.Panics(t, func() {
dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 0)})
dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 0)})
})
require.Nil(t, dva.DelegatedFree)
require.Nil(t, dva.DelegatedVesting)
@ -461,19 +464,19 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) {
// vest 50% and delegate to two validators
bacc.SetCoins(origCoins)
dva = NewDelayedVestingAccount(&bacc, endTime.Unix())
dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
// undelegate from one validator that got slashed 50%
dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 25)})
dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)})
require.Nil(t, dva.DelegatedFree)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 75)}, dva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, dva.GetCoins())
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 75)}, dva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 25)}, dva.GetCoins())
// undelegate from the other validator that did not get slashed
dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 50)})
dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)})
require.Nil(t, dva.DelegatedFree)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, dva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 75)}, dva.GetCoins())
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, dva.DelegatedVesting)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 75)}, dva.GetCoins())
}