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