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