satoshi_test.go 3.6 KB

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