github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/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 tmtime "github.com/ari-anchor/sei-tendermint/libs/time" 13 14 tmstate "github.com/ari-anchor/sei-tendermint/proto/tendermint/state" 15 tmversion "github.com/ari-anchor/sei-tendermint/proto/tendermint/version" 16 "github.com/ari-anchor/sei-tendermint/types" 17 "github.com/ari-anchor/sei-tendermint/version" 18 ) 19 20 //----------------------------------------------------------------------------- 21 22 type Version struct { 23 Consensus version.Consensus ` json:"consensus"` 24 Software string ` json:"software"` 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 = Version{ 32 Consensus: version.Consensus{ 33 Block: version.BlockProtocol, 34 App: 0, 35 }, 36 Software: version.TMVersion, 37 } 38 39 func (v *Version) ToProto() tmstate.Version { 40 return tmstate.Version{ 41 Consensus: tmversion.Consensus{ 42 Block: v.Consensus.Block, 43 App: v.Consensus.App, 44 }, 45 Software: v.Software, 46 } 47 } 48 49 func VersionFromProto(v tmstate.Version) Version { 50 return Version{ 51 Consensus: version.Consensus{ 52 Block: v.Consensus.Block, 53 App: v.Consensus.App, 54 }, 55 Software: v.Software, 56 } 57 } 58 59 //----------------------------------------------------------------------------- 60 61 // State is a short description of the latest committed block of the Tendermint consensus. 62 // It keeps all information necessary to validate new blocks, 63 // including the last validator set and the consensus params. 64 // All fields are exposed so the struct can be easily serialized, 65 // but none of them should be mutated directly. 66 // Instead, use state.Copy() or updateState(...). 67 // NOTE: not goroutine-safe. 68 type State struct { 69 // FIXME: This can be removed as TMVersion is a constant, and version.Consensus should 70 // eventually be replaced by VersionParams in ConsensusParams 71 Version Version 72 73 // immutable 74 ChainID string 75 InitialHeight int64 // should be 1, not 0, when starting from height 1 76 77 // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) 78 LastBlockHeight int64 79 LastBlockID types.BlockID 80 LastBlockTime time.Time 81 82 // LastValidators is used to validate block.LastCommit. 83 // Validators are persisted to the database separately every time they change, 84 // so we can query for historical validator sets. 85 // Note that if s.LastBlockHeight causes a valset change, 86 // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1 87 // Extra +1 due to nextValSet delay. 88 NextValidators *types.ValidatorSet 89 Validators *types.ValidatorSet 90 LastValidators *types.ValidatorSet 91 LastHeightValidatorsChanged int64 92 93 // Consensus parameters used for validating blocks. 94 // Changes returned by FinalizeBlock and updated after Commit. 95 ConsensusParams types.ConsensusParams 96 LastHeightConsensusParamsChanged int64 97 98 // Merkle root of the results from executing prev block 99 LastResultsHash []byte 100 101 // the latest AppHash we've received from calling abci.Commit() 102 AppHash []byte 103 } 104 105 // Copy makes a copy of the State for mutating. 106 func (state State) Copy() State { 107 108 return State{ 109 Version: state.Version, 110 ChainID: state.ChainID, 111 InitialHeight: state.InitialHeight, 112 113 LastBlockHeight: state.LastBlockHeight, 114 LastBlockID: state.LastBlockID, 115 LastBlockTime: state.LastBlockTime, 116 117 NextValidators: state.NextValidators.Copy(), 118 Validators: state.Validators.Copy(), 119 LastValidators: state.LastValidators.Copy(), 120 LastHeightValidatorsChanged: state.LastHeightValidatorsChanged, 121 122 ConsensusParams: state.ConsensusParams, 123 LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged, 124 125 AppHash: state.AppHash, 126 127 LastResultsHash: state.LastResultsHash, 128 } 129 } 130 131 // Equals returns true if the States are identical. 132 func (state State) Equals(state2 State) (bool, error) { 133 sbz, err := state.Bytes() 134 if err != nil { 135 return false, err 136 } 137 s2bz, err := state2.Bytes() 138 if err != nil { 139 return false, err 140 } 141 return bytes.Equal(sbz, s2bz), nil 142 } 143 144 // Bytes serializes the State using protobuf, propagating marshaling 145 // errors 146 func (state State) Bytes() ([]byte, error) { 147 sm, err := state.ToProto() 148 if err != nil { 149 return nil, err 150 } 151 bz, err := proto.Marshal(sm) 152 if err != nil { 153 return nil, err 154 } 155 return bz, nil 156 } 157 158 // IsEmpty returns true if the State is equal to the empty State. 159 func (state State) IsEmpty() bool { 160 return state.Validators == nil // XXX can't compare to Empty 161 } 162 163 // ToProto takes the local state type and returns the equivalent proto type 164 func (state *State) ToProto() (*tmstate.State, error) { 165 if state == nil { 166 return nil, errors.New("state is nil") 167 } 168 169 sm := new(tmstate.State) 170 171 sm.Version = state.Version.ToProto() 172 sm.ChainID = state.ChainID 173 sm.InitialHeight = state.InitialHeight 174 sm.LastBlockHeight = state.LastBlockHeight 175 176 sm.LastBlockID = state.LastBlockID.ToProto() 177 sm.LastBlockTime = state.LastBlockTime 178 vals, err := state.Validators.ToProto() 179 if err != nil { 180 return nil, err 181 } 182 sm.Validators = vals 183 184 nVals, err := state.NextValidators.ToProto() 185 if err != nil { 186 return nil, err 187 } 188 sm.NextValidators = nVals 189 190 if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil 191 lVals, err := state.LastValidators.ToProto() 192 if err != nil { 193 return nil, err 194 } 195 sm.LastValidators = lVals 196 } 197 198 sm.LastHeightValidatorsChanged = state.LastHeightValidatorsChanged 199 sm.ConsensusParams = state.ConsensusParams.ToProto() 200 sm.LastHeightConsensusParamsChanged = state.LastHeightConsensusParamsChanged 201 sm.LastResultsHash = state.LastResultsHash 202 sm.AppHash = state.AppHash 203 204 return sm, nil 205 } 206 207 // FromProto takes a state proto message & returns the local state type 208 func FromProto(pb *tmstate.State) (*State, error) { 209 if pb == nil { 210 return nil, errors.New("nil State") 211 } 212 213 state := new(State) 214 215 state.Version = VersionFromProto(pb.Version) 216 state.ChainID = pb.ChainID 217 state.InitialHeight = pb.InitialHeight 218 219 bi, err := types.BlockIDFromProto(&pb.LastBlockID) 220 if err != nil { 221 return nil, err 222 } 223 state.LastBlockID = *bi 224 state.LastBlockHeight = pb.LastBlockHeight 225 state.LastBlockTime = pb.LastBlockTime 226 227 vals, err := types.ValidatorSetFromProto(pb.Validators) 228 if err != nil { 229 return nil, err 230 } 231 state.Validators = vals 232 233 nVals, err := types.ValidatorSetFromProto(pb.NextValidators) 234 if err != nil { 235 return nil, err 236 } 237 state.NextValidators = nVals 238 239 if state.LastBlockHeight >= 1 { // At Block 1 LastValidators is nil 240 lVals, err := types.ValidatorSetFromProto(pb.LastValidators) 241 if err != nil { 242 return nil, err 243 } 244 state.LastValidators = lVals 245 } else { 246 state.LastValidators = types.NewValidatorSet(nil) 247 } 248 249 state.LastHeightValidatorsChanged = pb.LastHeightValidatorsChanged 250 state.ConsensusParams = types.ConsensusParamsFromProto(pb.ConsensusParams) 251 state.LastHeightConsensusParamsChanged = pb.LastHeightConsensusParamsChanged 252 state.LastResultsHash = pb.LastResultsHash 253 state.AppHash = pb.AppHash 254 255 return state, nil 256 } 257 258 //------------------------------------------------------------------------ 259 // Create a block from the latest state 260 261 // MakeBlock builds a block from the current state with the given txs, commit, 262 // and evidence. Note it also takes a proposerAddress because the state does not 263 // track rounds, and hence does not know the correct proposer. TODO: fix this! 264 func (state State) MakeBlock( 265 height int64, 266 txs []types.Tx, 267 commit *types.Commit, 268 evidence []types.Evidence, 269 proposerAddress []byte, 270 ) *types.Block { 271 272 // Build base block with block data. 273 block := types.MakeBlock(height, txs, commit, evidence) 274 275 // Fill rest of header with state data. 276 block.Header.Populate( 277 state.Version.Consensus, state.ChainID, 278 tmtime.Now(), state.LastBlockID, 279 state.Validators.Hash(), state.NextValidators.Hash(), 280 state.ConsensusParams.HashConsensusParams(), state.AppHash, state.LastResultsHash, 281 proposerAddress, 282 ) 283 284 return block 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: %w", err) 307 } 308 genDoc, err := types.GenesisDocFromJSON(genDocJSON) 309 if err != nil { 310 return nil, fmt.Errorf("error reading GenesisDoc: %w", 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 doc: %w", err) 320 } 321 322 var validatorSet, nextValidatorSet *types.ValidatorSet 323 if genDoc.Validators == nil || len(genDoc.Validators) == 0 { 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 }