Skip to content

Commit

Permalink
Merge pull request #800 from cosmos/rigel/tick-endblock
Browse files Browse the repository at this point in the history
staking endblock /tick upgrade
  • Loading branch information
rigelrozanski authored Apr 6, 2018
2 parents 66e5d3c + 27087bb commit 52f317a
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 71 deletions.
12 changes: 12 additions & 0 deletions x/stake/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
72 changes: 44 additions & 28 deletions x/stake/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -131,22 +136,22 @@ 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
}

// 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)
}
Expand All @@ -156,59 +161,73 @@ 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)

// clear the recent validators store, add to the ToKickOut Temp store
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()
}

// 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()

Expand Down Expand Up @@ -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)
Expand All @@ -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())
}
Expand Down
68 changes: 39 additions & 29 deletions x/stake/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -233,25 +234,25 @@ 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
candidates[3].Assets = sdk.NewRat(300)
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
candidates[0].Assets = sdk.NewRat(600)
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
Expand All @@ -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)
}

Expand Down Expand Up @@ -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}
Expand All @@ -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])

Expand Down Expand Up @@ -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'}
Expand All @@ -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}
Expand All @@ -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]
Expand All @@ -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}
Expand Down Expand Up @@ -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} -> {}
Expand All @@ -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
Expand Down
Loading

0 comments on commit 52f317a

Please sign in to comment.