github.com/luckypickle/go-ethereum-vet@v1.14.2/core/block_validator_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 "runtime" 21 "testing" 22 "time" 23 24 "github.com/luckypickle/go-ethereum-vet/consensus/ethash" 25 "github.com/luckypickle/go-ethereum-vet/core/types" 26 "github.com/luckypickle/go-ethereum-vet/core/vm" 27 "github.com/luckypickle/go-ethereum-vet/ethdb" 28 "github.com/luckypickle/go-ethereum-vet/params" 29 ) 30 31 // Tests that simple header verification works, for both good and bad blocks. 32 func TestHeaderVerification(t *testing.T) { 33 // Create a simple chain to verify 34 var ( 35 testdb = ethdb.NewMemDatabase() 36 gspec = &Genesis{Config: params.TestChainConfig} 37 genesis = gspec.MustCommit(testdb) 38 blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) 39 ) 40 headers := make([]*types.Header, len(blocks)) 41 for i, block := range blocks { 42 headers[i] = block.Header() 43 } 44 // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces 45 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) 46 defer chain.Stop() 47 48 for i := 0; i < len(blocks); i++ { 49 for j, valid := range []bool{true, false} { 50 var results <-chan error 51 52 if valid { 53 engine := ethash.NewFaker() 54 _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) 55 } else { 56 engine := ethash.NewFakeFailer(headers[i].Number.Uint64()) 57 _, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) 58 } 59 // Wait for the verification result 60 select { 61 case result := <-results: 62 if (result == nil) != valid { 63 t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, result, valid) 64 } 65 case <-time.After(time.Second): 66 t.Fatalf("test %d.%d: verification timeout", i, j) 67 } 68 // Make sure no more data is returned 69 select { 70 case result := <-results: 71 t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result) 72 case <-time.After(25 * time.Millisecond): 73 } 74 } 75 chain.InsertChain(blocks[i : i+1]) 76 } 77 } 78 79 // Tests that concurrent header verification works, for both good and bad blocks. 80 func TestHeaderConcurrentVerification2(t *testing.T) { testHeaderConcurrentVerification(t, 2) } 81 func TestHeaderConcurrentVerification8(t *testing.T) { testHeaderConcurrentVerification(t, 8) } 82 func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) } 83 84 func testHeaderConcurrentVerification(t *testing.T, threads int) { 85 // Create a simple chain to verify 86 var ( 87 testdb = ethdb.NewMemDatabase() 88 gspec = &Genesis{Config: params.TestChainConfig} 89 genesis = gspec.MustCommit(testdb) 90 blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) 91 ) 92 headers := make([]*types.Header, len(blocks)) 93 seals := make([]bool, len(blocks)) 94 95 for i, block := range blocks { 96 headers[i] = block.Header() 97 seals[i] = true 98 } 99 // Set the number of threads to verify on 100 old := runtime.GOMAXPROCS(threads) 101 defer runtime.GOMAXPROCS(old) 102 103 // Run the header checker for the entire block chain at once both for a valid and 104 // also an invalid chain (enough if one arbitrary block is invalid). 105 for i, valid := range []bool{true, false} { 106 var results <-chan error 107 108 if valid { 109 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) 110 _, results = chain.engine.VerifyHeaders(chain, headers, seals) 111 chain.Stop() 112 } else { 113 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}) 114 _, results = chain.engine.VerifyHeaders(chain, headers, seals) 115 chain.Stop() 116 } 117 // Wait for all the verification results 118 checks := make(map[int]error) 119 for j := 0; j < len(blocks); j++ { 120 select { 121 case result := <-results: 122 checks[j] = result 123 124 case <-time.After(time.Second): 125 t.Fatalf("test %d.%d: verification timeout", i, j) 126 } 127 } 128 // Check nonce check validity 129 for j := 0; j < len(blocks); j++ { 130 want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail 131 if (checks[j] == nil) != want { 132 t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want) 133 } 134 if !want { 135 // A few blocks after the first error may pass verification due to concurrent 136 // workers. We don't care about those in this test, just that the correct block 137 // errors out. 138 break 139 } 140 } 141 // Make sure no more data is returned 142 select { 143 case result := <-results: 144 t.Fatalf("test %d: unexpected result returned: %v", i, result) 145 case <-time.After(25 * time.Millisecond): 146 } 147 } 148 } 149 150 // Tests that aborting a header validation indeed prevents further checks from being 151 // run, as well as checks that no left-over goroutines are leaked. 152 func TestHeaderConcurrentAbortion2(t *testing.T) { testHeaderConcurrentAbortion(t, 2) } 153 func TestHeaderConcurrentAbortion8(t *testing.T) { testHeaderConcurrentAbortion(t, 8) } 154 func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) } 155 156 func testHeaderConcurrentAbortion(t *testing.T, threads int) { 157 // Create a simple chain to verify 158 var ( 159 testdb = ethdb.NewMemDatabase() 160 gspec = &Genesis{Config: params.TestChainConfig} 161 genesis = gspec.MustCommit(testdb) 162 blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil) 163 ) 164 headers := make([]*types.Header, len(blocks)) 165 seals := make([]bool, len(blocks)) 166 167 for i, block := range blocks { 168 headers[i] = block.Header() 169 seals[i] = true 170 } 171 // Set the number of threads to verify on 172 old := runtime.GOMAXPROCS(threads) 173 defer runtime.GOMAXPROCS(old) 174 175 // Start the verifications and immediately abort 176 chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}) 177 defer chain.Stop() 178 179 abort, results := chain.engine.VerifyHeaders(chain, headers, seals) 180 close(abort) 181 182 // Deplete the results channel 183 verified := 0 184 for depleted := false; !depleted; { 185 select { 186 case result := <-results: 187 if result != nil { 188 t.Errorf("header %d: validation failed: %v", verified, result) 189 } 190 verified++ 191 case <-time.After(50 * time.Millisecond): 192 depleted = true 193 } 194 } 195 // Check that abortion was honored by not processing too many POWs 196 if verified > 2*threads { 197 t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads) 198 } 199 }