github.com/aigarnetwork/aigar@v0.0.0-20191115204914-d59a6eb70f8e/consensus/ethash/sealer_test.go (about)

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