Merge branch 'develop' into release/v0.30.0

This commit is contained in:
Christopher Goes 2019-01-24 22:03:21 +01:00
commit 5ab744082a
22 changed files with 264 additions and 75 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@
# Build
vendor
vendor-deps
.vendor-new
build
tools/bin/*

View File

@ -33,6 +33,7 @@ BREAKING CHANGES
* [\#3249\(https://github.com/cosmos/cosmos-sdk/issues/3249) `tendermint`'s `show-validator` and `show-address` `--json` flags removed in favor of `--output-format=json`.
* SDK
* [distribution] \#3359 Always round down when calculating rewards-to-be-withdrawn in F1 fee distribution
* [#3336](https://github.com/cosmos/cosmos-sdk/issues/3336) Ensure all SDK
messages have their signature bytes contain canonical fields `value` and `type`.
* [\#3333](https://github.com/cosmos/cosmos-sdk/issues/3333) - F1 storage efficiency improvements - automatic withdrawals when unbonded, historical reward reference counting

View File

@ -62,13 +62,13 @@ else
go build $(BUILD_FLAGS) -o build/gaiakeyutil ./cmd/gaia/cmd/gaiakeyutil
endif
build-linux:
build-linux: vendor-deps
LEDGER_ENABLED=false GOOS=linux GOARCH=amd64 $(MAKE) build
update_gaia_lite_docs:
@statik -src=client/lcd/swagger-ui -dest=client/lcd -f
install: check-ledger update_gaia_lite_docs
install: vendor-deps check-ledger update_gaia_lite_docs
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiad
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiacli
go install $(BUILD_FLAGS) ./cmd/gaia/cmd/gaiareplay
@ -156,15 +156,15 @@ test_sim_gaia_fast:
test_sim_gaia_import_export:
@echo "Running Gaia import/export simulation. This may take several minutes..."
@bash scripts/multisim.sh 50 TestGaiaImportExport
@bash scripts/multisim.sh 50 5 TestGaiaImportExport
test_sim_gaia_simulation_after_import:
@echo "Running Gaia simulation-after-import. This may take several minutes..."
@bash scripts/multisim.sh 50 TestGaiaSimulationAfterImport
@bash scripts/multisim.sh 50 5 TestGaiaSimulationAfterImport
test_sim_gaia_multi_seed:
@echo "Running multi-seed Gaia simulation. This may take awhile!"
@bash scripts/multisim.sh 400 TestFullGaiaSimulation
@bash scripts/multisim.sh 400 5 TestFullGaiaSimulation
SIM_NUM_BLOCKS ?= 500
SIM_BLOCK_SIZE ?= 200

View File

@ -64,9 +64,13 @@ type GenesisAccount struct {
Coins sdk.Coins `json:"coins"`
Sequence uint64 `json:"sequence_number"`
AccountNumber uint64 `json:"account_number"`
Vesting bool `json:"vesting"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
// vesting account fields
OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization
DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation
DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation
StartTime int64 `json:"start_time"` // vesting start time
EndTime int64 `json:"end_time"` // vesting end time
}
func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount {
@ -88,7 +92,9 @@ func NewGenesisAccountI(acc auth.Account) GenesisAccount {
vacc, ok := acc.(auth.VestingAccount)
if ok {
gacc.Vesting = true
gacc.OriginalVesting = vacc.GetOriginalVesting()
gacc.DelegatedFree = vacc.GetDelegatedFree()
gacc.DelegatedVesting = vacc.GetDelegatedVesting()
gacc.StartTime = vacc.GetStartTime()
gacc.EndTime = vacc.GetEndTime()
}
@ -105,11 +111,24 @@ func (ga *GenesisAccount) ToAccount() auth.Account {
Sequence: ga.Sequence,
}
if ga.Vesting {
if !ga.OriginalVesting.IsZero() {
baseVestingAcc := &auth.BaseVestingAccount{
BaseAccount: bacc,
OriginalVesting: ga.OriginalVesting,
DelegatedFree: ga.DelegatedFree,
DelegatedVesting: ga.DelegatedVesting,
EndTime: ga.EndTime,
}
if ga.StartTime != 0 && ga.EndTime != 0 {
return auth.NewContinuousVestingAccount(bacc, ga.StartTime, ga.EndTime)
return &auth.ContinuousVestingAccount{
BaseVestingAccount: baseVestingAcc,
StartTime: ga.StartTime,
}
} else if ga.EndTime != 0 {
return auth.NewDelayedVestingAccount(bacc, ga.EndTime)
return &auth.DelayedVestingAccount{
BaseVestingAccount: baseVestingAcc,
}
} else {
panic(fmt.Sprintf("invalid genesis vesting account: %+v", ga))
}

View File

@ -56,6 +56,7 @@ func TestToAccount(t *testing.T) {
priv := ed25519.GenPrivKey()
addr := sdk.AccAddress(priv.PubKey().Address())
authAcc := auth.NewBaseAccountWithAddress(addr)
authAcc.SetCoins(sdk.Coins{sdk.NewInt64Coin(bondDenom, 150)})
genAcc := NewGenesisAccount(&authAcc)
acc := genAcc.ToAccount()
require.IsType(t, &auth.BaseAccount{}, acc)

View File

@ -425,8 +425,10 @@ func TestGaiaImportExport(t *testing.T) {
storeB := ctxB.KVStore(storeKeyB)
kvA, kvB, count, equal := sdk.DiffKVStores(storeA, storeB, prefixes)
fmt.Printf("Compared %d key/value pairs between %s and %s\n", count, storeKeyA, storeKeyB)
require.True(t, equal, "unequal stores: %s / %s:\nstore A %s (%X) => %s (%X)\nstore B %s (%X) => %s (%X)",
storeKeyA, storeKeyB, kvA.Key, kvA.Key, kvA.Value, kvA.Value, kvB.Key, kvB.Key, kvB.Value, kvB.Value)
require.True(t, equal,
"unequal stores: %s / %s:\nstore A %X => %X\nstore B %X => %X",
storeKeyA, storeKeyB, kvA.Key, kvA.Value, kvB.Key, kvB.Value,
)
}
}

View File

@ -16,14 +16,15 @@ This module will be used in the Cosmos Hub.
1. **[State](state.md)**
1. [Accounts](state.md#accounts)
1. [Account Interface](state.md#account-interface)
1. [Base Account](state.md#baseaccount)
1. [Vesting Account](state.md#vestingaccount)
2. [Base Account](state.md#baseaccount)
3. [Vesting Account](state.md#vestingaccount)
1. **[Types](types.md)**
1. [StdFee](types.md#stdfee)
1. [StdSignature](types.md#stdsignature)
1. [StdTx](types.md#stdtx)
1. [StdSignDoc](types.md#stdsigndoc)
2. [StdSignature](types.md#stdsignature)
3. [StdTx](types.md#stdtx)
4. [StdSignDoc](types.md#stdsigndoc)
1. **[Keepers](keepers.md)**
1. [AccountKeeper](keepers.md#account-keeper)
1. **[Handlers](handlers.md)**
1. [Ante Handler](handlers.md#ante-handler)
1. **[Gas & Fees](gas_fees.md)**

View File

@ -0,0 +1,28 @@
# Gas & Fees
Fees serve two purposes for an operator of the network.
Fees limit the growth of the state stored by every full node and allow for
general purpose censorship of transactions of little economic value. Fees
are best suited as an anti-spam mechanism where validators are disinterested in
the use of the network and identities of users.
Fees are determined by the gas limits and gas prices transactions provide.
Operators should set minimum gas prices when starting their nodes. They must set
the unit costs of gas in each token denomination they wish to support:
`gaiad start ... --minimum-gas-prices=0.00001steak,0.05photinos`
When adding transactions to mempool or gossipping transactions, validators check
if the transaction's gas prices, which are determined by the provided fees, meet
any of the validator's minimum gas prices. In other words, a transaction must
provide a fee of at least one denomination that matches a validator's minimum
gas price.
Tendermint does not currently provide fee based mempool prioritization, and fee
based mempool filtering is local to node and not part of consensus. But with
minimum gas prices set, such a mechanism could be implemented by node operators.
Because the market value for tokens will fluctuate, validators are expected to
dynamically adjust their minimum gas prices to a level that would encourage the
use of the network.

View File

@ -316,19 +316,22 @@ and return the correct accounts accordingly based off of these new fields.
type GenesisAccount struct {
// ...
Vesting bool
EndTime int64
StartTime int64
// vesting account fields
OriginalVesting sdk.Coins `json:"original_vesting"`
DelegatedFree sdk.Coins `json:"delegated_free"`
DelegatedVesting sdk.Coins `json:"delegated_vesting"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
}
func ToAccount(gacc GenesisAccount) Account {
bacc := NewBaseAccount(gacc)
if gacc.Vesting {
if gacc.OriginalVesting > 0 {
if ga.StartTime != 0 && ga.EndTime != 0 {
return NewContinuousVestingAccount(bacc, gacc.StartTime, gacc.EndTime)
// return a continuous vesting account
} else if ga.EndTime != 0 {
return NewDelayedVestingAccount(bacc, gacc.EndTime)
// return a delayed vesting account
} else {
// invalid genesis vesting account provided
panic()

View File

@ -23,7 +23,7 @@ sim() {
file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -u +"%Y-%m-%dT%H:%M:%S+00:00").stdout"
echo "Writing stdout to $file..."
go test ./cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks \
-SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -SimulationPeriod=$period -v -timeout 24h | tee $file
-SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -SimulationPeriod=$period -v -timeout 24h > $file
}
i=0

View File

@ -201,6 +201,19 @@ func (coins DecCoins) MulDec(d Dec) DecCoins {
return res
}
// multiply all the coins by a decimal, truncating
func (coins DecCoins) MulDecTruncate(d Dec) DecCoins {
res := make([]DecCoin, len(coins))
for i, coin := range coins {
product := DecCoin{
Denom: coin.Denom,
Amount: coin.Amount.MulTruncate(d),
}
res[i] = product
}
return res
}
// divide all the coins by a decimal
func (coins DecCoins) QuoDec(d Dec) DecCoins {
res := make([]DecCoin, len(coins))
@ -214,6 +227,19 @@ func (coins DecCoins) QuoDec(d Dec) DecCoins {
return res
}
// divide all the coins by a decimal, truncating
func (coins DecCoins) QuoDecTruncate(d Dec) DecCoins {
res := make([]DecCoin, len(coins))
for i, coin := range coins {
quotient := DecCoin{
Denom: coin.Denom,
Amount: coin.Amount.QuoTruncate(d),
}
res[i] = quotient
}
return res
}
// returns the amount of a denom from deccoins
func (coins DecCoins) AmountOf(denom string) Dec {
switch len(coins) {

View File

@ -228,6 +228,17 @@ func (d Dec) Mul(d2 Dec) Dec {
return Dec{chopped}
}
// multiplication truncate
func (d Dec) MulTruncate(d2 Dec) Dec {
mul := new(big.Int).Mul(d.Int, d2.Int)
chopped := chopPrecisionAndTruncate(mul)
if chopped.BitLen() > 255+DecimalPrecisionBits {
panic("Int overflow")
}
return Dec{chopped}
}
// multiplication
func (d Dec) MulInt(i Int) Dec {
mul := new(big.Int).Mul(d.Int, i.i)
@ -254,6 +265,22 @@ func (d Dec) Quo(d2 Dec) Dec {
return Dec{chopped}
}
// quotient truncate
func (d Dec) QuoTruncate(d2 Dec) Dec {
// multiply precision twice
mul := new(big.Int).Mul(d.Int, precisionReuse)
mul.Mul(mul, precisionReuse)
quo := new(big.Int).Quo(mul, d2.Int)
chopped := chopPrecisionAndTruncate(quo)
if chopped.BitLen() > 255+DecimalPrecisionBits {
panic("Int overflow")
}
return Dec{chopped}
}
// quotient
func (d Dec) QuoInt(i Int) Dec {
mul := new(big.Int).Quo(d.Int, i.i)

View File

@ -55,6 +55,10 @@ type VestingAccount interface {
GetStartTime() int64
GetEndTime() int64
GetOriginalVesting() sdk.Coins
GetDelegatedFree() sdk.Coins
GetDelegatedVesting() sdk.Coins
}
// AccountDecoder unmarshals account bytes
@ -179,18 +183,20 @@ type BaseVestingAccount struct {
// String implements fmt.Stringer
func (bva BaseVestingAccount) String() string {
return fmt.Sprintf(`Vesting Account %s:
return fmt.Sprintf(`Vesting Account:
Address: %s
Coins: %s
PubKey: %s
AccountNumber: %d
Sequence: %d
OriginalVesting: %s
DelegatedFree: %s
DelegatedVesting: %s
EndTime: %d `, bva.Address, bva.Coins,
bva.PubKey.Address(), bva.AccountNumber, bva.Sequence,
EndTime: %d `,
bva.Address, bva.Coins,
bva.AccountNumber, bva.Sequence,
bva.OriginalVesting, bva.DelegatedFree,
bva.DelegatedVesting, bva.EndTime)
bva.DelegatedVesting, bva.EndTime,
)
}
// spendableCoins returns all the spendable coins for a vesting account given a
@ -342,6 +348,23 @@ func (bva *BaseVestingAccount) TrackUndelegation(amount sdk.Coins) {
}
}
// GetOriginalVesting returns a vesting account's original vesting amount
func (bva BaseVestingAccount) GetOriginalVesting() sdk.Coins {
return bva.OriginalVesting
}
// GetDelegatedFree returns a vesting account's delegation amount that is not
// vesting.
func (bva BaseVestingAccount) GetDelegatedFree() sdk.Coins {
return bva.DelegatedFree
}
// GetDelegatedVesting returns a vesting account's delegation amount that is
// still vesting.
func (bva BaseVestingAccount) GetDelegatedVesting() sdk.Coins {
return bva.DelegatedVesting
}
//-----------------------------------------------------------------------------
// Continuous Vesting Account
@ -372,19 +395,21 @@ func NewContinuousVestingAccount(
}
func (cva ContinuousVestingAccount) String() string {
return fmt.Sprintf(`Vesting Account %s:
return fmt.Sprintf(`Continuous Vesting Account:
Address: %s
Coins: %s
PubKey: %s
AccountNumber: %d
Sequence: %d
OriginalVesting: %s
DelegatedFree: %s
DelegatedVesting: %s
StartTime: %d
EndTime: %d `, cva.Address, cva.Coins,
cva.PubKey.Address(), cva.AccountNumber, cva.Sequence,
EndTime: %d `,
cva.Address, cva.Coins,
cva.AccountNumber, cva.Sequence,
cva.OriginalVesting, cva.DelegatedFree,
cva.DelegatedVesting, cva.StartTime, cva.EndTime)
cva.DelegatedVesting, cva.StartTime, cva.EndTime,
)
}
// GetVestedCoins returns the total number of vested coins. If no coins are vested,

View File

@ -28,24 +28,36 @@ func SimulateDeductFee(m auth.AccountKeeper, f auth.FeeCollectionKeeper) simulat
}
denomIndex := r.Intn(len(initCoins))
amt, err := randPositiveInt(r, initCoins[denomIndex].Amount)
randCoin := initCoins[denomIndex]
amt, err := randPositiveInt(r, randCoin.Amount)
if err != nil {
event(fmt.Sprintf("auth/SimulateDeductFee/false"))
return action, nil, nil
}
coins := sdk.Coins{sdk.NewCoin(initCoins[denomIndex].Denom, amt)}
err = stored.SetCoins(initCoins.Minus(coins))
if err != nil {
// Create a random fee and verify the fees are within the account's spendable
// balance.
fees := sdk.Coins{sdk.NewCoin(randCoin.Denom, amt)}
spendableCoins := stored.SpendableCoins(ctx.BlockHeader().Time)
if _, hasNeg := spendableCoins.SafeMinus(fees); hasNeg {
event(fmt.Sprintf("auth/SimulateDeductFee/false"))
return action, nil, nil
}
// get the new account balance
newCoins, hasNeg := initCoins.SafeMinus(fees)
if hasNeg {
event(fmt.Sprintf("auth/SimulateDeductFee/false"))
return action, nil, nil
}
if err := stored.SetCoins(newCoins); err != nil {
panic(err)
}
m.SetAccount(ctx, stored)
if !coins.IsNotNegative() {
panic("setting negative fees")
}
f.AddCollectedFees(ctx, coins)
f.AddCollectedFees(ctx, fees)
event(fmt.Sprintf("auth/SimulateDeductFee/true"))
action = "TestDeductFee"

View File

@ -11,28 +11,41 @@ func (k Keeper) initializeDelegation(ctx sdk.Context, val sdk.ValAddress, del sd
// period has already been incremented - we want to store the period ended by this delegation action
previousPeriod := k.GetValidatorCurrentRewards(ctx, val).Period - 1
// increment reference count for the period we're going to track
k.incrementReferenceCount(ctx, val, previousPeriod)
validator := k.stakingKeeper.Validator(ctx, val)
delegation := k.stakingKeeper.Delegation(ctx, del, val)
// calculate delegation stake in tokens
// we don't store directly, so multiply delegation shares * (tokens per share)
stake := delegation.GetShares().Mul(validator.GetDelegatorShareExRate())
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
stake := delegation.GetShares().MulTruncate(validator.GetDelegatorShareExRate())
k.SetDelegatorStartingInfo(ctx, val, del, types.NewDelegatorStartingInfo(previousPeriod, stake, uint64(ctx.BlockHeight())))
}
// calculate the rewards accrued by a delegation between two periods
func (k Keeper) calculateDelegationRewardsBetween(ctx sdk.Context, val sdk.Validator,
startingPeriod, endingPeriod uint64, staking sdk.Dec) (rewards sdk.DecCoins) {
startingPeriod, endingPeriod uint64, stake sdk.Dec) (rewards sdk.DecCoins) {
// sanity check
if startingPeriod > endingPeriod {
panic("startingPeriod cannot be greater than endingPeriod")
}
// sanity check
if stake.LT(sdk.ZeroDec()) {
panic("stake should not be negative")
}
// return staking * (ending - starting)
starting := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), startingPeriod)
ending := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), endingPeriod)
difference := ending.CumulativeRewardRatio.Minus(starting.CumulativeRewardRatio)
rewards = difference.MulDec(staking)
if difference.HasNegative() {
panic("negative rewards should not be possible")
}
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
rewards = difference.MulDecTruncate(stake)
return
}
@ -54,7 +67,8 @@ func (k Keeper) calculateDelegationRewards(ctx sdk.Context, val sdk.Validator, d
func(height uint64, event types.ValidatorSlashEvent) (stop bool) {
endingPeriod := event.ValidatorPeriod
rewards = rewards.Plus(k.calculateDelegationRewardsBetween(ctx, val, startingPeriod, endingPeriod, stake))
stake = stake.Mul(sdk.OneDec().Sub(event.Fraction))
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
stake = stake.MulTruncate(sdk.OneDec().Sub(event.Fraction))
startingPeriod = endingPeriod
return false
},

View File

@ -30,13 +30,13 @@ func TestCalculateRewardsBasic(t *testing.T) {
del := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1)
// historical count should be 2 (once for validator init, once for delegation init)
require.Equal(t, uint64(2), k.GetValidatorHistoricalRewardCount(ctx))
require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx))
// end period
endingPeriod := k.incrementValidatorPeriod(ctx, val)
// historical count should be 3 (since we ended the period, and haven't yet decremented a reference)
require.Equal(t, uint64(3), k.GetValidatorHistoricalRewardCount(ctx))
// historical count should be 2 still
require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx))
// calculate delegation rewards
rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod)
@ -283,13 +283,13 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) {
k.AllocateTokensToValidator(ctx, val, tokens)
// historical count should be 2 (initial + latest for delegation)
require.Equal(t, uint64(2), k.GetValidatorHistoricalRewardCount(ctx))
require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx))
// withdraw rewards
require.Nil(t, k.WithdrawDelegationRewards(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1))
// historical count should still be 2 (added one record, cleared one)
require.Equal(t, uint64(2), k.GetValidatorHistoricalRewardCount(ctx))
require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx))
// assert correct balance
require.Equal(t, sdk.Coins{{staking.DefaultBondDenom, sdk.NewInt(balance - bond + (initial / 2))}}, ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins())
@ -460,14 +460,14 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
k.AllocateTokensToValidator(ctx, val, tokens)
// historical count should be 2 (validator init, delegation init)
require.Equal(t, uint64(2), k.GetValidatorHistoricalRewardCount(ctx))
require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx))
// second delegation
msg2 := staking.NewMsgDelegate(sdk.AccAddress(valOpAddr2), valOpAddr1, sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)))
require.True(t, sh(ctx, msg2).IsOK())
// historical count should be 3 (second delegation init)
require.Equal(t, uint64(3), k.GetValidatorHistoricalRewardCount(ctx))
require.Equal(t, uint64(3), k.GetValidatorHistoricalReferenceCount(ctx))
// fetch updated validator
val = sk.Validator(ctx, valOpAddr1)
@ -486,7 +486,7 @@ func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) {
k.WithdrawDelegationRewards(ctx, sdk.AccAddress(valOpAddr2), valOpAddr1)
// historical count should be 3 (validator init + two delegations)
require.Equal(t, uint64(3), k.GetValidatorHistoricalRewardCount(ctx))
require.Equal(t, uint64(3), k.GetValidatorHistoricalReferenceCount(ctx))
// validator withdraws commission
k.WithdrawValidatorCommission(ctx, valOpAddr1)

View File

@ -175,13 +175,15 @@ func (k Keeper) DeleteAllValidatorHistoricalRewards(ctx sdk.Context) {
}
}
// historical record count (used for testcases)
func (k Keeper) GetValidatorHistoricalRewardCount(ctx sdk.Context) (count uint64) {
// historical reference count (used for testcases)
func (k Keeper) GetValidatorHistoricalReferenceCount(ctx sdk.Context) (count uint64) {
store := ctx.KVStore(k.storeKey)
iter := sdk.KVStorePrefixIterator(store, ValidatorHistoricalRewardsPrefix)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
count++
var rewards types.ValidatorHistoricalRewards
k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &rewards)
count += uint64(rewards.ReferenceCount)
}
return
}

View File

@ -38,12 +38,16 @@ func (k Keeper) incrementValidatorPeriod(ctx sdk.Context, val sdk.Validator) uin
current = sdk.DecCoins{}
} else {
current = rewards.Rewards.QuoDec(sdk.NewDecFromInt(val.GetTokens()))
// note: necessary to truncate so we don't allow withdrawing more rewards than owed
current = rewards.Rewards.QuoDecTruncate(sdk.NewDecFromInt(val.GetTokens()))
}
// fetch historical rewards for last period
historical := k.GetValidatorHistoricalRewards(ctx, val.GetOperator(), rewards.Period-1).CumulativeRewardRatio
// decrement reference count
k.decrementReferenceCount(ctx, val.GetOperator(), rewards.Period-1)
// set new historical rewards with reference count of 1
k.SetValidatorHistoricalRewards(ctx, val.GetOperator(), rewards.Period, types.NewValidatorHistoricalRewards(historical.Plus(current), 1))
@ -56,8 +60,8 @@ func (k Keeper) incrementValidatorPeriod(ctx sdk.Context, val sdk.Validator) uin
// increment the reference count for a historical rewards value
func (k Keeper) incrementReferenceCount(ctx sdk.Context, valAddr sdk.ValAddress, period uint64) {
historical := k.GetValidatorHistoricalRewards(ctx, valAddr, period)
if historical.ReferenceCount > 1 {
panic("reference count should never exceed 1")
if historical.ReferenceCount > 2 {
panic("reference count should never exceed 2")
}
historical.ReferenceCount++
k.SetValidatorHistoricalRewards(ctx, valAddr, period, historical)
@ -78,6 +82,9 @@ func (k Keeper) decrementReferenceCount(ctx sdk.Context, valAddr sdk.ValAddress,
}
func (k Keeper) updateValidatorSlashFraction(ctx sdk.Context, valAddr sdk.ValAddress, fraction sdk.Dec) {
if fraction.GT(sdk.OneDec()) {
panic("fraction greater than one")
}
height := uint64(ctx.BlockHeight())
currentFraction := sdk.ZeroDec()
endedPeriod := k.GetValidatorCurrentRewards(ctx, valAddr).Period - 1
@ -91,9 +98,14 @@ func (k Keeper) updateValidatorSlashFraction(ctx sdk.Context, valAddr sdk.ValAdd
val := k.stakingKeeper.Validator(ctx, valAddr)
// increment current period
endedPeriod = k.incrementValidatorPeriod(ctx, val)
// increment reference count on period we need to track
k.incrementReferenceCount(ctx, valAddr, endedPeriod)
}
currentMultiplicand := sdk.OneDec().Sub(currentFraction)
newMultiplicand := sdk.OneDec().Sub(fraction)
updatedFraction := sdk.OneDec().Sub(currentMultiplicand.Mul(newMultiplicand))
if updatedFraction.LT(sdk.ZeroDec()) {
panic("negative slash fraction")
}
k.SetValidatorSlashEvent(ctx, valAddr, height, types.NewValidatorSlashEvent(endedPeriod, updatedFraction))
}

View File

@ -85,12 +85,12 @@ func ReferenceCountInvariant(k distr.Keeper, sk staking.Keeper) simulation.Invar
return false
})
// one record per validator (zeroeth period), one record per delegation (previous period), one record per slash (previous period)
// one record per validator (last tracked period), one record per delegation (previous period), one record per slash (previous period)
expected := valCount + uint64(len(dels)) + slashCount
count := k.GetValidatorHistoricalReferenceCount(ctx)
count := k.GetValidatorHistoricalRewardCount(ctx)
if count != expected {
return fmt.Errorf("unexpected number of historical rewards records: expected %v, got %v", expected, count)
return fmt.Errorf("unexpected number of historical rewards records: expected %v (%v vals + %v dels + %v slashes), got %v", expected, valCount, len(dels), slashCount, count)
}
return nil

View File

@ -34,7 +34,9 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
// Manually set indices for the first time
keeper.SetValidatorByConsAddr(ctx, validator)
keeper.SetValidatorByPowerIndex(ctx, validator)
keeper.AfterValidatorCreated(ctx, validator.OperatorAddr)
if !data.Exported {
keeper.AfterValidatorCreated(ctx, validator.OperatorAddr)
}
// Set timeslice if necessary
if validator.Status == sdk.Unbonding {
@ -43,9 +45,13 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [
}
for _, delegation := range data.Bonds {
keeper.BeforeDelegationCreated(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
if !data.Exported {
keeper.BeforeDelegationCreated(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
}
keeper.SetDelegation(ctx, delegation)
keeper.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
if !data.Exported {
keeper.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
}
}
for _, ubd := range data.UnbondingDelegations {

View File

@ -77,7 +77,6 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) {
store := ctx.KVStore(k.storeKey)
b := types.MustMarshalDelegation(k.cdc, delegation)
store.Set(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr), b)
k.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
}
// remove a delegation from store
@ -468,6 +467,7 @@ func (k Keeper) Delegate(ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Co
// Update delegation
delegation.Shares = delegation.Shares.Add(newShares)
k.SetDelegation(ctx, delegation)
k.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
return newShares, nil
}
@ -512,6 +512,7 @@ func (k Keeper) unbond(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValA
} else {
// update the delegation
k.SetDelegation(ctx, delegation)
k.AfterDelegationModified(ctx, delegation.DelegatorAddr, delegation.ValidatorAddr)
}
// remove the coins from the validator
@ -569,10 +570,13 @@ func (k Keeper) Undelegate(ctx sdk.Context, delAddr sdk.AccAddress,
// no need to create the ubd object just complete now
if completeNow {
_, err := k.bankKeeper.UndelegateCoins(ctx, delAddr, sdk.Coins{balance})
if err != nil {
return completionTime, err
// track undelegation only when remaining or truncated shares are non-zero
if !balance.IsZero() {
if _, err := k.bankKeeper.UndelegateCoins(ctx, delAddr, sdk.Coins{balance}); err != nil {
return completionTime, err
}
}
return completionTime, nil
}

View File

@ -57,7 +57,12 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh
// we need to calculate the *effective* slash fraction for distribution
if validator.Tokens.GT(sdk.ZeroInt()) {
k.BeforeValidatorSlashed(ctx, operatorAddress, slashAmountDec.Quo(sdk.NewDecFromInt(validator.Tokens)))
effectiveFraction := slashAmountDec.Quo(sdk.NewDecFromInt(validator.Tokens))
// possible if power has changed
if effectiveFraction.GT(sdk.OneDec()) {
effectiveFraction = sdk.OneDec()
}
k.BeforeValidatorSlashed(ctx, operatorAddress, effectiveFraction)
}
// Track remaining slash amount for the validator