github.com/DFWallet/tendermint-cosmos@v0.0.2/state/state.go (about) 1 package state 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "time" 9 10 "github.com/gogo/protobuf/proto" 11 12 tmstate "github.com/DFWallet/tendermint-cosmos/proto/tendermint/state" 13 tmproto "github.com/DFWallet/tendermint-cosmos/proto/tendermint/types" 14 tmversion "github.com/DFWallet/tendermint-cosmos/proto/tendermint/version" 15 "github.com/DFWallet/tendermint-cosmos/types" 16 tmtime "github.com/DFWallet/tendermint-cosmos/types/time" 17 "github.com/DFWallet/tendermint-cosmos/version" 18 ) 19 20 // database keys 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 86 return State{ 87 Version: state.Version, 88 ChainID: state.ChainID, 89 InitialHeight: state.InitialHeight, 90 91 LastBlockHeight: state.LastBlockHeight, 92 LastBlockID: state.LastBlockID, 93 LastBlockTime: state.LastBlockTime, 94 95 NextValidators: state.NextValidators.Copy(), 96 Validators: state.Validators.Copy(), 97 LastValidators: state.LastValidators.Copy(), 98 LastHeightValidatorsChanged: state.LastHeightValidatorsChanged, 99 100 ConsensusParams: state.ConsensusParams, 101 LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged, 102 103 AppHash: state.AppHash, 104 105 LastResultsHash: state.LastResultsHash, 106 } 107 } 108 109 // Equals returns true if the States are identical. 110 func (state State) Equals(state2 State) bool { 111 sbz, s2bz := state.Bytes(), state2.Bytes() 112 return bytes.Equal(sbz, s2bz) 113 } 114 115 // Bytes serializes the State using protobuf. 116 // It panics if either casting to protobuf or serialization fails. 117 func (state State) Bytes() []byte { 118 sm, err := state.ToProto() 119 if err != nil { 120 panic(err) 121 } 122 bz, err := proto.Marshal(sm) 123 if err != nil { 124 panic(err) 125 } 126 return bz 127 } 128 129 // IsEmpty returns true if the State is equal to the empty State. 130 func (state State) IsEmpty() bool { 131 return state.Validators == nil // XXX can't compare to Empty 132 } 133 134 // ToProto takes the local state type and returns the equivalent proto type 135 func (state *State) ToProto() (*tmstate.State, error) { 136 if state == nil { 137 return nil, errors.New("state is nil") 138 } 139 140 sm := new(tmstate.State) 141 142 sm.Version = state.Version 143 sm.ChainID = state.ChainID 144 sm.InitialHeight = state.InitialHeight 145 sm.LastBlockHeight = state.LastBlockHeight 146 147 sm.LastBlockID = state.LastBlockID.ToProto() 148 sm.LastBlockTime = state.LastBlockTime 149 vals, err := state.Validators.ToProto() 150 if err != nil { 151 return nil, err 152 } 153 sm.Validators = vals 154 155 nVals, err := state.NextValidators.ToProto() 156 if err != nil { 157 return nil, err 158 } 159 sm.NextValidators = nVals 160 161 if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil 162 lVals, err := state.LastValidators.ToProto() 163 if err != nil { 164 return nil, err 165 } 166 sm.LastValidators = lVals 167 } 168 169 sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged 170 sm.ConsensusParams = state.ConsensusParams 171 sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged 172 sm.LastResultsHash = state.LastResultsHash 173 sm.AppHash = state.AppHash 174 175 return sm, nil 176 } 177 178 // StateFromProto takes a state proto message & returns the local state type 179 func StateFromProto(pb *tmstate.State) (*State, error) { //nolint:golint 180 if pb == nil { 181 return nil, errors.New("nil State") 182 } 183 184 state := new(State) 185 186 state.Version = pb.Version 187 state.ChainID = pb.ChainID 188 state.InitialHeight = pb.InitialHeight 189 190 bi, err := types.BlockIDFromProto(&pb.LastBlockID) 191 if err != nil { 192 return nil, err 193 } 194 state.LastBlockID = *bi 195 state.LastBlockHeight = pb.LastBlockHeight 196 state.LastBlockTime = pb.LastBlockTime 197 198 vals, err := types.ValidatorSetFromProto(pb.Validators) 199 if err != nil { 200 return nil, err 201 } 202 state.Validators = vals 203 204 nVals, err := types.ValidatorSetFromProto(pb.NextValidators) 205 if err != nil { 206 return nil, err 207 } 208 state.NextValidators = nVals 209 210 if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil 211 lVals, err := types.ValidatorSetFromProto(pb.LastValidators) 212 if err != nil { 213 return nil, err 214 } 215 state.LastValidators = lVals 216 } else { 217 state.LastValidators = types.NewValidatorSet(nil) 218 } 219 220 state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged 221 state.ConsensusParams = pb.ConsensusParams 222 state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged 223 state.LastResultsHash = pb.LastResultsHash 224 state.AppHash = pb.AppHash 225 226 return state, nil 227 } 228 229 //------------------------------------------------------------------------ 230 // Create a block from the latest state 231 232 // MakeBlock builds a block from the current state with the given txs, commit, 233 // and evidence. Note it also takes a proposerAddress because the state does not 234 // track rounds, and hence does not know the correct proposer. TODO: fix this! 235 func (state State) MakeBlock( 236 height int64, 237 txs []types.Tx, 238 commit *types.Commit, 239 evidence []types.Evidence, 240 proposerAddress []byte, 241 ) (*types.Block, *types.PartSet) { 242 243 // Build base block with block data. 244 block := types.MakeBlock(height, txs, commit, evidence) 245 246 // Set time. 247 var timestamp time.Time 248 if height == state.InitialHeight { 249 timestamp = state.LastBlockTime // genesis time 250 } else { 251 timestamp = MedianTime(commit, state.LastValidators) 252 } 253 254 // Fill rest of header with state data. 255 block.Header.Populate( 256 state.Version.Consensus, state.ChainID, 257 timestamp, state.LastBlockID, 258 state.Validators.Hash(), state.NextValidators.Hash(), 259 types.HashConsensusParams(state.ConsensusParams), state.AppHash, state.LastResultsHash, 260 proposerAddress, 261 ) 262 263 return block, block.MakePartSet(types.BlockPartSizeBytes) 264 } 265 266 // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the 267 // corresponding validator set. The computed time is always between timestamps of 268 // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the 269 // computed value. 270 func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time { 271 weightedTimes := make([]*tmtime.WeightedTime, len(commit.Signatures)) 272 totalVotingPower := int64(0) 273 274 for i, commitSig := range commit.Signatures { 275 if commitSig.Absent() { 276 continue 277 } 278 _, validator := validators.GetByAddress(commitSig.ValidatorAddress) 279 // If there's no condition, TestValidateBlockCommit panics; not needed normally. 280 if validator != nil { 281 totalVotingPower += validator.VotingPower 282 weightedTimes[i] = tmtime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower) 283 } 284 } 285 286 return tmtime.WeightedMedian(weightedTimes, totalVotingPower) 287 } 288 289 //------------------------------------------------------------------------ 290 // Genesis 291 292 // MakeGenesisStateFromFile reads and unmarshals state from the given 293 // file. 294 // 295 // Used during replay and in tests. 296 func MakeGenesisStateFromFile(genDocFile string) (State, error) { 297 genDoc, err := MakeGenesisDocFromFile(genDocFile) 298 if err != nil { 299 return State{}, err 300 } 301 return MakeGenesisState(genDoc) 302 } 303 304 // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file. 305 func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { 306 genDocJSON, err := ioutil.ReadFile(genDocFile) 307 if err != nil { 308 return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err) 309 } 310 genDoc, err := types.GenesisDocFromJSON(genDocJSON) 311 if err != nil { 312 return nil, fmt.Errorf("error reading GenesisDoc: %v", err) 313 } 314 return genDoc, nil 315 } 316 317 // MakeGenesisState creates state from types.GenesisDoc. 318 func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { 319 err := genDoc.ValidateAndComplete() 320 if err != nil { 321 return State{}, fmt.Errorf("error in genesis file: %v", err) 322 } 323 324 var validatorSet, nextValidatorSet *types.ValidatorSet 325 if genDoc.Validators == nil { 326 validatorSet = types.NewValidatorSet(nil) 327 nextValidatorSet = types.NewValidatorSet(nil) 328 } else { 329 validators := make([]*types.Validator, len(genDoc.Validators)) 330 for i, val := range genDoc.Validators { 331 validators[i] = types.NewValidator(val.PubKey, val.Power) 332 } 333 validatorSet = types.NewValidatorSet(validators) 334 nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1) 335 } 336 337 return State{ 338 Version: InitStateVersion, 339 ChainID: genDoc.ChainID, 340 InitialHeight: genDoc.InitialHeight, 341 342 LastBlockHeight: 0, 343 LastBlockID: types.BlockID{}, 344 LastBlockTime: genDoc.GenesisTime, 345 346 NextValidators: nextValidatorSet, 347 Validators: validatorSet, 348 LastValidators: types.NewValidatorSet(nil), 349 LastHeightValidatorsChanged: genDoc.InitialHeight, 350 351 ConsensusParams: *genDoc.ConsensusParams, 352 LastHeightConsensusParamsChanged: genDoc.InitialHeight, 353 354 AppHash: genDoc.AppHash, 355 }, nil 356 }