github.com/core-coin/go-core/v2@v2.1.9/consensus/cryptore/sealer_test.go (about)

     1  // Copyright 2018 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package cryptore
    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/core-coin/go-core/v2/common"
    29  	"github.com/core-coin/go-core/v2/core/types"
    30  	"github.com/core-coin/go-core/v2/internal/testlog"
    31  	"github.com/core-coin/go-core/v2/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 cryptore engine.
    52  	cryptore := NewTester([]string{server.URL}, false)
    53  	defer cryptore.Close()
    54  
    55  	// Stream a work task and ensure the notification bubbles out.
    56  	header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(2)}
    57  	block := types.NewBlockWithHeader(header)
    58  
    59  	cryptore.Seal(nil, block, nil, nil)
    60  	select {
    61  	case work := <-sink:
    62  		if want := cryptore.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(10 * 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 cryptore engine.
    96  	cryptore := NewTester([]string{server.URL}, false)
    97  	cryptore.config.Log = testlog.Logger(t, log.LvlWarn)
    98  	defer cryptore.Close()
    99  
   100  	// Stream a lot of work task and ensure all the notifications bubble out.
   101  	for i := 0; i < cap(sink); i++ {
   102  		header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(2)}
   103  		block := types.NewBlockWithHeader(header)
   104  		cryptore.Seal(nil, block, nil, nil)
   105  	}
   106  
   107  	for i := 0; i < cap(sink); i++ {
   108  		select {
   109  		case <-sink:
   110  		case <-time.After(60 * time.Second):
   111  			t.Fatalf("notification %d timed out", i)
   112  		}
   113  	}
   114  }
   115  
   116  // Tests whether stale solutions are correctly processed.
   117  func TestStaleSubmission(t *testing.T) {
   118  	cryptore := NewTester(nil, true)
   119  	defer cryptore.Close()
   120  	api := &API{cryptore}
   121  
   122  	fakeNonce := types.BlockNonce{0x01, 0x02, 0x03}
   123  
   124  	testcases := []struct {
   125  		headers     []*types.Header
   126  		submitIndex int
   127  		submitRes   bool
   128  	}{
   129  		// Case1: submit solution for the latest mining package
   130  		{
   131  			[]*types.Header{
   132  				{ParentHash: common.BytesToHash([]byte{0xa}), Number: big.NewInt(1), Difficulty: big.NewInt(100000000)},
   133  			},
   134  			0,
   135  			true,
   136  		},
   137  		// Case2: submit solution for the previous package but have same parent.
   138  		{
   139  			[]*types.Header{
   140  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000000)},
   141  				{ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000001)},
   142  			},
   143  			0,
   144  			true,
   145  		},
   146  		// Case3: submit stale but acceptable solution
   147  		{
   148  			[]*types.Header{
   149  				{ParentHash: common.BytesToHash([]byte{0xc}), Number: big.NewInt(3), Difficulty: big.NewInt(100000000)},
   150  				{ParentHash: common.BytesToHash([]byte{0xd}), Number: big.NewInt(9), Difficulty: big.NewInt(100000000)},
   151  			},
   152  			0,
   153  			true,
   154  		},
   155  		// Case4: submit very old solution
   156  		{
   157  			[]*types.Header{
   158  				{ParentHash: common.BytesToHash([]byte{0xe}), Number: big.NewInt(10), Difficulty: big.NewInt(100000000)},
   159  				{ParentHash: common.BytesToHash([]byte{0xf}), Number: big.NewInt(17), Difficulty: big.NewInt(100000000)},
   160  			},
   161  			0,
   162  			false,
   163  		},
   164  	}
   165  	results := make(chan *types.Block, 16)
   166  	stop := make(chan struct{})
   167  	for id, c := range testcases {
   168  		for _, h := range c.headers {
   169  			cryptore.Seal(nil, types.NewBlockWithHeader(h), results, stop)
   170  		}
   171  		if res := api.SubmitWork(fakeNonce, cryptore.SealHash(c.headers[c.submitIndex])); res != c.submitRes {
   172  			t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res)
   173  		}
   174  		if !c.submitRes {
   175  			close(stop)
   176  			continue
   177  		}
   178  		select {
   179  		case res := <-results:
   180  			if res.Header().Nonce != fakeNonce {
   181  				t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce)
   182  			}
   183  			if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
   184  				t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)
   185  			}
   186  			if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() {
   187  				t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64())
   188  			}
   189  			if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash {
   190  				t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex())
   191  			}
   192  		case <-time.NewTimer(time.Second).C:
   193  			t.Errorf("case %d fetch cryptore result timeout", id+1)
   194  		}
   195  	}
   196  }