github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/protocol/validation/block.go (about) 1 package validation 2 3 import ( 4 "time" 5 6 log "github.com/sirupsen/logrus" 7 8 "github.com/bytom/bytom/consensus" 9 "github.com/bytom/bytom/consensus/difficulty" 10 "github.com/bytom/bytom/errors" 11 "github.com/bytom/bytom/protocol/bc" 12 "github.com/bytom/bytom/protocol/bc/types" 13 "github.com/bytom/bytom/protocol/state" 14 ) 15 16 const logModule = "leveldb" 17 18 var ( 19 errBadTimestamp = errors.New("block timestamp is not in the valid range") 20 errBadBits = errors.New("block bits is invalid") 21 errMismatchedBlock = errors.New("mismatched block") 22 errMismatchedMerkleRoot = errors.New("mismatched merkle root") 23 errMisorderedBlockHeight = errors.New("misordered block height") 24 errOverBlockLimit = errors.New("block's gas is over the limit") 25 errWorkProof = errors.New("invalid difficulty proof of work") 26 errVersionRegression = errors.New("version regression") 27 ) 28 29 func checkBlockTime(b *bc.Block, parent *state.BlockNode) error { 30 if b.Timestamp > uint64(time.Now().Unix())+consensus.MaxTimeOffsetSeconds { 31 return errBadTimestamp 32 } 33 34 if b.Timestamp <= parent.CalcPastMedianTime() { 35 return errBadTimestamp 36 } 37 return nil 38 } 39 40 func checkCoinbaseAmount(b *bc.Block, amount uint64) error { 41 if len(b.Transactions) == 0 { 42 return errors.Wrap(ErrWrongCoinbaseTransaction, "block is empty") 43 } 44 45 tx := b.Transactions[0] 46 if len(tx.TxHeader.ResultIds) != 1 { 47 return errors.Wrap(ErrWrongCoinbaseTransaction, "have more than 1 output") 48 } 49 50 output, err := tx.Output(*tx.TxHeader.ResultIds[0]) 51 if err != nil { 52 return err 53 } 54 55 if output.Source.Value.Amount != amount { 56 return errors.Wrap(ErrWrongCoinbaseTransaction, "dismatch output amount") 57 } 58 return nil 59 } 60 61 // ValidateBlockHeader check the block's header 62 func ValidateBlockHeader(b *bc.Block, parent *state.BlockNode) error { 63 if b.Version != 1 { 64 return errors.WithDetailf(errVersionRegression, "previous block verson %d, current block version %d", parent.Version, b.Version) 65 } 66 if b.Height != parent.Height+1 { 67 return errors.WithDetailf(errMisorderedBlockHeight, "previous block height %d, current block height %d", parent.Height, b.Height) 68 } 69 if b.Bits != parent.CalcNextBits() { 70 return errBadBits 71 } 72 if parent.Hash != *b.PreviousBlockId { 73 return errors.WithDetailf(errMismatchedBlock, "previous block ID %x, current block wants %x", parent.Hash.Bytes(), b.PreviousBlockId.Bytes()) 74 } 75 if err := checkBlockTime(b, parent); err != nil { 76 return err 77 } 78 if !difficulty.CheckProofOfWork(&b.ID, parent.CalcNextSeed(), b.BlockHeader.Bits) { 79 return errWorkProof 80 } 81 return nil 82 } 83 84 // ValidateBlock validates a block and the transactions within. 85 func ValidateBlock(b *bc.Block, parent *state.BlockNode) error { 86 startTime := time.Now() 87 if err := ValidateBlockHeader(b, parent); err != nil { 88 return err 89 } 90 91 blockGasSum := uint64(0) 92 coinbaseAmount := consensus.BlockSubsidy(b.BlockHeader.Height) 93 b.TransactionStatus = bc.NewTransactionStatus() 94 validateResults := ValidateTxs(b.Transactions, b) 95 for i, validateResult := range validateResults { 96 if !validateResult.gasStatus.GasValid { 97 return errors.Wrapf(validateResult.err, "validate of transaction %d of %d", i, len(b.Transactions)) 98 } 99 100 if err := b.TransactionStatus.SetStatus(i, validateResult.err != nil); err != nil { 101 return err 102 } 103 104 coinbaseAmount += validateResult.gasStatus.BTMValue 105 if blockGasSum += uint64(validateResult.gasStatus.GasUsed); blockGasSum > consensus.MaxBlockGas { 106 return errOverBlockLimit 107 } 108 } 109 110 if err := checkCoinbaseAmount(b, coinbaseAmount); err != nil { 111 return err 112 } 113 114 txMerkleRoot, err := types.TxMerkleRoot(b.Transactions) 115 if err != nil { 116 return errors.Wrap(err, "computing transaction id merkle root") 117 } 118 if txMerkleRoot != *b.TransactionsRoot { 119 return errors.WithDetailf(errMismatchedMerkleRoot, "transaction id merkle root") 120 } 121 122 txStatusHash, err := types.TxStatusMerkleRoot(b.TransactionStatus.VerifyStatus) 123 if err != nil { 124 return errors.Wrap(err, "computing transaction status merkle root") 125 } 126 if txStatusHash != *b.TransactionStatusHash { 127 return errors.WithDetailf(errMismatchedMerkleRoot, "transaction status merkle root") 128 } 129 130 log.WithFields(log.Fields{ 131 "module": logModule, 132 "height": b.Height, 133 "hash": b.ID.String(), 134 "duration": time.Since(startTime), 135 }).Debug("finish validate block") 136 return nil 137 }