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 }