From e3c305dcf47f5e538d4b20657617d002d44aca67 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 16 May 2018 11:59:16 -0400 Subject: [PATCH] ... --- x/stake/keeper.go | 50 ++++++----- x/stake/keeper_test.go | 189 +++++++++++++++++++++-------------------- 2 files changed, 124 insertions(+), 115 deletions(-) diff --git a/x/stake/keeper.go b/x/stake/keeper.go index da3c205fc..72c88c84c 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -204,7 +204,7 @@ func (k Keeper) GetValidatorsBondedByPower(ctx sdk.Context) []Validator { validators := make([]Validator, maxValidators) iterator := store.ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest i := 0 - for ; ; i++ { + for { if !iterator.Valid() || i > int(maxValidators-1) { iterator.Close() break @@ -212,7 +212,10 @@ func (k Keeper) GetValidatorsBondedByPower(ctx sdk.Context) []Validator { bz := iterator.Value() var validator Validator k.cdc.MustUnmarshalBinary(bz, &validator) - validators[i] = validator + if validator.Status == sdk.Bonded { + validators[i] = validator + i++ + } iterator.Next() } return validators[:i] // trim @@ -232,14 +235,12 @@ func (k Keeper) GetValidatorsBondedByPower(ctx sdk.Context) []Validator { // validator without needing to iterate over the subspace as we do in // GetValidators. func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedValidatorAddr sdk.Address) (updatedIsBonded bool) { - fmt.Println("wackydebugoutput updateValidators 0") updatedIsBonded = false // clear the current validators store, add to the ToKickOut temp store toKickOut := make(map[string][]byte) // map[key]value iterator := store.SubspaceIterator(ValidatorsBondedKey) for ; iterator.Valid(); iterator.Next() { - fmt.Println("wackydebugoutput updateValidators 1") bz := iterator.Value() var validator Validator @@ -251,7 +252,6 @@ func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedVali toKickOut[string(addr)] = iterator.Value() store.Delete(iterator.Key()) } - fmt.Println("wackydebugoutput updateValidators 2") iterator.Close() // add the actual validator power sorted store @@ -260,14 +260,11 @@ func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedVali iterator = store.ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest i := 0 for ; ; i++ { - fmt.Println("wackydebugoutput updateValidators 3") fmt.Printf("debug i: %v\n", i) if !iterator.Valid() || i > int(maxValidators-1) { - fmt.Println("wackydebugoutput updateValidators 4") iterator.Close() break } - fmt.Println("wackydebugoutput updateValidators 5") bz := iterator.Value() var validator Validator k.cdc.MustUnmarshalBinary(bz, &validator) @@ -280,36 +277,47 @@ func (k Keeper) updateValidators(ctx sdk.Context, store sdk.KVStore, updatedVali // MOST IMPORTANTLY, add to the accumulated changes if this is the modified validator if bytes.Equal(updatedValidatorAddr, validator.Address) { - fmt.Println("wackydebugoutput updateValidators 6") bz = k.cdc.MustMarshalBinary(validator.abciValidator(k.cdc)) store.Set(GetTendermintUpdatesKey(updatedValidatorAddr), bz) updatedIsBonded = true // the updatedValidatorAddr is for a bonded validator } - fmt.Println("wackydebugoutput updateValidators 7") iterator.Next() } - fmt.Println("wackydebugoutput updateValidators 8") - // add any kicked out validators to the accumulated changes for tendermint - for key, value := range toKickOut { - fmt.Println("wackydebugoutput updateValidators 9") + for _, value := range toKickOut { if value == nil { - fmt.Println("wackydebugoutput updateValidators 10") continue } - fmt.Println("wackydebugoutput updateValidators 11") - addr := AddrFromKey([]byte(key)) - var validator Validator k.cdc.MustUnmarshalBinary(value, &validator) - bz := k.cdc.MustMarshalBinary(validator.abciValidatorZero(k.cdc)) - store.Set(GetTendermintUpdatesKey(addr), bz) + fmt.Printf("debug validator: %v\n", validator) + k.unbondValidator(ctx, store, validator) } - fmt.Println("wackydebugoutput updateValidators 12") return } +// perform all the store operations for when a validator is moved from bonded to unbonded +func (k Keeper) unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) { + pool := k.GetPool(ctx) + + // set the status + validator.Status = sdk.Unbonded + validator, pool = validator.UpdateSharesLocation(pool) + k.setPool(ctx, pool) + + // save the now unbonded validator record + bz := k.cdc.MustMarshalBinary(validator) + store.Set(GetValidatorKey(validator.Address), bz) + + // add to accumulated changes for tendermint + bz = k.cdc.MustMarshalBinary(validator.abciValidatorZero(k.cdc)) + store.Set(GetTendermintUpdatesKey(validator.Address), bz) + + // also remove from the Bonded Validators Store + store.Delete(GetValidatorsBondedKey(validator.PubKey)) +} + //_________________________________________________________________________ // Accumulated updates to the active/bonded validator set for tendermint diff --git a/x/stake/keeper_test.go b/x/stake/keeper_test.go index 43b383456..a72a4d2b9 100644 --- a/x/stake/keeper_test.go +++ b/x/stake/keeper_test.go @@ -125,71 +125,71 @@ func GetValidatorSortingUnmixed(t *testing.T) { } // first make sure everything made it in to the gotValidator group - gotValidators := keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, n, len(gotValidators)) - assert.Equal(t, sdk.NewRat(400), gotValidators[0].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(200), gotValidators[1].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(100), gotValidators[2].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(1), gotValidators[3].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(0), gotValidators[4].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, validators[3].Address, gotValidators[0].Address, "%v", gotValidators) - assert.Equal(t, validators[4].Address, gotValidators[1].Address, "%v", gotValidators) - assert.Equal(t, validators[1].Address, gotValidators[2].Address, "%v", gotValidators) - assert.Equal(t, validators[2].Address, gotValidators[3].Address, "%v", gotValidators) - assert.Equal(t, validators[0].Address, gotValidators[4].Address, "%v", gotValidators) + resValidators := keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, n, len(resValidators)) + assert.Equal(t, sdk.NewRat(400), resValidators[0].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(200), resValidators[1].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(100), resValidators[2].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(1), resValidators[3].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(0), resValidators[4].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, validators[3].Address, resValidators[0].Address, "%v", resValidators) + assert.Equal(t, validators[4].Address, resValidators[1].Address, "%v", resValidators) + assert.Equal(t, validators[1].Address, resValidators[2].Address, "%v", resValidators) + assert.Equal(t, validators[2].Address, resValidators[3].Address, "%v", resValidators) + assert.Equal(t, validators[0].Address, resValidators[4].Address, "%v", resValidators) // test a basic increase in voting power validators[3].PShares = NewBondedShares(sdk.NewRat(500)) keeper.setValidator(ctx, validators[3]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n) - assert.True(ValEq(t, validators[3], gotValidators[0])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) // test a decrease in voting power validators[3].PShares = NewBondedShares(sdk.NewRat(300)) keeper.setValidator(ctx, validators[3]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n) - assert.True(ValEq(t, validators[3], gotValidators[0])) - assert.True(ValEq(t, validators[4], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) // test equal voting power, different age validators[3].PShares = NewBondedShares(sdk.NewRat(200)) ctx = ctx.WithBlockHeight(10) keeper.setValidator(ctx, validators[3]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n) - assert.True(ValEq(t, validators[3], gotValidators[0])) - assert.True(ValEq(t, validators[4], gotValidators[1])) - assert.Equal(t, int64(0), gotValidators[0].BondHeight, "%v", gotValidators) - assert.Equal(t, int64(0), gotValidators[1].BondHeight, "%v", gotValidators) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) + assert.Equal(t, int64(0), resValidators[0].BondHeight, "%v", resValidators) + assert.Equal(t, int64(0), resValidators[1].BondHeight, "%v", resValidators) // no change in voting power - no change in sort ctx = ctx.WithBlockHeight(20) keeper.setValidator(ctx, validators[4]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n) - assert.True(ValEq(t, validators[3], gotValidators[0])) - assert.True(ValEq(t, validators[4], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, len(resValidators), n) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) // change in voting power of both validators, both still in v-set, no age change validators[3].PShares = NewBondedShares(sdk.NewRat(300)) validators[4].PShares = NewBondedShares(sdk.NewRat(300)) keeper.setValidator(ctx, validators[3]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, len(resValidators), n) ctx = ctx.WithBlockHeight(30) keeper.setValidator(ctx, validators[4]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n, "%v", gotValidators) - assert.True(ValEq(t, validators[3], gotValidators[0])) - assert.True(ValEq(t, validators[4], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, len(resValidators), n, "%v", resValidators) + assert.True(ValEq(t, validators[3], resValidators[0])) + assert.True(ValEq(t, validators[4], resValidators[1])) } func GetValidatorSortingMixed(t *testing.T) { ctx, _, keeper := createTestInput(t, false, 0) - // now 2 max gotValidators + // now 2 max resValidators params := keeper.GetParams(ctx) params.MaxValidators = 2 keeper.setParams(ctx, params) @@ -228,25 +228,25 @@ func GetValidatorSortingMixed(t *testing.T) { assert.Equal(t, sdk.Bonded, val4.Status) // first make sure everything made it in to the gotValidator group - gotValidators := keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, n, len(gotValidators)) - assert.Equal(t, sdk.NewRat(400), gotValidators[0].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(200), gotValidators[1].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(100), gotValidators[2].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(1), gotValidators[3].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, sdk.NewRat(0), gotValidators[4].PShares.Bonded(), "%v", gotValidators) - assert.Equal(t, validators[3].Address, gotValidators[0].Address, "%v", gotValidators) - assert.Equal(t, validators[4].Address, gotValidators[1].Address, "%v", gotValidators) - assert.Equal(t, validators[1].Address, gotValidators[2].Address, "%v", gotValidators) - assert.Equal(t, validators[2].Address, gotValidators[3].Address, "%v", gotValidators) - assert.Equal(t, validators[0].Address, gotValidators[4].Address, "%v", gotValidators) + resValidators := keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, n, len(resValidators)) + assert.Equal(t, sdk.NewRat(400), resValidators[0].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(200), resValidators[1].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(100), resValidators[2].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(1), resValidators[3].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, sdk.NewRat(0), resValidators[4].PShares.Bonded(), "%v", resValidators) + assert.Equal(t, validators[3].Address, resValidators[0].Address, "%v", resValidators) + assert.Equal(t, validators[4].Address, resValidators[1].Address, "%v", resValidators) + assert.Equal(t, validators[1].Address, resValidators[2].Address, "%v", resValidators) + assert.Equal(t, validators[2].Address, resValidators[3].Address, "%v", resValidators) + assert.Equal(t, validators[0].Address, resValidators[4].Address, "%v", resValidators) } // TODO seperate out into multiple tests func TestGetValidatorsEdgeCases(t *testing.T) { ctx, _, keeper := createTestInput(t, false, 0) - // now 2 max gotValidators + // now 2 max resValidators params := keeper.GetParams(ctx) nMax := uint16(2) params.MaxValidators = nMax @@ -269,12 +269,12 @@ func TestGetValidatorsEdgeCases(t *testing.T) { validators[0].PShares = NewUnbondedShares(sdk.NewRat(500)) keeper.setValidator(ctx, validators[0]) - gotValidators := keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, nMax, uint16(len(gotValidators))) - assert.True(ValEq(t, validators[0], gotValidators[0])) + resValidators := keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) // validator 3 was set before validator 4 - assert.True(ValEq(t, validators[2], gotValidators[1])) + assert.True(ValEq(t, validators[2], resValidators[1])) // A validator which leaves the gotValidator set due to a decrease in voting power, // then increases to the original voting power, does not get its spot back in the @@ -282,27 +282,27 @@ func TestGetValidatorsEdgeCases(t *testing.T) { // ref https://github.com/cosmos/cosmos-sdk/issues/582#issuecomment-380757108 validators[3].PShares = NewBondedShares(sdk.NewRat(401)) keeper.setValidator(ctx, validators[3]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, nMax, uint16(len(gotValidators))) - assert.True(ValEq(t, validators[0], gotValidators[0])) - assert.True(ValEq(t, validators[3], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[3], resValidators[1])) ctx = ctx.WithBlockHeight(40) // validator 3 kicked out temporarily validators[3].PShares = NewBondedShares(sdk.NewRat(200)) keeper.setValidator(ctx, validators[3]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, nMax, uint16(len(gotValidators))) - assert.True(ValEq(t, validators[0], gotValidators[0])) - assert.True(ValEq(t, validators[2], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) // validator 4 does not get spot back validators[3].PShares = NewBondedShares(sdk.NewRat(400)) keeper.setValidator(ctx, validators[3]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, nMax, uint16(len(gotValidators))) - assert.True(ValEq(t, validators[0], gotValidators[0])) - assert.True(ValEq(t, validators[2], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, nMax, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) validator, exists := keeper.GetValidator(ctx, validators[3].Address) require.Equal(t, exists, true) require.Equal(t, validator.BondHeight, int64(40)) @@ -311,7 +311,7 @@ func TestGetValidatorsEdgeCases(t *testing.T) { func TestValidatorBondHeight(t *testing.T) { ctx, _, keeper := createTestInput(t, false, 0) - // now 2 max gotValidators + // now 2 max resValidators params := keeper.GetParams(ctx) params.MaxValidators = 2 keeper.setParams(ctx, params) @@ -334,32 +334,29 @@ func TestValidatorBondHeight(t *testing.T) { // the one with the first transaction should become bonded keeper.setValidator(ctx, validators[1]) keeper.setValidator(ctx, validators[2]) - gotValidators := keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, uint16(len(gotValidators)), params.MaxValidators) - assert.True(ValEq(t, validators[0], gotValidators[0])) - assert.True(ValEq(t, validators[1], gotValidators[1])) + resValidators := keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, uint16(len(resValidators)), params.MaxValidators) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[1], resValidators[1])) validators[1].PShares = NewUnbondedShares(sdk.NewRat(150)) validators[2].PShares = NewUnbondedShares(sdk.NewRat(150)) keeper.setValidator(ctx, validators[2]) keeper.setValidator(ctx, validators[1]) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, params.MaxValidators, uint16(len(gotValidators))) - assert.True(ValEq(t, validators[0], gotValidators[0])) - assert.True(ValEq(t, validators[2], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, params.MaxValidators, uint16(len(resValidators))) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) } -// XXX rename test -func TestGetValidatorsEdgeCases2(t *testing.T) { +func TestFullValidatorSetPowerChange(t *testing.T) { ctx, _, keeper := createTestInput(t, false, 0) - - // now 2 max gotValidators params := keeper.GetParams(ctx) - params.MaxValidators = 2 + max := 2 + params.MaxValidators = uint16(2) keeper.setParams(ctx, params) // initialize some validators into the state amts := []int64{0, 100, 400, 400, 200} - n := len(amts) var validators [5]Validator for i, amt := range amts { validators[i] = NewValidator(addrs[i], pks[i], Description{}) @@ -367,24 +364,28 @@ func TestGetValidatorsEdgeCases2(t *testing.T) { validators[i].DelegatorShares = sdk.NewRat(amt) keeper.setValidator(ctx, validators[i]) } + for i := range amts { + var found bool + validators[i], found = keeper.GetValidator(ctx, validators[i].Address) + require.True(t, found) + } + assert.Equal(t, sdk.Unbonded, validators[0].Status) + assert.Equal(t, sdk.Unbonded, validators[1].Status) + assert.Equal(t, sdk.Bonded, validators[2].Status) + assert.Equal(t, sdk.Bonded, validators[3].Status) + assert.Equal(t, sdk.Unbonded, validators[4].Status) + resValidators := keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, max, len(resValidators)) + assert.True(ValEq(t, validators[2], resValidators[0])) // in the order of txs + assert.True(ValEq(t, validators[3], resValidators[1])) // test a swap in voting power validators[0].PShares = NewBondedShares(sdk.NewRat(600)) keeper.setValidator(ctx, validators[0]) - gotValidators := keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n) - assert.True(ValEq(t, validators[0], gotValidators[0])) - assert.True(ValEq(t, validators[3], gotValidators[1])) - - // test the max gotValidators term - params = keeper.GetParams(ctx) - n = 2 - params.MaxValidators = uint16(n) - keeper.setParams(ctx, params) - gotValidators = keeper.GetValidatorsBondedByPower(ctx) - require.Equal(t, len(gotValidators), n) - assert.True(ValEq(t, validators[0], gotValidators[0])) - assert.True(ValEq(t, validators[3], gotValidators[1])) + resValidators = keeper.GetValidatorsBondedByPower(ctx) + require.Equal(t, max, len(resValidators)) + assert.True(ValEq(t, validators[0], resValidators[0])) + assert.True(ValEq(t, validators[2], resValidators[1])) } // clear the tracked changes to the gotValidator set