[unit-testing] Add units test for roster.go & calculate.go (#1885)

* [unit-testing]  Add unit test for effective stake calculation

* [unit-testing] Intermediate commit

* [unit-testing] Another intermediate commit

* [unit-testing] Add unit test for Roster compute
	* Remove unnecessary code
pull/1890/head
janet-harmony 5 years ago committed by Edgar Aroutiounian
parent 4b46e5f708
commit 1cc2518d31
  1. 2
      consensus/votepower/roster.go
  2. 140
      consensus/votepower/roster_test.go
  3. 2
      shard/shard_state.go
  4. 23
      staking/effective/calculate_test.go

@ -45,7 +45,6 @@ func Compute(staked shard.SlotList) *Roster {
ourCount := numeric.NewDec(roster.HmySlotCount)
ourPercentage := numeric.ZeroDec()
theirPercentage := numeric.ZeroDec()
totalStakedPercent := numeric.ZeroDec()
for i := range staked {
member := stakedVoter{
@ -72,7 +71,6 @@ func Compute(staked shard.SlotList) *Roster {
ourPercentage = ourPercentage.Add(member.EffectivePercent)
}
totalStakedPercent = totalStakedPercent.Add(member.EffectivePercent)
roster.Voters[staked[i].BlsPublicKey] = member
}

@ -0,0 +1,140 @@
package votepower
import (
"encoding/json"
"math/big"
"math/rand"
"strconv"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/harmony-one/bls/ffi/go/bls"
"github.com/harmony-one/harmony/numeric"
"github.com/harmony-one/harmony/shard"
)
var (
slotList shard.SlotList
totalStake numeric.Dec
harmonyNodes = 10
stakedNodes = 10
maxAccountGen = int64(98765654323123134)
accountGen = rand.New(rand.NewSource(1337))
maxKeyGen = int64(98765654323123134)
keyGen = rand.New(rand.NewSource(42))
maxStakeGen = int64(200)
stakeGen = rand.New(rand.NewSource(541))
)
func init() {
for i := 0; i < harmonyNodes; i++ {
newSlot := generateRandomSlot()
newSlot.TotalStake = nil
slotList = append(slotList, newSlot)
}
totalStake = numeric.ZeroDec()
for j := 0; j < stakedNodes; j++ {
newSlot := generateRandomSlot()
totalStake = totalStake.Add(*newSlot.TotalStake)
slotList = append(slotList, newSlot)
}
}
func generateRandomSlot() shard.Slot {
addr := common.Address{}
addr.SetBytes(big.NewInt(int64(accountGen.Int63n(maxAccountGen))).Bytes())
secretKey := bls.SecretKey{}
secretKey.Deserialize(big.NewInt(int64(keyGen.Int63n(maxKeyGen))).Bytes())
key := shard.BlsPublicKey{}
key.FromLibBLSPublicKey(secretKey.GetPublicKey())
stake := numeric.NewDecFromBigInt(big.NewInt(int64(stakeGen.Int63n(maxStakeGen))))
return shard.Slot{addr, key, &stake}
}
func TestCompute(t *testing.T) {
expectedRoster := NewRoster()
// Parameterized
expectedRoster.HmySlotCount = int64(harmonyNodes)
// Calculated when generated
expectedRoster.RawStakedTotal = totalStake
for _, slot := range slotList {
newMember := stakedVoter{
IsActive: true,
IsHarmonyNode: false,
EarningAccount: slot.EcdsaAddress,
EffectivePercent: numeric.ZeroDec(),
RawStake: numeric.ZeroDec(),
}
// Not Harmony node
if slot.TotalStake != nil {
newMember.RawStake = *slot.TotalStake
newMember.EffectivePercent = slot.TotalStake.Quo(expectedRoster.RawStakedTotal).Mul(StakersShare)
expectedRoster.TheirVotingPowerTotalPercentage = expectedRoster.TheirVotingPowerTotalPercentage.Add(newMember.EffectivePercent)
} else {
// Harmony node
newMember.IsHarmonyNode = true
newMember.RawStake = numeric.ZeroDec()
newMember.EffectivePercent = HarmonysShare.Quo(numeric.NewDec(expectedRoster.HmySlotCount))
expectedRoster.OurVotingPowerTotalPercentage = expectedRoster.OurVotingPowerTotalPercentage.Add(newMember.EffectivePercent)
}
expectedRoster.Voters[slot.BlsPublicKey] = newMember
}
computedRoster := Compute(slotList)
if !compareRosters(expectedRoster, computedRoster, t) {
t.Errorf("Compute Roster mismatch with expected Roster")
}
// Check that voting percents sum to 100
if !computedRoster.OurVotingPowerTotalPercentage.Add(computedRoster.TheirVotingPowerTotalPercentage).Equal(numeric.OneDec()) {
t.Errorf("Total voting power does not equal 1. Harmony voting power: %s, Staked voting power: %s",
computedRoster.OurVotingPowerTotalPercentage, computedRoster.TheirVotingPowerTotalPercentage)
}
}
func compareRosters(a, b *Roster, t *testing.T) bool {
// Compare stakedVoter maps
voterMatch := true
for k, voter := range a.Voters {
if other, exists := b.Voters[k]; exists {
if !compareStakedVoter(voter, other) {
t.Errorf("Expecting %s\n Got %s", voter.formatString(), other.formatString())
voterMatch = false
}
} else {
t.Errorf("Computed roster missing %s", voter.formatString())
voterMatch = false
}
}
return a.OurVotingPowerTotalPercentage.Equal(b.OurVotingPowerTotalPercentage) &&
a.TheirVotingPowerTotalPercentage.Equal(b.TheirVotingPowerTotalPercentage) &&
a.RawStakedTotal.Equal(b.RawStakedTotal) &&
a.HmySlotCount == b.HmySlotCount && voterMatch
}
func compareStakedVoter(a, b stakedVoter) bool {
return a.IsActive == b.IsActive &&
a.IsHarmonyNode == b.IsHarmonyNode &&
a.EarningAccount == b.EarningAccount &&
a.EffectivePercent.Equal(b.EffectivePercent) &&
a.RawStake.Equal(b.RawStake)
}
func (s *stakedVoter) formatString() string {
type t struct {
IsActive string `json:"active"`
IsHarmony string `json:"harmony-node"`
EarningAccount string `json:"one-address"`
EffectivePercent string `json:"effective-percent"`
RawStake string `json:"raw-stake"`
}
data := t{
strconv.FormatBool(s.IsActive),
strconv.FormatBool(s.IsHarmonyNode),
s.EarningAccount.String(),
s.EffectivePercent.String(),
s.RawStake.String(),
}
output, _ := json.Marshal(data)
return string(output)
}

@ -36,7 +36,7 @@ type Slot struct {
TotalStake *numeric.Dec `json:"total-stake" rlp:"nil"`
}
// SlotList is a list of SlotList.
// SlotList is a list of Slot.
type SlotList []Slot
// Committee contains the active nodes in one shard

@ -16,11 +16,12 @@ import (
)
const eposTestingFile = "epos.json"
const slotTestingFile = "slots.json"
var (
testingNumber = 20
testingSlots slotsData
testingPurchases Slots
expectedMedian numeric.Dec
maxAccountGen = int64(98765654323123134)
accountGen = rand.New(rand.NewSource(1337))
maxKeyGen = int64(98765654323123134)
@ -49,7 +50,7 @@ func init() {
fmt.Println(oops.Error())
panic("Could not unmarshal slots data into memory")
}
testingPurchases = generateRandomSlots(20)
testingPurchases = generateRandomSlots(testingNumber)
}
func generateRandomSlots(num int) Slots {
@ -72,20 +73,26 @@ func TestMedian(t *testing.T) {
sort.SliceStable(copyPurchases,
func(i, j int) bool { return copyPurchases[i].Dec.LTE(copyPurchases[j].Dec) })
numPurchases := len(copyPurchases) / 2
expectedResult := numeric.ZeroDec()
if len(copyPurchases)%2 == 0 {
expectedResult = copyPurchases[numPurchases-1].Dec.Add(copyPurchases[numPurchases].Dec).Quo(two)
expectedMedian = copyPurchases[numPurchases-1].Dec.Add(copyPurchases[numPurchases].Dec).Quo(two)
} else {
expectedResult = copyPurchases[numPurchases].Dec
expectedMedian = copyPurchases[numPurchases].Dec
}
med := median(testingPurchases)
if !med.Equal(expectedResult) {
t.Errorf("Expected: %s, Got: %s", expectedResult.String(), med.String())
if !med.Equal(expectedMedian) {
t.Errorf("Expected: %s, Got: %s", expectedMedian.String(), med.String())
}
}
func TestEffectiveStake(t *testing.T) {
//
for _, val := range testingPurchases {
expectedStake := numeric.MaxDec(numeric.MinDec(numeric.OneDec().Add(c).Mul(expectedMedian), val.Dec),
numeric.OneDec().Sub(c).Mul(expectedMedian))
calculatedStake := effectiveStake(expectedMedian, val.Dec)
if !expectedStake.Equal(calculatedStake) {
t.Errorf("Expected: %s, Got: %s", expectedStake.String(), calculatedStake.String())
}
}
}
func TestApply(t *testing.T) {

Loading…
Cancel
Save