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

     1  package consensus
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	"github.com/NebulousLabs/Sia/modules"
     8  	"github.com/NebulousLabs/Sia/persist"
     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.BlockID, types.Timestamp, types.Target, types.BlockHeight, *persist.Logger) 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 marshaler
    34  }
    35  
    36  // NewBlockValidator creates a new stdBlockValidator with default settings.
    37  func NewBlockValidator() stdBlockValidator {
    38  	return stdBlockValidator{
    39  		clock:     types.StdClock{},
    40  		marshaler: stdMarshaler{},
    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).Equals(payoutSum)
    56  }
    57  
    58  // checkTarget returns true if the block's ID meets the given target.
    59  func checkTarget(b types.Block, id types.BlockID, target types.Target) bool {
    60  	return bytes.Compare(target[:], id[:]) >= 0
    61  }
    62  
    63  // ValidateBlock validates a block against a minimum timestamp, a block target,
    64  // and a block height. Returns nil if the block is valid and an appropriate
    65  // error otherwise.
    66  func (bv stdBlockValidator) ValidateBlock(b types.Block, id types.BlockID, minTimestamp types.Timestamp, target types.Target, height types.BlockHeight, log *persist.Logger) error {
    67  	// Check that the timestamp is not too far in the past to be acceptable.
    68  	if minTimestamp > b.Timestamp {
    69  		return errEarlyTimestamp
    70  	}
    71  
    72  	// Check that the target of the new block is sufficient.
    73  	if !checkTarget(b, id, target) {
    74  		return modules.ErrBlockUnsolved
    75  	}
    76  
    77  	// Check that the block is below the size limit.
    78  	blockSize := len(bv.marshaler.Marshal(b))
    79  	if uint64(blockSize) > 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  
   103  	if log != nil {
   104  		log.Debugf("validated block at height %v, block size: %vB", height, blockSize)
   105  	}
   106  	return nil
   107  }