github.com/devfans/go-ethereum@v1.5.10-0.20170326212234-7419d0c38291/core/chain_pow_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  	"errors"
    21  	"math/big"
    22  	"runtime"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core/types"
    28  	"github.com/ethereum/go-ethereum/ethdb"
    29  	"github.com/ethereum/go-ethereum/params"
    30  	"github.com/ethereum/go-ethereum/pow"
    31  )
    32  
    33  // failPow is a non-validating proof of work implementation, that returns true
    34  // from Verify for all but one block.
    35  type failPow struct {
    36  	failing uint64
    37  }
    38  
    39  func (pow failPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) {
    40  	return 0, nil
    41  }
    42  func (pow failPow) Verify(block pow.Block) error {
    43  	if block.NumberU64() == pow.failing {
    44  		return errors.New("failed")
    45  	}
    46  	return nil
    47  }
    48  func (pow failPow) Hashrate() float64 { return 0 }
    49  
    50  // delayedPow is a non-validating proof of work implementation, that returns true
    51  // from Verify for all blocks, but delays them the configured amount of time.
    52  type delayedPow struct {
    53  	delay time.Duration
    54  }
    55  
    56  func (pow delayedPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) {
    57  	return 0, nil
    58  }
    59  func (pow delayedPow) Verify(block pow.Block) error { time.Sleep(pow.delay); return nil }
    60  func (pow delayedPow) Hashrate() float64            { return 0 }
    61  
    62  // Tests that simple POW verification works, for both good and bad blocks.
    63  func TestPowVerification(t *testing.T) {
    64  	// Create a simple chain to verify
    65  	var (
    66  		testdb, _ = ethdb.NewMemDatabase()
    67  		genesis   = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
    68  		blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
    69  	)
    70  	headers := make([]*types.Header, len(blocks))
    71  	for i, block := range blocks {
    72  		headers[i] = block.Header()
    73  	}
    74  	// Run the POW checker for blocks one-by-one, checking for both valid and invalid nonces
    75  	for i := 0; i < len(blocks); i++ {
    76  		for j, full := range []bool{true, false} {
    77  			for k, valid := range []bool{true, false} {
    78  				var results <-chan nonceCheckResult
    79  
    80  				switch {
    81  				case full && valid:
    82  					_, results = verifyNoncesFromBlocks(pow.FakePow{}, []*types.Block{blocks[i]})
    83  				case full && !valid:
    84  					_, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]})
    85  				case !full && valid:
    86  					_, results = verifyNoncesFromHeaders(pow.FakePow{}, []*types.Header{headers[i]})
    87  				case !full && !valid:
    88  					_, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]})
    89  				}
    90  				// Wait for the verification result
    91  				select {
    92  				case result := <-results:
    93  					if result.index != 0 {
    94  						t.Errorf("test %d.%d.%d: invalid index: have %d, want 0", i, j, k, result.index)
    95  					}
    96  					if result.valid != valid {
    97  						t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, result.valid, valid)
    98  					}
    99  				case <-time.After(time.Second):
   100  					t.Fatalf("test %d.%d.%d: verification timeout", i, j, k)
   101  				}
   102  				// Make sure no more data is returned
   103  				select {
   104  				case result := <-results:
   105  					t.Fatalf("test %d.%d.%d: unexpected result returned: %v", i, j, k, result)
   106  				case <-time.After(25 * time.Millisecond):
   107  				}
   108  			}
   109  		}
   110  	}
   111  }
   112  
   113  // Tests that concurrent POW verification works, for both good and bad blocks.
   114  func TestPowConcurrentVerification2(t *testing.T)  { testPowConcurrentVerification(t, 2) }
   115  func TestPowConcurrentVerification8(t *testing.T)  { testPowConcurrentVerification(t, 8) }
   116  func TestPowConcurrentVerification32(t *testing.T) { testPowConcurrentVerification(t, 32) }
   117  
   118  func testPowConcurrentVerification(t *testing.T, threads int) {
   119  	// Create a simple chain to verify
   120  	var (
   121  		testdb, _ = ethdb.NewMemDatabase()
   122  		genesis   = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
   123  		blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
   124  	)
   125  	headers := make([]*types.Header, len(blocks))
   126  	for i, block := range blocks {
   127  		headers[i] = block.Header()
   128  	}
   129  	// Set the number of threads to verify on
   130  	old := runtime.GOMAXPROCS(threads)
   131  	defer runtime.GOMAXPROCS(old)
   132  
   133  	// Run the POW checker for the entire block chain at once both for a valid and
   134  	// also an invalid chain (enough if one is invalid, last but one (arbitrary)).
   135  	for i, full := range []bool{true, false} {
   136  		for j, valid := range []bool{true, false} {
   137  			var results <-chan nonceCheckResult
   138  
   139  			switch {
   140  			case full && valid:
   141  				_, results = verifyNoncesFromBlocks(pow.FakePow{}, blocks)
   142  			case full && !valid:
   143  				_, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks)
   144  			case !full && valid:
   145  				_, results = verifyNoncesFromHeaders(pow.FakePow{}, headers)
   146  			case !full && !valid:
   147  				_, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers)
   148  			}
   149  			// Wait for all the verification results
   150  			checks := make(map[int]bool)
   151  			for k := 0; k < len(blocks); k++ {
   152  				select {
   153  				case result := <-results:
   154  					if _, ok := checks[result.index]; ok {
   155  						t.Fatalf("test %d.%d.%d: duplicate results for %d", i, j, k, result.index)
   156  					}
   157  					if result.index < 0 || result.index >= len(blocks) {
   158  						t.Fatalf("test %d.%d.%d: result %d out of bounds [%d, %d]", i, j, k, result.index, 0, len(blocks)-1)
   159  					}
   160  					checks[result.index] = result.valid
   161  
   162  				case <-time.After(time.Second):
   163  					t.Fatalf("test %d.%d.%d: verification timeout", i, j, k)
   164  				}
   165  			}
   166  			// Check nonce check validity
   167  			for k := 0; k < len(blocks); k++ {
   168  				want := valid || (k != len(blocks)-2) // We chose the last but one nonce in the chain to fail
   169  				if checks[k] != want {
   170  					t.Errorf("test %d.%d.%d: validity mismatch: have %v, want %v", i, j, k, checks[k], want)
   171  				}
   172  			}
   173  			// Make sure no more data is returned
   174  			select {
   175  			case result := <-results:
   176  				t.Fatalf("test %d.%d: unexpected result returned: %v", i, j, result)
   177  			case <-time.After(25 * time.Millisecond):
   178  			}
   179  		}
   180  	}
   181  }
   182  
   183  // Tests that aborting a POW validation indeed prevents further checks from being
   184  // run, as well as checks that no left-over goroutines are leaked.
   185  func TestPowConcurrentAbortion2(t *testing.T)  { testPowConcurrentAbortion(t, 2) }
   186  func TestPowConcurrentAbortion8(t *testing.T)  { testPowConcurrentAbortion(t, 8) }
   187  func TestPowConcurrentAbortion32(t *testing.T) { testPowConcurrentAbortion(t, 32) }
   188  
   189  func testPowConcurrentAbortion(t *testing.T, threads int) {
   190  	// Create a simple chain to verify
   191  	var (
   192  		testdb, _ = ethdb.NewMemDatabase()
   193  		genesis   = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
   194  		blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
   195  	)
   196  	headers := make([]*types.Header, len(blocks))
   197  	for i, block := range blocks {
   198  		headers[i] = block.Header()
   199  	}
   200  	// Set the number of threads to verify on
   201  	old := runtime.GOMAXPROCS(threads)
   202  	defer runtime.GOMAXPROCS(old)
   203  
   204  	// Run the POW checker for the entire block chain at once
   205  	for i, full := range []bool{true, false} {
   206  		var abort chan<- struct{}
   207  		var results <-chan nonceCheckResult
   208  
   209  		// Start the verifications and immediately abort
   210  		if full {
   211  			abort, results = verifyNoncesFromBlocks(delayedPow{time.Millisecond}, blocks)
   212  		} else {
   213  			abort, results = verifyNoncesFromHeaders(delayedPow{time.Millisecond}, headers)
   214  		}
   215  		close(abort)
   216  
   217  		// Deplete the results channel
   218  		verified := make(map[int]struct{})
   219  		for depleted := false; !depleted; {
   220  			select {
   221  			case result := <-results:
   222  				verified[result.index] = struct{}{}
   223  			case <-time.After(50 * time.Millisecond):
   224  				depleted = true
   225  			}
   226  		}
   227  		// Check that abortion was honored by not processing too many POWs
   228  		if len(verified) > 2*threads {
   229  			t.Errorf("test %d: verification count too large: have %d, want below %d", i, len(verified), 2*threads)
   230  		}
   231  		// Check that there are no gaps in the results
   232  		for j := 0; j < len(verified); j++ {
   233  			if _, ok := verified[j]; !ok {
   234  				t.Errorf("test %d.%d: gap found in verification results", i, j)
   235  			}
   236  		}
   237  	}
   238  }