github.com/palisadeinc/bor@v0.0.0-20230615125219-ab7196213d15/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  	"context"
    21  	"encoding/json"
    22  	"io/ioutil"
    23  	"math/big"
    24  	"net/http"
    25  	"net/http/httptest"
    26  	"strconv"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/ethereum/go-ethereum/common"
    31  	"github.com/ethereum/go-ethereum/core/types"
    32  	"github.com/ethereum/go-ethereum/internal/testlog"
    33  	"github.com/ethereum/go-ethereum/log"
    34  )
    35  
    36  // Tests whether remote HTTP servers are correctly notified of new work.
    37  func TestRemoteNotify(t *testing.T) {
    38  	// Start a simple web server to capture notifications.
    39  	sink := make(chan [3]string)
    40  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    41  		blob, err := ioutil.ReadAll(req.Body)
    42  		if err != nil {
    43  			t.Errorf("failed to read miner notification: %v", err)
    44  		}
    45  		var work [3]string
    46  		if err := json.Unmarshal(blob, &work); err != nil {
    47  			t.Errorf("failed to unmarshal miner notification: %v", err)
    48  		}
    49  		sink <- work
    50  	}))
    51  	defer server.Close()
    52  
    53  	// Create the custom ethash engine.
    54  	ethash := NewTester([]string{server.URL}, false)
    55  	defer ethash.Close()
    56  
    57  	// Stream a work task and ensure the notification bubbles out.
    58  	header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
    59  	block := types.NewBlockWithHeader(header)
    60  
    61  	err := ethash.Seal(context.Background(), nil, block, nil, nil)
    62  
    63  	if err != nil {
    64  		t.Error("error in sealing block")
    65  	}
    66  	select {
    67  	case work := <-sink:
    68  		if want := ethash.SealHash(header).Hex(); work[0] != want {
    69  			t.Errorf("work packet hash mismatch: have %s, want %s", work[0], want)
    70  		}
    71  		if want := common.BytesToHash(SeedHash(header.Number.Uint64())).Hex(); work[1] != want {
    72  			t.Errorf("work packet seed mismatch: have %s, want %s", work[1], want)
    73  		}
    74  		target := new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), header.Difficulty)
    75  		if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want {
    76  			t.Errorf("work packet target mismatch: have %s, want %s", work[2], want)
    77  		}
    78  	case <-time.After(5 * time.Second):
    79  		t.Fatalf("notification timed out")
    80  	}
    81  }
    82  
    83  // Tests whether remote HTTP servers are correctly notified of new work. (Full pending block body / --miner.notify.full)
    84  func TestRemoteNotifyFull(t *testing.T) {
    85  	// Start a simple web server to capture notifications.
    86  	sink := make(chan map[string]interface{})
    87  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    88  		blob, err := ioutil.ReadAll(req.Body)
    89  		if err != nil {
    90  			t.Errorf("failed to read miner notification: %v", err)
    91  		}
    92  		var work map[string]interface{}
    93  		if err := json.Unmarshal(blob, &work); err != nil {
    94  			t.Errorf("failed to unmarshal miner notification: %v", err)
    95  		}
    96  		sink <- work
    97  	}))
    98  	defer server.Close()
    99  
   100  	// Create the custom ethash engine.
   101  	config := Config{
   102  		PowMode:    ModeTest,
   103  		NotifyFull: true,
   104  		Log:        testlog.Logger(t, log.LvlWarn),
   105  	}
   106  	ethash := New(config, []string{server.URL}, false)
   107  	defer ethash.Close()
   108  
   109  	// Stream a work task and ensure the notification bubbles out.
   110  	header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
   111  	block := types.NewBlockWithHeader(header)
   112  
   113  	err := ethash.Seal(context.Background(), nil, block, nil, nil)
   114  
   115  	if err != nil {
   116  		t.Error("error in sealing block")
   117  	}
   118  	select {
   119  	case work := <-sink:
   120  		if want := "0x" + strconv.FormatUint(header.Number.Uint64(), 16); work["number"] != want {
   121  			t.Errorf("pending block number mismatch: have %v, want %v", work["number"], want)
   122  		}
   123  		if want := "0x" + header.Difficulty.Text(16); work["difficulty"] != want {
   124  			t.Errorf("pending block difficulty mismatch: have %s, want %s", work["difficulty"], want)
   125  		}
   126  	case <-time.After(3 * time.Second):
   127  		t.Fatalf("notification timed out")
   128  	}
   129  }
   130  
   131  // Tests that pushing work packages fast to the miner doesn't cause any data race
   132  // issues in the notifications.
   133  func TestRemoteMultiNotify(t *testing.T) {
   134  	// Start a simple web server to capture notifications.
   135  	sink := make(chan [3]string, 64)
   136  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   137  		blob, err := ioutil.ReadAll(req.Body)
   138  		if err != nil {
   139  			t.Errorf("failed to read miner notification: %v", err)
   140  		}
   141  		var work [3]string
   142  		if err := json.Unmarshal(blob, &work); err != nil {
   143  			t.Errorf("failed to unmarshal miner notification: %v", err)
   144  		}
   145  		sink <- work
   146  	}))
   147  	defer server.Close()
   148  
   149  	// Create the custom ethash engine.
   150  	ethash := NewTester([]string{server.URL}, false)
   151  	ethash.config.Log = testlog.Logger(t, log.LvlWarn)
   152  	defer ethash.Close()
   153  
   154  	// Provide a results reader.
   155  	// Otherwise the unread results will be logged asynchronously
   156  	// and this can happen after the test is finished, causing a panic.
   157  	results := make(chan *types.Block, cap(sink))
   158  
   159  	// Stream a lot of work task and ensure all the notifications bubble out.
   160  	for i := 0; i < cap(sink); i++ {
   161  		header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
   162  		block := types.NewBlockWithHeader(header)
   163  		err := ethash.Seal(context.Background(), nil, block, results, nil)
   164  
   165  		if err != nil {
   166  			t.Error("error in sealing block")
   167  		}
   168  	}
   169  
   170  	for i := 0; i < cap(sink); i++ {
   171  		select {
   172  		case <-sink:
   173  			<-results
   174  		case <-time.After(10 * time.Second):
   175  			t.Fatalf("notification %d timed out", i)
   176  		}
   177  	}
   178  }
   179  
   180  // Tests that pushing work packages fast to the miner doesn't cause any data race
   181  // issues in the notifications. Full pending block body / --miner.notify.full)
   182  func TestRemoteMultiNotifyFull(t *testing.T) {
   183  	// Start a simple web server to capture notifications.
   184  	sink := make(chan map[string]interface{}, 64)
   185  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   186  		blob, err := ioutil.ReadAll(req.Body)
   187  		if err != nil {
   188  			t.Errorf("failed to read miner notification: %v", err)
   189  		}
   190  		var work map[string]interface{}
   191  		if err := json.Unmarshal(blob, &work); err != nil {
   192  			t.Errorf("failed to unmarshal miner notification: %v", err)
   193  		}
   194  		sink <- work
   195  	}))
   196  
   197  	// Allowing the server to start listening.
   198  	time.Sleep(2 * time.Second)
   199  	defer server.Close()
   200  
   201  	// Create the custom ethash engine.
   202  	config := Config{
   203  		PowMode:    ModeTest,
   204  		NotifyFull: true,
   205  		Log:        testlog.Logger(t, log.LvlWarn),
   206  	}
   207  	ethash := New(config, []string{server.URL}, false)
   208  	defer ethash.Close()
   209  
   210  	// Provide a results reader.
   211  	// Otherwise the unread results will be logged asynchronously
   212  	// and this can happen after the test is finished, causing a panic.
   213  	results := make(chan *types.Block, cap(sink))
   214  
   215  	// Stream a lot of work task and ensure all the notifications bubble out.
   216  	for i := 0; i < cap(sink); i++ {
   217  		header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
   218  		block := types.NewBlockWithHeader(header)
   219  		err := ethash.Seal(context.Background(), nil, block, results, nil)
   220  
   221  		if err != nil {
   222  			t.Error("error in sealing block")
   223  		}
   224  	}
   225  
   226  	for i := 0; i < cap(sink); i++ {
   227  		select {
   228  		case <-sink:
   229  			<-results
   230  		case <-time.After(10 * time.Second):
   231  			t.Fatalf("notification %d timed out", i)
   232  		}
   233  	}
   234  }
   235  
   236  // Tests whether stale solutions are correctly processed.
   237  func TestStaleSubmission(t *testing.T) {
   238  	ethash := NewTester(nil, true)
   239  	defer ethash.Close()
   240  	api := &API{ethash}
   241  
   242  	fakeNonce, fakeDigest := types.BlockNonce{0x01, 0x02, 0x03}, common.HexToHash("deadbeef")
   243  
   244  	testcases := []struct {
   245  		headers     []*types.Header
   246  		submitIndex int
   247  		submitRes   bool
   248  	}{
   249  		// Case1: submit solution for the latest mining package
   250  		{
   251  			[]*types.Header{
   252  				{ParentHash: common.BytesToHash([]byte{0xa}), Number: big.NewInt(1), Difficulty: big.NewInt(100000000)},
   253  			},
   254  			0,
   255  			true,
   256  		},
   257  		// Case2: submit solution for the previous package but have same parent.
   258  		{
   259  			[]*types.Header{
   260  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000000)},
   261  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000001)},
   262  			},
   263  			0,
   264  			true,
   265  		},
   266  		// Case3: submit stale but acceptable solution
   267  		{
   268  			[]*types.Header{
   269  				{ParentHash: common.BytesToHash([]byte{0xc}), Number: big.NewInt(3), Difficulty: big.NewInt(100000000)},
   270  				{ParentHash: common.BytesToHash([]byte{0xd}), Number: big.NewInt(9), Difficulty: big.NewInt(100000000)},
   271  			},
   272  			0,
   273  			true,
   274  		},
   275  		// Case4: submit very old solution
   276  		{
   277  			[]*types.Header{
   278  				{ParentHash: common.BytesToHash([]byte{0xe}), Number: big.NewInt(10), Difficulty: big.NewInt(100000000)},
   279  				{ParentHash: common.BytesToHash([]byte{0xf}), Number: big.NewInt(17), Difficulty: big.NewInt(100000000)},
   280  			},
   281  			0,
   282  			false,
   283  		},
   284  	}
   285  	results := make(chan *types.Block, 16)
   286  
   287  	for id, c := range testcases {
   288  		for _, h := range c.headers {
   289  			err := ethash.Seal(context.Background(), nil, types.NewBlockWithHeader(h), results, nil)
   290  
   291  			if err != nil {
   292  				t.Error("error in sealing block")
   293  			}
   294  		}
   295  		if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes {
   296  			t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)
   297  		}
   298  		if !c.submitRes {
   299  			continue
   300  		}
   301  		select {
   302  		case res := <-results:
   303  			if res.Header().Nonce != fakeNonce {
   304  				t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce)
   305  			}
   306  			if res.Header().MixDigest != fakeDigest {
   307  				t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.Header().MixDigest)
   308  			}
   309  			if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
   310  				t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)
   311  			}
   312  			if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() {
   313  				t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64())
   314  			}
   315  			if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash {
   316  				t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex())
   317  			}
   318  		case <-time.NewTimer(time.Second).C:
   319  			t.Errorf("case %d fetch ethash result timeout", id+1)
   320  		}
   321  	}
   322  }