Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort validators like tendermint in HistoricalInfo #7691

Merged
merged 13 commits into from
Oct 29, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x/staking/keeper/historical_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (k Keeper) TrackHistoricalInfo(ctx sdk.Context) {
}

// Create HistoricalInfo struct
lastVals := k.GetLastValidators(ctx)
lastVals := k.GetBondedValidatorsByPower(ctx)
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
historicalEntry := types.NewHistoricalInfo(ctx.BlockHeader(), lastVals)

// Set latest HistoricalInfo at current height
Expand Down
17 changes: 11 additions & 6 deletions x/staking/keeper/historical_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestHistoricalInfo(t *testing.T) {
recv, found := app.StakingKeeper.GetHistoricalInfo(ctx, 2)
require.True(t, found, "HistoricalInfo not found after set")
require.Equal(t, hi, recv, "HistoricalInfo not equal")
require.True(t, sort.IsSorted(types.Validators(recv.Valset)), "HistoricalInfo validators is not sorted")
require.True(t, sort.IsSorted(types.ValidatorsByVotingPower(recv.Valset)), "HistoricalInfo validators is not sorted")

app.StakingKeeper.DeleteHistoricalInfo(ctx, 2)

Expand Down Expand Up @@ -76,15 +76,20 @@ func TestTrackHistoricalInfo(t *testing.T) {
require.True(t, found)
require.Equal(t, hi5, recv)

// Set last validators in keeper
// Set bonded validators in keeper
val1 := teststaking.NewValidator(t, addrVals[2], PKs[2])
val1.Status = types.Bonded
val1.Tokens = sdk.NewInt(100)
app.StakingKeeper.SetValidator(ctx, val1)
app.StakingKeeper.SetLastValidatorPower(ctx, val1.GetOperator(), 10)
app.StakingKeeper.SetValidatorByPowerIndex(ctx, val1)
val2 := teststaking.NewValidator(t, addrVals[3], PKs[3])
vals := []types.Validator{val1, val2}
sort.Sort(types.Validators(vals))
val2.Status = types.Bonded
val2.Tokens = sdk.NewInt(80)
app.StakingKeeper.SetValidator(ctx, val2)
app.StakingKeeper.SetLastValidatorPower(ctx, val2.GetOperator(), 8)
app.StakingKeeper.SetValidatorByPowerIndex(ctx, val2)

vals := []types.Validator{val1, val2}
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
sort.Sort(types.ValidatorsByVotingPower(vals))

// Set Header for BeginBlock context
header := tmproto.Header{
Expand Down
3 changes: 2 additions & 1 deletion x/staking/types/historical_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import (
// NewHistoricalInfo will create a historical information struct from header and valset
// it will first sort valset before inclusion into historical info
func NewHistoricalInfo(header tmproto.Header, valSet Validators) HistoricalInfo {
sort.Sort(valSet)
// Must sort in the same way that tendermint does
sort.Sort(ValidatorsByVotingPower(valSet))

return HistoricalInfo{
Header: header,
Expand Down
27 changes: 27 additions & 0 deletions x/staking/types/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,33 @@ func (v Validators) Swap(i, j int) {
v[j] = it
}

// ValidatorsByVotingPower implements sort.Interface for []Validator based on
// the VotingPower and Address fields.
// Copied from tendermint/types/validator_set.go
type ValidatorsByVotingPower []Validator

func (valz ValidatorsByVotingPower) Len() int { return len(valz) }

func (valz ValidatorsByVotingPower) Less(i, j int) bool {
if valz[i].ConsensusPower() == valz[j].ConsensusPower() {
var addrI, addrJ []byte
pkI, errI := valz[i].TmConsPubKey()
pkJ, errJ := valz[j].TmConsPubKey()
// If errors are nil, sort by address,
// otherwise don't sort by address and just return false
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
if errI == nil && errJ == nil {
addrI = pkI.Address().Bytes()
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
addrJ = pkJ.Address().Bytes()
}
return bytes.Compare(addrI, addrJ) == -1
}
return valz[i].ConsensusPower() > valz[j].ConsensusPower()
AdityaSripal marked this conversation as resolved.
Show resolved Hide resolved
}

func (valz ValidatorsByVotingPower) Swap(i, j int) {
valz[i], valz[j] = valz[j], valz[i]
}

// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
func (v Validators) UnpackInterfaces(c codectypes.AnyUnpacker) error {
for i := range v {
Expand Down
31 changes: 31 additions & 0 deletions x/staking/types/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,37 @@ func TestValidatorsSortDeterminism(t *testing.T) {
}
}

// Check SortTendermint sorts the same as tendermint
func TestValidatorsSortTendermint(t *testing.T) {
vals := make([]Validator, 100)

for i := range vals {
pk := ed25519.GenPrivKey().PubKey()
pk2 := ed25519.GenPrivKey().PubKey()
vals[i] = newValidator(t, sdk.ValAddress(pk2.Address()), pk)
vals[i].Status = Bonded
vals[i].Tokens = sdk.NewInt(rand.Int63())
}
// create some validators with the same power
for i := 0; i < 10; i++ {
vals[i].Tokens = sdk.NewInt(1000000)
colin-axner marked this conversation as resolved.
Show resolved Hide resolved
}

valz := Validators(vals)

// create expected tendermint validators by converting to tendermint then sorting
expectedVals, err := valz.ToTmValidators()
require.NoError(t, err)
sort.Sort(tmtypes.ValidatorsByVotingPower(expectedVals))

// sort in SDK and then convert to tendermint
sort.Sort(ValidatorsByVotingPower(valz))
actualVals, err := valz.ToTmValidators()
require.NoError(t, err)

require.Equal(t, expectedVals, actualVals, "sorting in SDK is not the same as sorting in Tendermint")
}

func TestValidatorToTm(t *testing.T) {
vals := make(Validators, 10)
expected := make([]*tmtypes.Validator, 10)
Expand Down