bsc/consensus/parlia/parlia_test.go
2020-05-20 12:19:24 +08:00

138 lines
3.4 KiB
Go

package parlia
import (
"fmt"
"math/rand"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
)
func TestImpactOfValidatorOutOfService(t *testing.T) {
testCases := []struct {
totalValidators int
downValidators int
}{
{3, 1},
{5, 2},
{10, 1},
{10, 4},
{21, 1},
{21, 3},
{21, 5},
{21, 10},
}
for _, tc := range testCases {
simulateValidatorOutOfService(tc.totalValidators, tc.downValidators)
}
}
func simulateValidatorOutOfService(totalValidators int, downValidators int) {
downBlocks := 10000
recoverBlocks := 10000
recents := make(map[uint64]int)
validators := make(map[int]bool, totalValidators)
down := make([]int, totalValidators)
for i := 0; i < totalValidators; i++ {
validators[i] = true
down[i] = i
}
rand.Shuffle(totalValidators, func(i, j int) {
down[i], down[j] = down[j], down[i]
})
for i := 0; i < downValidators; i++ {
delete(validators, down[i])
}
isRecentSign := func(idx int) bool {
for _, signIdx := range recents {
if signIdx == idx {
return true
}
}
return false
}
isInService := func(idx int) bool {
return validators[idx]
}
downDelay := time.Duration(0)
for h := 1; h <= downBlocks; h++ {
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
delete(recents, uint64(h)-limit)
}
proposer := h % totalValidators
if !isInService(proposer) || isRecentSign(proposer) {
candidates := make(map[int]bool, totalValidators/2)
for v := range validators {
if !isRecentSign(v) {
candidates[v] = true
}
}
if len(candidates) == 0 {
panic("can not test such case")
}
idx, delay := producerBlockDelay(candidates, totalValidators)
downDelay = downDelay + delay
recents[uint64(h)] = idx
} else {
recents[uint64(h)] = proposer
}
}
fmt.Printf("average delay is %v when there is %d validators and %d is down \n",
downDelay/time.Duration(downBlocks), totalValidators, downValidators)
for i := 0; i < downValidators; i++ {
validators[down[i]] = true
}
recoverDelay := time.Duration(0)
lastseen := downBlocks
for h := downBlocks + 1; h <= downBlocks+recoverBlocks; h++ {
if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
delete(recents, uint64(h)-limit)
}
proposer := h % totalValidators
if !isInService(proposer) || isRecentSign(proposer) {
lastseen = h
candidates := make(map[int]bool, totalValidators/2)
for v := range validators {
if !isRecentSign(v) {
candidates[v] = true
}
}
if len(candidates) == 0 {
panic("can not test such case")
}
idx, delay := producerBlockDelay(candidates, totalValidators)
recoverDelay = recoverDelay + delay
recents[uint64(h)] = idx
} else {
recents[uint64(h)] = proposer
}
}
fmt.Printf("total delay is %v after recover when there is %d validators down ever, last seen not proposer at height %d\n",
recoverDelay, downValidators, lastseen)
}
func producerBlockDelay(candidates map[int]bool, numOfValidators int) (int, time.Duration) {
minDur := time.Duration(0)
minIdx := 0
wiggle := time.Duration(numOfValidators/2+1) * wiggleTime
for idx := range candidates {
sleepTime := rand.Int63n(int64(wiggle))
if int64(minDur) < sleepTime {
minDur = time.Duration(rand.Int63n(int64(wiggle)))
minIdx = idx
}
}
return minIdx, minDur
}
func randomAddress() common.Address {
addrBytes := make([]byte, 20)
rand.Read(addrBytes)
return common.BytesToAddress(addrBytes)
}