/
validation.go
177 lines (161 loc) · 5.29 KB
/
validation.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package state
import (
"bytes"
"errors"
"fmt"
"github.com/Finschia/ostracon/crypto"
"github.com/Finschia/ostracon/types"
)
//-----------------------------------------------------
// Validate block
func validateBlock(state State, round int32, block *types.Block) error {
// Validate internal consistency.
if err := block.ValidateBasic(); err != nil {
return err
}
// Validate basic info.
if block.Version.App != state.Version.Consensus.App ||
block.Version.Block != state.Version.Consensus.Block {
return fmt.Errorf("wrong Block.Header.Version. Expected %v, got %v",
state.Version.Consensus,
block.Version,
)
}
if block.ChainID != state.ChainID {
return fmt.Errorf("wrong Block.Header.ChainID. Expected %v, got %v",
state.ChainID,
block.ChainID,
)
}
if state.LastBlockHeight == 0 && block.Height != state.InitialHeight {
return fmt.Errorf("wrong Block.Header.Height. Expected %v for initial block, got %v",
block.Height, state.InitialHeight)
}
if state.LastBlockHeight > 0 && block.Height != state.LastBlockHeight+1 {
return fmt.Errorf("wrong Block.Header.Height. Expected %v, got %v",
state.LastBlockHeight+1,
block.Height,
)
}
// Validate prev block info.
if !block.LastBlockID.Equals(state.LastBlockID) {
return fmt.Errorf("wrong Block.Header.LastBlockID. Expected %v, got %v",
state.LastBlockID,
block.LastBlockID,
)
}
// Validate app info
if !bytes.Equal(block.AppHash, state.AppHash) {
return fmt.Errorf("wrong Block.Header.AppHash. Expected %X, got %v",
state.AppHash,
block.AppHash,
)
}
hashCP := types.HashConsensusParams(state.ConsensusParams)
if !bytes.Equal(block.ConsensusHash, hashCP) {
return fmt.Errorf("wrong Block.Header.ConsensusHash. Expected %X, got %v",
hashCP,
block.ConsensusHash,
)
}
if !bytes.Equal(block.LastResultsHash, state.LastResultsHash) {
return fmt.Errorf("wrong Block.Header.LastResultsHash. Expected %X, got %v",
state.LastResultsHash,
block.LastResultsHash,
)
}
if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) {
return fmt.Errorf("wrong Block.Header.ValidatorsHash. Expected %X, got %v",
state.Validators.Hash(),
block.ValidatorsHash,
)
}
if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash()) {
return fmt.Errorf("wrong Block.Header.NextValidatorsHash. Expected %X, got %v",
state.NextValidators.Hash(),
block.NextValidatorsHash,
)
}
// Validate block LastCommit.
if block.Height == state.InitialHeight {
if len(block.LastCommit.Signatures) != 0 {
return errors.New("initial block can't have LastCommit signatures")
}
} else {
// LastCommit.Signatures length is checked in VerifyCommit.
if err := state.LastValidators.VerifyCommit(
state.ChainID, state.LastBlockID, block.Height-1, block.LastCommit); err != nil {
return err
}
}
// NOTE: We can't actually verify it's the right proposer because we don't
// know what round the block was first proposed. So just check that it's
// a legit address and a known validator.
if len(block.ProposerAddress) != crypto.AddressSize {
return fmt.Errorf("expected ProposerAddress size %d, got %d",
crypto.AddressSize,
len(block.ProposerAddress),
)
}
if !state.Validators.HasAddress(block.ProposerAddress) {
return fmt.Errorf("block.Header.ProposerAddress %X is not a validator",
block.ProposerAddress,
)
}
// Validate block Time
switch {
case block.Height > state.InitialHeight:
if !block.Time.After(state.LastBlockTime) {
return fmt.Errorf("block time %v not greater than last block time %v",
block.Time,
state.LastBlockTime,
)
}
medianTime := MedianTime(block.LastCommit, state.LastValidators)
if !block.Time.Equal(medianTime) {
return fmt.Errorf("invalid block time. Expected %v, got %v",
medianTime,
block.Time,
)
}
case block.Height == state.InitialHeight:
genesisTime := state.LastBlockTime
if !block.Time.Equal(genesisTime) {
return fmt.Errorf("block time %v is not equal to genesis time %v",
block.Time,
genesisTime,
)
}
default:
return fmt.Errorf("block height %v lower than initial height %v",
block.Height, state.InitialHeight)
}
// Check evidence doesn't exceed the limit amount of bytes.
if max, got := state.ConsensusParams.Evidence.MaxBytes, block.Evidence.ByteSize(); got > max {
return types.NewErrEvidenceOverflow(max, got)
}
// validate round
// The block round must be less than or equal to the current round
// If some proposer proposes his ValidBlock as a proposal, then the proposal block round is less than current round
if block.Round > round {
return types.NewErrInvalidRound(round, block.Round)
}
// validate proposer
proposer := state.Validators.SelectProposer(state.LastProofHash, block.Height, block.Round)
if !bytes.Equal(block.ProposerAddress.Bytes(), proposer.Address.Bytes()) {
return fmt.Errorf("block.ProposerAddress, %X, is not the proposer %X",
block.ProposerAddress,
proposer.Address,
)
}
// validate vrf proof
message := state.MakeHashMessage(block.Round)
proof := crypto.Proof(block.Proof)
_, err := proposer.PubKey.VRFVerify(proof, message)
if err != nil {
return types.NewErrInvalidProof(fmt.Sprintf(
"verification failed: %s; proof: %v, height=%d, round=%d, addr: %v",
err.Error(), block.Proof, block.Height, block.Round, block.ProposerAddress))
}
return nil
}