github.com/aakash4dev/cometbft@v0.38.2/state/state.go (about) 1 package state 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "os" 8 "time" 9 10 "github.com/cosmos/gogoproto/proto" 11 12 cmtstate "github.com/aakash4dev/cometbft/proto/tendermint/state" 13 cmtversion "github.com/aakash4dev/cometbft/proto/tendermint/version" 14 "github.com/aakash4dev/cometbft/types" 15 cmttime "github.com/aakash4dev/cometbft/types/time" 16 "github.com/aakash4dev/cometbft/version" 17 ) 18 19 // database keys 20 var ( 21 stateKey = []byte("stateKey") 22 ) 23 24 //----------------------------------------------------------------------------- 25 26 // InitStateVersion sets the Consensus.Block and Software versions, 27 // but leaves the Consensus.App version blank. 28 // The Consensus.App version will be set during the Handshake, once 29 // we hear from the app what protocol version it is running. 30 var InitStateVersion = cmtstate.Version{ 31 Consensus: cmtversion.Consensus{ 32 Block: version.BlockProtocol, 33 App: 0, 34 }, 35 Software: version.TMCoreSemVer, 36 } 37 38 //----------------------------------------------------------------------------- 39 40 // State is a short description of the latest committed block of the consensus protocol. 41 // It keeps all information necessary to validate new blocks, 42 // including the last validator set and the consensus params. 43 // All fields are exposed so the struct can be easily serialized, 44 // but none of them should be mutated directly. 45 // Instead, use state.Copy() or state.NextState(...). 46 // NOTE: not goroutine-safe. 47 type State struct { 48 Version cmtstate.Version 49 50 // immutable 51 ChainID string 52 InitialHeight int64 // should be 1, not 0, when starting from height 1 53 54 // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) 55 LastBlockHeight int64 56 LastBlockID types.BlockID 57 LastBlockTime time.Time 58 59 // LastValidators is used to validate block.LastCommit. 60 // Validators are persisted to the database separately every time they change, 61 // so we can query for historical validator sets. 62 // Note that if s.LastBlockHeight causes a valset change, 63 // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1 64 // Extra +1 due to nextValSet delay. 65 NextValidators *types.ValidatorSet 66 Validators *types.ValidatorSet 67 LastValidators *types.ValidatorSet 68 LastHeightValidatorsChanged int64 69 70 // Consensus parameters used for validating blocks. 71 // Changes returned by FinalizeBlock and updated after Commit. 72 ConsensusParams types.ConsensusParams 73 LastHeightConsensusParamsChanged int64 74 75 // Merkle root of the results from executing prev block 76 LastResultsHash []byte 77 78 // the latest AppHash we've received from calling abci.Commit() 79 AppHash []byte 80 } 81 82 // Copy makes a copy of the State for mutating. 83 func (state State) Copy() State { 84 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() (*cmtstate.State, error) { 135 if state == nil { 136 return nil, errors.New("state is nil") 137 } 138 139 sm := new(cmtstate.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.ToProto() 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 *cmtstate.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 = types.ConsensusParamsFromProto(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 lastCommit *types.Commit, 238 evidence []types.Evidence, 239 proposerAddress []byte, 240 ) *types.Block { 241 242 // Build base block with block data. 243 block := types.MakeBlock(height, txs, lastCommit, evidence) 244 245 // Set time. 246 var timestamp time.Time 247 if height == state.InitialHeight { 248 timestamp = state.LastBlockTime // genesis time 249 } else { 250 timestamp = MedianTime(lastCommit, state.LastValidators) 251 } 252 253 // Fill rest of header with state data. 254 block.Header.Populate( 255 state.Version.Consensus, state.ChainID, 256 timestamp, state.LastBlockID, 257 state.Validators.Hash(), state.NextValidators.Hash(), 258 state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash, 259 proposerAddress, 260 ) 261 262 return block 263 } 264 265 // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the 266 // corresponding validator set. The computed time is always between timestamps of 267 // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the 268 // computed value. 269 func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time { 270 weightedTimes := make([]*cmttime.WeightedTime, len(commit.Signatures)) 271 totalVotingPower := int64(0) 272 273 for i, commitSig := range commit.Signatures { 274 if commitSig.BlockIDFlag == types.BlockIDFlagAbsent { 275 continue 276 } 277 _, validator := validators.GetByAddress(commitSig.ValidatorAddress) 278 // If there's no condition, TestValidateBlockCommit panics; not needed normally. 279 if validator != nil { 280 totalVotingPower += validator.VotingPower 281 weightedTimes[i] = cmttime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower) 282 } 283 } 284 285 return cmttime.WeightedMedian(weightedTimes, totalVotingPower) 286 } 287 288 //------------------------------------------------------------------------ 289 // Genesis 290 291 // MakeGenesisStateFromFile reads and unmarshals state from the given 292 // file. 293 // 294 // Used during replay and in tests. 295 func MakeGenesisStateFromFile(genDocFile string) (State, error) { 296 genDoc, err := MakeGenesisDocFromFile(genDocFile) 297 if err != nil { 298 return State{}, err 299 } 300 return MakeGenesisState(genDoc) 301 } 302 303 // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file. 304 func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { 305 genDocJSON, err := os.ReadFile(genDocFile) 306 if err != nil { 307 return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err) 308 } 309 genDoc, err := types.GenesisDocFromJSON(genDocJSON) 310 if err != nil { 311 return nil, fmt.Errorf("error reading GenesisDoc: %v", err) 312 } 313 return genDoc, nil 314 } 315 316 // MakeGenesisState creates state from types.GenesisDoc. 317 func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { 318 err := genDoc.ValidateAndComplete() 319 if err != nil { 320 return State{}, fmt.Errorf("error in genesis doc: %w", err) 321 } 322 323 var validatorSet, nextValidatorSet *types.ValidatorSet 324 if genDoc.Validators == nil { 325 validatorSet = types.NewValidatorSet(nil) 326 nextValidatorSet = types.NewValidatorSet(nil) 327 } else { 328 validators := make([]*types.Validator, len(genDoc.Validators)) 329 for i, val := range genDoc.Validators { 330 validators[i] = types.NewValidator(val.PubKey, val.Power) 331 } 332 validatorSet = types.NewValidatorSet(validators) 333 nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1) 334 } 335 336 return State{ 337 Version: InitStateVersion, 338 ChainID: genDoc.ChainID, 339 InitialHeight: genDoc.InitialHeight, 340 341 LastBlockHeight: 0, 342 LastBlockID: types.BlockID{}, 343 LastBlockTime: genDoc.GenesisTime, 344 345 NextValidators: nextValidatorSet, 346 Validators: validatorSet, 347 LastValidators: types.NewValidatorSet(nil), 348 LastHeightValidatorsChanged: genDoc.InitialHeight, 349 350 ConsensusParams: *genDoc.ConsensusParams, 351 LastHeightConsensusParamsChanged: genDoc.InitialHeight, 352 353 AppHash: genDoc.AppHash, 354 }, nil 355 }