Merge remote-tracking branch 'origin/develop' into bez/2339-fix-lcd-val-init

This commit is contained in:
Aleksandr Bezobchuk 2018-10-08 09:14:11 +08:00
commit 23dc24e9e4
21 changed files with 1068 additions and 20 deletions

View File

@ -206,6 +206,12 @@ jobs:
- run:
name: run localnet and exit on failure
command: |
pushd /tmp
wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz
sudo tar -xvf go1.11.linux-amd64.tar.gz
sudo rm -rf /usr/local/go
sudo mv go /usr/local
popd
set -x
make get_tools
make get_vendor_deps

View File

@ -111,6 +111,7 @@ FEATURES
* [x/auth] \#2376 Remove FeePayer() from StdTx
* [x/stake] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Implement
basis for the validator commission model.
* [x/auth] Support account removal in the account mapper.
* Tendermint
@ -135,6 +136,7 @@ IMPROVEMENTS
* [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
* \#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
* [tools] Make get_vendor_deps deletes `.vendor-new` directories, in case scratch files are present.

View File

@ -83,6 +83,13 @@ func (am AccountMapper) SetAccount(ctx sdk.Context, acc Account) {
store.Set(AddressStoreKey(addr), bz)
}
// RemoveAccount removes an account for the account mapper store.
func (am AccountMapper) RemoveAccount(ctx sdk.Context, acc Account) {
addr := acc.GetAddress()
store := ctx.KVStore(am.key)
store.Delete(AddressStoreKey(addr))
}
// Implements sdk.AccountMapper.
func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) {
store := ctx.KVStore(am.key)

View File

@ -61,6 +61,44 @@ func TestAccountMapperGetSet(t *testing.T) {
require.Equal(t, newSequence, acc.GetSequence())
}
func TestAccountMapperRemoveAccount(t *testing.T) {
ms, capKey, _ := setupMultiStore()
cdc := codec.New()
RegisterBaseAccount(cdc)
// make context and mapper
ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewNopLogger())
mapper := NewAccountMapper(cdc, capKey, ProtoBaseAccount)
addr1 := sdk.AccAddress([]byte("addr1"))
addr2 := sdk.AccAddress([]byte("addr2"))
// create accounts
acc1 := mapper.NewAccountWithAddress(ctx, addr1)
acc2 := mapper.NewAccountWithAddress(ctx, addr2)
accSeq1 := int64(20)
accSeq2 := int64(40)
acc1.SetSequence(accSeq1)
acc2.SetSequence(accSeq2)
mapper.SetAccount(ctx, acc1)
mapper.SetAccount(ctx, acc2)
acc1 = mapper.GetAccount(ctx, addr1)
require.NotNil(t, acc1)
require.Equal(t, accSeq1, acc1.GetSequence())
// remove one account
mapper.RemoveAccount(ctx, acc1)
acc1 = mapper.GetAccount(ctx, addr1)
require.Nil(t, acc1)
acc2 = mapper.GetAccount(ctx, addr2)
require.NotNil(t, acc2)
require.Equal(t, accSeq2, acc2.GetSequence())
}
func BenchmarkAccountMapperGetAccountFound(b *testing.B) {
ms, capKey, _ := setupMultiStore()
cdc := codec.New()

View File

@ -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()
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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))
}

View File

@ -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")
}

View File

@ -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{},
}
}

View File

@ -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))
}

View File

@ -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(),
}
}

View File

@ -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)
}

175
x/distribution/types/msg.go Normal file
View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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
)

View File

@ -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
}

View File

@ -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))
}

View File

@ -177,28 +177,28 @@ func GetREDByValDstIndexKey(delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.V
return key
}
// rearranges the ValSrcIndexKey to get the REDKey
func GetREDKeyFromValSrcIndexKey(IndexKey []byte) []byte {
addrs := IndexKey[1:] // remove prefix bytes
if len(addrs) != 3*sdk.AddrLen {
// GetREDKeyFromValSrcIndexKey rearranges the ValSrcIndexKey to get the REDKey
func GetREDKeyFromValSrcIndexKey(indexKey []byte) []byte {
// note that first byte is prefix byte
if len(indexKey) != 3*sdk.AddrLen+1 {
panic("unexpected key length")
}
valSrcAddr := addrs[:sdk.AddrLen]
delAddr := addrs[sdk.AddrLen : 2*sdk.AddrLen]
valDstAddr := addrs[2*sdk.AddrLen:]
valSrcAddr := indexKey[1 : sdk.AddrLen+1]
delAddr := indexKey[sdk.AddrLen+1 : 2*sdk.AddrLen+1]
valDstAddr := indexKey[2*sdk.AddrLen+1 : 3*sdk.AddrLen+1]
return GetREDKey(delAddr, valSrcAddr, valDstAddr)
}
// rearranges the ValDstIndexKey to get the REDKey
func GetREDKeyFromValDstIndexKey(IndexKey []byte) []byte {
addrs := IndexKey[1:] // remove prefix bytes
if len(addrs) != 3*sdk.AddrLen {
// GetREDKeyFromValDstIndexKey rearranges the ValDstIndexKey to get the REDKey
func GetREDKeyFromValDstIndexKey(indexKey []byte) []byte {
// note that first byte is prefix byte
if len(indexKey) != 3*sdk.AddrLen+1 {
panic("unexpected key length")
}
valDstAddr := addrs[:sdk.AddrLen]
delAddr := addrs[sdk.AddrLen : 2*sdk.AddrLen]
valSrcAddr := addrs[2*sdk.AddrLen:]
valDstAddr := indexKey[1 : sdk.AddrLen+1]
delAddr := indexKey[sdk.AddrLen+1 : 2*sdk.AddrLen+1]
valSrcAddr := indexKey[2*sdk.AddrLen+1 : 3*sdk.AddrLen+1]
return GetREDKey(delAddr, valSrcAddr, valDstAddr)
}

View File

@ -2,30 +2,43 @@ package keeper
import (
"encoding/hex"
"math/big"
"testing"
"github.com/stretchr/testify/assert"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/crypto/ed25519"
)
var (
pk1 = ed25519.GenPrivKey().PubKey()
pk1 = ed25519.GenPrivKeyFromSecret([]byte{1}).PubKey()
pk2 = ed25519.GenPrivKeyFromSecret([]byte{2}).PubKey()
pk3 = ed25519.GenPrivKeyFromSecret([]byte{3}).PubKey()
addr1 = pk1.Address()
addr2 = pk2.Address()
addr3 = pk3.Address()
)
func TestGetValidatorPowerRank(t *testing.T) {
valAddr1 := sdk.ValAddress(pk1.Bytes())
valAddr1 := sdk.ValAddress(addr1)
emptyDesc := types.Description{}
val1 := types.NewValidator(valAddr1, pk1, emptyDesc)
val1.Tokens = sdk.NewDec(12)
val1.Tokens = sdk.NewDec(0)
val2, val3, val4 := val1, val1, val1
val2.Tokens = sdk.NewDec(1)
val3.Tokens = sdk.NewDec(10)
x := new(big.Int).Exp(big.NewInt(2), big.NewInt(40), big.NewInt(0))
val4.Tokens = sdk.NewDecFromBigInt(x)
tests := []struct {
validator types.Validator
wantHex string
}{
{val1, "05303030303030303030303132ffffffffffffffffffff"},
{val1, "05303030303030303030303030ffffffffffffffffffff"},
{val2, "05303030303030303030303031ffffffffffffffffffff"},
{val3, "05303030303030303030303130ffffffffffffffffffff"},
{val4, "0531303939353131363237373736ffffffffffffffffffff"},
}
for i, tt := range tests {
got := hex.EncodeToString(getValidatorPowerRank(tt.validator))
@ -33,3 +46,45 @@ func TestGetValidatorPowerRank(t *testing.T) {
assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i)
}
}
func TestGetREDByValDstIndexKey(t *testing.T) {
tests := []struct {
delAddr sdk.AccAddress
valSrcAddr sdk.ValAddress
valDstAddr sdk.ValAddress
wantHex string
}{
{sdk.AccAddress(addr1), sdk.ValAddress(addr1), sdk.ValAddress(addr1),
"0c63d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"},
{sdk.AccAddress(addr1), sdk.ValAddress(addr2), sdk.ValAddress(addr3),
"0c3ab62f0d93849be495e21e3e9013a517038f45bd63d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f2"},
{sdk.AccAddress(addr2), sdk.ValAddress(addr1), sdk.ValAddress(addr3),
"0c3ab62f0d93849be495e21e3e9013a517038f45bd5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f08609"},
}
for i, tt := range tests {
got := hex.EncodeToString(GetREDByValDstIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr))
assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i)
}
}
func TestGetREDByValSrcIndexKey(t *testing.T) {
tests := []struct {
delAddr sdk.AccAddress
valSrcAddr sdk.ValAddress
valDstAddr sdk.ValAddress
wantHex string
}{
{sdk.AccAddress(addr1), sdk.ValAddress(addr1), sdk.ValAddress(addr1),
"0b63d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f0860963d771218209d8bd03c482f69dfba57310f08609"},
{sdk.AccAddress(addr1), sdk.ValAddress(addr2), sdk.ValAddress(addr3),
"0b5ef3b5f25c54946d4a89fc0d09d2f126614540f263d771218209d8bd03c482f69dfba57310f086093ab62f0d93849be495e21e3e9013a517038f45bd"},
{sdk.AccAddress(addr2), sdk.ValAddress(addr1), sdk.ValAddress(addr3),
"0b63d771218209d8bd03c482f69dfba57310f086095ef3b5f25c54946d4a89fc0d09d2f126614540f23ab62f0d93849be495e21e3e9013a517038f45bd"},
}
for i, tt := range tests {
got := hex.EncodeToString(GetREDByValSrcIndexKey(tt.delAddr, tt.valSrcAddr, tt.valDstAddr))
assert.Equal(t, tt.wantHex, got, "Keys did not match on test case %d", i)
}
}