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