chain_pow_test.go 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // Copyright 2015 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package core
  17. import (
  18. "math/big"
  19. "runtime"
  20. "testing"
  21. "time"
  22. "github.com/ethereum/go-ethereum/common"
  23. "github.com/ethereum/go-ethereum/core/types"
  24. "github.com/ethereum/go-ethereum/ethdb"
  25. "github.com/ethereum/go-ethereum/params"
  26. "github.com/ethereum/go-ethereum/pow"
  27. )
  28. // failPow is a non-validating proof of work implementation, that returns true
  29. // from Verify for all but one block.
  30. type failPow struct {
  31. failing uint64
  32. }
  33. func (pow failPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) {
  34. return 0, nil
  35. }
  36. func (pow failPow) Verify(block pow.Block) bool { return block.NumberU64() != pow.failing }
  37. func (pow failPow) GetHashrate() int64 { return 0 }
  38. func (pow failPow) Turbo(bool) {}
  39. // delayedPow is a non-validating proof of work implementation, that returns true
  40. // from Verify for all blocks, but delays them the configured amount of time.
  41. type delayedPow struct {
  42. delay time.Duration
  43. }
  44. func (pow delayedPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) {
  45. return 0, nil
  46. }
  47. func (pow delayedPow) Verify(block pow.Block) bool { time.Sleep(pow.delay); return true }
  48. func (pow delayedPow) GetHashrate() int64 { return 0 }
  49. func (pow delayedPow) Turbo(bool) {}
  50. // Tests that simple POW verification works, for both good and bad blocks.
  51. func TestPowVerification(t *testing.T) {
  52. // Create a simple chain to verify
  53. var (
  54. testdb, _ = ethdb.NewMemDatabase()
  55. genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
  56. blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
  57. )
  58. headers := make([]*types.Header, len(blocks))
  59. for i, block := range blocks {
  60. headers[i] = block.Header()
  61. }
  62. // Run the POW checker for blocks one-by-one, checking for both valid and invalid nonces
  63. for i := 0; i < len(blocks); i++ {
  64. for j, full := range []bool{true, false} {
  65. for k, valid := range []bool{true, false} {
  66. var results <-chan nonceCheckResult
  67. switch {
  68. case full && valid:
  69. _, results = verifyNoncesFromBlocks(FakePow{}, []*types.Block{blocks[i]})
  70. case full && !valid:
  71. _, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]})
  72. case !full && valid:
  73. _, results = verifyNoncesFromHeaders(FakePow{}, []*types.Header{headers[i]})
  74. case !full && !valid:
  75. _, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]})
  76. }
  77. // Wait for the verification result
  78. select {
  79. case result := <-results:
  80. if result.index != 0 {
  81. t.Errorf("test %d.%d.%d: invalid index: have %d, want 0", i, j, k, result.index)
  82. }
  83. if result.valid != valid {
  84. t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, result.valid, valid)
  85. }
  86. case <-time.After(time.Second):
  87. t.Fatalf("test %d.%d.%d: verification timeout", i, j, k)
  88. }
  89. // Make sure no more data is returned
  90. select {
  91. case result := <-results:
  92. t.Fatalf("test %d.%d.%d: unexpected result returned: %v", i, j, k, result)
  93. case <-time.After(25 * time.Millisecond):
  94. }
  95. }
  96. }
  97. }
  98. }
  99. // Tests that concurrent POW verification works, for both good and bad blocks.
  100. func TestPowConcurrentVerification2(t *testing.T) { testPowConcurrentVerification(t, 2) }
  101. func TestPowConcurrentVerification8(t *testing.T) { testPowConcurrentVerification(t, 8) }
  102. func TestPowConcurrentVerification32(t *testing.T) { testPowConcurrentVerification(t, 32) }
  103. func testPowConcurrentVerification(t *testing.T, threads int) {
  104. // Create a simple chain to verify
  105. var (
  106. testdb, _ = ethdb.NewMemDatabase()
  107. genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
  108. blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
  109. )
  110. headers := make([]*types.Header, len(blocks))
  111. for i, block := range blocks {
  112. headers[i] = block.Header()
  113. }
  114. // Set the number of threads to verify on
  115. old := runtime.GOMAXPROCS(threads)
  116. defer runtime.GOMAXPROCS(old)
  117. // Run the POW checker for the entire block chain at once both for a valid and
  118. // also an invalid chain (enough if one is invalid, last but one (arbitrary)).
  119. for i, full := range []bool{true, false} {
  120. for j, valid := range []bool{true, false} {
  121. var results <-chan nonceCheckResult
  122. switch {
  123. case full && valid:
  124. _, results = verifyNoncesFromBlocks(FakePow{}, blocks)
  125. case full && !valid:
  126. _, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks)
  127. case !full && valid:
  128. _, results = verifyNoncesFromHeaders(FakePow{}, headers)
  129. case !full && !valid:
  130. _, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers)
  131. }
  132. // Wait for all the verification results
  133. checks := make(map[int]bool)
  134. for k := 0; k < len(blocks); k++ {
  135. select {
  136. case result := <-results:
  137. if _, ok := checks[result.index]; ok {
  138. t.Fatalf("test %d.%d.%d: duplicate results for %d", i, j, k, result.index)
  139. }
  140. if result.index < 0 || result.index >= len(blocks) {
  141. t.Fatalf("test %d.%d.%d: result %d out of bounds [%d, %d]", i, j, k, result.index, 0, len(blocks)-1)
  142. }
  143. checks[result.index] = result.valid
  144. case <-time.After(time.Second):
  145. t.Fatalf("test %d.%d.%d: verification timeout", i, j, k)
  146. }
  147. }
  148. // Check nonce check validity
  149. for k := 0; k < len(blocks); k++ {
  150. want := valid || (k != len(blocks)-2) // We chose the last but one nonce in the chain to fail
  151. if checks[k] != want {
  152. t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, checks[k], want)
  153. }
  154. }
  155. // Make sure no more data is returned
  156. select {
  157. case result := <-results:
  158. t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result)
  159. case <-time.After(25 * time.Millisecond):
  160. }
  161. }
  162. }
  163. }
  164. // Tests that aborting a POW validation indeed prevents further checks from being
  165. // run, as well as checks that no left-over goroutines are leaked.
  166. func TestPowConcurrentAbortion2(t *testing.T) { testPowConcurrentAbortion(t, 2) }
  167. func TestPowConcurrentAbortion8(t *testing.T) { testPowConcurrentAbortion(t, 8) }
  168. func TestPowConcurrentAbortion32(t *testing.T) { testPowConcurrentAbortion(t, 32) }
  169. func testPowConcurrentAbortion(t *testing.T, threads int) {
  170. // Create a simple chain to verify
  171. var (
  172. testdb, _ = ethdb.NewMemDatabase()
  173. genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
  174. blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
  175. )
  176. headers := make([]*types.Header, len(blocks))
  177. for i, block := range blocks {
  178. headers[i] = block.Header()
  179. }
  180. // Set the number of threads to verify on
  181. old := runtime.GOMAXPROCS(threads)
  182. defer runtime.GOMAXPROCS(old)
  183. // Run the POW checker for the entire block chain at once
  184. for i, full := range []bool{true, false} {
  185. var abort chan<- struct{}
  186. var results <-chan nonceCheckResult
  187. // Start the verifications and immediately abort
  188. if full {
  189. abort, results = verifyNoncesFromBlocks(delayedPow{time.Millisecond}, blocks)
  190. } else {
  191. abort, results = verifyNoncesFromHeaders(delayedPow{time.Millisecond}, headers)
  192. }
  193. close(abort)
  194. // Deplete the results channel
  195. verified := make(map[int]struct{})
  196. for depleted := false; !depleted; {
  197. select {
  198. case result := <-results:
  199. verified[result.index] = struct{}{}
  200. case <-time.After(50 * time.Millisecond):
  201. depleted = true
  202. }
  203. }
  204. // Check that abortion was honored by not processing too many POWs
  205. if len(verified) > 2*threads {
  206. t.Errorf("test %d: verification count too large: have %d, want below %d", i, len(verified), 2*threads)
  207. }
  208. // Check that there are no gaps in the results
  209. for j := 0; j < len(verified); j++ {
  210. if _, ok := verified[j]; !ok {
  211. t.Errorf("test %d.%d: gap found in verification results", i, j)
  212. }
  213. }
  214. }
  215. }