cosmos-sdk/x/staking/types/validator_test.go

320 lines
10 KiB
Go

package types
import (
"math/rand"
"reflect"
"sort"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func TestValidatorTestEquivalent(t *testing.T) {
val1 := NewValidator(valAddr1, pk1, Description{})
val2 := NewValidator(valAddr1, pk1, Description{})
ok := val1.Equal(val2)
require.True(t, ok)
val2 = NewValidator(valAddr2, pk2, Description{})
ok = val1.Equal(val2)
require.False(t, ok)
}
func TestUpdateDescription(t *testing.T) {
d1 := Description{
Website: "https://validator.cosmos",
Details: "Test validator",
}
d2 := Description{
Moniker: DoNotModifyDesc,
Identity: DoNotModifyDesc,
Website: DoNotModifyDesc,
Details: DoNotModifyDesc,
}
d3 := Description{
Moniker: "",
Identity: "",
Website: "",
Details: "",
}
d, err := d1.UpdateDescription(d2)
require.Nil(t, err)
require.Equal(t, d, d1)
d, err = d1.UpdateDescription(d3)
require.Nil(t, err)
require.Equal(t, d, d3)
}
func TestABCIValidatorUpdate(t *testing.T) {
validator := NewValidator(valAddr1, pk1, Description{})
abciVal := validator.ABCIValidatorUpdate()
require.Equal(t, tmtypes.TM2PB.PubKey(validator.GetConsPubKey()), abciVal.PubKey)
require.Equal(t, validator.BondedTokens().Int64(), abciVal.Power)
}
func TestABCIValidatorUpdateZero(t *testing.T) {
validator := NewValidator(valAddr1, pk1, Description{})
abciVal := validator.ABCIValidatorUpdateZero()
require.Equal(t, tmtypes.TM2PB.PubKey(validator.GetConsPubKey()), abciVal.PubKey)
require.Equal(t, int64(0), abciVal.Power)
}
func TestShareTokens(t *testing.T) {
validator := Validator{
OperatorAddress: valAddr1,
ConsensusPubkey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, pk1),
Status: sdk.Bonded,
Tokens: sdk.NewInt(100),
DelegatorShares: sdk.NewDec(100),
}
assert.True(sdk.DecEq(t, sdk.NewDec(50), validator.TokensFromShares(sdk.NewDec(50))))
validator.Tokens = sdk.NewInt(50)
assert.True(sdk.DecEq(t, sdk.NewDec(25), validator.TokensFromShares(sdk.NewDec(50))))
assert.True(sdk.DecEq(t, sdk.NewDec(5), validator.TokensFromShares(sdk.NewDec(10))))
}
func TestRemoveTokens(t *testing.T) {
valPubKey := pk1
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
validator := Validator{
OperatorAddress: valAddr,
ConsensusPubkey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, valPubKey),
Status: sdk.Bonded,
Tokens: sdk.NewInt(100),
DelegatorShares: sdk.NewDec(100),
}
// remove tokens and test check everything
validator = validator.RemoveTokens(sdk.NewInt(10))
require.Equal(t, int64(90), validator.Tokens.Int64())
// update validator to from bonded -> unbonded
validator = validator.UpdateStatus(sdk.Unbonded)
require.Equal(t, sdk.Unbonded, validator.Status)
validator = validator.RemoveTokens(sdk.NewInt(10))
require.Panics(t, func() { validator.RemoveTokens(sdk.NewInt(-1)) })
require.Panics(t, func() { validator.RemoveTokens(sdk.NewInt(100)) })
}
func TestAddTokensValidatorBonded(t *testing.T) {
validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{})
validator = validator.UpdateStatus(sdk.Bonded)
validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10))
assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares))
assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.BondedTokens()))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
}
func TestAddTokensValidatorUnbonding(t *testing.T) {
validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{})
validator = validator.UpdateStatus(sdk.Unbonding)
validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10))
assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares))
assert.Equal(t, sdk.Unbonding, validator.Status)
assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
}
func TestAddTokensValidatorUnbonded(t *testing.T) {
validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{})
validator = validator.UpdateStatus(sdk.Unbonded)
validator, delShares := validator.AddTokensFromDel(sdk.NewInt(10))
assert.True(sdk.DecEq(t, sdk.NewDec(10), delShares))
assert.Equal(t, sdk.Unbonded, validator.Status)
assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
}
// TODO refactor to make simpler like the AddToken tests above
func TestRemoveDelShares(t *testing.T) {
valA := Validator{
OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()),
ConsensusPubkey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, pk1),
Status: sdk.Bonded,
Tokens: sdk.NewInt(100),
DelegatorShares: sdk.NewDec(100),
}
// Remove delegator shares
valB, coinsB := valA.RemoveDelShares(sdk.NewDec(10))
require.Equal(t, int64(10), coinsB.Int64())
require.Equal(t, int64(90), valB.DelegatorShares.RoundInt64())
require.Equal(t, int64(90), valB.BondedTokens().Int64())
// specific case from random tests
poolTokens := sdk.NewInt(5102)
delShares := sdk.NewDec(115)
validator := Validator{
OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()),
ConsensusPubkey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, pk1),
Status: sdk.Bonded,
Tokens: poolTokens,
DelegatorShares: delShares,
}
shares := sdk.NewDec(29)
_, tokens := validator.RemoveDelShares(shares)
require.True(sdk.IntEq(t, sdk.NewInt(1286), tokens))
}
func TestAddTokensFromDel(t *testing.T) {
validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{})
validator, shares := validator.AddTokensFromDel(sdk.NewInt(6))
require.True(sdk.DecEq(t, sdk.NewDec(6), shares))
require.True(sdk.DecEq(t, sdk.NewDec(6), validator.DelegatorShares))
require.True(sdk.IntEq(t, sdk.NewInt(6), validator.Tokens))
validator, shares = validator.AddTokensFromDel(sdk.NewInt(3))
require.True(sdk.DecEq(t, sdk.NewDec(3), shares))
require.True(sdk.DecEq(t, sdk.NewDec(9), validator.DelegatorShares))
require.True(sdk.IntEq(t, sdk.NewInt(9), validator.Tokens))
}
func TestUpdateStatus(t *testing.T) {
validator := NewValidator(sdk.ValAddress(pk1.Address().Bytes()), pk1, Description{})
validator, _ = validator.AddTokensFromDel(sdk.NewInt(100))
require.Equal(t, sdk.Unbonded, validator.Status)
require.Equal(t, int64(100), validator.Tokens.Int64())
// Unbonded to Bonded
validator = validator.UpdateStatus(sdk.Bonded)
require.Equal(t, sdk.Bonded, validator.Status)
// Bonded to Unbonding
validator = validator.UpdateStatus(sdk.Unbonding)
require.Equal(t, sdk.Unbonding, validator.Status)
// Unbonding to Bonded
validator = validator.UpdateStatus(sdk.Bonded)
require.Equal(t, sdk.Bonded, validator.Status)
}
func TestPossibleOverflow(t *testing.T) {
delShares := sdk.NewDec(391432570689183511).Quo(sdk.NewDec(40113011844664))
validator := Validator{
OperatorAddress: sdk.ValAddress(pk1.Address().Bytes()),
ConsensusPubkey: sdk.MustBech32ifyPubKey(sdk.Bech32PubKeyTypeConsPub, pk1),
Status: sdk.Bonded,
Tokens: sdk.NewInt(2159),
DelegatorShares: delShares,
}
newValidator, _ := validator.AddTokensFromDel(sdk.NewInt(71))
require.False(t, newValidator.DelegatorShares.IsNegative())
require.False(t, newValidator.Tokens.IsNegative())
}
func TestValidatorMarshalUnmarshalJSON(t *testing.T) {
validator := NewValidator(valAddr1, pk1, Description{})
js, err := codec.Cdc.MarshalJSON(validator)
require.NoError(t, err)
require.NotEmpty(t, js)
require.Contains(t, string(js), "\"consensus_pubkey\":\"cosmosvalconspu")
got := &Validator{}
err = codec.Cdc.UnmarshalJSON(js, got)
assert.NoError(t, err)
assert.Equal(t, validator, *got)
}
func TestValidatorSetInitialCommission(t *testing.T) {
val := NewValidator(valAddr1, pk1, Description{})
testCases := []struct {
validator Validator
commission Commission
expectedErr bool
}{
{val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), false},
{val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec()), true},
{val, NewCommission(sdk.ZeroDec(), sdk.NewDec(15000000000), sdk.ZeroDec()), true},
{val, NewCommission(sdk.NewDecWithPrec(-1, 1), sdk.ZeroDec(), sdk.ZeroDec()), true},
{val, NewCommission(sdk.NewDecWithPrec(2, 1), sdk.NewDecWithPrec(1, 1), sdk.ZeroDec()), true},
{val, NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.NewDecWithPrec(-1, 1)), true},
{val, NewCommission(sdk.ZeroDec(), sdk.NewDecWithPrec(1, 1), sdk.NewDecWithPrec(2, 1)), true},
}
for i, tc := range testCases {
val, err := tc.validator.SetInitialCommission(tc.commission)
if tc.expectedErr {
require.Error(t, err,
"expected error for test case #%d with commission: %s", i, tc.commission,
)
} else {
require.NoError(t, err,
"unexpected error for test case #%d with commission: %s", i, tc.commission,
)
require.Equal(t, tc.commission, val.Commission,
"invalid validator commission for test case #%d with commission: %s", i, tc.commission,
)
}
}
}
// Check that sort will create deterministic ordering of validators
func TestValidatorsSortDeterminism(t *testing.T) {
vals := make([]Validator, 10)
sortedVals := make([]Validator, 10)
// Create random validator slice
for i := range vals {
pk := ed25519.GenPrivKey().PubKey()
vals[i] = NewValidator(sdk.ValAddress(pk.Address()), pk, Description{})
}
// Save sorted copy
sort.Sort(Validators(vals))
copy(sortedVals, vals)
// Randomly shuffle validators, sort, and check it is equal to original sort
for i := 0; i < 10; i++ {
rand.Shuffle(10, func(i, j int) {
it := vals[i]
vals[i] = vals[j]
vals[j] = it
})
Validators(vals).Sort()
require.True(t, reflect.DeepEqual(sortedVals, vals), "Validator sort returned different slices")
}
}
func TestValidatorToTm(t *testing.T) {
vals := make(Validators, 10)
expected := make([]*tmtypes.Validator, 10)
for i := range vals {
pk := ed25519.GenPrivKey().PubKey()
val := NewValidator(sdk.ValAddress(pk.Address()), pk, Description{})
val.Status = sdk.Bonded
val.Tokens = sdk.NewInt(rand.Int63())
vals[i] = val
expected[i] = tmtypes.NewValidator(pk, val.ConsensusPower())
}
require.Equal(t, expected, vals.ToTmValidators())
}