validator_info tests

This commit is contained in:
rigelrozanski 2018-10-04 03:00:24 -04:00
parent dcf0ddaa03
commit 1e74da1abe
10 changed files with 197 additions and 50 deletions

View File

@ -462,6 +462,6 @@ func MaxDec(d1, d2 Dec) Dec {
}
// intended to be used with require/assert: require.True(DecEq(...))
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, Dec, Dec) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp, got
func DecEq(t *testing.T, exp, got Dec) (*testing.T, bool, string, string, string) {
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
}

View File

@ -26,22 +26,22 @@ func (k Keeper) AllocateFees(ctx sdk.Context) {
sumPowerPrecommitValidators := sdk.NewDec(1) // XXX TODO actually calculate this
proposerMultiplier := sdk.NewDecWithPrec(1, 2).Add(sdk.NewDecWithPrec(4, 2).Mul(
sumPowerPrecommitValidators).Quo(bondedTokens))
proposerReward := feesCollectedDec.Mul(proposerMultiplier)
proposerReward := feesCollectedDec.MulDec(proposerMultiplier)
// apply commission
commission := proposerReward.Mul(proposerValidator.GetCommission())
remaining := proposerReward.Mul(sdk.OneDec().Sub(proposerValidator.GetCommission()))
commission := proposerReward.MulDec(proposerValidator.GetCommission())
remaining := proposerReward.MulDec(sdk.OneDec().Sub(proposerValidator.GetCommission()))
proposerDist.PoolCommission = proposerDist.PoolCommission.Plus(commission)
proposerDist.Pool = proposerDist.Pool.Plus(remaining)
// allocate community funding
communityTax := k.GetCommunityTax(ctx)
communityFunding := feesCollectedDec.Mul(communityTax)
communityFunding := feesCollectedDec.MulDec(communityTax)
feePool := k.GetFeePool(ctx)
feePool.CommunityPool = feePool.CommunityPool.Plus(communityFunding)
// set the global pool within the distribution module
poolReceived := feesCollectedDec.Mul(sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax))
poolReceived := feesCollectedDec.MulDec(sdk.OneDec().Sub(proposerMultiplier).Sub(communityTax))
feePool.Pool = feePool.Pool.Plus(poolReceived)
k.SetValidatorDistInfo(ctx, proposerDist)

View File

@ -27,6 +27,14 @@ func (coin DecCoin) Plus(coinB DecCoin) DecCoin {
return DecCoin{coin.Denom, coin.Amount.Add(coinB.Amount)}
}
// Subtracts amounts of two coins with same denom
func (coin DecCoin) Minus(coinB DecCoin) DecCoin {
if !(coin.Denom == coinB.Denom) {
return coin
}
return DecCoin{coin.Denom, coin.Amount.Sub(coinB.Amount)}
}
// return the decimal coins with trunctated decimals
func (coin DecCoin) TruncateDecimal() sdk.Coin {
return sdk.NewCoin(coin.Denom, coin.Amount.TruncateInt())
@ -89,15 +97,45 @@ func (coins DecCoins) Plus(coinsB DecCoins) DecCoins {
}
}
// multiply all the coins by a multiple
func (coins DecCoins) Mul(multiple sdk.Dec) DecCoins {
products := make([]DecCoin, len(coins))
// Negative returns a set of coins with all amount negative
func (coins DecCoins) Negative() DecCoins {
res := make([]DecCoin, 0, len(coins))
for _, coin := range coins {
res = append(res, DecCoin{
Denom: coin.Denom,
Amount: coin.Amount.Neg(),
})
}
return res
}
// Minus subtracts a set of coins from another (adds the inverse)
func (coins DecCoins) Minus(coinsB DecCoins) DecCoins {
return coins.Plus(coinsB.Negative())
}
// multiply all the coins by a decimal
func (coins DecCoins) MulDec(d sdk.Dec) DecCoins {
res := make([]DecCoin, len(coins))
for i, coin := range coins {
product := DecCoin{
Denom: coin.Denom,
Amount: coin.Amount.Mul(multiple),
Amount: coin.Amount.Mul(d),
}
products[i] = product
res[i] = product
}
return products
return res
}
// divide all the coins by a multiple
func (coins DecCoins) QuoDec(d sdk.Dec) DecCoins {
res := make([]DecCoin, len(coins))
for i, coin := range coins {
quotient := DecCoin{
Denom: coin.Denom,
Amount: coin.Amount.Quo(d),
}
res[i] = quotient
}
return res
}

View File

@ -4,17 +4,16 @@ import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPlusDecCoin(t *testing.T) {
decCoinA1 := DecCoin("A", sdk.NewDecWithPrec(11, 1))
decCoinA2 := DecCoin("A", sdk.NewDecWithPrec(22, 1))
decCoinB1 := DecCoin("B", sdk.NewDecWithPrec(11, 1))
decCoinC1 := DecCoin("C", sdk.NewDecWithPrec(11, 1))
decCoinCn4 := DecCoin("C", sdk.NewDecWithPrec(-44, 1))
decCoinC5 := DecCoin("C", sdk.NewDecWithPrec(55, 1))
decCoinA1 := DecCoin{"A", sdk.NewDecWithPrec(11, 1)}
decCoinA2 := DecCoin{"A", sdk.NewDecWithPrec(22, 1)}
decCoinB1 := DecCoin{"B", sdk.NewDecWithPrec(11, 1)}
decCoinC1 := DecCoin{"C", sdk.NewDecWithPrec(11, 1)}
decCoinCn4 := DecCoin{"C", sdk.NewDecWithPrec(-44, 1)}
decCoinC5 := DecCoin{"C", sdk.NewDecWithPrec(55, 1)}
cases := []struct {
inputOne DecCoin
@ -51,7 +50,6 @@ func TestPlusCoins(t *testing.T) {
for tcIndex, tc := range cases {
res := tc.inputOne.Plus(tc.inputTwo)
assert.True(t, res.IsValid())
require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex)
}
}

View File

@ -9,6 +9,16 @@ type DelegatorDistInfo struct {
WithdrawalHeight int64 `json:"withdrawal_height"` // last time this delegation withdrew rewards
}
func NewDelegatorDistInfo(delegatorAddr sdk.AccAddress, valOperatorAddr sdk.ValAddress,
currentHeight int64) DelegatorDistInfo {
return DelegatorDistInfo{
DelegatorAddr: delegatorAddr,
ValOperatorAddr: valOperatorAddr,
WithdrawalHeight: currentHeight,
}
}
// withdraw rewards from delegator
func (di DelegatorDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo,
height int64, totalBonded, vdTokens, totalDelShares, delegatorShares,
@ -24,8 +34,8 @@ func (di DelegatorDistInfo) WithdrawRewards(fp FeePool, vi ValidatorDistInfo,
blocks := height - di.WithdrawalHeight
di.WithdrawalHeight = height
accum := delegatorShares.Mul(sdk.NewDec(blocks))
withdrawalTokens := vi.Pool.Mul(accum.Quo(vi.DelAccum.Accum))
remainingTokens := vi.Pool.Mul(sdk.OneDec().Sub(accum.Quo(vi.DelAccum.Accum)))
withdrawalTokens := vi.Pool.MulDec(accum.Quo(vi.DelAccum.Accum))
remainingTokens := vi.Pool.MulDec(sdk.OneDec().Sub(accum.Quo(vi.DelAccum.Accum)))
vi.Pool = remainingTokens
vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum)

View File

@ -1,6 +1,8 @@
package types
import sdk "github.com/cosmos/cosmos-sdk/types"
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// total accumulation tracker
type TotalAccum struct {
@ -29,7 +31,7 @@ func (ta TotalAccum) Update(height int64, accumCreatedPerBlock sdk.Dec) TotalAcc
type FeePool struct {
ValAccum TotalAccum `json:"val_accum"` // total valdator accum held by validators
Pool DecCoins `json:"pool"` // funds for all validators which have yet to be withdrawn
CommunityPool DecCoins `json:"community_pool"` // pool for community funds yet to be spent}
CommunityPool DecCoins `json:"community_pool"` // pool for community funds yet to be spent
}
// update total validator accumulation factor

View File

@ -11,9 +11,20 @@ func TestTotalAccumUpdate(t *testing.T) {
ta := NewTotalAccum(0)
ta.Update(5, sdk.NewDec(3))
require.True(DecEq(t, sdk.NewDec(15), ta.Accum))
ta = ta.Update(5, sdk.NewDec(3))
require.True(sdk.DecEq(t, sdk.NewDec(15), ta.Accum))
ta.Update(8, sdk.NewDec(2))
require.True(DecEq(t, sdk.NewDec(21), ta.Accum))
ta = ta.Update(8, sdk.NewDec(2))
require.True(sdk.DecEq(t, sdk.NewDec(21), ta.Accum))
}
func TestUpdateTotalValAccum(t *testing.T) {
fp := InitialFeePool()
fp = fp.UpdateTotalValAccum(5, sdk.NewDec(3))
require.True(sdk.DecEq(t, sdk.NewDec(15), fp.ValAccum.Accum))
fp = fp.UpdateTotalValAccum(8, sdk.NewDec(2))
require.True(sdk.DecEq(t, sdk.NewDec(21), fp.ValAccum.Accum))
}

View File

@ -22,12 +22,12 @@ func TestMsgSetWithdrawAddress(t *testing.T) {
{emptyDelAddr, emptyDelAddr, false},
}
for _, tc := range tests {
for i, tc := range tests {
msg := NewMsgSetWithdrawAddress(tc.delegatorAddr, tc.withdrawAddr)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
} else {
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
}
}
}
@ -44,12 +44,12 @@ func TestMsgWithdrawDelegatorReward(t *testing.T) {
{delAddr1, emptyValAddr, false},
{emptyDelAddr, emptyValAddr, false},
}
for _, tc := range tests {
for i, tc := range tests {
msg := NewMsgWithdrawDelegatorReward(tc.delegatorAddr, tc.validatorAddr)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
} else {
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
}
}
}
@ -60,15 +60,15 @@ func TestMsgWithdrawDelegatorRewardsAll(t *testing.T) {
delegatorAddr sdk.AccAddress
expectPass bool
}{
{delAddr, true},
{delAddr1, true},
{emptyDelAddr, false},
}
for _, tc := range tests {
for i, tc := range tests {
msg := NewMsgWithdrawDelegatorRewardsAll(tc.delegatorAddr)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
} else {
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
}
}
}
@ -82,12 +82,12 @@ func TestMsgWithdrawValidatorRewardsAll(t *testing.T) {
{valAddr1, true},
{emptyValAddr, false},
}
for _, tc := range tests {
for i, tc := range tests {
msg := NewMsgWithdrawValidatorRewardsAll(tc.validatorAddr)
if tc.expectPass {
require.Nil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
} else {
require.NotNil(t, msg.ValidateBasic(), "test: %v", tc.name)
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
}
}
}

View File

@ -1,6 +1,8 @@
package types
import sdk "github.com/cosmos/cosmos-sdk/types"
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// distribution info for a particular validator
type ValidatorDistInfo struct {
@ -13,6 +15,16 @@ type ValidatorDistInfo struct {
DelAccum TotalAccum `json:"del_accum"` // total proposer pool accumulation factor held by delegators
}
func NewValidatorDistInfo(operatorAddr sdk.ValAddress, currentHeight int64) ValidatorDistInfo {
return ValidatorDistInfo{
OperatorAddr: operatorAddr,
FeePoolWithdrawalHeight: currentHeight,
Pool: DecCoins{},
PoolCommission: DecCoins{},
DelAccum: NewTotalAccum(currentHeight),
}
}
// update total delegator accumululation
func (vi ValidatorDistInfo) UpdateTotalDelAccum(height int64, totalDelShares sdk.Dec) ValidatorDistInfo {
vi.DelAccum = vi.DelAccum.Update(height, totalDelShares)
@ -23,7 +35,7 @@ func (vi ValidatorDistInfo) UpdateTotalDelAccum(height int64, totalDelShares sdk
func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBonded, vdTokens,
commissionRate sdk.Dec) (ValidatorDistInfo, FeePool) {
fp.UpdateTotalValAccum(height, totalBonded)
fp = fp.UpdateTotalValAccum(height, totalBonded)
if fp.ValAccum.Accum.IsZero() {
return vi, fp
@ -33,11 +45,11 @@ func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBo
blocks := height - vi.FeePoolWithdrawalHeight
vi.FeePoolWithdrawalHeight = height
accum := sdk.NewDec(blocks).Mul(vdTokens)
withdrawalTokens := fp.Pool.Mul(accum.Quo(fp.ValAccum.Accum))
remainingTokens := fp.Pool.Mul(sdk.OneDec().Sub(accum.Quo(fp.ValAccum.Accum)))
withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.ValAccum.Accum)
remainingTokens := fp.Pool.Minus(withdrawalTokens)
commission := withdrawalTokens.Mul(commissionRate)
afterCommission := withdrawalTokens.Mul(sdk.OneDec().Sub(commissionRate))
commission := withdrawalTokens.MulDec(commissionRate)
afterCommission := withdrawalTokens.MulDec(sdk.OneDec().Sub(commissionRate))
fp.ValAccum.Accum = fp.ValAccum.Accum.Sub(accum)
fp.Pool = remainingTokens

View File

@ -1,9 +1,85 @@
package types
import "testing"
import (
"testing"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestTakeFeePoolRewards(t *testing.T) {
// initialize
height := int64(0)
fp := InitialFeePool()
vi1 := NewValidatorDistInfo(valAddr1, height)
vi2 := NewValidatorDistInfo(valAddr2, height)
vi3 := NewValidatorDistInfo(valAddr3, height)
commissionRate1 := sdk.NewDecWithPrec(2, 2)
commissionRate2 := sdk.NewDecWithPrec(3, 2)
commissionRate3 := sdk.NewDecWithPrec(4, 2)
validatorTokens1 := sdk.NewDec(10)
validatorTokens2 := sdk.NewDec(40)
validatorTokens3 := sdk.NewDec(50)
totalBondedTokens := validatorTokens1.Add(validatorTokens2).Add(validatorTokens3)
// simulate adding some stake for inflation
height = 10
fp.Pool = DecCoins{DecCoin{"stake", sdk.NewDec(1000)}}
vi1, fp = vi1.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens1, commissionRate1)
require.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.PoolCommission[0].Amount))
vi2, fp = vi2.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens2, commissionRate2)
require.True(sdk.DecEq(t, sdk.NewDec(500), fp.ValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(400-12), vi2.Pool[0].Amount))
assert.True(sdk.DecEq(t, vi2.PoolCommission[0].Amount, sdk.NewDec(12)))
// add more blocks and inflation
height = 20
fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000))
vi3, fp = vi3.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens3, commissionRate3)
require.True(sdk.DecEq(t, sdk.NewDec(500), fp.ValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(500), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(1000-40), vi3.Pool[0].Amount))
assert.True(sdk.DecEq(t, vi3.PoolCommission[0].Amount, sdk.NewDec(40)))
}
func WithdrawCommission(t *testing.T) {
func TestWithdrawCommission(t *testing.T) {
// initialize
height := int64(0)
fp := InitialFeePool()
vi1 := NewValidatorDistInfo(valAddr1, height)
commissionRate1 := sdk.NewDecWithPrec(2, 2)
validatorTokens1 := sdk.NewDec(10)
totalBondedTokens := validatorTokens1.Add(sdk.NewDec(90)) // validator-1 is 10% of total power
// simulate adding some stake for inflation
height = 10
fp.Pool = DecCoins{DecCoin{"stake", sdk.NewDec(1000)}}
// for a more fun staring condition, have an non-withdraw update
vi1, fp = vi1.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens1, commissionRate1)
require.True(sdk.DecEq(t, sdk.NewDec(900), fp.ValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(900), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(100-2), vi1.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi1.PoolCommission[0].Amount))
// add more blocks and inflation
height = 20
fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000))
vi1, fp, commissionRecv := vi1.WithdrawCommission(fp, height, totalBondedTokens, validatorTokens1, commissionRate1)
require.True(sdk.DecEq(t, sdk.NewDec(1800), fp.ValAccum.Accum))
assert.True(sdk.DecEq(t, sdk.NewDec(1800), fp.Pool[0].Amount))
assert.True(sdk.DecEq(t, sdk.NewDec(200-4), vi1.Pool[0].Amount))
assert.Zero(t, len(vi1.PoolCommission))
assert.True(sdk.DecEq(t, sdk.NewDec(4), commissionRecv[0].Amount))
}