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  }