git.luolix.top/ethereum/go-ethereum@v1.9.7/consensus/ethash/sealer_test.go (about)

     1  // Copyright 2018 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 ethash
    18  
    19  import (
    20  	"encoding/json"
    21  	"io/ioutil"
    22  	"math/big"
    23  	"net"
    24  	"net/http"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/ethereum/go-ethereum/common"
    29  	"github.com/ethereum/go-ethereum/core/types"
    30  )
    31  
    32  // Tests whether remote HTTP servers are correctly notified of new work.
    33  func TestRemoteNotify(t *testing.T) {
    34  	// Start a simple webserver to capture notifications
    35  	sink := make(chan [3]string)
    36  
    37  	server := &http.Server{
    38  		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    39  			blob, err := ioutil.ReadAll(req.Body)
    40  			if err != nil {
    41  				t.Fatalf("failed to read miner notification: %v", err)
    42  			}
    43  			var work [3]string
    44  			if err := json.Unmarshal(blob, &work); err != nil {
    45  				t.Fatalf("failed to unmarshal miner notification: %v", err)
    46  			}
    47  			sink <- work
    48  		}),
    49  	}
    50  	// Open a custom listener to extract its local address
    51  	listener, err := net.Listen("tcp", "localhost:0")
    52  	if err != nil {
    53  		t.Fatalf("failed to open notification server: %v", err)
    54  	}
    55  	defer listener.Close()
    56  
    57  	go server.Serve(listener)
    58  
    59  	// Wait for server to start listening
    60  	var tries int
    61  	for tries = 0; tries < 10; tries++ {
    62  		conn, _ := net.DialTimeout("tcp", listener.Addr().String(), 1*time.Second)
    63  		if conn != nil {
    64  			break
    65  		}
    66  	}
    67  	if tries == 10 {
    68  		t.Fatal("tcp listener not ready for more than 10 seconds")
    69  	}
    70  
    71  	// Create the custom ethash engine
    72  	ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
    73  	defer ethash.Close()
    74  
    75  	// Stream a work task and ensure the notification bubbles out
    76  	header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
    77  	block := types.NewBlockWithHeader(header)
    78  
    79  	ethash.Seal(nil, block, nil, nil)
    80  	select {
    81  	case work := <-sink:
    82  		if want := ethash.SealHash(header).Hex(); work[0] != want {
    83  			t.Errorf("work packet hash mismatch: have %s, want %s", work[0], want)
    84  		}
    85  		if want := common.BytesToHash(SeedHash(header.Number.Uint64())).Hex(); work[1] != want {
    86  			t.Errorf("work packet seed mismatch: have %s, want %s", work[1], want)
    87  		}
    88  		target := new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), header.Difficulty)
    89  		if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want {
    90  			t.Errorf("work packet target mismatch: have %s, want %s", work[2], want)
    91  		}
    92  	case <-time.After(3 * time.Second):
    93  		t.Fatalf("notification timed out")
    94  	}
    95  }
    96  
    97  // Tests that pushing work packages fast to the miner doesn't cause any data race
    98  // issues in the notifications.
    99  func TestRemoteMultiNotify(t *testing.T) {
   100  	// Start a simple webserver to capture notifications
   101  	sink := make(chan [3]string, 64)
   102  
   103  	server := &http.Server{
   104  		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   105  			blob, err := ioutil.ReadAll(req.Body)
   106  			if err != nil {
   107  				t.Fatalf("failed to read miner notification: %v", err)
   108  			}
   109  			var work [3]string
   110  			if err := json.Unmarshal(blob, &work); err != nil {
   111  				t.Fatalf("failed to unmarshal miner notification: %v", err)
   112  			}
   113  			sink <- work
   114  		}),
   115  	}
   116  	// Open a custom listener to extract its local address
   117  	listener, err := net.Listen("tcp", "localhost:0")
   118  	if err != nil {
   119  		t.Fatalf("failed to open notification server: %v", err)
   120  	}
   121  	defer listener.Close()
   122  
   123  	go server.Serve(listener)
   124  
   125  	// Create the custom ethash engine
   126  	ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
   127  	defer ethash.Close()
   128  
   129  	// Stream a lot of work task and ensure all the notifications bubble out
   130  	for i := 0; i < cap(sink); i++ {
   131  		header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
   132  		block := types.NewBlockWithHeader(header)
   133  
   134  		ethash.Seal(nil, block, nil, nil)
   135  	}
   136  	for i := 0; i < cap(sink); i++ {
   137  		select {
   138  		case <-sink:
   139  		case <-time.After(3 * time.Second):
   140  			t.Fatalf("notification %d timed out", i)
   141  		}
   142  	}
   143  }
   144  
   145  // Tests whether stale solutions are correctly processed.
   146  func TestStaleSubmission(t *testing.T) {
   147  	ethash := NewTester(nil, true)
   148  	defer ethash.Close()
   149  	api := &API{ethash}
   150  
   151  	fakeNonce, fakeDigest := types.BlockNonce{0x01, 0x02, 0x03}, common.HexToHash("deadbeef")
   152  
   153  	testcases := []struct {
   154  		headers     []*types.Header
   155  		submitIndex int
   156  		submitRes   bool
   157  	}{
   158  		// Case1: submit solution for the latest mining package
   159  		{
   160  			[]*types.Header{
   161  				{ParentHash: common.BytesToHash([]byte{0xa}), Number: big.NewInt(1), Difficulty: big.NewInt(100000000)},
   162  			},
   163  			0,
   164  			true,
   165  		},
   166  		// Case2: submit solution for the previous package but have same parent.
   167  		{
   168  			[]*types.Header{
   169  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000000)},
   170  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000001)},
   171  			},
   172  			0,
   173  			true,
   174  		},
   175  		// Case3: submit stale but acceptable solution
   176  		{
   177  			[]*types.Header{
   178  				{ParentHash: common.BytesToHash([]byte{0xc}), Number: big.NewInt(3), Difficulty: big.NewInt(100000000)},
   179  				{ParentHash: common.BytesToHash([]byte{0xd}), Number: big.NewInt(9), Difficulty: big.NewInt(100000000)},
   180  			},
   181  			0,
   182  			true,
   183  		},
   184  		// Case4: submit very old solution
   185  		{
   186  			[]*types.Header{
   187  				{ParentHash: common.BytesToHash([]byte{0xe}), Number: big.NewInt(10), Difficulty: big.NewInt(100000000)},
   188  				{ParentHash: common.BytesToHash([]byte{0xf}), Number: big.NewInt(17), Difficulty: big.NewInt(100000000)},
   189  			},
   190  			0,
   191  			false,
   192  		},
   193  	}
   194  	results := make(chan *types.Block, 16)
   195  
   196  	for id, c := range testcases {
   197  		for _, h := range c.headers {
   198  			ethash.Seal(nil, types.NewBlockWithHeader(h), results, nil)
   199  		}
   200  		if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes {
   201  			t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)
   202  		}
   203  		if !c.submitRes {
   204  			continue
   205  		}
   206  		select {
   207  		case res := <-results:
   208  			if res.Header().Nonce != fakeNonce {
   209  				t.Errorf("case %d block nonce mismatch, want %s, get %s", id+1, fakeNonce, res.Header().Nonce)
   210  			}
   211  			if res.Header().MixDigest != fakeDigest {
   212  				t.Errorf("case %d block digest mismatch, want %s, get %s", id+1, fakeDigest, res.Header().MixDigest)
   213  			}
   214  			if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
   215  				t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)
   216  			}
   217  			if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() {
   218  				t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64())
   219  			}
   220  			if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash {
   221  				t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex())
   222  			}
   223  		case <-time.NewTimer(time.Second).C:
   224  			t.Errorf("case %d fetch ethash result timeout", id+1)
   225  		}
   226  	}
   227  }