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 }