github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/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/http"
    24  	"net/http/httptest"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/kisexp/xdchain/common"
    29  	"github.com/kisexp/xdchain/core/types"
    30  	"github.com/kisexp/xdchain/internal/testlog"
    31  	"github.com/kisexp/xdchain/log"
    32  )
    33  
    34  // Tests whether remote HTTP servers are correctly notified of new work.
    35  func TestRemoteNotify(t *testing.T) {
    36  	// Start a simple web server to capture notifications.
    37  	sink := make(chan [3]string)
    38  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    39  		blob, err := ioutil.ReadAll(req.Body)
    40  		if err != nil {
    41  			t.Errorf("failed to read miner notification: %v", err)
    42  		}
    43  		var work [3]string
    44  		if err := json.Unmarshal(blob, &work); err != nil {
    45  			t.Errorf("failed to unmarshal miner notification: %v", err)
    46  		}
    47  		sink <- work
    48  	}))
    49  	defer server.Close()
    50  
    51  	// Create the custom ethash engine.
    52  	ethash := NewTester([]string{server.URL}, false)
    53  	defer ethash.Close()
    54  
    55  	// Stream a work task and ensure the notification bubbles out.
    56  	header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
    57  	block := types.NewBlockWithHeader(header)
    58  
    59  	ethash.Seal(nil, block, nil, nil)
    60  	select {
    61  	case work := <-sink:
    62  		if want := ethash.SealHash(header).Hex(); work[0] != want {
    63  			t.Errorf("work packet hash mismatch: have %s, want %s", work[0], want)
    64  		}
    65  		if want := common.BytesToHash(SeedHash(header.Number.Uint64())).Hex(); work[1] != want {
    66  			t.Errorf("work packet seed mismatch: have %s, want %s", work[1], want)
    67  		}
    68  		target := new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), header.Difficulty)
    69  		if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want {
    70  			t.Errorf("work packet target mismatch: have %s, want %s", work[2], want)
    71  		}
    72  	case <-time.After(3 * time.Second):
    73  		t.Fatalf("notification timed out")
    74  	}
    75  }
    76  
    77  // Tests that pushing work packages fast to the miner doesn't cause any data race
    78  // issues in the notifications.
    79  func TestRemoteMultiNotify(t *testing.T) {
    80  	// Start a simple web server to capture notifications.
    81  	sink := make(chan [3]string, 64)
    82  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    83  		blob, err := ioutil.ReadAll(req.Body)
    84  		if err != nil {
    85  			t.Errorf("failed to read miner notification: %v", err)
    86  		}
    87  		var work [3]string
    88  		if err := json.Unmarshal(blob, &work); err != nil {
    89  			t.Errorf("failed to unmarshal miner notification: %v", err)
    90  		}
    91  		sink <- work
    92  	}))
    93  	defer server.Close()
    94  
    95  	// Create the custom ethash engine.
    96  	ethash := NewTester([]string{server.URL}, false)
    97  	ethash.config.Log = testlog.Logger(t, log.LvlWarn)
    98  	defer ethash.Close()
    99  
   100  	// Provide a results reader.
   101  	// Otherwise the unread results will be logged asynchronously
   102  	// and this can happen after the test is finished, causing a panic.
   103  	results := make(chan *types.Block, cap(sink))
   104  
   105  	// Stream a lot of work task and ensure all the notifications bubble out.
   106  	for i := 0; i < cap(sink); i++ {
   107  		header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
   108  		block := types.NewBlockWithHeader(header)
   109  		ethash.Seal(nil, block, results, nil)
   110  	}
   111  
   112  	for i := 0; i < cap(sink); i++ {
   113  		select {
   114  		case <-sink:
   115  			<-results
   116  		case <-time.After(10 * time.Second):
   117  			t.Fatalf("notification %d timed out", i)
   118  		}
   119  	}
   120  }
   121  
   122  // Tests whether stale solutions are correctly processed.
   123  func TestStaleSubmission(t *testing.T) {
   124  	ethash := NewTester(nil, true)
   125  	defer ethash.Close()
   126  	api := &API{ethash}
   127  
   128  	fakeNonce, fakeDigest := types.BlockNonce{0x01, 0x02, 0x03}, common.HexToHash("deadbeef")
   129  
   130  	testcases := []struct {
   131  		headers     []*types.Header
   132  		submitIndex int
   133  		submitRes   bool
   134  	}{
   135  		// Case1: submit solution for the latest mining package
   136  		{
   137  			[]*types.Header{
   138  				{ParentHash: common.BytesToHash([]byte{0xa}), Number: big.NewInt(1), Difficulty: big.NewInt(100000000)},
   139  			},
   140  			0,
   141  			true,
   142  		},
   143  		// Case2: submit solution for the previous package but have same parent.
   144  		{
   145  			[]*types.Header{
   146  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000000)},
   147  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000001)},
   148  			},
   149  			0,
   150  			true,
   151  		},
   152  		// Case3: submit stale but acceptable solution
   153  		{
   154  			[]*types.Header{
   155  				{ParentHash: common.BytesToHash([]byte{0xc}), Number: big.NewInt(3), Difficulty: big.NewInt(100000000)},
   156  				{ParentHash: common.BytesToHash([]byte{0xd}), Number: big.NewInt(9), Difficulty: big.NewInt(100000000)},
   157  			},
   158  			0,
   159  			true,
   160  		},
   161  		// Case4: submit very old solution
   162  		{
   163  			[]*types.Header{
   164  				{ParentHash: common.BytesToHash([]byte{0xe}), Number: big.NewInt(10), Difficulty: big.NewInt(100000000)},
   165  				{ParentHash: common.BytesToHash([]byte{0xf}), Number: big.NewInt(17), Difficulty: big.NewInt(100000000)},
   166  			},
   167  			0,
   168  			false,
   169  		},
   170  	}
   171  	results := make(chan *types.Block, 16)
   172  
   173  	for id, c := range testcases {
   174  		for _, h := range c.headers {
   175  			ethash.Seal(nil, types.NewBlockWithHeader(h), results, nil)
   176  		}
   177  		if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes {
   178  			t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)
   179  		}
   180  		if !c.submitRes {
   181  			continue
   182  		}
   183  		select {
   184  		case res := <-results:
   185  			if res.Header().Nonce != fakeNonce {
   186  				t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce)
   187  			}
   188  			if res.Header().MixDigest != fakeDigest {
   189  				t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.Header().MixDigest)
   190  			}
   191  			if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
   192  				t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)
   193  			}
   194  			if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() {
   195  				t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64())
   196  			}
   197  			if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash {
   198  				t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex())
   199  			}
   200  		case <-time.NewTimer(time.Second).C:
   201  			t.Errorf("case %d fetch ethash result timeout", id+1)
   202  		}
   203  	}
   204  }