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