From 83c1183e25a8bccc980a158be750c8e060f99f00 Mon Sep 17 00:00:00 2001 From: Rigel Date: Wed, 13 Jun 2018 00:12:57 -0700 Subject: [PATCH] Merge PR #1233: Inflation now unbonded working debug fix add broken test fix testnet bug updated provision test changelog cwgoes comments --- CHANGELOG.md | 2 ++ x/stake/handler_test.go | 73 +++++++++++++++++++++++++++++++++++++++ x/stake/inflation.go | 4 ++- x/stake/inflation_test.go | 59 ++++++------------------------- x/stake/keeper.go | 6 ++++ x/stake/keeper_test.go | 41 ++++++++++++++++++++++ x/stake/shares.go | 5 +-- 7 files changed, 138 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3123bef82a..300b71fb6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,10 @@ IMPROVEMENTS * [tests] Application module tests now use a mock application * [gaiacli] Fix error message when account isn't found when running gaiacli account * [lcd] refactored to eliminate use of global variables, and interdependent tests +* [x/stake] More stake tests added to test ByPower index FIXES +* [x/stake] bonded inflation removed, non-bonded inflation partially implemented * [lcd] Switch to bech32 for addresses on all human readable inputs and outputs * [lcd] fixed tx indexing/querying * [cli] Added `--gas` flag to specify transaction gas limit diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 0c086f06db..6dcf3e66d8 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -33,6 +33,78 @@ func newTestMsgDelegate(delegatorAddr, validatorAddr sdk.Address, amt int64) Msg //______________________________________________________________________ +func TestValidatorByPowerIndex(t *testing.T) { + validatorAddr, validatorAddr3 := addrs[0], addrs[1] + + initBond := int64(1000000) + initBondStr := "1000" + ctx, _, keeper := createTestInput(t, false, initBond) + + // create validator + msgCreateValidator := newTestMsgCreateValidator(validatorAddr, pks[0], initBond) + got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + + // verify the self-delegation exists + bond, found := keeper.GetDelegation(ctx, validatorAddr, validatorAddr) + require.True(t, found) + gotBond := bond.Shares.Evaluate() + require.Equal(t, initBond, gotBond, + "initBond: %v\ngotBond: %v\nbond: %v\n", + initBond, gotBond, bond) + + // verify that the by power index exists + validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + pool := keeper.GetPool(ctx) + power := GetValidatorsByPowerKey(validator, pool) + require.True(t, keeper.validatorByPowerIndexExists(ctx, power)) + + // create a second validator keep it bonded + msgCreateValidator = newTestMsgCreateValidator(validatorAddr3, pks[2], int64(1000000)) + got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) + assert.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) + + // slash and revoke the first validator + keeper.Slash(ctx, pks[0], 0, sdk.NewRat(1, 2)) + keeper.Revoke(ctx, pks[0]) + validator, found = keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + require.Equal(t, sdk.Unbonded, validator.PoolShares.Status) // ensure is unbonded + require.Equal(t, int64(500000), validator.PoolShares.Amount.Evaluate()) // ensure is unbonded + + // the old power record should have been deleted as the power changed + assert.False(t, keeper.validatorByPowerIndexExists(ctx, power)) + + // but the new power record should have been created + validator, found = keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) + pool = keeper.GetPool(ctx) + power2 := GetValidatorsByPowerKey(validator, pool) + require.True(t, keeper.validatorByPowerIndexExists(ctx, power2)) + + // inflate a bunch + for i := 0; i < 20000; i++ { + pool = keeper.processProvisions(ctx) + keeper.setPool(ctx, pool) + } + + // now the new record power index should be the same as the original record + power3 := GetValidatorsByPowerKey(validator, pool) + assert.Equal(t, power2, power3) + + // unbond self-delegation + msgUnbond := NewMsgUnbond(validatorAddr, validatorAddr, "MAX") + got = handleMsgUnbond(ctx, msgUnbond, keeper) + assert.True(t, got.IsOK(), + "got: %v\nmsgUnbond: %v\ninitBondStr: %v\n", got, msgUnbond, initBondStr) + + // verify that by power key nolonger exists + _, found = keeper.GetValidator(ctx, validatorAddr) + require.False(t, found) + assert.False(t, keeper.validatorByPowerIndexExists(ctx, power3)) +} + func TestDuplicatesMsgCreateValidator(t *testing.T) { ctx, _, keeper := createTestInput(t, false, 1000) @@ -42,6 +114,7 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) assert.True(t, got.IsOK(), "%v", got) validator, found := keeper.GetValidator(ctx, validatorAddr) + require.True(t, found) assert.Equal(t, sdk.Bonded, validator.Status()) assert.Equal(t, validatorAddr, validator.Owner) diff --git a/x/stake/inflation.go b/x/stake/inflation.go index d613a478ae..fe3f59435b 100644 --- a/x/stake/inflation.go +++ b/x/stake/inflation.go @@ -22,7 +22,9 @@ func (k Keeper) processProvisions(ctx sdk.Context) Pool { // which needs to be updated is the `BondedPool`. So for each previsions cycle: provisions := pool.Inflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat).Evaluate() - pool.BondedTokens += provisions + + // TODO add to the fees provisions + pool.LooseUnbondedTokens += provisions return pool } diff --git a/x/stake/inflation_test.go b/x/stake/inflation_test.go index 7fa393edc8..0d5183f4cf 100644 --- a/x/stake/inflation_test.go +++ b/x/stake/inflation_test.go @@ -74,8 +74,6 @@ func TestProcessProvisions(t *testing.T) { initialBondedTokens int64 = 250000000 initialUnbondedTokens int64 = 300000000 cumulativeExpProvs int64 - initialBondedShares = sdk.NewRat(250000000, 1) - initialUnbondedShares = sdk.NewRat(300000000, 1) validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000} bondedValidators uint16 = 2 ) @@ -93,9 +91,7 @@ func TestProcessProvisions(t *testing.T) { //get the pool and do the final value checks from checkFinalPoolValues pool = keeper.GetPool(ctx) - checkFinalPoolValues(t, pool, initialTotalTokens, - initialUnbondedTokens, cumulativeExpProvs, - 0, 0, initialBondedShares, initialUnbondedShares) + checkFinalPoolValues(t, pool, initialTotalTokens, cumulativeExpProvs) } // Tests that the hourly rate of change of inflation will be positive, negative, or zero, depending on bonded ratio and inflation rate @@ -109,8 +105,6 @@ func TestHourlyInflationRateOfChange(t *testing.T) { initialBondedTokens int64 = 150000000 initialUnbondedTokens int64 = 400000000 cumulativeExpProvs int64 - bondedShares = sdk.NewRat(150000000, 1) - unbondedShares = sdk.NewRat(400000000, 1) validatorTokens = []int64{150000000, 100000000, 100000000, 100000000, 100000000} bondedValidators uint16 = 1 ) @@ -131,9 +125,7 @@ func TestHourlyInflationRateOfChange(t *testing.T) { // Final check that the pool equals initial values + cumulative provisions and adjustments we recorded pool = keeper.GetPool(ctx) - checkFinalPoolValues(t, pool, initialTotalTokens, - initialUnbondedTokens, cumulativeExpProvs, - 0, 0, bondedShares, unbondedShares) + checkFinalPoolValues(t, pool, initialTotalTokens, cumulativeExpProvs) } //Test that a large unbonding will significantly lower the bonded ratio @@ -181,9 +173,7 @@ func TestLargeUnbond(t *testing.T) { // Final check that the pool equals initial values + provisions and adjustments we recorded pool = keeper.GetPool(ctx) - checkFinalPoolValues(t, pool, initialTotalTokens, - initialUnbondedTokens, expProvisionsAfter, - -val0UnbondedTokens, val0UnbondedTokens, bondedShares, unbondedShares) + checkFinalPoolValues(t, pool, initialTotalTokens, expProvisionsAfter) } //Test that a large bonding will significantly increase the bonded ratio @@ -192,12 +182,9 @@ func TestLargeBond(t *testing.T) { pool := keeper.GetPool(ctx) var ( - initialTotalTokens int64 = 1600000000 - initialBondedTokens int64 = 400000000 - initialUnbondedTokens int64 = 1200000000 - val9UnbondedTokens int64 = 400000000 - val9BondedTokens int64 - bondedShares = sdk.NewRat(400000000, 1) + initialTotalTokens int64 = 1600000000 + initialBondedTokens int64 = 400000000 + initialUnbondedTokens int64 = 1200000000 unbondedShares = sdk.NewRat(1200000000, 1) unbondedSharesVal9 = sdk.NewRat(400000000, 1) validatorTokens = []int64{400000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 100000000, 400000000} @@ -225,10 +212,6 @@ func TestLargeBond(t *testing.T) { // process provisions after the bonding, to compare the difference in expProvisions and expInflation _, expProvisionsAfter, pool := updateProvisions(t, keeper, pool, ctx, 0) unbondedShares = unbondedShares.Sub(unbondedSharesVal9) - val9BondedTokens = val9UnbondedTokens - val9UnbondedTokens = 0 - bondedTokens := initialBondedTokens + val9BondedTokens + expProvisionsAfter - bondedShares = sdk.NewRat(bondedTokens, 1).Quo(pool.bondedShareExRate()) // unbonded shares should decrease assert.True(t, unbondedShares.LT(sdk.NewRat(1200000000, 1))) @@ -237,9 +220,7 @@ func TestLargeBond(t *testing.T) { // Final check that the pool equals initial values + provisions and adjustments we recorded pool = keeper.GetPool(ctx) - checkFinalPoolValues(t, pool, initialTotalTokens, - initialUnbondedTokens, expProvisionsAfter, - val9BondedTokens, -val9BondedTokens, bondedShares, unbondedShares) + checkFinalPoolValues(t, pool, initialTotalTokens, expProvisionsAfter) } // Tests that inflation increases or decreases as expected when we do a random operation on 20 different validators @@ -296,30 +277,13 @@ func TestInflationWithRandomOperations(t *testing.T) { } } +//_________________________________________________________________________________________ ////////////////////////////////HELPER FUNCTIONS BELOW///////////////////////////////////// -// Final check on the global pool values for what the total tokens accumulated from each hour of provisions and other functions -// bondedAdjustment and unbondedAdjustment are the accumulated changes for the operations of the test -// (i.e. if three unbond operations happened, their total value would be passed as unbondedAdjustment) -func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, initialUnbondedTokens, - cumulativeExpProvs, bondedAdjustment, unbondedAdjustment int64, bondedShares, unbondedShares sdk.Rat) { - - initialBonded := initialTotalTokens - initialUnbondedTokens +// Final check on the global pool values for what the total tokens accumulated from each hour of provisions +func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, cumulativeExpProvs int64) { calculatedTotalTokens := initialTotalTokens + cumulativeExpProvs - calculatedBondedTokens := initialBonded + cumulativeExpProvs + bondedAdjustment - calculatedUnbondedTokens := initialUnbondedTokens + unbondedAdjustment - - // test that the bonded ratio the pool has is equal to what we calculated for tokens - assert.True(t, pool.bondedRatio().Equal(sdk.NewRat(calculatedBondedTokens, calculatedTotalTokens)), "%v", pool.bondedRatio()) - - // test global supply assert.Equal(t, calculatedTotalTokens, pool.TokenSupply()) - assert.Equal(t, calculatedBondedTokens, pool.BondedTokens) - assert.Equal(t, calculatedUnbondedTokens, pool.UnbondedTokens) - - // test the value of validator shares - assert.True(t, pool.bondedShareExRate().Mul(bondedShares).Equal(sdk.NewRat(calculatedBondedTokens)), "%v", pool.bondedShareExRate()) - assert.True(t, pool.unbondedShareExRate().Mul(unbondedShares).Equal(sdk.NewRat(calculatedUnbondedTokens)), "%v", pool.unbondedShareExRate()) } // Processes provisions are added to the pool correctly every hour @@ -327,13 +291,11 @@ func checkFinalPoolValues(t *testing.T, pool Pool, initialTotalTokens, initialUn func updateProvisions(t *testing.T, keeper Keeper, pool Pool, ctx sdk.Context, hr int) (sdk.Rat, int64, Pool) { expInflation := keeper.nextInflation(ctx) expProvisions := (expInflation.Mul(sdk.NewRat(pool.TokenSupply())).Quo(hrsPerYrRat)).Evaluate() - startBondedPool := pool.BondedTokens startTotalSupply := pool.TokenSupply() pool = keeper.processProvisions(ctx) keeper.setPool(ctx, pool) //check provisions were added to pool - require.Equal(t, startBondedPool+expProvisions, pool.BondedTokens, "hr %v", hr) require.Equal(t, startTotalSupply+expProvisions, pool.TokenSupply()) return expInflation, expProvisions, pool @@ -405,5 +367,4 @@ func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation assert.Equal(t, true, inflationChange.LT(sdk.ZeroRat()), msg) } } - } diff --git a/x/stake/keeper.go b/x/stake/keeper.go index fad1f86ef1..4a2e6ff4be 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -78,6 +78,12 @@ func (k Keeper) setValidatorByPowerIndex(ctx sdk.Context, validator Validator, p store.Set(GetValidatorsByPowerKey(validator, pool), validator.Owner) } +// used in testing +func (k Keeper) validatorByPowerIndexExists(ctx sdk.Context, power []byte) bool { + store := ctx.KVStore(k.storeKey) + return store.Get(power) != nil +} + // Get the set of all validators with no limits, used during genesis dump func (k Keeper) getAllValidators(ctx sdk.Context) (validators Validators) { store := ctx.KVStore(k.storeKey) diff --git a/x/stake/keeper_test.go b/x/stake/keeper_test.go index 2868853e00..76bfc507a4 100644 --- a/x/stake/keeper_test.go +++ b/x/stake/keeper_test.go @@ -24,6 +24,47 @@ var ( } ) +func TestUpdateValidatorByPowerIndex(t *testing.T) { + ctx, _, keeper := createTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + + // create a random pool + pool.BondedTokens = 1234 + pool.BondedShares = sdk.NewRat(124) + pool.UnbondingTokens = 13934 + pool.UnbondingShares = sdk.NewRat(145) + pool.UnbondedTokens = 154 + pool.UnbondedShares = sdk.NewRat(1333) + keeper.setPool(ctx, pool) + + // add a validator + validator := NewValidator(addrVals[0], pks[0], Description{}) + validator, pool, delSharesCreated := validator.addTokensFromDel(pool, 100) + require.Equal(t, sdk.Unbonded, validator.Status()) + assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate()) + keeper.setPool(ctx, pool) + keeper.updateValidator(ctx, validator) + validator, found := keeper.GetValidator(ctx, addrVals[0]) + require.True(t, found) + assert.Equal(t, int64(100), validator.PoolShares.Tokens(pool).Evaluate(), "\nvalidator %v\npool %v", validator, pool) + + pool = keeper.GetPool(ctx) + power := GetValidatorsByPowerKey(validator, pool) + assert.True(t, keeper.validatorByPowerIndexExists(ctx, power)) + + // burn half the delegator shares + validator, pool, burned := validator.removeDelShares(pool, delSharesCreated.Quo(sdk.NewRat(2))) + assert.Equal(t, int64(50), burned) + keeper.setPool(ctx, pool) // update the pool + keeper.updateValidator(ctx, validator) // update the validator, possibly kicking it out + assert.False(t, keeper.validatorByPowerIndexExists(ctx, power)) + + pool = keeper.GetPool(ctx) + validator, found = keeper.GetValidator(ctx, addrVals[0]) + power = GetValidatorsByPowerKey(validator, pool) + assert.True(t, keeper.validatorByPowerIndexExists(ctx, power)) +} + func TestSetValidator(t *testing.T) { ctx, _, keeper := createTestInput(t, false, 0) pool := keeper.GetPool(ctx) diff --git a/x/stake/shares.go b/x/stake/shares.go index d5fe93844d..e30fa3738e 100644 --- a/x/stake/shares.go +++ b/x/stake/shares.go @@ -118,13 +118,14 @@ func (s PoolShares) ToBonded(p Pool) PoolShares { //_________________________________________________________________________________________________________ +// TODO better tests // get the equivalent amount of tokens contained by the shares func (s PoolShares) Tokens(p Pool) sdk.Rat { switch s.Status { case sdk.Bonded: - return p.unbondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares + return p.bondedShareExRate().Mul(s.Amount) // (tokens/shares) * shares case sdk.Unbonding: - return p.unbondedShareExRate().Mul(s.Amount) + return p.unbondingShareExRate().Mul(s.Amount) case sdk.Unbonded: return p.unbondedShareExRate().Mul(s.Amount) default: