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