Skip to content

Commit cd44f29

Browse files
authored
Merge PR #3359: Round down when calculating rewards (fixes F1 sim bug)
1 parent 39cb0e0 commit cd44f29

File tree

5 files changed

+63
-5
lines changed

5 files changed

+63
-5
lines changed

PENDING.md

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ BREAKING CHANGES
3030
* [\#1894](https://github.com/cosmos/cosmos-sdk/pull/1894) `version` command now shows latest commit, vendor dir hash, and build machine info.
3131

3232
* SDK
33+
* [distribution] \#3359 Always round down when calculating rewards-to-be-withdrawn in F1 fee distribution
3334
* [staking] \#2513 Validator power type from Dec -> Int
3435
* [staking] \#3233 key and value now contain duplicate fields to simplify code
3536
* [\#3064](https://github.com/cosmos/cosmos-sdk/issues/3064) Sanitize `sdk.Coin` denom. Coins denoms are now case insensitive, i.e. 100fooToken equals to 100FOOTOKEN.

types/dec_coin.go

+26
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,19 @@ func (coins DecCoins) MulDec(d Dec) DecCoins {
201201
return res
202202
}
203203

204+
// multiply all the coins by a decimal, truncating
205+
func (coins DecCoins) MulDecTruncate(d Dec) DecCoins {
206+
res := make([]DecCoin, len(coins))
207+
for i, coin := range coins {
208+
product := DecCoin{
209+
Denom: coin.Denom,
210+
Amount: coin.Amount.MulTruncate(d),
211+
}
212+
res[i] = product
213+
}
214+
return res
215+
}
216+
204217
// divide all the coins by a decimal
205218
func (coins DecCoins) QuoDec(d Dec) DecCoins {
206219
res := make([]DecCoin, len(coins))
@@ -214,6 +227,19 @@ func (coins DecCoins) QuoDec(d Dec) DecCoins {
214227
return res
215228
}
216229

230+
// divide all the coins by a decimal, truncating
231+
func (coins DecCoins) QuoDecTruncate(d Dec) DecCoins {
232+
res := make([]DecCoin, len(coins))
233+
for i, coin := range coins {
234+
quotient := DecCoin{
235+
Denom: coin.Denom,
236+
Amount: coin.Amount.QuoTruncate(d),
237+
}
238+
res[i] = quotient
239+
}
240+
return res
241+
}
242+
217243
// returns the amount of a denom from deccoins
218244
func (coins DecCoins) AmountOf(denom string) Dec {
219245
switch len(coins) {

types/decimal.go

+27
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,17 @@ func (d Dec) Mul(d2 Dec) Dec {
228228
return Dec{chopped}
229229
}
230230

231+
// multiplication truncate
232+
func (d Dec) MulTruncate(d2 Dec) Dec {
233+
mul := new(big.Int).Mul(d.Int, d2.Int)
234+
chopped := chopPrecisionAndTruncate(mul)
235+
236+
if chopped.BitLen() > 255+DecimalPrecisionBits {
237+
panic("Int overflow")
238+
}
239+
return Dec{chopped}
240+
}
241+
231242
// multiplication
232243
func (d Dec) MulInt(i Int) Dec {
233244
mul := new(big.Int).Mul(d.Int, i.i)
@@ -254,6 +265,22 @@ func (d Dec) Quo(d2 Dec) Dec {
254265
return Dec{chopped}
255266
}
256267

268+
// quotient truncate
269+
func (d Dec) QuoTruncate(d2 Dec) Dec {
270+
271+
// multiply precision twice
272+
mul := new(big.Int).Mul(d.Int, precisionReuse)
273+
mul.Mul(mul, precisionReuse)
274+
275+
quo := new(big.Int).Quo(mul, d2.Int)
276+
chopped := chopPrecisionAndTruncate(quo)
277+
278+
if chopped.BitLen() > 255+DecimalPrecisionBits {
279+
panic("Int overflow")
280+
}
281+
return Dec{chopped}
282+
}
283+
257284
// quotient
258285
func (d Dec) QuoInt(i Int) Dec {
259286
mul := new(big.Int).Quo(d.Int, i.i)

x/distribution/keeper/delegation.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sd
1616

1717
// calculate delegation stake in tokens
1818
// we don't store directly, so multiply delegation shares * (tokens per share)
19-
stake := delegation.GetShares().Mul(validator.GetDelegatorShareExRate())
19+
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
20+
stake := delegation.GetShares().MulTruncate(validator.GetDelegatorShareExRate())
2021
k.SetDelegatorStartingInfo(ctx, val, del, types.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight())))
2122
}
2223

2324
// calculate the rewards accrued by a delegation between two periods
2425
func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val sdk.Validator,
25-
startingPeriod, endingPeriod uint64, staking sdk.Dec) (rewards sdk.DecCoins) {
26+
startingPeriod, endingPeriod uint64, stake sdk.Dec) (rewards sdk.DecCoins) {
2627
// sanity check
2728
if startingPeriod > endingPeriod {
2829
panic("startingPeriod cannot be greater than endingPeriod")
@@ -32,7 +33,8 @@ func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val sdk.Valid
3233
starting := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), startingPeriod)
3334
ending := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), endingPeriod)
3435
difference := ending.CumulativeRewardRatio.Minus(starting.CumulativeRewardRatio)
35-
rewards = difference.MulDec(staking)
36+
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
37+
rewards = difference.MulDecTruncate(stake)
3638
return
3739
}
3840

@@ -54,7 +56,8 @@ func (k Keeper) calculateDelegationRewards(ctx sdk.Context, val sdk.Validator, d
5456
func(height uint64, event types.ValidatorSlashEvent) (stop bool) {
5557
endingPeriod := event.ValidatorPeriod
5658
rewards = rewards.Plus(k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake))
57-
stake = stake.Mul(sdk.OneDec().Sub(event.Fraction))
59+
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
60+
stake = stake.MulTruncate(sdk.OneDec().Sub(event.Fraction))
5861
startingPeriod = endingPeriod
5962
return false
6063
},

x/distribution/keeper/validator.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ func (k Keeper) incrementValidatorPeriod(ctx sdk.Context, val sdk.Validator) uin
3838

3939
current = sdk.DecCoins{}
4040
} else {
41-
current = rewards.Rewards.QuoDec(sdk.NewDecFromInt(val.GetTokens()))
41+
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
42+
current = rewards.Rewards.QuoDecTruncate(sdk.NewDecFromInt(val.GetTokens()))
4243
}
4344

4445
// fetch historical rewards for last period

0 commit comments

Comments
 (0)