OSDN Git Service

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