From e498d43d03569dc8dbd7cc19c0e95844c9b31988 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 10 Sep 2018 18:37:58 -0400 Subject: [PATCH] dec coins, refactoring distr --- types/decimal.go | 26 +++++++++++ types/decimal_test.go | 26 +++++++++++ x/distribution/client/cli/tx.go | 6 +-- x/distribution/handler.go | 1 - x/distribution/keeper/delegation.go | 47 +++++++++---------- x/distribution/keeper/validator.go | 21 +++++---- x/distribution/types/dec_coin.go | 72 +++++++++++++++++++++++++++-- 7 files changed, 156 insertions(+), 43 deletions(-) diff --git a/types/decimal.go b/types/decimal.go index 8e7db1340b..d6c93f617a 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -323,6 +323,32 @@ func (d Dec) RoundInt() Int { //___________________________________________________________________________________ +// similar to chopPrecisionAndRound, but always rounds down +func chopPrecisionAndTruncate(d *big.Int) *big.Int { + return d.Quo(d, precisionReuse) +} + +func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndTruncate(tmp) +} + +// RoundInt64 rounds the decimal using bankers rounding +func (d Dec) TruncateInt64() int64 { + chopped := chopPrecisionAndTruncateNonMutative(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// RoundInt round the decimal using bankers rounding +func (d Dec) TruncateInt() Int { + return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) +} + +//___________________________________________________________________________________ + // reuse nil values var ( nilAmino string diff --git a/types/decimal_test.go b/types/decimal_test.go index 115eeacb01..b3e10b5101 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -202,6 +202,32 @@ func TestBankerRoundChop(t *testing.T) { } } +func TestTruncate(t *testing.T) { + tests := []struct { + d1 Dec + exp int64 + }{ + {mustNewDecFromStr(t, "0"), 0}, + {mustNewDecFromStr(t, "0.25"), 0}, + {mustNewDecFromStr(t, "0.75"), 0}, + {mustNewDecFromStr(t, "1"), 1}, + {mustNewDecFromStr(t, "1.5"), 1}, + {mustNewDecFromStr(t, "7.5"), 7}, + {mustNewDecFromStr(t, "7.6"), 7}, + {mustNewDecFromStr(t, "7.4"), 7}, + {mustNewDecFromStr(t, "100.1"), 100}, + {mustNewDecFromStr(t, "1000.1"), 1000}, + } + + for tcIndex, tc := range tests { + resNeg := tc.d1.Neg().TruncateInt64() + require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) + + resPos := tc.d1.TruncateInt64() + require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) + } +} + func TestToLeftPadded(t *testing.T) { tests := []struct { dec Dec diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 771572074d..b794d08cb4 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -7,7 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/stake" + distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/spf13/cobra" "github.com/spf13/viper" wire "github.com/tendermint/go-wire" @@ -38,7 +38,7 @@ var ( func GetCmdWithdrawDelegationRewardsAll(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "withdraw-rewards [delegator]", - Short: "withdraw rewards from delegation accounts", + Short: "withdraw rewards for all delegations", RunE: func(cmd *cobra.Command, args []string) error { txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). @@ -61,7 +61,7 @@ func GetCmdWithdrawDelegationRewardsAll(cdc *wire.Codec) *cobra.Command { return err } - msg := stake.NewMsgDelegate(delAddr, valAddr, amount) + msg := distr.NewMsgDelegate(delAddr, valAddr, amount) // build and sign the transaction, then broadcast to Tendermint return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg}) diff --git a/x/distribution/handler.go b/x/distribution/handler.go index 02fb1c3ed6..39371610f4 100644 --- a/x/distribution/handler.go +++ b/x/distribution/handler.go @@ -7,7 +7,6 @@ import ( // distribution fee handler func DistributionFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) { - k. } ////////////////////////////////////////////////////////////////////////////////// diff --git a/x/distribution/keeper/delegation.go b/x/distribution/keeper/delegation.go index 72e96715b3..4c0939aa3c 100644 --- a/x/distribution/keeper/delegation.go +++ b/x/distribution/keeper/delegation.go @@ -30,19 +30,18 @@ func (k Keeper) SetDelegatorDistInfo(ctx sdk.Context, ddi types.DelegatorDistInf //___________________________________________________________________________________________ -// XXX TODO -func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr, validatorAddr, withdrawAddr sdk.AccAddress) { - height = ctx.BlockHeight() +// withdraw all the rewards for a single delegation +func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr, + withdrawAddr sdk.AccAddress, validatorAddr sdk.ValAddress) { - // get all distribution scenarios - pool = stake.GetPool() - feePool = GetFeePool() - delInfo = GetDelegationDistInfo(delegatorAddr, - validatorAddr) - valInfo = GetValidatorDistInfo(validatorAddr) - validator = GetValidator(validatorAddr) + height := ctx.BlockHeight() + pool := stake.GetPool() + feePool := GetFeePool() + delInfo := GetDelegationDistInfo(delegatorAddr, validatorAddr) + valInfo := GetValidatorDistInfo(validatorAddr) + validator := GetValidator(validatorAddr) - feePool, withdraw = delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens, + feePool, withdraw := delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens, validator.Tokens, validator.DelegatorShares, validator.Commission) SetFeePool(feePool) @@ -51,30 +50,30 @@ func (k Keeper) WithdrawDelegationReward(ctx sdk.Context, delegatorAddr, validat /////////////////////////////////////////////////////////////////////////////////////// -// XXX TODO +// return all rewards for all delegations of a delegator func (k Keeper) WithdrawDelegationRewardsAll(ctx sdk.Context, delegatorAddr, withdrawAddr sdk.AccAddress) { - height = ctx.BlockHeight() - withdraw = GetDelegatorRewardsAll(delegatorAddr, height) + height := ctx.BlockHeight() + withdraw = GetDelegatorRewardsAll(ctx, delegatorAddr, height) k.coinsKeeper.AddCoins(withdrawAddr, withdraw.Amount.TruncateDecimal()) } -// XXX TODO +// return all rewards for all delegations of a delegator func (k Keeper) GetDelegatorRewardsAll(ctx sdk.Context, delAddr sdk.AccAddress, height int64) DecCoins { - // collect all entitled rewards - withdraw = 0 - pool = stake.GetPool() - feePool = GetFeePool() + withdraw := sdk.NewDec(0) + pool := stake.GetPool() + feePool := GetFeePool() // iterate over all the delegations operationAtDelegation := func(_ int64, del types.Delegation) (stop bool) { - delInfo = GetDelegationDistInfo(delAddr, del.ValidatorAddr) - valInfo = GetValidatorDistInfo(del.ValidatorAddr) - validator = GetValidator(del.ValidatorAddr) + delInfo := GetDelegationDistInfo(delAddr, del.ValidatorAddr) + valInfo := GetValidatorDistInfo(del.ValidatorAddr) + validator := GetValidator(del.ValidatorAddr) - feePool, diWithdraw = delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens, + feePool, diWithdraw := delInfo.WithdrawRewards(feePool, valInfo, height, pool.BondedTokens, validator.Tokens, validator.DelegatorShares, validator.Commission) - withdraw += diWithdraw + withdraw = withdraw.Add(diWithdraw) + SetFeePool(feePool) return false } k.stakeKeeper.IterateDelegations(ctx, delAddr, operationAtDelegation) diff --git a/x/distribution/keeper/validator.go b/x/distribution/keeper/validator.go index 9a057b85b9..7643dd834b 100644 --- a/x/distribution/keeper/validator.go +++ b/x/distribution/keeper/validator.go @@ -28,21 +28,22 @@ func (k Keeper) SetValidatorDistInfo(ctx sdk.Context, vdi types.ValidatorDistInf } // XXX TODO -func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, operatorAddr, withdrawAddr sdk.AccAddress) { - height = ctx.BlockHeight() - feePool = k.GetFeePool(ctx) - pool = k.stakeKeeper.GetPool(ctx) - ValInfo = k.GetValidatorDistInfo(delegation.ValidatorAddr) - validator = k.GetValidator(delegation.ValidatorAddr) +func (k Keeper) WithdrawValidatorRewardsAll(ctx sdk.Context, + operatorAddr sdk.ValAddress, withdrawAddr sdk.AccAddress) { // withdraw self-delegation - withdraw = k.GetDelegatorRewardsAll(validator.OperatorAddr, height) + height := ctx.BlockHeight() + validator := k.GetValidator(operatorAddr) + withdraw := k.GetDelegatorRewardsAll(validator.OperatorAddr, height) // withdrawal validator commission rewards - feePool, commission = valInfo.WithdrawCommission(feePool, valInfo, height, pool.BondedTokens, + pool := k.stakeKeeper.GetPool(ctx) + valInfo := k.GetValidatorDistInfo(operatorAddr) + feePool := k.GetFeePool(ctx) + feePool, commission := valInfo.WithdrawCommission(feePool, valInfo, height, pool.BondedTokens, validator.Tokens, validator.Commission) - withdraw += commission - SetFeePool(feePool) + withdraw = withdraw.Add(commission) + k.SetFeePool(feePool) k.coinKeeper.AddCoins(withdrawAddr, withdraw.TruncateDecimal()) } diff --git a/x/distribution/types/dec_coin.go b/x/distribution/types/dec_coin.go index 1bc803530a..70fd4a5820 100644 --- a/x/distribution/types/dec_coin.go +++ b/x/distribution/types/dec_coin.go @@ -1,23 +1,42 @@ package types -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + "strings" -// coins with decimal -type DecCoins []DecCoin + sdk "github.com/cosmos/cosmos-sdk/types" +) // Coins which can have additional decimal points type DecCoin struct { - Amount sdk.Dec `json:"amount"` Denom string `json:"denom"` + Amount sdk.Dec `json:"amount"` } func NewDecCoin(coin sdk.Coin) DecCoin { return DecCoins{ - Amount: sdk.NewDec(coin.Amount), Denom: coin.Denom, + Amount: sdk.NewDec(coin.Amount), } } +// Adds amounts of two coins with same denom +func (coin DecCoin) Plus(coinB DecCoin) DecCoin { + if !(coin.Denom == coinB.Denom) { + return coin + } + return Coin{coin.Denom, coin.Amount.Add(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)) @@ -25,3 +44,46 @@ func NewDecCoins(coins sdk.Coins) DecCoins { dcs[i] = NewDecCoin(coin) } } + +// return the decimal coins with trunctated decimals +func (coins DecCoins) TruncateDecimal() sdk.Coins { + out := make(DecCoins, len(coins)) + for i, coin := range coins { + out[i] = coin.TruncateDecimal() + } +} + +// 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++ + } + } +}