github.com/phillinzzz/newBsc@v1.1.6/consensus/parlia/parlia_test.go (about)

     1  package parlia
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"testing"
     7  
     8  	"github.com/phillinzzz/newBsc/common"
     9  )
    10  
    11  func TestImpactOfValidatorOutOfService(t *testing.T) {
    12  	testCases := []struct {
    13  		totalValidators int
    14  		downValidators  int
    15  	}{
    16  		{3, 1},
    17  		{5, 2},
    18  		{10, 1},
    19  		{10, 4},
    20  		{21, 1},
    21  		{21, 3},
    22  		{21, 5},
    23  		{21, 10},
    24  	}
    25  	for _, tc := range testCases {
    26  		simulateValidatorOutOfService(tc.totalValidators, tc.downValidators)
    27  	}
    28  }
    29  
    30  func simulateValidatorOutOfService(totalValidators int, downValidators int) {
    31  	downBlocks := 10000
    32  	recoverBlocks := 10000
    33  	recents := make(map[uint64]int)
    34  
    35  	validators := make(map[int]bool, totalValidators)
    36  	down := make([]int, totalValidators)
    37  	for i := 0; i < totalValidators; i++ {
    38  		validators[i] = true
    39  		down[i] = i
    40  	}
    41  	rand.Shuffle(totalValidators, func(i, j int) {
    42  		down[i], down[j] = down[j], down[i]
    43  	})
    44  	for i := 0; i < downValidators; i++ {
    45  		delete(validators, down[i])
    46  	}
    47  	isRecentSign := func(idx int) bool {
    48  		for _, signIdx := range recents {
    49  			if signIdx == idx {
    50  				return true
    51  			}
    52  		}
    53  		return false
    54  	}
    55  	isInService := func(idx int) bool {
    56  		return validators[idx]
    57  	}
    58  
    59  	downDelay := uint64(0)
    60  	for h := 1; h <= downBlocks; h++ {
    61  		if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
    62  			delete(recents, uint64(h)-limit)
    63  		}
    64  		proposer := h % totalValidators
    65  		if !isInService(proposer) || isRecentSign(proposer) {
    66  			candidates := make(map[int]bool, totalValidators/2)
    67  			for v := range validators {
    68  				if !isRecentSign(v) {
    69  					candidates[v] = true
    70  				}
    71  			}
    72  			if len(candidates) == 0 {
    73  				panic("can not test such case")
    74  			}
    75  			idx, delay := producerBlockDelay(candidates, h, totalValidators)
    76  			downDelay = downDelay + delay
    77  			recents[uint64(h)] = idx
    78  		} else {
    79  			recents[uint64(h)] = proposer
    80  		}
    81  	}
    82  	fmt.Printf("average delay is %v  when there is %d validators and %d is down \n",
    83  		downDelay/uint64(downBlocks), totalValidators, downValidators)
    84  
    85  	for i := 0; i < downValidators; i++ {
    86  		validators[down[i]] = true
    87  	}
    88  
    89  	recoverDelay := uint64(0)
    90  	lastseen := downBlocks
    91  	for h := downBlocks + 1; h <= downBlocks+recoverBlocks; h++ {
    92  		if limit := uint64(totalValidators/2 + 1); uint64(h) >= limit {
    93  			delete(recents, uint64(h)-limit)
    94  		}
    95  		proposer := h % totalValidators
    96  		if !isInService(proposer) || isRecentSign(proposer) {
    97  			lastseen = h
    98  			candidates := make(map[int]bool, totalValidators/2)
    99  			for v := range validators {
   100  				if !isRecentSign(v) {
   101  					candidates[v] = true
   102  				}
   103  			}
   104  			if len(candidates) == 0 {
   105  				panic("can not test such case")
   106  			}
   107  			idx, delay := producerBlockDelay(candidates, h, totalValidators)
   108  			recoverDelay = recoverDelay + delay
   109  			recents[uint64(h)] = idx
   110  		} else {
   111  			recents[uint64(h)] = proposer
   112  		}
   113  	}
   114  	fmt.Printf("total delay is %v after recover when there is %d validators down ever, last seen not proposer at height %d\n",
   115  		recoverDelay, downValidators, lastseen)
   116  }
   117  
   118  func producerBlockDelay(candidates map[int]bool, height, numOfValidators int) (int, uint64) {
   119  
   120  	s := rand.NewSource(int64(height))
   121  	r := rand.New(s)
   122  	n := numOfValidators
   123  	backOffSteps := make([]int, 0, n)
   124  	for idx := 0; idx < n; idx++ {
   125  		backOffSteps = append(backOffSteps, idx)
   126  	}
   127  	r.Shuffle(n, func(i, j int) {
   128  		backOffSteps[i], backOffSteps[j] = backOffSteps[j], backOffSteps[i]
   129  	})
   130  	minDelay := numOfValidators
   131  	minCandidate := 0
   132  	for c := range candidates {
   133  		if minDelay > backOffSteps[c] {
   134  			minDelay = backOffSteps[c]
   135  			minCandidate = c
   136  		}
   137  	}
   138  	delay := initialBackOffTime + uint64(minDelay)*wiggleTime
   139  	return minCandidate, delay
   140  }
   141  
   142  func randomAddress() common.Address {
   143  	addrBytes := make([]byte, 20)
   144  	rand.Read(addrBytes)
   145  	return common.BytesToAddress(addrBytes)
   146  }