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