github.com/okex/exchain@v1.8.0/libs/tendermint/state/validation.go (about) 1 package state 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 8 dbm "github.com/okex/exchain/libs/tm-db" 9 10 "github.com/okex/exchain/libs/tendermint/crypto" 11 "github.com/okex/exchain/libs/tendermint/types" 12 ) 13 14 //----------------------------------------------------- 15 // Validate block 16 17 func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block *types.Block) error { 18 // Validate internal consistency. 19 if err := block.ValidateBasic(); err != nil { 20 return err 21 } 22 23 // Validate basic info. 24 if block.Version != state.Version.Consensus { 25 return fmt.Errorf("wrong Block.Header.Version. Expected %v, got %v", 26 state.Version.Consensus, 27 block.Version, 28 ) 29 } 30 if block.ChainID != state.ChainID { 31 return fmt.Errorf("wrong Block.Header.ChainID. Expected %v, got %v", 32 state.ChainID, 33 block.ChainID, 34 ) 35 } 36 if block.Height != state.LastBlockHeight+1 { 37 return fmt.Errorf("wrong Block.Header.Height. Expected %v, got %v", 38 state.LastBlockHeight+1, 39 block.Height, 40 ) 41 } 42 43 // Validate prev block info. 44 if !block.LastBlockID.Equals(state.LastBlockID) { 45 return fmt.Errorf("wrong Block.Header.LastBlockID. Expected %v, got %v", 46 state.LastBlockID, 47 block.LastBlockID, 48 ) 49 } 50 51 // Validate app info 52 if !bytes.Equal(block.AppHash, state.AppHash) { 53 return fmt.Errorf("wrong Block.Header.AppHash. Expected %X, got %v", 54 state.AppHash, 55 block.AppHash, 56 ) 57 } 58 if !bytes.Equal(block.ConsensusHash, state.ConsensusParams.Hash()) { 59 return fmt.Errorf("wrong Block.Header.ConsensusHash. Expected %X, got %v", 60 state.ConsensusParams.Hash(), 61 block.ConsensusHash, 62 ) 63 } 64 if !bytes.Equal(block.LastResultsHash, state.LastResultsHash) { 65 return fmt.Errorf("wrong Block.Header.LastResultsHash. Expected %X, got %v", 66 state.LastResultsHash, 67 block.LastResultsHash, 68 ) 69 } 70 if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash(block.Height)) { 71 return fmt.Errorf("wrong Block.Header.ValidatorsHash. Expected %X, got %v", 72 state.Validators.Hash(block.Height), 73 block.ValidatorsHash, 74 ) 75 } 76 if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash(block.Height+1)) { 77 return fmt.Errorf("wrong Block.Header.NextValidatorsHash. Expected %X, got %v", 78 state.NextValidators.Hash(block.Height+1), 79 block.NextValidatorsHash, 80 ) 81 } 82 83 // Validate block LastCommit. 84 if block.Height == types.GetStartBlockHeight()+1 { 85 if len(block.LastCommit.Signatures) != 0 { 86 return errors.New(fmt.Sprintf("block at height %d can't have LastCommit signatures", types.GetStartBlockHeight()+1)) 87 } 88 } else { 89 if len(block.LastCommit.Signatures) != state.LastValidators.Size() { 90 return types.NewErrInvalidCommitSignatures(state.LastValidators.Size(), len(block.LastCommit.Signatures)) 91 } 92 err := state.LastValidators.VerifyCommit( 93 state.ChainID, state.LastBlockID, block.Height-1, block.LastCommit) 94 if err != nil { 95 return err 96 } 97 } 98 99 // Validate block Time 100 if block.Height > types.GetStartBlockHeight()+1 { 101 if !block.Time.After(state.LastBlockTime) { 102 return fmt.Errorf("block time %v not greater than last block time %v", 103 block.Time, 104 state.LastBlockTime, 105 ) 106 } 107 108 medianTime := MedianTime(block.LastCommit, state.LastValidators) 109 if !block.Time.Equal(medianTime) { 110 return fmt.Errorf("invalid block time. Expected %v, got %v", 111 medianTime, 112 block.Time, 113 ) 114 } 115 } else if block.Height == types.GetStartBlockHeight()+1 { 116 genesisTime := state.LastBlockTime 117 if !block.Time.Equal(genesisTime) { 118 return fmt.Errorf("block time %v is not equal to genesis time %v", 119 block.Time, 120 genesisTime, 121 ) 122 } 123 } 124 125 // Limit the amount of evidence 126 maxNumEvidence, _ := types.MaxEvidencePerBlock(state.ConsensusParams.Block.MaxBytes) 127 numEvidence := int64(len(block.Evidence.Evidence)) 128 if numEvidence > maxNumEvidence { 129 return types.NewErrEvidenceOverflow(maxNumEvidence, numEvidence) 130 131 } 132 133 // Validate all evidence. 134 for _, ev := range block.Evidence.Evidence { 135 if err := VerifyEvidence(stateDB, state, ev); err != nil { 136 return types.NewErrEvidenceInvalid(ev, err) 137 } 138 if evidencePool != nil && evidencePool.IsCommitted(ev) { 139 return types.NewErrEvidenceInvalid(ev, errors.New("evidence was already committed")) 140 } 141 } 142 143 // NOTE: We can't actually verify it's the right proposer because we dont 144 // know what round the block was first proposed. So just check that it's 145 // a legit address and a known validator. 146 if len(block.ProposerAddress) != crypto.AddressSize || 147 !state.Validators.HasAddress(block.ProposerAddress) { 148 return fmt.Errorf("block.Header.ProposerAddress, %X, is not a validator", 149 block.ProposerAddress, 150 ) 151 } 152 153 return nil 154 } 155 156 // VerifyEvidence verifies the evidence fully by checking: 157 // - it is sufficiently recent (MaxAge) 158 // - it is from a key who was a validator at the given height 159 // - it is internally consistent 160 // - it was properly signed by the alleged equivocator 161 func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence) error { 162 var ( 163 height = state.LastBlockHeight 164 evidenceParams = state.ConsensusParams.Evidence 165 166 ageDuration = state.LastBlockTime.Sub(evidence.Time()) 167 ageNumBlocks = height - evidence.Height() 168 ) 169 170 if ageDuration > evidenceParams.MaxAgeDuration && ageNumBlocks > evidenceParams.MaxAgeNumBlocks { 171 return fmt.Errorf( 172 "evidence from height %d (created at: %v) is too old; min height is %d and evidence can not be older than %v", 173 evidence.Height(), 174 evidence.Time(), 175 height-evidenceParams.MaxAgeNumBlocks, 176 state.LastBlockTime.Add(evidenceParams.MaxAgeDuration), 177 ) 178 } 179 180 valset, err := LoadValidators(stateDB, evidence.Height()) 181 if err != nil { 182 // TODO: if err is just that we cant find it cuz we pruned, ignore. 183 // TODO: if its actually bad evidence, punish peer 184 return err 185 } 186 187 // The address must have been an active validator at the height. 188 // NOTE: we will ignore evidence from H if the key was not a validator 189 // at H, even if it is a validator at some nearby H' 190 // XXX: this makes lite-client bisection as is unsafe 191 // See https://github.com/tendermint/tendermint/issues/3244 192 ev := evidence 193 height, addr := ev.Height(), ev.Address() 194 _, val := valset.GetByAddress(addr) 195 if val == nil { 196 return fmt.Errorf("address %X was not a validator at height %d", addr, height) 197 } 198 199 if err := evidence.Verify(state.ChainID, val.PubKey); err != nil { 200 return err 201 } 202 203 return nil 204 }