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