github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/miner/blockmanager_test.go (about)

     1  package miner
     2  
     3  import (
     4  	"bytes"
     5  	"testing"
     6  
     7  	"github.com/NebulousLabs/Sia/crypto"
     8  	"github.com/NebulousLabs/Sia/modules"
     9  	"github.com/NebulousLabs/Sia/types"
    10  )
    11  
    12  // solveHeader takes a block header as input and returns a solved block header
    13  // as output.
    14  func solveHeader(header types.BlockHeader, target types.Target) types.BlockHeader {
    15  	// Solve the header.
    16  	for {
    17  		// Incrememnt the nonce first to guarantee that a new header is formed
    18  		// - this helps check for pointer errors.
    19  		header.Nonce[0]++
    20  		id := crypto.HashObject(header)
    21  		if bytes.Compare(target[:], id[:]) >= 0 {
    22  			break
    23  		}
    24  	}
    25  	return header
    26  }
    27  
    28  // TestIntegrationHeaderForWork checks that header requesting, solving, and
    29  // submitting naively works.
    30  func TestIntegrationHeaderForWork(t *testing.T) {
    31  	if testing.Short() {
    32  		t.SkipNow()
    33  	}
    34  	mt, err := createMinerTester("TestIntegreationHeaderForWork")
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  
    39  	// Get a header and solve it.
    40  	header, target, err := mt.miner.HeaderForWork()
    41  	if err != nil {
    42  		t.Fatal(err)
    43  	}
    44  	solvedHeader := solveHeader(header, target)
    45  	// Sanity check - header and solvedHeader should be different. (within the
    46  	// testing file, 'header' should always have a nonce of '0' and
    47  	// solvedHeader should never have a nonce of '0'.
    48  	if header.Nonce == solvedHeader.Nonce {
    49  		t.Fatal("nonce memory is not independent")
    50  	}
    51  
    52  	// Submit the header.
    53  	err = mt.miner.SubmitHeader(solvedHeader)
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  }
    58  
    59  // TestIntegrationHeaderForWorkUpdates checks that HeaderForWork starts
    60  // returning headers on the new block after a block has been submitted to the
    61  // consensus set.
    62  func TestIntegrationHeaderForWorkUpdates(t *testing.T) {
    63  	if testing.Short() {
    64  		t.SkipNow()
    65  	}
    66  	mt, err := createMinerTester("TestIntegreationHeaderForWorkUpdates")
    67  	if err != nil {
    68  		t.Fatal(err)
    69  	}
    70  
    71  	// Get a header to advance into the header memory.
    72  	_, _, err = mt.miner.HeaderForWork()
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	// Submit a block, which should trigger a header change.
    78  	_, err = mt.miner.AddBlock()
    79  	if err != nil {
    80  		t.Fatal(err)
    81  	}
    82  
    83  	// Get a header to grind on.
    84  	header, target, err := mt.miner.HeaderForWork()
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  	solvedHeader := solveHeader(header, target)
    89  
    90  	// Submit the header.
    91  	err = mt.miner.SubmitHeader(solvedHeader)
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  	if !mt.cs.InCurrentPath(types.BlockID(crypto.HashObject(solvedHeader))) {
    96  		t.Error("header from solved block is not in the current path")
    97  	}
    98  }
    99  
   100  // TestIntegrationManyHeaders checks that requesting a full set of headers a
   101  // row results in all unique headers, and that all of them can be reassembled
   102  // into valid blocks.
   103  func TestIntegrationManyHeaders(t *testing.T) {
   104  	if testing.Short() {
   105  		t.SkipNow()
   106  	}
   107  	mt, err := createMinerTester("TestIntegrationManyHeaders")
   108  	if err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  	// Create a suite of headers for imaginary parallel mining.
   113  	solvedHeaders := make([]types.BlockHeader, HeaderMemory/BlockMemory*2)
   114  	for i := range solvedHeaders {
   115  		header, target, err := mt.miner.HeaderForWork()
   116  		if err != nil {
   117  			t.Fatal(err)
   118  		}
   119  		solvedHeaders[i] = solveHeader(header, target)
   120  	}
   121  
   122  	// Submit the headers randomly and make sure they are all considered valid.
   123  	selectionOrder, err := crypto.Perm(len(solvedHeaders))
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	for _, selection := range selectionOrder {
   128  		err = mt.miner.SubmitHeader(solvedHeaders[selection])
   129  		if err != nil && err != modules.ErrNonExtendingBlock {
   130  			t.Error(err)
   131  		}
   132  	}
   133  }
   134  
   135  // TestIntegrationHeaderBlockOverflow triggers a header overflow by requesting
   136  // a block that triggers the overflow.
   137  func TestIntegrationHeaderBlockOverflow(t *testing.T) {
   138  	if testing.Short() {
   139  		t.SkipNow()
   140  	}
   141  	mt, err := createMinerTester("TestIntegrationHeaderBlockOverflow")
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	// Grab a header that will be overwritten.
   147  	header, target, err := mt.miner.HeaderForWork()
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  	header = solveHeader(header, target)
   152  
   153  	// Mine blocks to wrap the memProgress around and wipe the old header.
   154  	for i := 0; i < BlockMemory; i++ {
   155  		_, err = mt.miner.AddBlock()
   156  		if err != nil {
   157  			t.Fatal(err)
   158  		}
   159  		// Grab a header to advance the mempool progress.
   160  		_, _, err = mt.miner.HeaderForWork()
   161  		if err != nil {
   162  			t.Fatal(err)
   163  		}
   164  	}
   165  
   166  	// Previous header should no longer be in memory.
   167  	err = mt.miner.SubmitHeader(header)
   168  	if err != errLateHeader {
   169  		t.Error(err)
   170  	}
   171  }
   172  
   173  // TestIntegrationHeaderRequestOverflow triggers a header overflow by
   174  // requesting a header that triggers overflow.
   175  func TestIntegrationHeaderRequestOverflow(t *testing.T) {
   176  	if testing.Short() {
   177  		t.SkipNow()
   178  	}
   179  	mt, err := createMinerTester("TestIntegrationHeaderRequestOverflow")
   180  	if err != nil {
   181  		t.Fatal(err)
   182  	}
   183  
   184  	// Grab a header that will be overwritten.
   185  	header, target, err := mt.miner.HeaderForWork()
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  	header = solveHeader(header, target)
   190  
   191  	// Mine blocks to bring memProgress up to the edge. The number is chosen
   192  	// specifically so that the overflow happens during the requesting of 200
   193  	// headers.
   194  	for i := 0; i < BlockMemory-1; i++ {
   195  		_, err = mt.miner.AddBlock()
   196  		if err != nil {
   197  			t.Fatal(err)
   198  		}
   199  		// Grab a header to advance the mempool progress.
   200  		_, _, err = mt.miner.HeaderForWork()
   201  		if err != nil {
   202  			t.Fatal(err)
   203  		}
   204  	}
   205  
   206  	// Header should still be in memory.
   207  	err = mt.miner.SubmitHeader(header)
   208  	if err != modules.ErrNonExtendingBlock {
   209  		t.Error(err)
   210  	}
   211  
   212  	// Request headers until the overflow is achieved.
   213  	for i := 0; i < HeaderMemory/BlockMemory; i++ {
   214  		_, _, err = mt.miner.HeaderForWork()
   215  		if err != nil {
   216  			t.Fatal(err)
   217  		}
   218  	}
   219  
   220  	err = mt.miner.SubmitHeader(header)
   221  	if err != errLateHeader {
   222  		t.Error(err)
   223  	}
   224  }