| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-23 18:35:11 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							| 
									
										
										
										
											2015-07-22 18:48:40 +02:00
										 |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-07-07 02:54:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | package core | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | 	"testing" | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/consensus/ethash" | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/rawdb" | 
					
						
							| 
									
										
										
										
											2015-05-22 22:44:51 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/vm" | 
					
						
							| 
									
										
										
										
											2016-10-20 13:36:29 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/params" | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | // Tests that simple header verification works, for both good and bad blocks. | 
					
						
							|  |  |  | func TestHeaderVerification(t *testing.T) { | 
					
						
							|  |  |  | 	// Create a simple chain to verify | 
					
						
							|  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		testdb    = rawdb.NewMemoryDatabase() | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 		gspec     = &Genesis{Config: params.TestChainConfig} | 
					
						
							|  |  |  | 		genesis   = gspec.MustCommit(testdb) | 
					
						
							| 
									
										
										
										
											2017-12-22 20:37:50 +08:00
										 |  |  | 		blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	headers := make([]*types.Header, len(blocks)) | 
					
						
							|  |  |  | 	for i, block := range blocks { | 
					
						
							|  |  |  | 		headers[i] = block.Header() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces | 
					
						
							| 
									
										
										
										
											2020-05-11 17:58:43 +02:00
										 |  |  | 	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) | 
					
						
							| 
									
										
										
										
											2017-08-07 15:47:25 +03:00
										 |  |  | 	defer chain.Stop() | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for i := 0; i < len(blocks); i++ { | 
					
						
							|  |  |  | 		for j, valid := range []bool{true, false} { | 
					
						
							|  |  |  | 			var results <-chan error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if valid { | 
					
						
							|  |  |  | 				engine := ethash.NewFaker() | 
					
						
							|  |  |  | 				_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				engine := ethash.NewFakeFailer(headers[i].Number.Uint64()) | 
					
						
							|  |  |  | 				_, results = engine.VerifyHeaders(chain, []*types.Header{headers[i]}, []bool{true}) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Wait for the verification result | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case result := <-results: | 
					
						
							|  |  |  | 				if (result == nil) != valid { | 
					
						
							|  |  |  | 					t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, result, valid) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case <-time.After(time.Second): | 
					
						
							|  |  |  | 				t.Fatalf("test %d.%d: verification timeout", i, j) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Make sure no more data is returned | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case result := <-results: | 
					
						
							|  |  |  | 				t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result) | 
					
						
							|  |  |  | 			case <-time.After(25 * time.Millisecond): | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		chain.InsertChain(blocks[i : i+1]) | 
					
						
							| 
									
										
										
										
											2015-06-08 12:12:13 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | // Tests that concurrent header verification works, for both good and bad blocks. | 
					
						
							|  |  |  | func TestHeaderConcurrentVerification2(t *testing.T)  { testHeaderConcurrentVerification(t, 2) } | 
					
						
							|  |  |  | func TestHeaderConcurrentVerification8(t *testing.T)  { testHeaderConcurrentVerification(t, 8) } | 
					
						
							|  |  |  | func TestHeaderConcurrentVerification32(t *testing.T) { testHeaderConcurrentVerification(t, 32) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func testHeaderConcurrentVerification(t *testing.T, threads int) { | 
					
						
							|  |  |  | 	// Create a simple chain to verify | 
					
						
							|  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		testdb    = rawdb.NewMemoryDatabase() | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 		gspec     = &Genesis{Config: params.TestChainConfig} | 
					
						
							|  |  |  | 		genesis   = gspec.MustCommit(testdb) | 
					
						
							| 
									
										
										
										
											2017-12-22 20:37:50 +08:00
										 |  |  | 		blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 8, nil) | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	headers := make([]*types.Header, len(blocks)) | 
					
						
							|  |  |  | 	seals := make([]bool, len(blocks)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, block := range blocks { | 
					
						
							|  |  |  | 		headers[i] = block.Header() | 
					
						
							|  |  |  | 		seals[i] = true | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	// Set the number of threads to verify on | 
					
						
							|  |  |  | 	old := runtime.GOMAXPROCS(threads) | 
					
						
							|  |  |  | 	defer runtime.GOMAXPROCS(old) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Run the header checker for the entire block chain at once both for a valid and | 
					
						
							|  |  |  | 	// also an invalid chain (enough if one arbitrary block is invalid). | 
					
						
							|  |  |  | 	for i, valid := range []bool{true, false} { | 
					
						
							|  |  |  | 		var results <-chan error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if valid { | 
					
						
							| 
									
										
										
										
											2020-05-11 17:58:43 +02:00
										 |  |  | 			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil) | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 			_, results = chain.engine.VerifyHeaders(chain, headers, seals) | 
					
						
							| 
									
										
										
										
											2017-08-07 15:47:25 +03:00
										 |  |  | 			chain.Stop() | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-05-11 17:58:43 +02:00
										 |  |  | 			chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}, nil, nil) | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 			_, results = chain.engine.VerifyHeaders(chain, headers, seals) | 
					
						
							| 
									
										
										
										
											2017-08-07 15:47:25 +03:00
										 |  |  | 			chain.Stop() | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		// Wait for all the verification results | 
					
						
							|  |  |  | 		checks := make(map[int]error) | 
					
						
							|  |  |  | 		for j := 0; j < len(blocks); j++ { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case result := <-results: | 
					
						
							|  |  |  | 				checks[j] = result | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 			case <-time.After(time.Second): | 
					
						
							|  |  |  | 				t.Fatalf("test %d.%d: verification timeout", i, j) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Check nonce check validity | 
					
						
							|  |  |  | 		for j := 0; j < len(blocks); j++ { | 
					
						
							|  |  |  | 			want := valid || (j < len(blocks)-2) // We chose the last-but-one nonce in the chain to fail | 
					
						
							|  |  |  | 			if (checks[j] == nil) != want { | 
					
						
							|  |  |  | 				t.Errorf("test %d.%d: validity mismatch: have %v, want %v", i, j, checks[j], want) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !want { | 
					
						
							|  |  |  | 				// A few blocks after the first error may pass verification due to concurrent | 
					
						
							|  |  |  | 				// workers. We don't care about those in this test, just that the correct block | 
					
						
							|  |  |  | 				// errors out. | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Make sure no more data is returned | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case result := <-results: | 
					
						
							|  |  |  | 			t.Fatalf("test %d: unexpected result returned: %v", i, result) | 
					
						
							|  |  |  | 		case <-time.After(25 * time.Millisecond): | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-02-18 13:14:21 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-05-22 22:44:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | // Tests that aborting a header validation indeed prevents further checks from being | 
					
						
							|  |  |  | // run, as well as checks that no left-over goroutines are leaked. | 
					
						
							|  |  |  | func TestHeaderConcurrentAbortion2(t *testing.T)  { testHeaderConcurrentAbortion(t, 2) } | 
					
						
							|  |  |  | func TestHeaderConcurrentAbortion8(t *testing.T)  { testHeaderConcurrentAbortion(t, 8) } | 
					
						
							|  |  |  | func TestHeaderConcurrentAbortion32(t *testing.T) { testHeaderConcurrentAbortion(t, 32) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func testHeaderConcurrentAbortion(t *testing.T, threads int) { | 
					
						
							|  |  |  | 	// Create a simple chain to verify | 
					
						
							|  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 		testdb    = rawdb.NewMemoryDatabase() | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 		gspec     = &Genesis{Config: params.TestChainConfig} | 
					
						
							|  |  |  | 		genesis   = gspec.MustCommit(testdb) | 
					
						
							| 
									
										
										
										
											2017-12-22 20:37:50 +08:00
										 |  |  | 		blocks, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), testdb, 1024, nil) | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	headers := make([]*types.Header, len(blocks)) | 
					
						
							|  |  |  | 	seals := make([]bool, len(blocks)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, block := range blocks { | 
					
						
							|  |  |  | 		headers[i] = block.Header() | 
					
						
							|  |  |  | 		seals[i] = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Set the number of threads to verify on | 
					
						
							|  |  |  | 	old := runtime.GOMAXPROCS(threads) | 
					
						
							|  |  |  | 	defer runtime.GOMAXPROCS(old) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start the verifications and immediately abort | 
					
						
							| 
									
										
										
										
											2020-05-11 17:58:43 +02:00
										 |  |  | 	chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}, nil, nil) | 
					
						
							| 
									
										
										
										
											2017-08-07 15:47:25 +03:00
										 |  |  | 	defer chain.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-05 01:16:29 +03:00
										 |  |  | 	abort, results := chain.engine.VerifyHeaders(chain, headers, seals) | 
					
						
							|  |  |  | 	close(abort) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deplete the results channel | 
					
						
							|  |  |  | 	verified := 0 | 
					
						
							|  |  |  | 	for depleted := false; !depleted; { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case result := <-results: | 
					
						
							|  |  |  | 			if result != nil { | 
					
						
							|  |  |  | 				t.Errorf("header %d: validation failed: %v", verified, result) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			verified++ | 
					
						
							|  |  |  | 		case <-time.After(50 * time.Millisecond): | 
					
						
							|  |  |  | 			depleted = true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Check that abortion was honored by not processing too many POWs | 
					
						
							|  |  |  | 	if verified > 2*threads { | 
					
						
							|  |  |  | 		t.Errorf("verification count too large: have %d, want below %d", verified, 2*threads) | 
					
						
							| 
									
										
										
										
											2015-05-22 22:44:51 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |