github.com/okex/exchain@v1.8.0/libs/tendermint/state/state.go (about) 1 package state 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "time" 8 9 "github.com/okex/exchain/libs/tendermint/types" 10 tmtime "github.com/okex/exchain/libs/tendermint/types/time" 11 "github.com/okex/exchain/libs/tendermint/version" 12 ) 13 14 // database keys 15 var ( 16 stateKey = []byte("stateKey") 17 ) 18 19 //----------------------------------------------------------------------------- 20 21 // Version is for versioning the State. 22 // It holds the Block and App version needed for making blocks, 23 // and the software version to support upgrades to the format of 24 // the State as stored on disk. 25 type Version struct { 26 Consensus version.Consensus 27 Software string 28 } 29 30 // initStateVersion sets the Consensus.Block and Software versions, 31 // but leaves the Consensus.App version blank. 32 // The Consensus.App version will be set during the Handshake, once 33 // we hear from the app what protocol version it is running. 34 var initStateVersion = Version{ 35 Consensus: version.Consensus{ 36 Block: version.BlockProtocol, 37 App: 0, 38 }, 39 Software: version.TMCoreSemVer, 40 } 41 42 //----------------------------------------------------------------------------- 43 44 // State is a short description of the latest committed block of the Tendermint consensus. 45 // It keeps all information necessary to validate new blocks, 46 // including the last validator set and the consensus params. 47 // All fields are exposed so the struct can be easily serialized, 48 // but none of them should be mutated directly. 49 // Instead, use state.Copy() or state.NextState(...). 50 // NOTE: not goroutine-safe. 51 type State struct { 52 Version Version 53 54 // immutable 55 ChainID string 56 57 // LastBlockHeight=0 at genesis (ie. block(H=0) does not exist) 58 LastBlockHeight int64 59 LastBlockID types.BlockID 60 LastBlockTime time.Time 61 62 // LastValidators is used to validate block.LastCommit. 63 // Validators are persisted to the database separately every time they change, 64 // so we can query for historical validator sets. 65 // Note that if s.LastBlockHeight causes a valset change, 66 // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1 67 // Extra +1 due to nextValSet delay. 68 NextValidators *types.ValidatorSet 69 Validators *types.ValidatorSet 70 LastValidators *types.ValidatorSet 71 LastHeightValidatorsChanged int64 72 73 // Consensus parameters used for validating blocks. 74 // Changes returned by EndBlock and updated after Commit. 75 ConsensusParams types.ConsensusParams 76 LastHeightConsensusParamsChanged int64 77 78 // Merkle root of the results from executing prev block 79 LastResultsHash []byte 80 81 // the latest AppHash we've received from calling abci.Commit() 82 AppHash []byte 83 } 84 85 // Copy makes a copy of the State for mutating. 86 func (state State) Copy() State { 87 return State{ 88 Version: state.Version, 89 ChainID: state.ChainID, 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 go-amino. 116 func (state State) Bytes() []byte { 117 return cdc.MustMarshalBinaryBare(state) 118 } 119 120 // IsEmpty returns true if the State is equal to the empty State. 121 func (state State) IsEmpty() bool { 122 return state.Validators == nil // XXX can't compare to Empty 123 } 124 125 //------------------------------------------------------------------------ 126 // Create a block from the latest state 127 128 // MakeBlock builds a block from the current state with the given txs, commit, 129 // and evidence. Note it also takes a proposerAddress because the state does not 130 // track rounds, and hence does not know the correct proposer. TODO: fix this! 131 func (state State) MakeBlock( 132 height int64, 133 txs []types.Tx, 134 commit *types.Commit, 135 evidence []types.Evidence, 136 proposerAddress []byte, 137 ) (*types.Block, *types.PartSet) { 138 139 // Build base block with block data. 140 block := types.MakeBlock(height, txs, commit, evidence) 141 142 // Set time. 143 var timestamp time.Time 144 if height == types.GetStartBlockHeight()+1 { 145 timestamp = state.LastBlockTime // genesis time 146 } else { 147 timestamp = MedianTime(commit, state.LastValidators) 148 } 149 150 // Fill rest of header with state data. 151 block.Header.Populate( 152 state.Version.Consensus, state.ChainID, 153 timestamp, state.LastBlockID, 154 state.Validators.Hash(height), state.NextValidators.Hash(height+1), 155 state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash, 156 proposerAddress, 157 ) 158 159 return block, block.MakePartSet(types.BlockPartSizeBytes) 160 } 161 162 func (state *State) String() string { 163 return fmt.Sprintf("%+v", *state) 164 } 165 166 // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the 167 // corresponding validator set. The computed time is always between timestamps of 168 // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the 169 // computed value. 170 func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time { 171 weightedTimes := make([]*tmtime.WeightedTime, len(commit.Signatures)) 172 totalVotingPower := int64(0) 173 174 for i, commitSig := range commit.Signatures { 175 if commitSig.Absent() { 176 continue 177 } 178 _, validator := validators.GetByAddress(commitSig.ValidatorAddress) 179 // If there's no condition, TestValidateBlockCommit panics; not needed normally. 180 if validator != nil { 181 totalVotingPower += validator.VotingPower 182 weightedTimes[i] = tmtime.NewWeightedTime(commitSig.Timestamp, validator.VotingPower) 183 } 184 } 185 186 return tmtime.WeightedMedian(weightedTimes, totalVotingPower) 187 } 188 189 //------------------------------------------------------------------------ 190 // Genesis 191 192 // MakeGenesisStateFromFile reads and unmarshals state from the given 193 // file. 194 // 195 // Used during replay and in tests. 196 func MakeGenesisStateFromFile(genDocFile string) (State, error) { 197 genDoc, err := MakeGenesisDocFromFile(genDocFile) 198 if err != nil { 199 return State{}, err 200 } 201 return MakeGenesisState(genDoc) 202 } 203 204 // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file. 205 func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { 206 genDocJSON, err := ioutil.ReadFile(genDocFile) 207 if err != nil { 208 return nil, fmt.Errorf("couldn't read GenesisDoc file: %v", err) 209 } 210 genDoc, err := types.GenesisDocFromJSON(genDocJSON) 211 if err != nil { 212 return nil, fmt.Errorf("error reading GenesisDoc: %v", err) 213 } 214 return genDoc, nil 215 } 216 217 // MakeGenesisState creates state from types.GenesisDoc. 218 func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { 219 err := genDoc.ValidateAndComplete() 220 if err != nil { 221 return State{}, fmt.Errorf("error in genesis file: %v", err) 222 } 223 224 var validatorSet, nextValidatorSet *types.ValidatorSet 225 if genDoc.Validators == nil { 226 validatorSet = types.NewValidatorSet(nil) 227 nextValidatorSet = types.NewValidatorSet(nil) 228 } else { 229 validators := make([]*types.Validator, len(genDoc.Validators)) 230 for i, val := range genDoc.Validators { 231 validators[i] = types.NewValidator(val.PubKey, val.Power) 232 } 233 validatorSet = types.NewValidatorSet(validators) 234 nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1) 235 } 236 237 return State{ 238 Version: GetStateVersion(types.GetStartBlockHeight()), 239 ChainID: genDoc.ChainID, 240 241 LastBlockHeight: types.GetStartBlockHeight(), 242 LastBlockID: types.BlockID{}, 243 LastBlockTime: genDoc.GenesisTime, 244 245 NextValidators: nextValidatorSet, 246 Validators: validatorSet, 247 LastValidators: types.NewValidatorSet(nil), 248 LastHeightValidatorsChanged: types.GetStartBlockHeight() + 1, 249 250 ConsensusParams: *genDoc.ConsensusParams, 251 LastHeightConsensusParamsChanged: types.GetStartBlockHeight() + 1, 252 253 AppHash: genDoc.AppHash, 254 }, nil 255 }