github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/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/tendermint/tendermint/abci/types" 9 tmmath "github.com/tendermint/tendermint/libs/math" 10 tmos "github.com/tendermint/tendermint/libs/os" 11 "github.com/tendermint/tendermint/types" 12 ) 13 14 const ( 15 // persist validators every valSetCheckpointInterval blocks to avoid 16 // LoadValidators taking too much time. 17 // https://github.com/tendermint/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 db.SetSync(key, state.Bytes()) 115 } 116 117 //------------------------------------------------------------------------ 118 119 // ABCIResponses retains the responses 120 // of the various ABCI calls during block processing. 121 // It is persisted to disk for each height before calling Commit. 122 type ABCIResponses struct { 123 DeliverTx []*abci.ResponseDeliverTx `json:"deliver_tx"` 124 EndBlock *abci.ResponseEndBlock `json:"end_block"` 125 BeginBlock *abci.ResponseBeginBlock `json:"begin_block"` 126 } 127 128 // PruneStates deletes states between the given heights (including from, excluding to). It is not 129 // guaranteed to delete all states, since the last checkpointed state and states being pointed to by 130 // e.g. `LastHeightChanged` must remain. The state at to must also exist. 131 // 132 // The from parameter is necessary since we can't do a key scan in a performant way due to the key 133 // encoding not preserving ordering: https://github.com/tendermint/tendermint/issues/4567 134 // This will cause some old states to be left behind when doing incremental partial prunes, 135 // specifically older checkpoints and LastHeightChanged targets. 136 func PruneStates(db dbm.DB, from int64, to int64) error { 137 if from <= 0 || to <= 0 { 138 return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to) 139 } 140 if from >= to { 141 return fmt.Errorf("from height %v must be lower than to height %v", from, to) 142 } 143 valInfo := loadValidatorsInfo(db, to) 144 if valInfo == nil { 145 return fmt.Errorf("validators at height %v not found", to) 146 } 147 paramsInfo := loadConsensusParamsInfo(db, to) 148 if paramsInfo == nil { 149 return fmt.Errorf("consensus params at height %v not found", to) 150 } 151 152 keepVals := make(map[int64]bool) 153 if valInfo.ValidatorSet == nil { 154 keepVals[valInfo.LastHeightChanged] = true 155 keepVals[lastStoredHeightFor(to, valInfo.LastHeightChanged)] = true // keep last checkpoint too 156 } 157 keepParams := make(map[int64]bool) 158 if paramsInfo.ConsensusParams.Equals(&types.ConsensusParams{}) { 159 keepParams[paramsInfo.LastHeightChanged] = true 160 } 161 162 batch := db.NewBatch() 163 defer batch.Close() 164 pruned := uint64(0) 165 var err error 166 167 // We have to delete in reverse order, to avoid deleting previous heights that have validator 168 // sets and consensus params that we may need to retrieve. 169 for h := to - 1; h >= from; h-- { 170 // For heights we keep, we must make sure they have the full validator set or consensus 171 // params, otherwise they will panic if they're retrieved directly (instead of 172 // indirectly via a LastHeightChanged pointer). 173 if keepVals[h] { 174 v := loadValidatorsInfo(db, h) 175 if v.ValidatorSet == nil { 176 v.ValidatorSet, err = LoadValidators(db, h) 177 if err != nil { 178 return err 179 } 180 v.LastHeightChanged = h 181 batch.Set(calcValidatorsKey(h), v.Bytes()) 182 } 183 } else { 184 batch.Delete(calcValidatorsKey(h)) 185 } 186 187 if keepParams[h] { 188 p := loadConsensusParamsInfo(db, h) 189 if p.ConsensusParams.Equals(&types.ConsensusParams{}) { 190 p.ConsensusParams, err = LoadConsensusParams(db, h) 191 if err != nil { 192 return err 193 } 194 p.LastHeightChanged = h 195 batch.Set(calcConsensusParamsKey(h), p.Bytes()) 196 } 197 } else { 198 batch.Delete(calcConsensusParamsKey(h)) 199 } 200 201 batch.Delete(calcABCIResponsesKey(h)) 202 pruned++ 203 204 // avoid batches growing too large by flushing to database regularly 205 if pruned%1000 == 0 && pruned > 0 { 206 err := batch.Write() 207 if err != nil { 208 return err 209 } 210 batch.Close() 211 batch = db.NewBatch() 212 defer batch.Close() 213 } 214 } 215 216 err = batch.WriteSync() 217 if err != nil { 218 return err 219 } 220 221 return nil 222 } 223 224 // NewABCIResponses returns a new ABCIResponses 225 func NewABCIResponses(block *types.Block) *ABCIResponses { 226 resDeliverTxs := make([]*abci.ResponseDeliverTx, len(block.Data.Txs)) 227 if len(block.Data.Txs) == 0 { 228 // This makes Amino encoding/decoding consistent. 229 resDeliverTxs = nil 230 } 231 return &ABCIResponses{ 232 DeliverTx: resDeliverTxs, 233 } 234 } 235 236 // Bytes serializes the ABCIResponse using go-amino. 237 func (arz *ABCIResponses) Bytes() []byte { 238 return cdc.MustMarshalBinaryBare(arz) 239 } 240 241 func (arz *ABCIResponses) ResultsHash() []byte { 242 results := types.NewResults(arz.DeliverTx) 243 return results.Hash() 244 } 245 246 // LoadABCIResponses loads the ABCIResponses for the given height from the database. 247 // This is useful for recovering from crashes where we called app.Commit and before we called 248 // s.Save(). It can also be used to produce Merkle proofs of the result of txs. 249 func LoadABCIResponses(db dbm.DB, height int64) (*ABCIResponses, error) { 250 buf, err := db.Get(calcABCIResponsesKey(height)) 251 if err != nil { 252 return nil, err 253 } 254 if len(buf) == 0 { 255 return nil, ErrNoABCIResponsesForHeight{height} 256 } 257 258 abciResponses := new(ABCIResponses) 259 err = cdc.UnmarshalBinaryBare(buf, abciResponses) 260 if err != nil { 261 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 262 tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has 263 changed: %v\n`, err)) 264 } 265 // TODO: ensure that buf is completely read. 266 267 return abciResponses, nil 268 } 269 270 // SaveABCIResponses persists the ABCIResponses to the database. 271 // This is useful in case we crash after app.Commit and before s.Save(). 272 // Responses are indexed by height so they can also be loaded later to produce 273 // Merkle proofs. 274 // 275 // Exposed for testing. 276 func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) { 277 db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes()) 278 } 279 280 //----------------------------------------------------------------------------- 281 282 // ValidatorsInfo represents the latest validator set, or the last height it changed 283 type ValidatorsInfo struct { 284 ValidatorSet *types.ValidatorSet 285 LastHeightChanged int64 286 } 287 288 // Bytes serializes the ValidatorsInfo using go-amino. 289 func (valInfo *ValidatorsInfo) Bytes() []byte { 290 return cdc.MustMarshalBinaryBare(valInfo) 291 } 292 293 // LoadValidators loads the ValidatorSet for a given height. 294 // Returns ErrNoValSetForHeight if the validator set can't be found for this height. 295 func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) { 296 valInfo := loadValidatorsInfo(db, height) 297 if valInfo == nil { 298 return nil, ErrNoValSetForHeight{height} 299 } 300 if valInfo.ValidatorSet == nil { 301 lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged) 302 valInfo2 := loadValidatorsInfo(db, lastStoredHeight) 303 if valInfo2 == nil || valInfo2.ValidatorSet == nil { 304 panic( 305 fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)", 306 lastStoredHeight, 307 height, 308 ), 309 ) 310 } 311 valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate 312 valInfo = valInfo2 313 } 314 315 return valInfo.ValidatorSet, nil 316 } 317 318 func lastStoredHeightFor(height, lastHeightChanged int64) int64 { 319 checkpointHeight := height - height%valSetCheckpointInterval 320 return tmmath.MaxInt64(checkpointHeight, lastHeightChanged) 321 } 322 323 // CONTRACT: Returned ValidatorsInfo can be mutated. 324 func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { 325 buf, err := db.Get(calcValidatorsKey(height)) 326 if err != nil { 327 panic(err) 328 } 329 if len(buf) == 0 { 330 return nil 331 } 332 333 v := new(ValidatorsInfo) 334 err = cdc.UnmarshalBinaryBare(buf, v) 335 if err != nil { 336 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 337 tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed: 338 %v\n`, err)) 339 } 340 // TODO: ensure that buf is completely read. 341 342 return v 343 } 344 345 // saveValidatorsInfo persists the validator set. 346 // 347 // `height` is the effective height for which the validator is responsible for 348 // signing. It should be called from s.Save(), right before the state itself is 349 // persisted. 350 func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) { 351 if lastHeightChanged > height { 352 panic("LastHeightChanged cannot be greater than ValidatorsInfo height") 353 } 354 valInfo := &ValidatorsInfo{ 355 LastHeightChanged: lastHeightChanged, 356 } 357 // Only persist validator set if it was updated or checkpoint height (see 358 // valSetCheckpointInterval) is reached. 359 //if height == lastHeightChanged || height%valSetCheckpointInterval == 0 { TODO !!! removed so that we can rollback to any height 360 // valInfo.ValidatorSet = valSet 361 //} 362 valInfo.ValidatorSet = valSet 363 db.Set(calcValidatorsKey(height), valInfo.Bytes()) 364 } 365 366 //----------------------------------------------------------------------------- 367 368 // ConsensusParamsInfo represents the latest consensus params, or the last height it changed 369 type ConsensusParamsInfo struct { 370 ConsensusParams types.ConsensusParams 371 LastHeightChanged int64 372 } 373 374 // Bytes serializes the ConsensusParamsInfo using go-amino. 375 func (params ConsensusParamsInfo) Bytes() []byte { 376 return cdc.MustMarshalBinaryBare(params) 377 } 378 379 // LoadConsensusParams loads the ConsensusParams for a given height. 380 func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error) { 381 empty := types.ConsensusParams{} 382 383 paramsInfo := loadConsensusParamsInfo(db, height) 384 if paramsInfo == nil { 385 return empty, ErrNoConsensusParamsForHeight{height} 386 } 387 388 if paramsInfo.ConsensusParams.Equals(&empty) { 389 paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged) 390 if paramsInfo2 == nil { 391 panic( 392 fmt.Sprintf( 393 "Couldn't find consensus params at height %d as last changed from height %d", 394 paramsInfo.LastHeightChanged, 395 height, 396 ), 397 ) 398 } 399 paramsInfo = paramsInfo2 400 } 401 402 return paramsInfo.ConsensusParams, nil 403 } 404 405 func loadConsensusParamsInfo(db dbm.DB, height int64) *ConsensusParamsInfo { 406 buf, err := db.Get(calcConsensusParamsKey(height)) 407 if err != nil { 408 panic(err) 409 } 410 if len(buf) == 0 { 411 return nil 412 } 413 414 paramsInfo := new(ConsensusParamsInfo) 415 err = cdc.UnmarshalBinaryBare(buf, paramsInfo) 416 if err != nil { 417 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 418 tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed: 419 %v\n`, err)) 420 } 421 // TODO: ensure that buf is completely read. 422 423 return paramsInfo 424 } 425 426 // saveConsensusParamsInfo persists the consensus params for the next block to disk. 427 // It should be called from s.Save(), right before the state itself is persisted. 428 // If the consensus params did not change after processing the latest block, 429 // only the last height for which they changed is persisted. 430 func saveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) { 431 paramsInfo := &ConsensusParamsInfo{ 432 LastHeightChanged: changeHeight, 433 } 434 if changeHeight == nextHeight { 435 paramsInfo.ConsensusParams = params 436 } 437 db.Set(calcConsensusParamsKey(nextHeight), paramsInfo.Bytes()) 438 } 439 440 // --------------------------------------------------------------------------------------------------------------------- 441 // Added only for unsafe rollback feature 442 443 func LoadValidatorsChanged(db dbm.DB, height int64) int64 { 444 buf, _ := db.Get(calcValidatorsKey(height)) 445 if len(buf) == 0 { 446 return 0 447 } 448 449 v := new(ValidatorsInfo) 450 err := cdc.UnmarshalBinaryBare(buf, v) 451 if err != nil { 452 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 453 tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed: 454 %v\n`, err)) 455 } 456 // TODO: ensure that buf is completely read. 457 458 return v.LastHeightChanged 459 } 460 461 func LoadConsensusParamsChanged(db dbm.DB, height int64) int64 { 462 buf, _ := db.Get(calcConsensusParamsKey(height)) 463 if len(buf) == 0 { 464 return 0 465 } 466 467 paramsInfo := new(ConsensusParamsInfo) 468 err := cdc.UnmarshalBinaryBare(buf, paramsInfo) 469 if err != nil { 470 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 471 tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed: 472 %v\n`, err)) 473 } 474 // TODO: ensure that buf is completely read. 475 476 return paramsInfo.LastHeightChanged 477 } 478 func calSoftwareHeight(height int64) []byte { 479 return []byte(fmt.Sprintf("softwareKey:%v", height)) 480 } 481 482 func LoadSoftware(db dbm.DB, height int64) string { 483 softwareByte, _ := db.Get(calSoftwareHeight(height)) 484 return string(softwareByte) 485 }