Merge PR #2437: Distr-PR-4 Distribution Types
This commit is contained in:
parent
2e040dd971
commit
9c9113fa3b
|
@ -136,6 +136,7 @@ IMPROVEMENTS
|
||||||
* [genesis] \#2229 Ensure that there are no duplicate accounts or validators in the genesis state.
|
* [genesis] \#2229 Ensure that there are no duplicate accounts or validators in the genesis state.
|
||||||
* Add SDK validation to `config.toml` (namely disabling `create_empty_blocks`) \#1571
|
* Add SDK validation to `config.toml` (namely disabling `create_empty_blocks`) \#1571
|
||||||
* \#1941(https://github.com/cosmos/cosmos-sdk/issues/1941) Version is now inferred via `git describe --tags`.
|
* \#1941(https://github.com/cosmos/cosmos-sdk/issues/1941) Version is now inferred via `git describe --tags`.
|
||||||
|
* [x/distribution] \#1671 add distribution types and tests
|
||||||
|
|
||||||
* SDK
|
* SDK
|
||||||
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.
|
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register concrete types on codec codec
|
||||||
|
func RegisterCodec(cdc *codec.Codec) {
|
||||||
|
cdc.RegisterConcrete(MsgWithdrawDelegatorRewardsAll{}, "cosmos-sdk/MsgWithdrawDelegationRewardsAll", nil)
|
||||||
|
cdc.RegisterConcrete(MsgWithdrawDelegatorReward{}, "cosmos-sdk/MsgWithdrawDelegationReward", nil)
|
||||||
|
cdc.RegisterConcrete(MsgWithdrawValidatorRewardsAll{}, "cosmos-sdk/MsgWithdrawValidatorRewardsAll", nil)
|
||||||
|
cdc.RegisterConcrete(MsgSetWithdrawAddress{}, "cosmos-sdk/MsgModifyWithdrawAddress", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generic sealed codec to be used throughout module
|
||||||
|
var MsgCdc *codec.Codec
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cdc := codec.New()
|
||||||
|
RegisterCodec(cdc)
|
||||||
|
codec.RegisterCrypto(cdc)
|
||||||
|
MsgCdc = cdc.Seal()
|
||||||
|
}
|
|
@ -0,0 +1,149 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Coins which can have additional decimal points
|
||||||
|
type DecCoin struct {
|
||||||
|
Denom string `json:"denom"`
|
||||||
|
Amount sdk.Dec `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecCoin(denom string, amount int64) DecCoin {
|
||||||
|
return DecCoin{
|
||||||
|
Denom: denom,
|
||||||
|
Amount: sdk.NewDec(amount),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDecCoinFromCoin(coin sdk.Coin) DecCoin {
|
||||||
|
return DecCoin{
|
||||||
|
Denom: coin.Denom,
|
||||||
|
Amount: sdk.NewDecFromInt(coin.Amount),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds amounts of two coins with same denom
|
||||||
|
func (coin DecCoin) Plus(coinB DecCoin) DecCoin {
|
||||||
|
if coin.Denom != coinB.Denom {
|
||||||
|
panic(fmt.Sprintf("coin denom different: %v %v\n", coin.Denom, coinB.Denom))
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
panic(fmt.Sprintf("coin denom different: %v %v\n", coin.Denom, coinB.Denom))
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
//_______________________________________________________________________
|
||||||
|
|
||||||
|
// coins with decimal
|
||||||
|
type DecCoins []DecCoin
|
||||||
|
|
||||||
|
func NewDecCoins(coins sdk.Coins) DecCoins {
|
||||||
|
dcs := make(DecCoins, len(coins))
|
||||||
|
for i, coin := range coins {
|
||||||
|
dcs[i] = NewDecCoinFromCoin(coin)
|
||||||
|
}
|
||||||
|
return dcs
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the coins with trunctated decimals
|
||||||
|
func (coins DecCoins) TruncateDecimal() sdk.Coins {
|
||||||
|
out := make(sdk.Coins, len(coins))
|
||||||
|
for i, coin := range coins {
|
||||||
|
out[i] = coin.TruncateDecimal()
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plus combines two sets of coins
|
||||||
|
// CONTRACT: Plus will never return Coins where one Coin has a 0 amount.
|
||||||
|
func (coins DecCoins) Plus(coinsB DecCoins) DecCoins {
|
||||||
|
sum := ([]DecCoin)(nil)
|
||||||
|
indexA, indexB := 0, 0
|
||||||
|
lenA, lenB := len(coins), len(coinsB)
|
||||||
|
for {
|
||||||
|
if indexA == lenA {
|
||||||
|
if indexB == lenB {
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
return append(sum, coinsB[indexB:]...)
|
||||||
|
} else if indexB == lenB {
|
||||||
|
return append(sum, coins[indexA:]...)
|
||||||
|
}
|
||||||
|
coinA, coinB := coins[indexA], coinsB[indexB]
|
||||||
|
switch strings.Compare(coinA.Denom, coinB.Denom) {
|
||||||
|
case -1:
|
||||||
|
sum = append(sum, coinA)
|
||||||
|
indexA++
|
||||||
|
case 0:
|
||||||
|
if coinA.Amount.Add(coinB.Amount).IsZero() {
|
||||||
|
// ignore 0 sum coin type
|
||||||
|
} else {
|
||||||
|
sum = append(sum, coinA.Plus(coinB))
|
||||||
|
}
|
||||||
|
indexA++
|
||||||
|
indexB++
|
||||||
|
case 1:
|
||||||
|
sum = append(sum, coinB)
|
||||||
|
indexB++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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(d),
|
||||||
|
}
|
||||||
|
res[i] = product
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
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)}
|
||||||
|
|
||||||
|
// regular add
|
||||||
|
res := decCoinA1.Plus(decCoinA1)
|
||||||
|
require.Equal(t, decCoinA2, res, "sum of coins is incorrect")
|
||||||
|
|
||||||
|
// bad denom add
|
||||||
|
assert.Panics(t, func() {
|
||||||
|
decCoinA1.Plus(decCoinB1)
|
||||||
|
}, "expected panic on sum of different denoms")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlusCoins(t *testing.T) {
|
||||||
|
one := sdk.NewDec(1)
|
||||||
|
zero := sdk.NewDec(0)
|
||||||
|
negone := sdk.NewDec(-1)
|
||||||
|
two := sdk.NewDec(2)
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
inputOne DecCoins
|
||||||
|
inputTwo DecCoins
|
||||||
|
expected DecCoins
|
||||||
|
}{
|
||||||
|
{DecCoins{{"A", one}, {"B", one}}, DecCoins{{"A", one}, {"B", one}}, DecCoins{{"A", two}, {"B", two}}},
|
||||||
|
{DecCoins{{"A", zero}, {"B", one}}, DecCoins{{"A", zero}, {"B", zero}}, DecCoins{{"B", one}}},
|
||||||
|
{DecCoins{{"A", zero}, {"B", zero}}, DecCoins{{"A", zero}, {"B", zero}}, DecCoins(nil)},
|
||||||
|
{DecCoins{{"A", one}, {"B", zero}}, DecCoins{{"A", negone}, {"B", zero}}, DecCoins(nil)},
|
||||||
|
{DecCoins{{"A", negone}, {"B", zero}}, DecCoins{{"A", zero}, {"B", zero}}, DecCoins{{"A", negone}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for tcIndex, tc := range cases {
|
||||||
|
res := tc.inputOne.Plus(tc.inputTwo)
|
||||||
|
require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// distribution info for a delegation - used to determine entitled rewards
|
||||||
|
type DelegatorDistInfo struct {
|
||||||
|
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
|
||||||
|
ValOperatorAddr sdk.ValAddress `json:"val_operator_addr"`
|
||||||
|
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,
|
||||||
|
commissionRate sdk.Dec) (DelegatorDistInfo, ValidatorDistInfo, FeePool, DecCoins) {
|
||||||
|
|
||||||
|
vi = vi.UpdateTotalDelAccum(height, totalDelShares)
|
||||||
|
|
||||||
|
if vi.DelAccum.Accum.IsZero() {
|
||||||
|
return di, vi, fp, DecCoins{}
|
||||||
|
}
|
||||||
|
|
||||||
|
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate)
|
||||||
|
|
||||||
|
blocks := height - di.WithdrawalHeight
|
||||||
|
di.WithdrawalHeight = height
|
||||||
|
accum := delegatorShares.MulInt(sdk.NewInt(blocks))
|
||||||
|
withdrawalTokens := vi.Pool.MulDec(accum).QuoDec(vi.DelAccum.Accum)
|
||||||
|
remainingTokens := vi.Pool.Minus(withdrawalTokens)
|
||||||
|
|
||||||
|
vi.Pool = remainingTokens
|
||||||
|
vi.DelAccum.Accum = vi.DelAccum.Accum.Sub(accum)
|
||||||
|
|
||||||
|
return di, vi, fp, withdrawalTokens
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWithdrawRewards(t *testing.T) {
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
height := int64(0)
|
||||||
|
fp := InitialFeePool()
|
||||||
|
vi := NewValidatorDistInfo(valAddr1, height)
|
||||||
|
commissionRate := sdk.NewDecWithPrec(2, 2)
|
||||||
|
validatorTokens := sdk.NewDec(10)
|
||||||
|
validatorDelShares := sdk.NewDec(10)
|
||||||
|
totalBondedTokens := validatorTokens.Add(sdk.NewDec(90)) // validator-1 is 10% of total power
|
||||||
|
|
||||||
|
di1 := NewDelegatorDistInfo(delAddr1, valAddr1, height)
|
||||||
|
di1Shares := sdk.NewDec(5) // this delegator has half the shares in the validator
|
||||||
|
|
||||||
|
di2 := NewDelegatorDistInfo(delAddr2, valAddr1, height)
|
||||||
|
di2Shares := sdk.NewDec(5)
|
||||||
|
|
||||||
|
// simulate adding some stake for inflation
|
||||||
|
height = 10
|
||||||
|
fp.Pool = DecCoins{NewDecCoin("stake", 1000)}
|
||||||
|
|
||||||
|
// withdraw rewards
|
||||||
|
di1, vi, fp, rewardRecv1 := di1.WithdrawRewards(fp, vi, height, totalBondedTokens,
|
||||||
|
validatorTokens, validatorDelShares, di1Shares, commissionRate)
|
||||||
|
|
||||||
|
assert.Equal(t, height, di1.WithdrawalHeight)
|
||||||
|
assert.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(49), vi.Pool[0].Amount))
|
||||||
|
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount))
|
||||||
|
assert.True(sdk.DecEq(t, sdk.NewDec(49), rewardRecv1[0].Amount))
|
||||||
|
|
||||||
|
// add more blocks and inflation
|
||||||
|
height = 20
|
||||||
|
fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000))
|
||||||
|
|
||||||
|
// withdraw rewards
|
||||||
|
di2, vi, fp, rewardRecv2 := di2.WithdrawRewards(fp, vi, height, totalBondedTokens,
|
||||||
|
validatorTokens, validatorDelShares, di2Shares, commissionRate)
|
||||||
|
|
||||||
|
assert.Equal(t, height, di2.WithdrawalHeight)
|
||||||
|
assert.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(49), vi.Pool[0].Amount))
|
||||||
|
assert.True(sdk.DecEq(t, sdk.NewDec(4), vi.PoolCommission[0].Amount))
|
||||||
|
assert.True(sdk.DecEq(t, sdk.NewDec(98), rewardRecv2[0].Amount))
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
// nolint
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeType = sdk.CodeType
|
||||||
|
|
||||||
|
const (
|
||||||
|
DefaultCodespace sdk.CodespaceType = 6
|
||||||
|
CodeInvalidInput CodeType = 103
|
||||||
|
)
|
||||||
|
|
||||||
|
func ErrNilDelegatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||||
|
return sdk.NewError(codespace, CodeInvalidInput, "delegator address is nil")
|
||||||
|
}
|
||||||
|
func ErrNilWithdrawAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||||
|
return sdk.NewError(codespace, CodeInvalidInput, "withdraw address is nil")
|
||||||
|
}
|
||||||
|
func ErrNilValidatorAddr(codespace sdk.CodespaceType) sdk.Error {
|
||||||
|
return sdk.NewError(codespace, CodeInvalidInput, "validator address is nil")
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// total accumulation tracker
|
||||||
|
type TotalAccum struct {
|
||||||
|
UpdateHeight int64 `json:"update_height"`
|
||||||
|
Accum sdk.Dec `json:"accum"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTotalAccum(height int64) TotalAccum {
|
||||||
|
return TotalAccum{
|
||||||
|
UpdateHeight: height,
|
||||||
|
Accum: sdk.ZeroDec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update total validator accumulation factor for the new height
|
||||||
|
// CONTRACT: height should be greater than the old height
|
||||||
|
func (ta TotalAccum) UpdateForNewHeight(height int64, accumCreatedPerBlock sdk.Dec) TotalAccum {
|
||||||
|
blocks := height - ta.UpdateHeight
|
||||||
|
if blocks < 0 {
|
||||||
|
panic("reverse updated for new height")
|
||||||
|
}
|
||||||
|
ta.Accum = ta.Accum.Add(accumCreatedPerBlock.MulInt(sdk.NewInt(blocks)))
|
||||||
|
ta.UpdateHeight = height
|
||||||
|
return ta
|
||||||
|
}
|
||||||
|
|
||||||
|
//___________________________________________________________________________________________
|
||||||
|
|
||||||
|
// global fee pool for distribution
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// update total validator accumulation factor
|
||||||
|
func (f FeePool) UpdateTotalValAccum(height int64, totalBondedTokens sdk.Dec) FeePool {
|
||||||
|
f.ValAccum = f.ValAccum.UpdateForNewHeight(height, totalBondedTokens)
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// zero fee pool
|
||||||
|
func InitialFeePool() FeePool {
|
||||||
|
return FeePool{
|
||||||
|
ValAccum: NewTotalAccum(0),
|
||||||
|
Pool: DecCoins{},
|
||||||
|
CommunityPool: DecCoins{},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTotalAccumUpdateForNewHeight(t *testing.T) {
|
||||||
|
|
||||||
|
ta := NewTotalAccum(0)
|
||||||
|
|
||||||
|
ta = ta.UpdateForNewHeight(5, sdk.NewDec(3))
|
||||||
|
require.True(sdk.DecEq(t, sdk.NewDec(15), ta.Accum))
|
||||||
|
|
||||||
|
ta = ta.UpdateForNewHeight(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))
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
// the address for where distributions rewards are withdrawn to by default
|
||||||
|
// this struct is only used at genesis to feed in default withdraw addresses
|
||||||
|
type DelegatorWithdrawInfo struct {
|
||||||
|
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
|
||||||
|
WithdrawAddr sdk.AccAddress `json:"withdraw_addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenesisState - all distribution state that must be provided at genesis
|
||||||
|
type GenesisState struct {
|
||||||
|
FeePool FeePool `json:"fee_pool"`
|
||||||
|
ValidatorDistInfos []ValidatorDistInfo `json:"validator_dist_infos"`
|
||||||
|
DelegatorDistInfos []DelegatorDistInfo `json:"delegator_dist_infos"`
|
||||||
|
DelegatorWithdrawInfos []DelegatorWithdrawInfo `json:"delegator_withdraw_infos"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGenesisState(feePool FeePool, vdis []ValidatorDistInfo, ddis []DelegatorDistInfo) GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
FeePool: feePool,
|
||||||
|
ValidatorDistInfos: vdis,
|
||||||
|
DelegatorDistInfos: ddis,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get raw genesis raw message for testing
|
||||||
|
func DefaultGenesisState() GenesisState {
|
||||||
|
return GenesisState{
|
||||||
|
FeePool: InitialFeePool(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
|
// expected stake keeper
|
||||||
|
type StakeKeeper interface {
|
||||||
|
IterateDelegations(ctx sdk.Context, delegator sdk.AccAddress,
|
||||||
|
fn func(index int64, delegation sdk.Delegation) (stop bool))
|
||||||
|
Delegation(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) sdk.Delegation
|
||||||
|
Validator(ctx sdk.Context, valAddr sdk.ValAddress) sdk.Validator
|
||||||
|
ValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) sdk.Validator
|
||||||
|
TotalPower(ctx sdk.Context) sdk.Dec
|
||||||
|
}
|
||||||
|
|
||||||
|
// expected coin keeper
|
||||||
|
type BankKeeper interface {
|
||||||
|
AddCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) (sdk.Coins, sdk.Tags, sdk.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// from ante handler
|
||||||
|
type FeeCollectionKeeper interface {
|
||||||
|
GetCollectedFees(ctx sdk.Context) sdk.Coins
|
||||||
|
ClearCollectedFees(ctx sdk.Context)
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
//nolint
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// name to identify transaction types
|
||||||
|
const MsgType = "distr"
|
||||||
|
|
||||||
|
// Verify interface at compile time
|
||||||
|
var _, _ sdk.Msg = &MsgSetWithdrawAddress{}, &MsgWithdrawDelegatorRewardsAll{}
|
||||||
|
var _, _ sdk.Msg = &MsgWithdrawDelegatorReward{}, &MsgWithdrawValidatorRewardsAll{}
|
||||||
|
|
||||||
|
//______________________________________________________________________
|
||||||
|
|
||||||
|
// msg struct for changing the withdraw address for a delegator (or validator self-delegation)
|
||||||
|
type MsgSetWithdrawAddress struct {
|
||||||
|
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
|
||||||
|
WithdrawAddr sdk.AccAddress `json:"delegator_addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMsgSetWithdrawAddress(delAddr, withdrawAddr sdk.AccAddress) MsgSetWithdrawAddress {
|
||||||
|
return MsgSetWithdrawAddress{
|
||||||
|
DelegatorAddr: delAddr,
|
||||||
|
WithdrawAddr: withdrawAddr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MsgSetWithdrawAddress) Type() string { return MsgType }
|
||||||
|
func (msg MsgSetWithdrawAddress) Name() string { return "set_withdraw_address" }
|
||||||
|
|
||||||
|
// Return address that must sign over msg.GetSignBytes()
|
||||||
|
func (msg MsgSetWithdrawAddress) GetSigners() []sdk.AccAddress {
|
||||||
|
return []sdk.AccAddress{sdk.AccAddress(msg.DelegatorAddr)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bytes for the message signer to sign on
|
||||||
|
func (msg MsgSetWithdrawAddress) GetSignBytes() []byte {
|
||||||
|
b, err := MsgCdc.MarshalJSON(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sdk.MustSortJSON(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// quick validity check
|
||||||
|
func (msg MsgSetWithdrawAddress) ValidateBasic() sdk.Error {
|
||||||
|
if msg.DelegatorAddr == nil {
|
||||||
|
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||||
|
}
|
||||||
|
if msg.WithdrawAddr == nil {
|
||||||
|
return ErrNilWithdrawAddr(DefaultCodespace)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//______________________________________________________________________
|
||||||
|
|
||||||
|
// msg struct for delegation withdraw for all of the delegator's delegations
|
||||||
|
type MsgWithdrawDelegatorRewardsAll struct {
|
||||||
|
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMsgWithdrawDelegatorRewardsAll(delAddr sdk.AccAddress) MsgWithdrawDelegatorRewardsAll {
|
||||||
|
return MsgWithdrawDelegatorRewardsAll{
|
||||||
|
DelegatorAddr: delAddr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MsgWithdrawDelegatorRewardsAll) Type() string { return MsgType }
|
||||||
|
func (msg MsgWithdrawDelegatorRewardsAll) Name() string { return "withdraw_delegation_rewards_all" }
|
||||||
|
|
||||||
|
// Return address that must sign over msg.GetSignBytes()
|
||||||
|
func (msg MsgWithdrawDelegatorRewardsAll) GetSigners() []sdk.AccAddress {
|
||||||
|
return []sdk.AccAddress{sdk.AccAddress(msg.DelegatorAddr)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bytes for the message signer to sign on
|
||||||
|
func (msg MsgWithdrawDelegatorRewardsAll) GetSignBytes() []byte {
|
||||||
|
b, err := MsgCdc.MarshalJSON(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sdk.MustSortJSON(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// quick validity check
|
||||||
|
func (msg MsgWithdrawDelegatorRewardsAll) ValidateBasic() sdk.Error {
|
||||||
|
if msg.DelegatorAddr == nil {
|
||||||
|
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//______________________________________________________________________
|
||||||
|
|
||||||
|
// msg struct for delegation withdraw from a single validator
|
||||||
|
type MsgWithdrawDelegatorReward struct {
|
||||||
|
DelegatorAddr sdk.AccAddress `json:"delegator_addr"`
|
||||||
|
ValidatorAddr sdk.ValAddress `json:"validator_addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMsgWithdrawDelegatorReward(delAddr sdk.AccAddress, valAddr sdk.ValAddress) MsgWithdrawDelegatorReward {
|
||||||
|
return MsgWithdrawDelegatorReward{
|
||||||
|
DelegatorAddr: delAddr,
|
||||||
|
ValidatorAddr: valAddr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MsgWithdrawDelegatorReward) Type() string { return MsgType }
|
||||||
|
func (msg MsgWithdrawDelegatorReward) Name() string { return "withdraw_delegation_reward" }
|
||||||
|
|
||||||
|
// Return address that must sign over msg.GetSignBytes()
|
||||||
|
func (msg MsgWithdrawDelegatorReward) GetSigners() []sdk.AccAddress {
|
||||||
|
return []sdk.AccAddress{sdk.AccAddress(msg.DelegatorAddr)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bytes for the message signer to sign on
|
||||||
|
func (msg MsgWithdrawDelegatorReward) GetSignBytes() []byte {
|
||||||
|
b, err := MsgCdc.MarshalJSON(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sdk.MustSortJSON(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// quick validity check
|
||||||
|
func (msg MsgWithdrawDelegatorReward) ValidateBasic() sdk.Error {
|
||||||
|
if msg.DelegatorAddr == nil {
|
||||||
|
return ErrNilDelegatorAddr(DefaultCodespace)
|
||||||
|
}
|
||||||
|
if msg.ValidatorAddr == nil {
|
||||||
|
return ErrNilValidatorAddr(DefaultCodespace)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//______________________________________________________________________
|
||||||
|
|
||||||
|
// msg struct for validator withdraw
|
||||||
|
type MsgWithdrawValidatorRewardsAll struct {
|
||||||
|
ValidatorAddr sdk.ValAddress `json:"validator_addr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMsgWithdrawValidatorRewardsAll(valAddr sdk.ValAddress) MsgWithdrawValidatorRewardsAll {
|
||||||
|
return MsgWithdrawValidatorRewardsAll{
|
||||||
|
ValidatorAddr: valAddr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg MsgWithdrawValidatorRewardsAll) Type() string { return MsgType }
|
||||||
|
func (msg MsgWithdrawValidatorRewardsAll) Name() string { return "withdraw_validator_rewards_all" }
|
||||||
|
|
||||||
|
// Return address that must sign over msg.GetSignBytes()
|
||||||
|
func (msg MsgWithdrawValidatorRewardsAll) GetSigners() []sdk.AccAddress {
|
||||||
|
return []sdk.AccAddress{sdk.AccAddress(msg.ValidatorAddr.Bytes())}
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bytes for the message signer to sign on
|
||||||
|
func (msg MsgWithdrawValidatorRewardsAll) GetSignBytes() []byte {
|
||||||
|
b, err := MsgCdc.MarshalJSON(msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return sdk.MustSortJSON(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// quick validity check
|
||||||
|
func (msg MsgWithdrawValidatorRewardsAll) ValidateBasic() sdk.Error {
|
||||||
|
if msg.ValidatorAddr == nil {
|
||||||
|
return ErrNilValidatorAddr(DefaultCodespace)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// test ValidateBasic for MsgCreateValidator
|
||||||
|
func TestMsgSetWithdrawAddress(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
delegatorAddr sdk.AccAddress
|
||||||
|
withdrawAddr sdk.AccAddress
|
||||||
|
expectPass bool
|
||||||
|
}{
|
||||||
|
{delAddr1, delAddr2, true},
|
||||||
|
{delAddr1, delAddr1, true},
|
||||||
|
{emptyDelAddr, delAddr1, false},
|
||||||
|
{delAddr1, emptyDelAddr, false},
|
||||||
|
{emptyDelAddr, emptyDelAddr, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range tests {
|
||||||
|
msg := NewMsgSetWithdrawAddress(tc.delegatorAddr, tc.withdrawAddr)
|
||||||
|
if tc.expectPass {
|
||||||
|
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
} else {
|
||||||
|
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test ValidateBasic for MsgEditValidator
|
||||||
|
func TestMsgWithdrawDelegatorReward(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
delegatorAddr sdk.AccAddress
|
||||||
|
validatorAddr sdk.ValAddress
|
||||||
|
expectPass bool
|
||||||
|
}{
|
||||||
|
{delAddr1, valAddr1, true},
|
||||||
|
{emptyDelAddr, valAddr1, false},
|
||||||
|
{delAddr1, emptyValAddr, false},
|
||||||
|
{emptyDelAddr, emptyValAddr, false},
|
||||||
|
}
|
||||||
|
for i, tc := range tests {
|
||||||
|
msg := NewMsgWithdrawDelegatorReward(tc.delegatorAddr, tc.validatorAddr)
|
||||||
|
if tc.expectPass {
|
||||||
|
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
} else {
|
||||||
|
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test ValidateBasic and GetSigners for MsgCreateValidatorOnBehalfOf
|
||||||
|
func TestMsgWithdrawDelegatorRewardsAll(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
delegatorAddr sdk.AccAddress
|
||||||
|
expectPass bool
|
||||||
|
}{
|
||||||
|
{delAddr1, true},
|
||||||
|
{emptyDelAddr, false},
|
||||||
|
}
|
||||||
|
for i, tc := range tests {
|
||||||
|
msg := NewMsgWithdrawDelegatorRewardsAll(tc.delegatorAddr)
|
||||||
|
if tc.expectPass {
|
||||||
|
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
} else {
|
||||||
|
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test ValidateBasic for MsgDelegate
|
||||||
|
func TestMsgWithdrawValidatorRewardsAll(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
validatorAddr sdk.ValAddress
|
||||||
|
expectPass bool
|
||||||
|
}{
|
||||||
|
{valAddr1, true},
|
||||||
|
{emptyValAddr, false},
|
||||||
|
}
|
||||||
|
for i, tc := range tests {
|
||||||
|
msg := NewMsgWithdrawValidatorRewardsAll(tc.validatorAddr)
|
||||||
|
if tc.expectPass {
|
||||||
|
require.Nil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
} else {
|
||||||
|
require.NotNil(t, msg.ValidateBasic(), "test index: %v", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/tendermint/tendermint/crypto"
|
||||||
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
delPk1 = ed25519.GenPrivKey().PubKey()
|
||||||
|
delPk2 = ed25519.GenPrivKey().PubKey()
|
||||||
|
delPk3 = ed25519.GenPrivKey().PubKey()
|
||||||
|
delAddr1 = sdk.AccAddress(delPk1.Address())
|
||||||
|
delAddr2 = sdk.AccAddress(delPk2.Address())
|
||||||
|
delAddr3 = sdk.AccAddress(delPk3.Address())
|
||||||
|
emptyDelAddr sdk.AccAddress
|
||||||
|
|
||||||
|
valPk1 = ed25519.GenPrivKey().PubKey()
|
||||||
|
valPk2 = ed25519.GenPrivKey().PubKey()
|
||||||
|
valPk3 = ed25519.GenPrivKey().PubKey()
|
||||||
|
valAddr1 = sdk.ValAddress(delPk1.Address())
|
||||||
|
valAddr2 = sdk.ValAddress(delPk2.Address())
|
||||||
|
valAddr3 = sdk.ValAddress(delPk3.Address())
|
||||||
|
emptyValAddr sdk.ValAddress
|
||||||
|
|
||||||
|
emptyPubkey crypto.PubKey
|
||||||
|
)
|
|
@ -0,0 +1,72 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// distribution info for a particular validator
|
||||||
|
type ValidatorDistInfo struct {
|
||||||
|
OperatorAddr sdk.ValAddress `json:"operator_addr"`
|
||||||
|
|
||||||
|
FeePoolWithdrawalHeight int64 `json:"global_withdrawal_height"` // last height this validator withdrew from the global pool
|
||||||
|
Pool DecCoins `json:"pool"` // rewards owed to delegators, commission has already been charged (includes proposer reward)
|
||||||
|
PoolCommission DecCoins `json:"pool_commission"` // commission collected by this validator (pending withdrawal)
|
||||||
|
|
||||||
|
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.UpdateForNewHeight(height, totalDelShares)
|
||||||
|
return vi
|
||||||
|
}
|
||||||
|
|
||||||
|
// move any available accumulated fees in the FeePool to the validator's pool
|
||||||
|
func (vi ValidatorDistInfo) TakeFeePoolRewards(fp FeePool, height int64, totalBonded, vdTokens,
|
||||||
|
commissionRate sdk.Dec) (ValidatorDistInfo, FeePool) {
|
||||||
|
|
||||||
|
fp = fp.UpdateTotalValAccum(height, totalBonded)
|
||||||
|
|
||||||
|
if fp.ValAccum.Accum.IsZero() {
|
||||||
|
return vi, fp
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the validators pool
|
||||||
|
blocks := height - vi.FeePoolWithdrawalHeight
|
||||||
|
vi.FeePoolWithdrawalHeight = height
|
||||||
|
accum := vdTokens.MulInt(sdk.NewInt(blocks))
|
||||||
|
withdrawalTokens := fp.Pool.MulDec(accum).QuoDec(fp.ValAccum.Accum)
|
||||||
|
remainingTokens := fp.Pool.Minus(withdrawalTokens)
|
||||||
|
|
||||||
|
commission := withdrawalTokens.MulDec(commissionRate)
|
||||||
|
afterCommission := withdrawalTokens.Minus(commission)
|
||||||
|
|
||||||
|
fp.ValAccum.Accum = fp.ValAccum.Accum.Sub(accum)
|
||||||
|
fp.Pool = remainingTokens
|
||||||
|
vi.PoolCommission = vi.PoolCommission.Plus(commission)
|
||||||
|
vi.Pool = vi.Pool.Plus(afterCommission)
|
||||||
|
|
||||||
|
return vi, fp
|
||||||
|
}
|
||||||
|
|
||||||
|
// withdraw commission rewards
|
||||||
|
func (vi ValidatorDistInfo) WithdrawCommission(fp FeePool, height int64,
|
||||||
|
totalBonded, vdTokens, commissionRate sdk.Dec) (vio ValidatorDistInfo, fpo FeePool, withdrawn DecCoins) {
|
||||||
|
|
||||||
|
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBonded, vdTokens, commissionRate)
|
||||||
|
|
||||||
|
withdrawalTokens := vi.PoolCommission
|
||||||
|
vi.PoolCommission = DecCoins{} // zero
|
||||||
|
|
||||||
|
return vi, fp, withdrawalTokens
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
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{NewDecCoin("stake", 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 TestWithdrawCommission(t *testing.T) {
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
height := int64(0)
|
||||||
|
fp := InitialFeePool()
|
||||||
|
vi := NewValidatorDistInfo(valAddr1, height)
|
||||||
|
commissionRate := sdk.NewDecWithPrec(2, 2)
|
||||||
|
validatorTokens := sdk.NewDec(10)
|
||||||
|
totalBondedTokens := validatorTokens.Add(sdk.NewDec(90)) // validator-1 is 10% of total power
|
||||||
|
|
||||||
|
// simulate adding some stake for inflation
|
||||||
|
height = 10
|
||||||
|
fp.Pool = DecCoins{NewDecCoin("stake", 1000)}
|
||||||
|
|
||||||
|
// for a more fun staring condition, have an non-withdraw update
|
||||||
|
vi, fp = vi.TakeFeePoolRewards(fp, height, totalBondedTokens, validatorTokens, commissionRate)
|
||||||
|
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), vi.Pool[0].Amount))
|
||||||
|
assert.True(sdk.DecEq(t, sdk.NewDec(2), vi.PoolCommission[0].Amount))
|
||||||
|
|
||||||
|
// add more blocks and inflation
|
||||||
|
height = 20
|
||||||
|
fp.Pool[0].Amount = fp.Pool[0].Amount.Add(sdk.NewDec(1000))
|
||||||
|
|
||||||
|
vi, fp, commissionRecv := vi.WithdrawCommission(fp, height, totalBondedTokens, validatorTokens, commissionRate)
|
||||||
|
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), vi.Pool[0].Amount))
|
||||||
|
assert.Zero(t, len(vi.PoolCommission))
|
||||||
|
assert.True(sdk.DecEq(t, sdk.NewDec(4), commissionRecv[0].Amount))
|
||||||
|
}
|
Loading…
Reference in New Issue