github.com/evdatsion/aphelion-dpos-bft@v0.32.1/state/state.go (about) 1 package state 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "time" 8 9 "github.com/evdatsion/aphelion-dpos-bft/types" 10 tmtime "github.com/evdatsion/aphelion-dpos-bft/types/time" 11 "github.com/evdatsion/aphelion-dpos-bft/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 LastBlockTotalTx int64 60 LastBlockID types.BlockID 61 LastBlockTime time.Time 62 63 // LastValidators is used to validate block.LastCommit. 64 // Validators are persisted to the database separately every time they change, 65 // so we can query for historical validator sets. 66 // Note that if s.LastBlockHeight causes a valset change, 67 // we set s.LastHeightValidatorsChanged = s.LastBlockHeight + 1 + 1 68 // Extra +1 due to nextValSet delay. 69 NextValidators *types.ValidatorSet 70 Validators *types.ValidatorSet 71 LastValidators *types.ValidatorSet 72 LastHeightValidatorsChanged int64 73 74 // Consensus parameters used for validating blocks. 75 // Changes returned by EndBlock and updated after Commit. 76 ConsensusParams types.ConsensusParams 77 LastHeightConsensusParamsChanged int64 78 79 // Merkle root of the results from executing prev block 80 LastResultsHash []byte 81 82 // the latest AppHash we've received from calling abci.Commit() 83 AppHash []byte 84 } 85 86 // Copy makes a copy of the State for mutating. 87 func (state State) Copy() State { 88 return State{ 89 Version: state.Version, 90 ChainID: state.ChainID, 91 92 LastBlockHeight: state.LastBlockHeight, 93 LastBlockTotalTx: state.LastBlockTotalTx, 94 LastBlockID: state.LastBlockID, 95 LastBlockTime: state.LastBlockTime, 96 97 NextValidators: state.NextValidators.Copy(), 98 Validators: state.Validators.Copy(), 99 LastValidators: state.LastValidators.Copy(), 100 LastHeightValidatorsChanged: state.LastHeightValidatorsChanged, 101 102 ConsensusParams: state.ConsensusParams, 103 LastHeightConsensusParamsChanged: state.LastHeightConsensusParamsChanged, 104 105 AppHash: state.AppHash, 106 107 LastResultsHash: state.LastResultsHash, 108 } 109 } 110 111 // Equals returns true if the States are identical. 112 func (state State) Equals(state2 State) bool { 113 sbz, s2bz := state.Bytes(), state2.Bytes() 114 return bytes.Equal(sbz, s2bz) 115 } 116 117 // Bytes serializes the State using go-amino. 118 func (state State) Bytes() []byte { 119 return cdc.MustMarshalBinaryBare(state) 120 } 121 122 // IsEmpty returns true if the State is equal to the empty State. 123 func (state State) IsEmpty() bool { 124 return state.Validators == nil // XXX can't compare to Empty 125 } 126 127 //------------------------------------------------------------------------ 128 // Create a block from the latest state 129 130 // MakeBlock builds a block from the current state with the given txs, commit, 131 // and evidence. Note it also takes a proposerAddress because the state does not 132 // track rounds, and hence does not know the correct proposer. TODO: fix this! 133 func (state State) MakeBlock( 134 height int64, 135 txs []types.Tx, 136 commit *types.Commit, 137 evidence []types.Evidence, 138 proposerAddress []byte, 139 ) (*types.Block, *types.PartSet) { 140 141 // Build base block with block data. 142 block := types.MakeBlock(height, txs, commit, evidence) 143 144 // Set time. 145 var timestamp time.Time 146 if height == 1 { 147 timestamp = state.LastBlockTime // genesis time 148 } else { 149 timestamp = MedianTime(commit, state.LastValidators) 150 } 151 152 // Fill rest of header with state data. 153 block.Header.Populate( 154 state.Version.Consensus, state.ChainID, 155 timestamp, state.LastBlockID, state.LastBlockTotalTx+block.NumTxs, 156 state.Validators.Hash(), state.NextValidators.Hash(), 157 state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash, 158 proposerAddress, 159 ) 160 161 return block, block.MakePartSet(types.BlockPartSizeBytes) 162 } 163 164 // MedianTime computes a median time for a given Commit (based on Timestamp field of votes messages) and the 165 // corresponding validator set. The computed time is always between timestamps of 166 // the votes sent by honest processes, i.e., a faulty processes can not arbitrarily increase or decrease the 167 // computed value. 168 func MedianTime(commit *types.Commit, validators *types.ValidatorSet) time.Time { 169 170 weightedTimes := make([]*tmtime.WeightedTime, len(commit.Precommits)) 171 totalVotingPower := int64(0) 172 173 for i, vote := range commit.Precommits { 174 if vote != nil { 175 _, validator := validators.GetByIndex(vote.ValidatorIndex) 176 totalVotingPower += validator.VotingPower 177 weightedTimes[i] = tmtime.NewWeightedTime(vote.Timestamp, validator.VotingPower) 178 } 179 } 180 181 return tmtime.WeightedMedian(weightedTimes, totalVotingPower) 182 } 183 184 //------------------------------------------------------------------------ 185 // Genesis 186 187 // MakeGenesisStateFromFile reads and unmarshals state from the given 188 // file. 189 // 190 // Used during replay and in tests. 191 func MakeGenesisStateFromFile(genDocFile string) (State, error) { 192 genDoc, err := MakeGenesisDocFromFile(genDocFile) 193 if err != nil { 194 return State{}, err 195 } 196 return MakeGenesisState(genDoc) 197 } 198 199 // MakeGenesisDocFromFile reads and unmarshals genesis doc from the given file. 200 func MakeGenesisDocFromFile(genDocFile string) (*types.GenesisDoc, error) { 201 genDocJSON, err := ioutil.ReadFile(genDocFile) 202 if err != nil { 203 return nil, fmt.Errorf("Couldn't read GenesisDoc file: %v", err) 204 } 205 genDoc, err := types.GenesisDocFromJSON(genDocJSON) 206 if err != nil { 207 return nil, fmt.Errorf("Error reading GenesisDoc: %v", err) 208 } 209 return genDoc, nil 210 } 211 212 // MakeGenesisState creates state from types.GenesisDoc. 213 func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) { 214 err := genDoc.ValidateAndComplete() 215 if err != nil { 216 return State{}, fmt.Errorf("Error in genesis file: %v", err) 217 } 218 219 var validatorSet, nextValidatorSet *types.ValidatorSet 220 if genDoc.Validators == nil { 221 validatorSet = types.NewValidatorSet(nil) 222 nextValidatorSet = types.NewValidatorSet(nil) 223 } else { 224 validators := make([]*types.Validator, len(genDoc.Validators)) 225 for i, val := range genDoc.Validators { 226 validators[i] = types.NewValidator(val.PubKey, val.Power) 227 } 228 validatorSet = types.NewValidatorSet(validators) 229 nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementProposerPriority(1) 230 } 231 232 return State{ 233 Version: initStateVersion, 234 ChainID: genDoc.ChainID, 235 236 LastBlockHeight: 0, 237 LastBlockID: types.BlockID{}, 238 LastBlockTime: genDoc.GenesisTime, 239 240 NextValidators: nextValidatorSet, 241 Validators: validatorSet, 242 LastValidators: types.NewValidatorSet(nil), 243 LastHeightValidatorsChanged: 1, 244 245 ConsensusParams: *genDoc.ConsensusParams, 246 LastHeightConsensusParamsChanged: 1, 247 248 AppHash: genDoc.AppHash, 249 }, nil 250 }