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

     1  package consensus
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	"github.com/NebulousLabs/Sia/encoding"
     8  	"github.com/NebulousLabs/Sia/modules"
     9  	"github.com/NebulousLabs/Sia/types"
    10  )
    11  
    12  var (
    13  	errBadMinerPayouts        = errors.New("miner payout sum does not equal block subsidy")
    14  	errEarlyTimestamp         = errors.New("block timestamp is too early")
    15  	errExtremeFutureTimestamp = errors.New("block timestamp too far in future, discarded")
    16  	errFutureTimestamp        = errors.New("block timestamp too far in future, but saved for later use")
    17  	errLargeBlock             = errors.New("block is too large to be accepted")
    18  )
    19  
    20  // blockValidator validates a Block against a set of block validity rules.
    21  type blockValidator interface {
    22  	// ValidateBlock validates a block against a minimum timestamp, a block
    23  	// target, and a block height.
    24  	ValidateBlock(types.Block, types.Timestamp, types.Target, types.BlockHeight) error
    25  }
    26  
    27  // stdBlockValidator is the standard implementation of blockValidator.
    28  type stdBlockValidator struct {
    29  	// clock is a Clock interface that indicates the current system time.
    30  	clock types.Clock
    31  
    32  	// marshaler encodes and decodes between objects and byte slices.
    33  	marshaler encoding.GenericMarshaler
    34  }
    35  
    36  // NewBlockValidator creates a new stdBlockValidator with default settings.
    37  func NewBlockValidator() stdBlockValidator {
    38  	return stdBlockValidator{
    39  		clock:     types.StdClock{},
    40  		marshaler: encoding.StdGenericMarshaler{},
    41  	}
    42  }
    43  
    44  // checkMinerPayouts compares a block's miner payouts to the block's subsidy and
    45  // returns true if they are equal.
    46  func checkMinerPayouts(b types.Block, height types.BlockHeight) bool {
    47  	// Add up the payouts and check that all values are legal.
    48  	var payoutSum types.Currency
    49  	for _, payout := range b.MinerPayouts {
    50  		if payout.Value.IsZero() {
    51  			return false
    52  		}
    53  		payoutSum = payoutSum.Add(payout.Value)
    54  	}
    55  	return b.CalculateSubsidy(height).Cmp(payoutSum) == 0
    56  }
    57  
    58  // checkTarget returns true if the block's ID meets the given target.
    59  func checkTarget(b types.Block, target types.Target) bool {
    60  	blockHash := b.ID()
    61  	return bytes.Compare(target[:], blockHash[:]) >= 0
    62  }
    63  
    64  // ValidateBlock validates a block against a minimum timestamp, a block target,
    65  // and a block height. Returns nil if the block is valid and an appropriate
    66  // error otherwise.
    67  func (bv stdBlockValidator) ValidateBlock(b types.Block, minTimestamp types.Timestamp, target types.Target, height types.BlockHeight) error {
    68  	// Check that the timestamp is not too far in the past to be acceptable.
    69  	if minTimestamp > b.Timestamp {
    70  		return errEarlyTimestamp
    71  	}
    72  
    73  	// Check that the target of the new block is sufficient.
    74  	if !checkTarget(b, target) {
    75  		return modules.ErrBlockUnsolved
    76  	}
    77  
    78  	// Check that the block is below the size limit.
    79  	if uint64(len(bv.marshaler.Marshal(b))) > types.BlockSizeLimit {
    80  		return errLargeBlock
    81  	}
    82  
    83  	// Check if the block is in the extreme future. We make a distinction between
    84  	// future and extreme future because there is an assumption that by the time
    85  	// the extreme future arrives, this block will no longer be a part of the
    86  	// longest fork because it will have been ignored by all of the miners.
    87  	if b.Timestamp > bv.clock.Now()+types.ExtremeFutureThreshold {
    88  		return errExtremeFutureTimestamp
    89  	}
    90  
    91  	// Verify that the miner payouts are valid.
    92  	if !checkMinerPayouts(b, height) {
    93  		return errBadMinerPayouts
    94  	}
    95  
    96  	// Check if the block is in the near future, but too far to be acceptable.
    97  	// This is the last check because it's an expensive check, and not worth
    98  	// performing if the payouts are incorrect.
    99  	if b.Timestamp > bv.clock.Now()+types.FutureThreshold {
   100  		return errFutureTimestamp
   101  	}
   102  	return nil
   103  }