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