github.com/NebulousLabs/Sia@v1.3.7/modules/consensus/block_validation_test.go (about)

     1  package consensus
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/NebulousLabs/Sia/types"
     7  )
     8  
     9  // mockMarshaler is a mock implementation of the encoding.GenericMarshaler
    10  // interface that allows the client to pre-define the length of the marshaled
    11  // data.
    12  type mockMarshaler struct {
    13  	marshalLength uint64
    14  }
    15  
    16  // Marshal marshals an object into an empty byte slice of marshalLength.
    17  func (m mockMarshaler) Marshal(interface{}) []byte {
    18  	return make([]byte, m.marshalLength)
    19  }
    20  
    21  // Unmarshal is not implemented.
    22  func (m mockMarshaler) Unmarshal([]byte, interface{}) error {
    23  	panic("not implemented")
    24  }
    25  
    26  // mockClock is a mock implementation of the types.Clock interface that allows
    27  // the client to pre-define a return value for Now().
    28  type mockClock struct {
    29  	now types.Timestamp
    30  }
    31  
    32  // Now returns mockClock's pre-defined Timestamp.
    33  func (c mockClock) Now() types.Timestamp {
    34  	return c.now
    35  }
    36  
    37  var validateBlockTests = []struct {
    38  	now            types.Timestamp
    39  	minTimestamp   types.Timestamp
    40  	blockTimestamp types.Timestamp
    41  	blockSize      uint64
    42  	errWant        error
    43  	msg            string
    44  }{
    45  	{
    46  		minTimestamp:   types.Timestamp(5),
    47  		blockTimestamp: types.Timestamp(4),
    48  		errWant:        errEarlyTimestamp,
    49  		msg:            "ValidateBlock should reject blocks with timestamps that are too early",
    50  	},
    51  	{
    52  		blockSize: types.BlockSizeLimit + 1,
    53  		errWant:   errLargeBlock,
    54  		msg:       "ValidateBlock should reject excessively large blocks",
    55  	},
    56  	{
    57  		now:            types.Timestamp(50),
    58  		blockTimestamp: types.Timestamp(50) + types.ExtremeFutureThreshold + 1,
    59  		errWant:        errExtremeFutureTimestamp,
    60  		msg:            "ValidateBlock should reject blocks timestamped in the extreme future",
    61  	},
    62  }
    63  
    64  // TestUnitValidateBlock runs a series of unit tests for ValidateBlock.
    65  func TestUnitValidateBlock(t *testing.T) {
    66  	// TODO(mtlynch): Populate all parameters to ValidateBlock so that everything
    67  	// is valid except for the attribute that causes validation to fail. (i.e.
    68  	// don't assume an ordering to the implementation of the validation function).
    69  	for _, tt := range validateBlockTests {
    70  		b := types.Block{
    71  			Timestamp: tt.blockTimestamp,
    72  		}
    73  		blockValidator := stdBlockValidator{
    74  			marshaler: mockMarshaler{
    75  				marshalLength: tt.blockSize,
    76  			},
    77  			clock: mockClock{
    78  				now: tt.now,
    79  			},
    80  		}
    81  		err := blockValidator.ValidateBlock(b, b.ID(), tt.minTimestamp, types.RootDepth, 0, nil)
    82  		if err != tt.errWant {
    83  			t.Errorf("%s: got %v, want %v", tt.msg, err, tt.errWant)
    84  		}
    85  	}
    86  }
    87  
    88  // TestCheckMinerPayouts probes the checkMinerPayouts function.
    89  func TestCheckMinerPayouts(t *testing.T) {
    90  	// All tests are done at height = 0.
    91  	coinbase := types.CalculateCoinbase(0)
    92  
    93  	// Create a block with a single valid payout.
    94  	b := types.Block{
    95  		MinerPayouts: []types.SiacoinOutput{
    96  			{Value: coinbase},
    97  		},
    98  	}
    99  	if !checkMinerPayouts(b, 0) {
   100  		t.Error("payouts evaluated incorrectly when there is only one payout.")
   101  	}
   102  
   103  	// Try a block with an incorrect payout.
   104  	b = types.Block{
   105  		MinerPayouts: []types.SiacoinOutput{
   106  			{Value: coinbase.Sub(types.NewCurrency64(1))},
   107  		},
   108  	}
   109  	if checkMinerPayouts(b, 0) {
   110  		t.Error("payouts evaluated incorrectly when there is a too-small payout")
   111  	}
   112  
   113  	// Try a block with 2 payouts.
   114  	b = types.Block{
   115  		MinerPayouts: []types.SiacoinOutput{
   116  			{Value: coinbase.Sub(types.NewCurrency64(1))},
   117  			{Value: types.NewCurrency64(1)},
   118  		},
   119  	}
   120  	if !checkMinerPayouts(b, 0) {
   121  		t.Error("payouts evaluated incorrectly when there are 2 payouts")
   122  	}
   123  
   124  	// Try a block with 2 payouts that are too large.
   125  	b = types.Block{
   126  		MinerPayouts: []types.SiacoinOutput{
   127  			{Value: coinbase},
   128  			{Value: coinbase},
   129  		},
   130  	}
   131  	if checkMinerPayouts(b, 0) {
   132  		t.Error("payouts evaluated incorrectly when there are two large payouts")
   133  	}
   134  
   135  	// Create a block with an empty payout.
   136  	b = types.Block{
   137  		MinerPayouts: []types.SiacoinOutput{
   138  			{Value: coinbase},
   139  			{},
   140  		},
   141  	}
   142  	if checkMinerPayouts(b, 0) {
   143  		t.Error("payouts evaluated incorrectly when there is only one payout.")
   144  	}
   145  }
   146  
   147  // TestCheckTarget probes the checkTarget function.
   148  func TestCheckTarget(t *testing.T) {
   149  	var b types.Block
   150  	lowTarget := types.RootDepth
   151  	highTarget := types.Target{}
   152  	sameTarget := types.Target(b.ID())
   153  
   154  	if !checkTarget(b, b.ID(), lowTarget) {
   155  		t.Error("CheckTarget failed for a low target")
   156  	}
   157  	if checkTarget(b, b.ID(), highTarget) {
   158  		t.Error("CheckTarget passed for a high target")
   159  	}
   160  	if !checkTarget(b, b.ID(), sameTarget) {
   161  		t.Error("CheckTarget failed for a same target")
   162  	}
   163  }