github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/state/store.go (about) 1 package state 2 3 import ( 4 "fmt" 5 6 dbm "github.com/tendermint/tm-db" 7 8 abci "github.com/franono/tendermint/abci/types" 9 tmmath "github.com/franono/tendermint/libs/math" 10 tmos "github.com/franono/tendermint/libs/os" 11 "github.com/franono/tendermint/types" 12 ) 13 14 const ( 15 // persist validators every valSetCheckpointInterval blocks to avoid 16 // LoadValidators taking too much time. 17 // https://github.com/franono/tendermint/pull/3438 18 // 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators) 19 valSetCheckpointInterval = 100000 20 ) 21 22 //------------------------------------------------------------------------ 23 24 func calcValidatorsKey(height int64) []byte { 25 return []byte(fmt.Sprintf("validatorsKey:%v", height)) 26 } 27 28 func calcConsensusParamsKey(height int64) []byte { 29 return []byte(fmt.Sprintf("consensusParamsKey:%v", height)) 30 } 31 32 func calcABCIResponsesKey(height int64) []byte { 33 return []byte(fmt.Sprintf("abciResponsesKey:%v", height)) 34 } 35 36 // LoadStateFromDBOrGenesisFile loads the most recent state from the database, 37 // or creates a new one from the given genesisFilePath and persists the result 38 // to the database. 39 func LoadStateFromDBOrGenesisFile(stateDB dbm.DB, genesisFilePath string) (State, error) { 40 state := LoadState(stateDB) 41 if state.IsEmpty() { 42 var err error 43 state, err = MakeGenesisStateFromFile(genesisFilePath) 44 if err != nil { 45 return state, err 46 } 47 SaveState(stateDB, state) 48 } 49 50 return state, nil 51 } 52 53 // LoadStateFromDBOrGenesisDoc loads the most recent state from the database, 54 // or creates a new one from the given genesisDoc and persists the result 55 // to the database. 56 func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) (State, error) { 57 state := LoadState(stateDB) 58 if state.IsEmpty() { 59 var err error 60 state, err = MakeGenesisState(genesisDoc) 61 if err != nil { 62 return state, err 63 } 64 SaveState(stateDB, state) 65 } 66 67 return state, nil 68 } 69 70 // LoadState loads the State from the database. 71 func LoadState(db dbm.DB) State { 72 return loadState(db, stateKey) 73 } 74 75 func loadState(db dbm.DB, key []byte) (state State) { 76 buf, err := db.Get(key) 77 if err != nil { 78 panic(err) 79 } 80 if len(buf) == 0 { 81 return state 82 } 83 84 err = cdc.UnmarshalBinaryBare(buf, &state) 85 if err != nil { 86 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 87 tmos.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed: 88 %v\n`, err)) 89 } 90 // TODO: ensure that buf is completely read. 91 92 return state 93 } 94 95 // SaveState persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database. 96 // This flushes the writes (e.g. calls SetSync). 97 func SaveState(db dbm.DB, state State) { 98 saveState(db, state, stateKey) 99 } 100 101 func saveState(db dbm.DB, state State, key []byte) { 102 nextHeight := state.LastBlockHeight + 1 103 // If first block, save validators for block 1. 104 if nextHeight == 1 { 105 // This extra logic due to Tendermint validator set changes being delayed 1 block. 106 // It may get overwritten due to InitChain validator updates. 107 lastHeightVoteChanged := int64(1) 108 saveValidatorsInfo(db, nextHeight, lastHeightVoteChanged, state.Validators) 109 } 110 // Save next validators. 111 saveValidatorsInfo(db, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) 112 // Save next consensus params. 113 saveConsensusParamsInfo(db, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams) 114 err := db.SetSync(key, state.Bytes()) 115 if err != nil { 116 panic(err) 117 } 118 } 119 120 // BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height. 121 func BootstrapState(db dbm.DB, state State) error { 122 height := state.LastBlockHeight 123 saveValidatorsInfo(db, height, height, state.LastValidators) 124 saveValidatorsInfo(db, height+1, height+1, state.Validators) 125 saveValidatorsInfo(db, height+2, height+2, state.NextValidators) 126 saveConsensusParamsInfo(db, height+1, height+1, state.ConsensusParams) 127 return db.SetSync(stateKey, state.Bytes()) 128 } 129 130 //------------------------------------------------------------------------ 131 132 // ABCIResponses retains the responses 133 // of the various ABCI calls during block processing. 134 // It is persisted to disk for each height before calling Commit. 135 type ABCIResponses struct { 136 DeliverTxs []*abci.ResponseDeliverTx `json:"deliver_txs"` 137 EndBlock *abci.ResponseEndBlock `json:"end_block"` 138 BeginBlock *abci.ResponseBeginBlock `json:"begin_block"` 139 } 140 141 // PruneStates deletes states between the given heights (including from, excluding to). It is not 142 // guaranteed to delete all states, since the last checkpointed state and states being pointed to by 143 // e.g. `LastHeightChanged` must remain. The state at to must also exist. 144 // 145 // The from parameter is necessary since we can't do a key scan in a performant way due to the key 146 // encoding not preserving ordering: https://github.com/franono/tendermint/issues/4567 147 // This will cause some old states to be left behind when doing incremental partial prunes, 148 // specifically older checkpoints and LastHeightChanged targets. 149 func PruneStates(db dbm.DB, from int64, to int64) error { 150 if from <= 0 || to <= 0 { 151 return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to) 152 } 153 if from >= to { 154 return fmt.Errorf("from height %v must be lower than to height %v", from, to) 155 } 156 valInfo := loadValidatorsInfo(db, to) 157 if valInfo == nil { 158 return fmt.Errorf("validators at height %v not found", to) 159 } 160 paramsInfo := loadConsensusParamsInfo(db, to) 161 if paramsInfo == nil { 162 return fmt.Errorf("consensus params at height %v not found", to) 163 } 164 165 keepVals := make(map[int64]bool) 166 if valInfo.ValidatorSet == nil { 167 keepVals[valInfo.LastHeightChanged] = true 168 keepVals[lastStoredHeightFor(to, valInfo.LastHeightChanged)] = true // keep last checkpoint too 169 } 170 keepParams := make(map[int64]bool) 171 if paramsInfo.ConsensusParams.Equals(&types.ConsensusParams{}) { 172 keepParams[paramsInfo.LastHeightChanged] = true 173 } 174 175 batch := db.NewBatch() 176 defer batch.Close() 177 pruned := uint64(0) 178 var err error 179 180 // We have to delete in reverse order, to avoid deleting previous heights that have validator 181 // sets and consensus params that we may need to retrieve. 182 for h := to - 1; h >= from; h-- { 183 // For heights we keep, we must make sure they have the full validator set or consensus 184 // params, otherwise they will panic if they're retrieved directly (instead of 185 // indirectly via a LastHeightChanged pointer). 186 if keepVals[h] { 187 v := loadValidatorsInfo(db, h) 188 if v.ValidatorSet == nil { 189 v.ValidatorSet, err = LoadValidators(db, h) 190 if err != nil { 191 return err 192 } 193 v.LastHeightChanged = h 194 batch.Set(calcValidatorsKey(h), v.Bytes()) 195 } 196 } else { 197 batch.Delete(calcValidatorsKey(h)) 198 } 199 200 if keepParams[h] { 201 p := loadConsensusParamsInfo(db, h) 202 if p.ConsensusParams.Equals(&types.ConsensusParams{}) { 203 p.ConsensusParams, err = LoadConsensusParams(db, h) 204 if err != nil { 205 return err 206 } 207 p.LastHeightChanged = h 208 batch.Set(calcConsensusParamsKey(h), p.Bytes()) 209 } 210 } else { 211 batch.Delete(calcConsensusParamsKey(h)) 212 } 213 214 batch.Delete(calcABCIResponsesKey(h)) 215 pruned++ 216 217 // avoid batches growing too large by flushing to database regularly 218 if pruned%1000 == 0 && pruned > 0 { 219 err := batch.Write() 220 if err != nil { 221 return err 222 } 223 batch.Close() 224 batch = db.NewBatch() 225 defer batch.Close() 226 } 227 } 228 229 err = batch.WriteSync() 230 if err != nil { 231 return err 232 } 233 234 return nil 235 } 236 237 // NewABCIResponses returns a new ABCIResponses 238 func NewABCIResponses(block *types.Block) *ABCIResponses { 239 resDeliverTxs := make([]*abci.ResponseDeliverTx, len(block.Data.Txs)) 240 if len(block.Data.Txs) == 0 { 241 // This makes Amino encoding/decoding consistent. 242 resDeliverTxs = nil 243 } 244 return &ABCIResponses{ 245 DeliverTxs: resDeliverTxs, 246 } 247 } 248 249 // Bytes serializes the ABCIResponse using go-amino. 250 func (arz *ABCIResponses) Bytes() []byte { 251 return cdc.MustMarshalBinaryBare(arz) 252 } 253 254 func (arz *ABCIResponses) ResultsHash() []byte { 255 results := types.NewResults(arz.DeliverTxs) 256 return results.Hash() 257 } 258 259 // LoadABCIResponses loads the ABCIResponses for the given height from the database. 260 // This is useful for recovering from crashes where we called app.Commit and before we called 261 // s.Save(). It can also be used to produce Merkle proofs of the result of txs. 262 func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) { 263 buf, err := db.Get(calcABCIResponsesKey(height)) 264 if err != nil { 265 return nil, err 266 } 267 if len(buf) == 0 { 268 return nil, ErrNoABCIResponsesForHeight{height} 269 } 270 271 abciResponses := new(ABCIResponses) 272 err = cdc.UnmarshalBinaryBare(buf, abciResponses) 273 if err != nil { 274 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 275 tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has 276 changed: %v\n`, err)) 277 } 278 // TODO: ensure that buf is completely read. 279 280 return abciResponses, nil 281 } 282 283 // SaveABCIResponses persists the ABCIResponses to the database. 284 // This is useful in case we crash after app.Commit and before s.Save(). 285 // Responses are indexed by height so they can also be loaded later to produce 286 // Merkle proofs. 287 // 288 // Exposed for testing. 289 func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) { 290 db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes()) 291 } 292 293 //----------------------------------------------------------------------------- 294 295 // ValidatorsInfo represents the latest validator set, or the last height it changed 296 type ValidatorsInfo struct { 297 ValidatorSet *types.ValidatorSet 298 LastHeightChanged int64 299 } 300 301 // Bytes serializes the ValidatorsInfo using go-amino. 302 func (valInfo *ValidatorsInfo) Bytes() []byte { 303 return cdc.MustMarshalBinaryBare(valInfo) 304 } 305 306 // LoadValidators loads the ValidatorSet for a given height. 307 // Returns ErrNoValSetForHeight if the validator set can't be found for this height. 308 func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) { 309 valInfo := loadValidatorsInfo(db, height) 310 if valInfo == nil { 311 return nil, ErrNoValSetForHeight{height} 312 } 313 if valInfo.ValidatorSet == nil { 314 lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged) 315 valInfo2 := loadValidatorsInfo(db, lastStoredHeight) 316 if valInfo2 == nil || valInfo2.ValidatorSet == nil { 317 panic( 318 fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)", 319 lastStoredHeight, 320 height, 321 ), 322 ) 323 } 324 valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate 325 valInfo = valInfo2 326 } 327 328 return valInfo.ValidatorSet, nil 329 } 330 331 func lastStoredHeightFor(height, lastHeightChanged int64) int64 { 332 checkpointHeight := height - height%valSetCheckpointInterval 333 return tmmath.MaxInt64(checkpointHeight, lastHeightChanged) 334 } 335 336 // CONTRACT: Returned ValidatorsInfo can be mutated. 337 func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { 338 buf, err := db.Get(calcValidatorsKey(height)) 339 if err != nil { 340 panic(err) 341 } 342 if len(buf) == 0 { 343 return nil 344 } 345 346 v := new(ValidatorsInfo) 347 err = cdc.UnmarshalBinaryBare(buf, v) 348 if err != nil { 349 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 350 tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed: 351 %v\n`, err)) 352 } 353 // TODO: ensure that buf is completely read. 354 355 return v 356 } 357 358 // saveValidatorsInfo persists the validator set. 359 // 360 // `height` is the effective height for which the validator is responsible for 361 // signing. It should be called from s.Save(), right before the state itself is 362 // persisted. 363 func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) { 364 if lastHeightChanged > height { 365 panic("LastHeightChanged cannot be greater than ValidatorsInfo height") 366 } 367 valInfo := &ValidatorsInfo{ 368 LastHeightChanged: lastHeightChanged, 369 } 370 // Only persist validator set if it was updated or checkpoint height (see 371 // valSetCheckpointInterval) is reached. 372 if height == lastHeightChanged || height%valSetCheckpointInterval == 0 { 373 valInfo.ValidatorSet = valSet 374 } 375 db.Set(calcValidatorsKey(height), valInfo.Bytes()) 376 } 377 378 //----------------------------------------------------------------------------- 379 380 // ConsensusParamsInfo represents the latest consensus params, or the last height it changed 381 type ConsensusParamsInfo struct { 382 ConsensusParams types.ConsensusParams 383 LastHeightChanged int64 384 } 385 386 // Bytes serializes the ConsensusParamsInfo using go-amino. 387 func (params ConsensusParamsInfo) Bytes() []byte { 388 return cdc.MustMarshalBinaryBare(params) 389 } 390 391 // LoadConsensusParams loads the ConsensusParams for a given height. 392 func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) { 393 empty := types.ConsensusParams{} 394 395 paramsInfo := loadConsensusParamsInfo(db, height) 396 if paramsInfo == nil { 397 return empty, ErrNoConsensusParamsForHeight{height} 398 } 399 400 if paramsInfo.ConsensusParams.Equals(&empty) { 401 paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged) 402 if paramsInfo2 == nil { 403 panic( 404 fmt.Sprintf( 405 "Couldn't find consensus params at height %d as last changed from height %d", 406 paramsInfo.LastHeightChanged, 407 height, 408 ), 409 ) 410 } 411 paramsInfo = paramsInfo2 412 } 413 414 return paramsInfo.ConsensusParams, nil 415 } 416 417 func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { 418 buf, err := db.Get(calcConsensusParamsKey(height)) 419 if err != nil { 420 panic(err) 421 } 422 if len(buf) == 0 { 423 return nil 424 } 425 426 paramsInfo := new(ConsensusParamsInfo) 427 err = cdc.UnmarshalBinaryBare(buf, paramsInfo) 428 if err != nil { 429 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 430 tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed: 431 %v\n`, err)) 432 } 433 // TODO: ensure that buf is completely read. 434 435 return paramsInfo 436 } 437 438 // saveConsensusParamsInfo persists the consensus params for the next block to disk. 439 // It should be called from s.Save(), right before the state itself is persisted. 440 // If the consensus params did not change after processing the latest block, 441 // only the last height for which they changed is persisted. 442 func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) { 443 paramsInfo := &ConsensusParamsInfo{ 444 LastHeightChanged: changeHeight, 445 } 446 if changeHeight == nextHeight { 447 paramsInfo.ConsensusParams = params 448 } 449 db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes()) 450 }