github.com/vipernet-xyz/tm@v0.34.24/state/state.go (about) 1 package state 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "os" 8 "time" 9 10 "github.com/gogo/protobuf/proto" 11 12 tmstate "github.com/vipernet-xyz/tm/proto/tendermint/state" 13 tmproto "github.com/vipernet-xyz/tm/proto/tendermint/types" 14 tmversion "github.com/vipernet-xyz/tm/proto/tendermint/version" 15 "github.com/vipernet-xyz/tm/types" 16 tmtime "github.com/vipernet-xyz/tm/types/time" 17 "github.com/vipernet-xyz/tm/version" 18 ) 19 20 // database key 21 var ( 22 stateKey = []byte("stateKey") 23 ) 24 25 //----------------------------------------------------------------------------- 26 27 // InitStateVersion sets the Consensus.Block and Software versions, 28 // but leaves the Consensus.App version blank. 29 // The Consensus.App version will be set during the Handshake, once 30 // we hear from the app what protocol version it is running. 31 var InitStateVersion = tmstate.Version{ 32 Consensus: tmversion.Consensus{ 33 Block: version.BlockProtocol, 34 App: 0, 35 }, 36 Software: version.TMCoreSemVer, 37 } 38 39 //----------------------------------------------------------------------------- 40 41 // State is a short description of the latest committed block of the Tendermint consensus. 42 // It keeps all information necessary to validate new blocks, 43 // including the last validator set and the consensus params. 44 // All fields are exposed so the struct can be easily serialized, 45 // but none of them should be mutated directly. 46 // Instead, use state.Copy() or state.NextState(...). 47 // NOTE: not goroutine-safe. 48 type State struct { 49 Version tmstate.Version 50 51 // immutable 52 ChainID string 53 InitialHeight int64 // should be 1, not 0, when starting from height 1 54 55 // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) 56 LastBlockHeight int64 57 LastBlockID types.BlockID 58 LastBlockTime time.Time 59 60 // LastValidators is used to validate block.LastCommit. 61 // Validators are persisted to the database separately every time they change, 62 // so we can query for historical validator sets. 63 // Note that if s.LastBlockHeight causes a valset change, 64 // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1 65 // Extra +1 due to nextValSet delay. 66 NextValidators *types.ValidatorSet 67 Validators *types.ValidatorSet 68 LastValidators *types.ValidatorSet 69 LastHeightValidatorsChanged int64 70 71 // Consensus parameters used for validating blocks. 72 // Changes returned by EndBlock and updated after Commit. 73 ConsensusParams tmproto.ConsensusParams 74 LastHeightConsensusParamsChanged int64 75 76 // Merkle root of the results from executing prev block 77 LastResultsHash []byte 78 79 // the latest AppHash we've received from calling abci.Commit() 80 AppHash []byte 81 } 82 83 // Copy makes a copy of the State for mutating. 84 func (state State) Copy() State { 85 return State{ 86 Version: state.Version, 87 ChainID: state.ChainID, 88 InitialHeight: state.InitialHeight, 89 90 LastBlockHeight: state.LastBlockHeight, 91 LastBlockID: state.LastBlockID, 92 LastBlockTime: state.LastBlockTime, 93 94 NextValidators: state.NextValidators.Copy(), 95 Validators: state.Validators.Copy(), 96 LastValidators: state.LastValidators.Copy(), 97 LastHeightValidatorsChanged: state.LastHeightValidatorsChanged, 98 99 ConsensusParams: state.ConsensusParams, 100 LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged, 101 102 AppHash: state.AppHash, 103 104 LastResultsHash: state.LastResultsHash, 105 } 106 } 107 108 // Equals returns true if the States are identical. 109 func (state State) Equals(state2 State) bool { 110 sbz, s2bz := state.Bytes(), state2.Bytes() 111 return bytes.Equal(sbz, s2bz) 112 } 113 114 // Bytes serializes the State using protobuf. 115 // It panics if either casting to protobuf or serialization fails. 116 func (state State) Bytes() []byte { 117 sm, err := state.ToProto() 118 if err != nil { 119 panic(err) 120 } 121 bz, err := proto.Marshal(sm) 122 if err != nil { 123 panic(err) 124 } 125 return bz 126 } 127 128 // IsEmpty returns true if the State is equal to the empty State. 129 func (state State) IsEmpty() bool { 130 return state.Validators == nil // XXX can't compare to Empty 131 } 132 133 // ToProto takes the local state type and returns the equivalent proto type 134 func (state *State) ToProto() (*tmstate.State, error) { 135 if state == nil { 136 return nil, errors.New("state is nil") 137 } 138 139 sm := new(tmstate.State) 140 141 sm.Version = state.Version 142 sm.ChainID = state.ChainID 143 sm.InitialHeight = state.InitialHeight 144 sm.LastBlockHeight = state.LastBlockHeight 145 146 sm.LastBlockID = state.LastBlockID.ToProto() 147 sm.LastBlockTime = state.LastBlockTime 148 vals, err := state.Validators.ToProto() 149 if err != nil { 150 return nil, err 151 } 152 sm.Validators = vals 153 154 nVals, err := state.NextValidators.ToProto() 155 if err != nil { 156 return nil, err 157 } 158 sm.NextValidators = nVals 159 160 if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil 161 lVals, err := state.LastValidators.ToProto() 162 if err != nil { 163 return nil, err 164 } 165 sm.LastValidators = lVals 166 } 167 168 sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged 169 sm.ConsensusParams = state.ConsensusParams 170 sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged 171 sm.LastResultsHash = state.LastResultsHash 172 sm.AppHash = state.AppHash 173 174 return sm, nil 175 } 176 177 // FromProto takes a state proto message & returns the local state type 178 func FromProto(pb *tmstate.State) (*State, error) { //nolint:golint 179 if pb == nil { 180 return nil, errors.New("nil State") 181 } 182 183 state := new(State) 184 185 state.Version = pb.Version 186 state.ChainID = pb.ChainID 187 state.InitialHeight = pb.InitialHeight 188 189 bi, err := types.BlockIDFromProto(&pb.LastBlockID) 190 if err != nil { 191 return nil, err 192 } 193 state.LastBlockID = *bi 194 state.LastBlockHeight = pb.LastBlockHeight 195 state.LastBlockTime = pb.LastBlockTime 196 197 vals, err := types.ValidatorSetFromProto(pb.Validators) 198 if err != nil { 199 return nil, err 200 } 201 state.Validators = vals 202 203 nVals, err := types.ValidatorSetFromProto(pb.NextValidators) 204 if err != nil { 205 return nil, err 206 } 207 state.NextValidators = nVals 208 209 if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil 210 lVals, err := types.ValidatorSetFromProto(pb.LastValidators) 211 if err != nil { 212 return nil, err 213 } 214 state.LastValidators = lVals 215 } else { 216 state.LastValidators = types.NewValidatorSet(nil) 217 } 218 219 state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged 220 state.ConsensusParams = pb.ConsensusParams 221 state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged 222 state.LastResultsHash = pb.LastResultsHash 223 state.AppHash = pb.AppHash 224 225 return state, nil 226 } 227 228 //------------------------------------------------------------------------ 229 // Create a block from the latest state 230 231 // MakeBlock builds a block from the current state with the given txs, commit, 232 // and evidence. Note it also takes a proposerAddress because the state does not 233 // track rounds, and hence does not know the correct proposer. TODO: fix this! 234 func (state State) MakeBlock( 235 height int64, 236 txs []types.Tx, 237 commit *types.Commit, 238 evidence []types.Evidence, 239 proposerAddress []byte, 240 ) (*types.Block, *types.PartSet) { 241 // Build base block with block data. 242 block := types.MakeBlock(height, txs, commit, evidence) 243 244 // Set time. 245 var timestamp time.Time 246 if height == state.InitialHeight { 247 timestamp = state.LastBlockTime // genesis time 248 } else { 249 timestamp = MedianTime(commit, state.LastValidators) 250 } 251 252 // Fill rest of header with state data. 253 block.Header.Populate( 254 state.Version.Consensus, state.ChainID, 255 timestamp, state.LastBlockID, 256 state.Validators.Hash(), state.NextValidators.Hash(), 257 types.HashConsensusParams(state.ConsensusParams), state.AppHash, state.LastResultsHash, 258 proposerAddress, 259 ) 260 261 return block, block.MakePartSet(types.BlockPartSizeBytes) 262 } 263 264 // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the 265 // corresponding validator set. The computed time is always between timestamps of 266 // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the 267 // computed value. 268 func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time { 269 weightedTimes := make([]*tmtime.WeightedTime, len(commit.Signatures)) 270 totalVotingPower := int64(0) 271 272 for i, commitSig := range commit.Signatures { 273 if commitSig.Absent() { 274 continue 275 } 276 _, validator := validators.GetByAddress(commitSig.ValidatorAddress) 277 // If there's no condition, TestValidateBlockCommit panics; not needed normally. 278 if validator != nil { 279 totalVotingPower += validator.VotingPower 280 weightedTimes[i] = tmtime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower) 281 } 282 } 283 284 return tmtime.WeightedMedian(weightedTimes, totalVotingPower) 285 } 286 287 //------------------------------------------------------------------------ 288 // Genesis 289 290 // MakeGenesisStateFromFile reads and unmarshals state from the given 291 // file. 292 // 293 // Used during replay and in tests. 294 func MakeGenesisStateFromFile(genDocFile string) (State, error) { 295 genDoc, err := MakeGenesisDocFromFile(genDocFile) 296 if err != nil { 297 return State{}, err 298 } 299 return MakeGenesisState(genDoc) 300 } 301 302 // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file. 303 func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { 304 genDocJSON, err := os.ReadFile(genDocFile) 305 if err != nil { 306 return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err) 307 } 308 genDoc, err := types.GenesisDocFromJSON(genDocJSON) 309 if err != nil { 310 return nil, fmt.Errorf("error reading GenesisDoc: %v", err) 311 } 312 return genDoc, nil 313 } 314 315 // MakeGenesisState creates state from types.GenesisDoc. 316 func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { 317 err := genDoc.ValidateAndComplete() 318 if err != nil { 319 return State{}, fmt.Errorf("error in genesis file: %v", err) 320 } 321 322 var validatorSet, nextValidatorSet *types.ValidatorSet 323 if genDoc.Validators == nil { 324 validatorSet = types.NewValidatorSet(nil) 325 nextValidatorSet = types.NewValidatorSet(nil) 326 } else { 327 validators := make([]*types.Validator, len(genDoc.Validators)) 328 for i, val := range genDoc.Validators { 329 validators[i] = types.NewValidator(val.PubKey, val.Power) 330 } 331 validatorSet = types.NewValidatorSet(validators) 332 nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1) 333 } 334 335 return State{ 336 Version: InitStateVersion, 337 ChainID: genDoc.ChainID, 338 InitialHeight: genDoc.InitialHeight, 339 340 LastBlockHeight: 0, 341 LastBlockID: types.BlockID{}, 342 LastBlockTime: genDoc.GenesisTime, 343 344 NextValidators: nextValidatorSet, 345 Validators: validatorSet, 346 LastValidators: types.NewValidatorSet(nil), 347 LastHeightValidatorsChanged: genDoc.InitialHeight, 348 349 ConsensusParams: *genDoc.ConsensusParams, 350 LastHeightConsensusParamsChanged: genDoc.InitialHeight, 351 352 AppHash: genDoc.AppHash, 353 }, nil 354 }