diff --git a/x/stake/handler.go b/x/stake/handler.go index 094d01aea..2f351a608 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -5,6 +5,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" + abci "github.com/tendermint/abci/types" ) //nolint @@ -35,6 +36,17 @@ func NewHandler(k Keeper, ck bank.CoinKeeper) sdk.Handler { } } +//_______________________________________________ + +// NewEndBlocker generates sdk.EndBlocker +// Performs tick functionality +func NewEndBlocker(k Keeper) sdk.EndBlocker { + return func(ctx sdk.Context, req abci.RequestEndBlock) (res abci.ResponseEndBlock) { + res.ValidatorUpdates = k.Tick(ctx) + return + } +} + //_____________________________________________________________________ // These functions assume everything has been authenticated, diff --git a/x/stake/keeper.go b/x/stake/keeper.go index d377df21f..0f29b177b 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/bank" + abci "github.com/tendermint/abci/types" ) // keeper of the staking store @@ -95,7 +96,7 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) { store.Set(GetCandidateKey(candidate.Address), bz) // mashal the new validator record - validator := Validator{address, candidate.Assets} + validator := candidate.validator() bz, err = k.cdc.MarshalBinary(validator) if err != nil { panic(err) @@ -110,7 +111,7 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) { if oldFound { store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc)) } - store.Set(GetValidatorKey(address, validator.VotingPower, k.cdc), bz) + store.Set(GetValidatorKey(address, validator.Power, k.cdc), bz) // add to the validators to update list if is already a validator // or is a new validator @@ -123,6 +124,10 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) { setAcc = true } if setAcc { + bz, err = k.cdc.MarshalBinary(validator.abciValidator(k.cdc)) + if err != nil { + panic(err) + } store.Set(GetAccUpdateValidatorKey(validator.Address), bz) } return @@ -131,7 +136,7 @@ func (k Keeper) setCandidate(ctx sdk.Context, candidate Candidate) { func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) { // first retreive the old candidate record - oldCandidate, found := k.GetCandidate(ctx, address) + candidate, found := k.GetCandidate(ctx, address) if !found { return } @@ -139,14 +144,14 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) { // delete the old candidate record store := ctx.KVStore(k.storeKey) store.Delete(GetCandidateKey(address)) - store.Delete(GetValidatorKey(address, oldCandidate.Assets, k.cdc)) + store.Delete(GetValidatorKey(address, candidate.Assets, k.cdc)) // delete from recent and power weighted validator groups if the validator // exists and add validator with zero power to the validator updates if store.Get(GetRecentValidatorKey(address)) == nil { return } - bz, err := k.cdc.MarshalBinary(Validator{address, sdk.ZeroRat}) + bz, err := k.cdc.MarshalBinary(candidate.validator().abciValidatorZero(k.cdc)) if err != nil { panic(err) } @@ -156,10 +161,12 @@ func (k Keeper) removeCandidate(ctx sdk.Context, address sdk.Address) { //___________________________________________________________________________ -// get the most recent updated validator set from the Candidates. These bonds -// are already sorted by Assets from the UpdateVotingPower function which -// is the only function which is to modify the Assets -// this function also updaates the most recent validators saved in store +// Get the validator set from the candidates. The correct subset is retrieved +// by iterating through an index of the candidates sorted by power, stored +// using the ValidatorsKey. Simultaniously the most recent the validator +// records are updated in store with the RecentValidatorsKey. This store is +// used to determine if a candidate is a validator without needing to iterate +// over the subspace as we do in GetValidators func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) { store := ctx.KVStore(k.storeKey) @@ -167,34 +174,36 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) { iterator := store.Iterator(subspace(RecentValidatorsKey)) for ; iterator.Valid(); iterator.Next() { addr := AddrFromKey(iterator.Key()) - store.Set(GetToKickOutValidatorKey(addr), []byte{}) + + // iterator.Value is the validator object + store.Set(GetToKickOutValidatorKey(addr), iterator.Value()) store.Delete(iterator.Key()) } iterator.Close() // add the actual validator power sorted store - maxVal := k.GetParams(ctx).MaxValidators + maxValidators := k.GetParams(ctx).MaxValidators iterator = store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest - validators = make([]Validator, maxVal) + validators = make([]Validator, maxValidators) i := 0 for ; ; i++ { - if !iterator.Valid() || i > int(maxVal-1) { + if !iterator.Valid() || i > int(maxValidators-1) { iterator.Close() break } bz := iterator.Value() - var val Validator - err := k.cdc.UnmarshalBinary(bz, &val) + var validator Validator + err := k.cdc.UnmarshalBinary(bz, &validator) if err != nil { panic(err) } - validators[i] = val + validators[i] = validator // remove from ToKickOut group - store.Delete(GetToKickOutValidatorKey(val.Address)) + store.Delete(GetToKickOutValidatorKey(validator.Address)) // also add to the recent validators group - store.Set(GetRecentValidatorKey(val.Address), bz) // XXX should store nothing + store.Set(GetRecentValidatorKey(validator.Address), bz) iterator.Next() } @@ -202,13 +211,23 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) { // add any kicked out validators to the acc change iterator = store.Iterator(subspace(ToKickOutValidatorsKey)) for ; iterator.Valid(); iterator.Next() { - addr := AddrFromKey(iterator.Key()) - bz, err := k.cdc.MarshalBinary(Validator{addr, sdk.ZeroRat}) + key := iterator.Key() + addr := AddrFromKey(key) + + // get the zero abci validator from the ToKickOut iterator value + bz := iterator.Value() + var validator Validator + err := k.cdc.UnmarshalBinary(bz, &validator) if err != nil { panic(err) } + bz, err = k.cdc.MarshalBinary(validator.abciValidatorZero(k.cdc)) + if err != nil { + panic(err) + } + store.Set(GetAccUpdateValidatorKey(addr), bz) - store.Delete(iterator.Key()) + store.Delete(key) } iterator.Close() @@ -255,13 +274,13 @@ func (k Keeper) IsRecentValidator(ctx sdk.Context, address sdk.Address) bool { // Accumulated updates to the validator set // get the most recently updated validators -func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []Validator) { +func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []abci.Validator) { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) //smallest to largest for ; iterator.Valid(); iterator.Next() { valBytes := iterator.Value() - var val Validator + var val abci.Validator err := k.cdc.UnmarshalBinary(valBytes, &val) if err != nil { panic(err) @@ -275,12 +294,9 @@ func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []Validator) { // remove all validator update entries func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) - k.deleteSubSpace(store, AccUpdateValidatorsKey) -} -// TODO move to common functionality somewhere -func (k Keeper) deleteSubSpace(store sdk.KVStore, key []byte) { - iterator := store.Iterator(subspace(key)) + // delete subspace + iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) for ; iterator.Valid(); iterator.Next() { store.Delete(iterator.Key()) } diff --git a/x/stake/keeper_test.go b/x/stake/keeper_test.go index 883ffcd17..088bc5e30 100644 --- a/x/stake/keeper_test.go +++ b/x/stake/keeper_test.go @@ -6,6 +6,7 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + crypto "github.com/tendermint/go-crypto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -217,11 +218,11 @@ func TestGetValidators(t *testing.T) { // first make sure everything as normal is ordered validators := keeper.GetValidators(ctx) require.Equal(t, len(validators), n) - assert.Equal(t, sdk.NewRat(400), validators[0].VotingPower, "%v", validators) - assert.Equal(t, sdk.NewRat(200), validators[1].VotingPower, "%v", validators) - assert.Equal(t, sdk.NewRat(100), validators[2].VotingPower, "%v", validators) - assert.Equal(t, sdk.NewRat(1), validators[3].VotingPower, "%v", validators) - assert.Equal(t, sdk.NewRat(0), validators[4].VotingPower, "%v", validators) + assert.Equal(t, sdk.NewRat(400), validators[0].Power, "%v", validators) + assert.Equal(t, sdk.NewRat(200), validators[1].Power, "%v", validators) + assert.Equal(t, sdk.NewRat(100), validators[2].Power, "%v", validators) + assert.Equal(t, sdk.NewRat(1), validators[3].Power, "%v", validators) + assert.Equal(t, sdk.NewRat(0), validators[4].Power, "%v", validators) assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators) assert.Equal(t, candidates[4].Address, validators[1].Address, "%v", validators) assert.Equal(t, candidates[1].Address, validators[2].Address, "%v", validators) @@ -233,7 +234,7 @@ func TestGetValidators(t *testing.T) { keeper.setCandidate(ctx, candidates[3]) validators = keeper.GetValidators(ctx) require.Equal(t, len(validators), n) - assert.Equal(t, sdk.NewRat(500), validators[0].VotingPower, "%v", validators) + assert.Equal(t, sdk.NewRat(500), validators[0].Power, "%v", validators) assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators) // test a decrease in voting power @@ -241,7 +242,7 @@ func TestGetValidators(t *testing.T) { keeper.setCandidate(ctx, candidates[3]) validators = keeper.GetValidators(ctx) require.Equal(t, len(validators), n) - assert.Equal(t, sdk.NewRat(300), validators[0].VotingPower, "%v", validators) + assert.Equal(t, sdk.NewRat(300), validators[0].Power, "%v", validators) assert.Equal(t, candidates[3].Address, validators[0].Address, "%v", validators) // test a swap in voting power @@ -249,9 +250,9 @@ func TestGetValidators(t *testing.T) { keeper.setCandidate(ctx, candidates[0]) validators = keeper.GetValidators(ctx) require.Equal(t, len(validators), n) - assert.Equal(t, sdk.NewRat(600), validators[0].VotingPower, "%v", validators) + assert.Equal(t, sdk.NewRat(600), validators[0].Power, "%v", validators) assert.Equal(t, candidates[0].Address, validators[0].Address, "%v", validators) - assert.Equal(t, sdk.NewRat(300), validators[1].VotingPower, "%v", validators) + assert.Equal(t, sdk.NewRat(300), validators[1].Power, "%v", validators) assert.Equal(t, candidates[3].Address, validators[1].Address, "%v", validators) // test the max validators term @@ -261,9 +262,9 @@ func TestGetValidators(t *testing.T) { keeper.setParams(ctx, params) validators = keeper.GetValidators(ctx) require.Equal(t, len(validators), n) - assert.Equal(t, sdk.NewRat(600), validators[0].VotingPower, "%v", validators) + assert.Equal(t, sdk.NewRat(600), validators[0].Power, "%v", validators) assert.Equal(t, candidates[0].Address, validators[0].Address, "%v", validators) - assert.Equal(t, sdk.NewRat(300), validators[1].VotingPower, "%v", validators) + assert.Equal(t, sdk.NewRat(300), validators[1].Power, "%v", validators) assert.Equal(t, candidates[3].Address, validators[1].Address, "%v", validators) } @@ -315,6 +316,15 @@ func TestGetAccUpdateValidators(t *testing.T) { } } + // to compare pubkeys between abci pubkey and crypto.PubKey + wirePK := func(pk crypto.PubKey) []byte { + pkBytes, err := keeper.cdc.MarshalBinary(pk) + if err != nil { + panic(err) + } + return pkBytes + } + // test from nothing to something // candidate set: {} -> {c1, c3} // validator set: {} -> {c1, c3} @@ -332,8 +342,8 @@ func TestGetAccUpdateValidators(t *testing.T) { require.Equal(t, 2, len(acc)) candidates := keeper.GetCandidates(ctx, 5) require.Equal(t, 2, len(candidates)) - assert.Equal(t, candidates[0].validator(), acc[0]) - assert.Equal(t, candidates[1].validator(), acc[1]) + assert.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0]) + assert.Equal(t, candidates[1].validator().abciValidator(keeper.cdc), acc[1]) assert.Equal(t, candidates[0].validator(), vals[1]) assert.Equal(t, candidates[1].validator(), vals[0]) @@ -365,7 +375,7 @@ func TestGetAccUpdateValidators(t *testing.T) { assert.True(t, candidates[0].Assets.Equal(sdk.NewRat(600))) acc = keeper.getAccUpdateValidators(ctx) require.Equal(t, 1, len(acc)) - assert.Equal(t, candidates[0].validator(), acc[0]) + assert.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0]) // test multiple value change // candidate set: {c1, c3} -> {c1', c3'} @@ -383,8 +393,8 @@ func TestGetAccUpdateValidators(t *testing.T) { require.Equal(t, 2, len(acc)) candidates = keeper.GetCandidates(ctx, 5) require.Equal(t, 2, len(candidates)) - require.Equal(t, candidates[0].validator(), acc[0]) - require.Equal(t, candidates[1].validator(), acc[1]) + require.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0]) + require.Equal(t, candidates[1].validator().abciValidator(keeper.cdc), acc[1]) // test validtor added at the beginning // candidate set: {c1, c3} -> {c0, c1, c3} @@ -398,7 +408,7 @@ func TestGetAccUpdateValidators(t *testing.T) { require.Equal(t, 1, len(acc)) candidates = keeper.GetCandidates(ctx, 5) require.Equal(t, 3, len(candidates)) - assert.Equal(t, candidates[0].validator(), acc[0]) + assert.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0]) // test validator added at the middle // candidate set: {c0, c1, c3} -> {c0, c1, c2, c3] @@ -412,7 +422,7 @@ func TestGetAccUpdateValidators(t *testing.T) { require.Equal(t, 1, len(acc)) candidates = keeper.GetCandidates(ctx, 5) require.Equal(t, 4, len(candidates)) - assert.Equal(t, candidates[2].validator(), acc[0]) + assert.Equal(t, candidates[2].validator().abciValidator(keeper.cdc), acc[0]) // test candidate added at the end but not inserted in the valset // candidate set: {c0, c1, c2, c3} -> {c0, c1, c2, c3, c4} @@ -469,9 +479,9 @@ func TestGetAccUpdateValidators(t *testing.T) { acc = keeper.getAccUpdateValidators(ctx) require.Equal(t, 2, len(acc), "%v", acc) - assert.Equal(t, candidatesIn[0].Address, acc[0].Address) - assert.Equal(t, int64(0), acc[0].VotingPower.Evaluate()) - assert.Equal(t, vals[0], acc[1]) + assert.Equal(t, wirePK(candidatesIn[0].PubKey), acc[0].PubKey) + assert.Equal(t, int64(0), acc[0].Power) + assert.Equal(t, vals[0].abciValidator(keeper.cdc), acc[1]) // test from something to nothing // candidate set: {c0, c1, c2, c3, c4} -> {} @@ -494,14 +504,14 @@ func TestGetAccUpdateValidators(t *testing.T) { require.Equal(t, 0, len(candidates)) acc = keeper.getAccUpdateValidators(ctx) require.Equal(t, 4, len(acc)) - assert.Equal(t, candidatesIn[1].Address, acc[0].Address) - assert.Equal(t, candidatesIn[2].Address, acc[1].Address) - assert.Equal(t, candidatesIn[3].Address, acc[2].Address) - assert.Equal(t, candidatesIn[4].Address, acc[3].Address) - assert.Equal(t, int64(0), acc[0].VotingPower.Evaluate()) - assert.Equal(t, int64(0), acc[1].VotingPower.Evaluate()) - assert.Equal(t, int64(0), acc[2].VotingPower.Evaluate()) - assert.Equal(t, int64(0), acc[3].VotingPower.Evaluate()) + assert.Equal(t, wirePK(candidatesIn[1].PubKey), acc[0].PubKey) + assert.Equal(t, wirePK(candidatesIn[2].PubKey), acc[1].PubKey) + assert.Equal(t, wirePK(candidatesIn[3].PubKey), acc[2].PubKey) + assert.Equal(t, wirePK(candidatesIn[4].PubKey), acc[3].PubKey) + assert.Equal(t, int64(0), acc[0].Power) + assert.Equal(t, int64(0), acc[1].Power) + assert.Equal(t, int64(0), acc[2].Power) + assert.Equal(t, int64(0), acc[3].Power) } // test if is a validator from the last update diff --git a/x/stake/tick.go b/x/stake/tick.go index 0a85cd865..129be58f0 100644 --- a/x/stake/tick.go +++ b/x/stake/tick.go @@ -2,6 +2,7 @@ package stake import ( sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/abci/types" ) const ( @@ -12,7 +13,7 @@ const ( var hrsPerYrRat = sdk.NewRat(hrsPerYr) // as defined by a julian year of 365.25 days // Tick - called at the end of every block -func (k Keeper) Tick(ctx sdk.Context) (change []Validator) { +func (k Keeper) Tick(ctx sdk.Context) (change []abci.Validator) { p := k.GetPool(ctx) // Process Validator Provisions @@ -26,6 +27,7 @@ func (k Keeper) Tick(ctx sdk.Context) (change []Validator) { k.setPool(ctx, p) change = k.getAccUpdateValidators(ctx) + return } diff --git a/x/stake/types.go b/x/stake/types.go index a5d85bc7a..1154f7962 100644 --- a/x/stake/types.go +++ b/x/stake/types.go @@ -2,6 +2,8 @@ package stake import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + abci "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" ) @@ -104,8 +106,9 @@ func (c Candidate) delegatorShareExRate() sdk.Rat { // Should only be called when the Candidate qualifies as a validator. func (c Candidate) validator() Validator { return Validator{ - Address: c.Address, - VotingPower: c.Assets, + Address: c.Address, + PubKey: c.PubKey, + Power: c.Assets, } } @@ -116,23 +119,35 @@ func (c Candidate) validator() Validator { // Validator is one of the top Candidates type Validator struct { - Address sdk.Address `json:"address"` // Address of validator - VotingPower sdk.Rat `json:"voting_power"` // Voting power if considered a validator + Address sdk.Address `json:"address"` + PubKey crypto.PubKey `json:"pub_key"` + Power sdk.Rat `json:"voting_power"` } -// ABCIValidator - Get the validator from a bond value -/* TODO -func (v Validator) ABCIValidator() (*abci.Validator, error) { - pkBytes, err := wire.MarshalBinary(v.PubKey) +// abci validator from stake validator type +func (v Validator) abciValidator(cdc *wire.Codec) abci.Validator { + pkBytes, err := cdc.MarshalBinary(v.PubKey) if err != nil { - return nil, err + panic(err) } - return &abci.Validator{ + return abci.Validator{ PubKey: pkBytes, - Power: v.VotingPower.Evaluate(), - }, nil + Power: v.Power.Evaluate(), + } +} + +// abci validator from stake validator type +// with zero power used for validator updates +func (v Validator) abciValidatorZero(cdc *wire.Codec) abci.Validator { + pkBytes, err := cdc.MarshalBinary(v.PubKey) + if err != nil { + panic(err) + } + return abci.Validator{ + PubKey: pkBytes, + Power: 0, + } } -*/ //_________________________________________________________________________