github.com/MagHErmit/tendermint@v0.282.1/state/store.go (about) 1 package state 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/gogo/protobuf/proto" 8 dbm "github.com/tendermint/tm-db" 9 10 abci "github.com/MagHErmit/tendermint/abci/types" 11 tmmath "github.com/MagHErmit/tendermint/libs/math" 12 tmos "github.com/MagHErmit/tendermint/libs/os" 13 tmstate "github.com/MagHErmit/tendermint/proto/tendermint/state" 14 tmproto "github.com/MagHErmit/tendermint/proto/tendermint/types" 15 "github.com/MagHErmit/tendermint/types" 16 ) 17 18 const ( 19 // persist validators every valSetCheckpointInterval blocks to avoid 20 // LoadValidators taking too much time. 21 // https://github.com/MagHErmit/tendermint/pull/3438 22 // 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators) 23 valSetCheckpointInterval = 100000 24 ) 25 26 //------------------------------------------------------------------------ 27 28 func calcValidatorsKey(height int64) []byte { 29 return []byte(fmt.Sprintf("validatorsKey:%v", height)) 30 } 31 32 func calcConsensusParamsKey(height int64) []byte { 33 return []byte(fmt.Sprintf("consensusParamsKey:%v", height)) 34 } 35 36 func calcABCIResponsesKey(height int64) []byte { 37 return []byte(fmt.Sprintf("abciResponsesKey:%v", height)) 38 } 39 40 //---------------------- 41 42 var ( 43 lastABCIResponseKey = []byte("lastABCIResponseKey") 44 ) 45 46 //go:generate ../scripts/mockery_generate.sh Store 47 48 // Store defines the state store interface 49 // 50 // It is used to retrieve current state and save and load ABCI responses, 51 // validators and consensus parameters 52 type Store interface { 53 // LoadFromDBOrGenesisFile loads the most recent state. 54 // If the chain is new it will use the genesis file from the provided genesis file path as the current state. 55 LoadFromDBOrGenesisFile(string) (State, error) 56 // LoadFromDBOrGenesisDoc loads the most recent state. 57 // If the chain is new it will use the genesis doc as the current state. 58 LoadFromDBOrGenesisDoc(*types.GenesisDoc) (State, error) 59 // Load loads the current state of the blockchain 60 Load() (State, error) 61 // LoadValidators loads the validator set at a given height 62 LoadValidators(int64) (*types.ValidatorSet, error) 63 // LoadABCIResponses loads the abciResponse for a given height 64 LoadABCIResponses(int64) (*tmstate.ABCIResponses, error) 65 // LoadLastABCIResponse loads the last abciResponse for a given height 66 LoadLastABCIResponse(int64) (*tmstate.ABCIResponses, error) 67 // LoadConsensusParams loads the consensus params for a given height 68 LoadConsensusParams(int64) (tmproto.ConsensusParams, error) 69 // Save overwrites the previous state with the updated one 70 Save(State) error 71 // SaveABCIResponses saves ABCIResponses for a given height 72 SaveABCIResponses(int64, *tmstate.ABCIResponses) error 73 // Bootstrap is used for bootstrapping state when not starting from a initial height. 74 Bootstrap(State) error 75 // PruneStates takes the height from which to start prning and which height stop at 76 PruneStates(int64, int64) error 77 // Close closes the connection with the database 78 Close() error 79 } 80 81 // dbStore wraps a db (github.com/tendermint/tm-db) 82 type dbStore struct { 83 db dbm.DB 84 85 StoreOptions 86 } 87 88 type StoreOptions struct { 89 90 // DiscardABCIResponses determines whether or not the store 91 // retains all ABCIResponses. If DiscardABCiResponses is enabled, 92 // the store will maintain only the response object from the latest 93 // height. 94 DiscardABCIResponses bool 95 } 96 97 var _ Store = (*dbStore)(nil) 98 99 // NewStore creates the dbStore of the state pkg. 100 func NewStore(db dbm.DB, options StoreOptions) Store { 101 return dbStore{db, options} 102 } 103 104 // LoadStateFromDBOrGenesisFile loads the most recent state from the database, 105 // or creates a new one from the given genesisFilePath. 106 func (store dbStore) LoadFromDBOrGenesisFile(genesisFilePath string) (State, error) { 107 state, err := store.Load() 108 if err != nil { 109 return State{}, err 110 } 111 if state.IsEmpty() { 112 var err error 113 state, err = MakeGenesisStateFromFile(genesisFilePath) 114 if err != nil { 115 return state, err 116 } 117 } 118 119 return state, nil 120 } 121 122 // LoadStateFromDBOrGenesisDoc loads the most recent state from the database, 123 // or creates a new one from the given genesisDoc. 124 func (store dbStore) LoadFromDBOrGenesisDoc(genesisDoc *types.GenesisDoc) (State, error) { 125 state, err := store.Load() 126 if err != nil { 127 return State{}, err 128 } 129 130 if state.IsEmpty() { 131 var err error 132 state, err = MakeGenesisState(genesisDoc) 133 if err != nil { 134 return state, err 135 } 136 } 137 138 return state, nil 139 } 140 141 // LoadState loads the State from the database. 142 func (store dbStore) Load() (State, error) { 143 return store.loadState(stateKey) 144 } 145 146 func (store dbStore) loadState(key []byte) (state State, err error) { 147 buf, err := store.db.Get(key) 148 if err != nil { 149 return state, err 150 } 151 if len(buf) == 0 { 152 return state, nil 153 } 154 155 sp := new(tmstate.State) 156 157 err = proto.Unmarshal(buf, sp) 158 if err != nil { 159 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 160 tmos.Exit(fmt.Sprintf(`LoadState: Data has been corrupted or its spec has changed: 161 %v\n`, err)) 162 } 163 164 sm, err := FromProto(sp) 165 if err != nil { 166 return state, err 167 } 168 169 return *sm, nil 170 } 171 172 // Save persists the State, the ValidatorsInfo, and the ConsensusParamsInfo to the database. 173 // This flushes the writes (e.g. calls SetSync). 174 func (store dbStore) Save(state State) error { 175 return store.save(state, stateKey) 176 } 177 178 func (store dbStore) save(state State, key []byte) error { 179 nextHeight := state.LastBlockHeight + 1 180 // If first block, save validators for the block. 181 if nextHeight == 1 { 182 nextHeight = state.InitialHeight 183 // This extra logic due to Tendermint validator set changes being delayed 1 block. 184 // It may get overwritten due to InitChain validator updates. 185 if err := store.saveValidatorsInfo(nextHeight, nextHeight, state.Validators); err != nil { 186 return err 187 } 188 } 189 // Save next validators. 190 if err := store.saveValidatorsInfo(nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators); err != nil { 191 return err 192 } 193 194 // Save next consensus params. 195 if err := store.saveConsensusParamsInfo(nextHeight, 196 state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil { 197 return err 198 } 199 err := store.db.SetSync(key, state.Bytes()) 200 if err != nil { 201 return err 202 } 203 return nil 204 } 205 206 // BootstrapState saves a new state, used e.g. by state sync when starting from non-zero height. 207 func (store dbStore) Bootstrap(state State) error { 208 height := state.LastBlockHeight + 1 209 if height == 1 { 210 height = state.InitialHeight 211 } 212 213 if height > 1 && !state.LastValidators.IsNilOrEmpty() { 214 if err := store.saveValidatorsInfo(height-1, height-1, state.LastValidators); err != nil { 215 return err 216 } 217 } 218 219 if err := store.saveValidatorsInfo(height, height, state.Validators); err != nil { 220 return err 221 } 222 223 if err := store.saveValidatorsInfo(height+1, height+1, state.NextValidators); err != nil { 224 return err 225 } 226 227 if err := store.saveConsensusParamsInfo(height, 228 state.LastHeightConsensusParamsChanged, state.ConsensusParams); err != nil { 229 return err 230 } 231 232 return store.db.SetSync(stateKey, state.Bytes()) 233 } 234 235 // PruneStates deletes states between the given heights (including from, excluding to). It is not 236 // guaranteed to delete all states, since the last checkpointed state and states being pointed to by 237 // e.g. `LastHeightChanged` must remain. The state at to must also exist. 238 // 239 // The from parameter is necessary since we can't do a key scan in a performant way due to the key 240 // encoding not preserving ordering: https://github.com/MagHErmit/tendermint/issues/4567 241 // This will cause some old states to be left behind when doing incremental partial prunes, 242 // specifically older checkpoints and LastHeightChanged targets. 243 func (store dbStore) PruneStates(from int64, to int64) error { 244 if from <= 0 || to <= 0 { 245 return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to) 246 } 247 if from >= to { 248 return fmt.Errorf("from height %v must be lower than to height %v", from, to) 249 } 250 valInfo, err := loadValidatorsInfo(store.db, to) 251 if err != nil { 252 return fmt.Errorf("validators at height %v not found: %w", to, err) 253 } 254 paramsInfo, err := store.loadConsensusParamsInfo(to) 255 if err != nil { 256 return fmt.Errorf("consensus params at height %v not found: %w", to, err) 257 } 258 259 keepVals := make(map[int64]bool) 260 if valInfo.ValidatorSet == nil { 261 keepVals[valInfo.LastHeightChanged] = true 262 keepVals[lastStoredHeightFor(to, valInfo.LastHeightChanged)] = true // keep last checkpoint too 263 } 264 keepParams := make(map[int64]bool) 265 if paramsInfo.ConsensusParams.Equal(&tmproto.ConsensusParams{}) { 266 keepParams[paramsInfo.LastHeightChanged] = true 267 } 268 269 batch := store.db.NewBatch() 270 defer batch.Close() 271 pruned := uint64(0) 272 273 // We have to delete in reverse order, to avoid deleting previous heights that have validator 274 // sets and consensus params that we may need to retrieve. 275 for h := to - 1; h >= from; h-- { 276 // For heights we keep, we must make sure they have the full validator set or consensus 277 // params, otherwise they will panic if they're retrieved directly (instead of 278 // indirectly via a LastHeightChanged pointer). 279 if keepVals[h] { 280 v, err := loadValidatorsInfo(store.db, h) 281 if err != nil || v.ValidatorSet == nil { 282 vip, err := store.LoadValidators(h) 283 if err != nil { 284 return err 285 } 286 287 pvi, err := vip.ToProto() 288 if err != nil { 289 return err 290 } 291 292 v.ValidatorSet = pvi 293 v.LastHeightChanged = h 294 295 bz, err := v.Marshal() 296 if err != nil { 297 return err 298 } 299 err = batch.Set(calcValidatorsKey(h), bz) 300 if err != nil { 301 return err 302 } 303 } 304 } else { 305 err = batch.Delete(calcValidatorsKey(h)) 306 if err != nil { 307 return err 308 } 309 } 310 311 if keepParams[h] { 312 p, err := store.loadConsensusParamsInfo(h) 313 if err != nil { 314 return err 315 } 316 317 if p.ConsensusParams.Equal(&tmproto.ConsensusParams{}) { 318 p.ConsensusParams, err = store.LoadConsensusParams(h) 319 if err != nil { 320 return err 321 } 322 323 p.LastHeightChanged = h 324 bz, err := p.Marshal() 325 if err != nil { 326 return err 327 } 328 329 err = batch.Set(calcConsensusParamsKey(h), bz) 330 if err != nil { 331 return err 332 } 333 } 334 } else { 335 err = batch.Delete(calcConsensusParamsKey(h)) 336 if err != nil { 337 return err 338 } 339 } 340 341 err = batch.Delete(calcABCIResponsesKey(h)) 342 if err != nil { 343 return err 344 } 345 pruned++ 346 347 // avoid batches growing too large by flushing to database regularly 348 if pruned%1000 == 0 && pruned > 0 { 349 err := batch.Write() 350 if err != nil { 351 return err 352 } 353 batch.Close() 354 batch = store.db.NewBatch() 355 defer batch.Close() 356 } 357 } 358 359 err = batch.WriteSync() 360 if err != nil { 361 return err 362 } 363 364 return nil 365 } 366 367 //------------------------------------------------------------------------ 368 369 // ABCIResponsesResultsHash returns the root hash of a Merkle tree of 370 // ResponseDeliverTx responses (see ABCIResults.Hash) 371 // 372 // See merkle.SimpleHashFromByteSlices 373 func ABCIResponsesResultsHash(ar *tmstate.ABCIResponses) []byte { 374 return types.NewResults(ar.DeliverTxs).Hash() 375 } 376 377 // LoadABCIResponses loads the ABCIResponses for the given height from the 378 // database. If the node has DiscardABCIResponses set to true, ErrABCIResponsesNotPersisted 379 // is persisted. If not found, ErrNoABCIResponsesForHeight is returned. 380 func (store dbStore) LoadABCIResponses(height int64) (*tmstate.ABCIResponses, error) { 381 if store.DiscardABCIResponses { 382 return nil, ErrABCIResponsesNotPersisted 383 } 384 385 buf, err := store.db.Get(calcABCIResponsesKey(height)) 386 if err != nil { 387 return nil, err 388 } 389 if len(buf) == 0 { 390 391 return nil, ErrNoABCIResponsesForHeight{height} 392 } 393 394 abciResponses := new(tmstate.ABCIResponses) 395 err = abciResponses.Unmarshal(buf) 396 if err != nil { 397 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 398 tmos.Exit(fmt.Sprintf(`LoadABCIResponses: Data has been corrupted or its spec has 399 changed: %v\n`, err)) 400 } 401 // TODO: ensure that buf is completely read. 402 403 return abciResponses, nil 404 } 405 406 // LoadLastABCIResponses loads the ABCIResponses from the most recent height. 407 // The height parameter is used to ensure that the response corresponds to the latest height. 408 // If not, an error is returned. 409 // 410 // This method is used for recovering in the case that we called the Commit ABCI 411 // method on the application but crashed before persisting the results. 412 func (store dbStore) LoadLastABCIResponse(height int64) (*tmstate.ABCIResponses, error) { 413 bz, err := store.db.Get(lastABCIResponseKey) 414 if err != nil { 415 return nil, err 416 } 417 418 if len(bz) == 0 { 419 return nil, errors.New("no last ABCI response has been persisted") 420 } 421 422 abciResponse := new(tmstate.ABCIResponsesInfo) 423 err = abciResponse.Unmarshal(bz) 424 if err != nil { 425 tmos.Exit(fmt.Sprintf(`LoadLastABCIResponses: Data has been corrupted or its spec has 426 changed: %v\n`, err)) 427 } 428 429 // Here we validate the result by comparing its height to the expected height. 430 if height != abciResponse.GetHeight() { 431 return nil, errors.New("expected height %d but last stored abci responses was at height %d") 432 } 433 434 return abciResponse.AbciResponses, nil 435 } 436 437 // SaveABCIResponses persists the ABCIResponses to the database. 438 // This is useful in case we crash after app.Commit and before s.Save(). 439 // Responses are indexed by height so they can also be loaded later to produce 440 // Merkle proofs. 441 // 442 // CONTRACT: height must be monotonically increasing every time this is called. 443 func (store dbStore) SaveABCIResponses(height int64, abciResponses *tmstate.ABCIResponses) error { 444 var dtxs []*abci.ResponseDeliverTx 445 // strip nil values, 446 for _, tx := range abciResponses.DeliverTxs { 447 if tx != nil { 448 dtxs = append(dtxs, tx) 449 } 450 } 451 abciResponses.DeliverTxs = dtxs 452 453 // If the flag is false then we save the ABCIResponse. This can be used for the /BlockResults 454 // query or to reindex an event using the command line. 455 if !store.DiscardABCIResponses { 456 bz, err := abciResponses.Marshal() 457 if err != nil { 458 return err 459 } 460 if err := store.db.Set(calcABCIResponsesKey(height), bz); err != nil { 461 return err 462 } 463 } 464 465 // We always save the last ABCI response for crash recovery. 466 // This overwrites the previous saved ABCI Response. 467 response := &tmstate.ABCIResponsesInfo{ 468 AbciResponses: abciResponses, 469 Height: height, 470 } 471 bz, err := response.Marshal() 472 if err != nil { 473 return err 474 } 475 476 return store.db.SetSync(lastABCIResponseKey, bz) 477 } 478 479 //----------------------------------------------------------------------------- 480 481 // LoadValidators loads the ValidatorSet for a given height. 482 // Returns ErrNoValSetForHeight if the validator set can't be found for this height. 483 func (store dbStore) LoadValidators(height int64) (*types.ValidatorSet, error) { 484 valInfo, err := loadValidatorsInfo(store.db, height) 485 if err != nil { 486 return nil, ErrNoValSetForHeight{height} 487 } 488 if valInfo.ValidatorSet == nil { 489 lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged) 490 valInfo2, err := loadValidatorsInfo(store.db, lastStoredHeight) 491 if err != nil || valInfo2.ValidatorSet == nil { 492 return nil, 493 fmt.Errorf("couldn't find validators at height %d (height %d was originally requested): %w", 494 lastStoredHeight, 495 height, 496 err, 497 ) 498 } 499 500 vs, err := types.ValidatorSetFromProto(valInfo2.ValidatorSet) 501 if err != nil { 502 return nil, err 503 } 504 505 vs.IncrementProposerPriority(tmmath.SafeConvertInt32(height - lastStoredHeight)) // mutate 506 vi2, err := vs.ToProto() 507 if err != nil { 508 return nil, err 509 } 510 511 valInfo2.ValidatorSet = vi2 512 valInfo = valInfo2 513 } 514 515 vip, err := types.ValidatorSetFromProto(valInfo.ValidatorSet) 516 if err != nil { 517 return nil, err 518 } 519 520 return vip, nil 521 } 522 523 func lastStoredHeightFor(height, lastHeightChanged int64) int64 { 524 checkpointHeight := height - height%valSetCheckpointInterval 525 return tmmath.MaxInt64(checkpointHeight, lastHeightChanged) 526 } 527 528 // CONTRACT: Returned ValidatorsInfo can be mutated. 529 func loadValidatorsInfo(db dbm.DB, height int64) (*tmstate.ValidatorsInfo, error) { 530 buf, err := db.Get(calcValidatorsKey(height)) 531 if err != nil { 532 return nil, err 533 } 534 535 if len(buf) == 0 { 536 return nil, errors.New("value retrieved from db is empty") 537 } 538 539 v := new(tmstate.ValidatorsInfo) 540 err = v.Unmarshal(buf) 541 if err != nil { 542 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 543 tmos.Exit(fmt.Sprintf(`LoadValidators: Data has been corrupted or its spec has changed: 544 %v\n`, err)) 545 } 546 // TODO: ensure that buf is completely read. 547 548 return v, nil 549 } 550 551 // saveValidatorsInfo persists the validator set. 552 // 553 // `height` is the effective height for which the validator is responsible for 554 // signing. It should be called from s.Save(), right before the state itself is 555 // persisted. 556 func (store dbStore) saveValidatorsInfo(height, lastHeightChanged int64, valSet *types.ValidatorSet) error { 557 if lastHeightChanged > height { 558 return errors.New("lastHeightChanged cannot be greater than ValidatorsInfo height") 559 } 560 valInfo := &tmstate.ValidatorsInfo{ 561 LastHeightChanged: lastHeightChanged, 562 } 563 // Only persist validator set if it was updated or checkpoint height (see 564 // valSetCheckpointInterval) is reached. 565 if height == lastHeightChanged || height%valSetCheckpointInterval == 0 { 566 pv, err := valSet.ToProto() 567 if err != nil { 568 return err 569 } 570 valInfo.ValidatorSet = pv 571 } 572 573 bz, err := valInfo.Marshal() 574 if err != nil { 575 return err 576 } 577 578 err = store.db.Set(calcValidatorsKey(height), bz) 579 if err != nil { 580 return err 581 } 582 583 return nil 584 } 585 586 //----------------------------------------------------------------------------- 587 588 // ConsensusParamsInfo represents the latest consensus params, or the last height it changed 589 590 // LoadConsensusParams loads the ConsensusParams for a given height. 591 func (store dbStore) LoadConsensusParams(height int64) (tmproto.ConsensusParams, error) { 592 empty := tmproto.ConsensusParams{} 593 594 paramsInfo, err := store.loadConsensusParamsInfo(height) 595 if err != nil { 596 return empty, fmt.Errorf("could not find consensus params for height #%d: %w", height, err) 597 } 598 599 if paramsInfo.ConsensusParams.Equal(&empty) { 600 paramsInfo2, err := store.loadConsensusParamsInfo(paramsInfo.LastHeightChanged) 601 if err != nil { 602 return empty, fmt.Errorf( 603 "couldn't find consensus params at height %d as last changed from height %d: %w", 604 paramsInfo.LastHeightChanged, 605 height, 606 err, 607 ) 608 } 609 610 paramsInfo = paramsInfo2 611 } 612 613 return paramsInfo.ConsensusParams, nil 614 } 615 616 func (store dbStore) loadConsensusParamsInfo(height int64) (*tmstate.ConsensusParamsInfo, error) { 617 buf, err := store.db.Get(calcConsensusParamsKey(height)) 618 if err != nil { 619 return nil, err 620 } 621 if len(buf) == 0 { 622 return nil, errors.New("value retrieved from db is empty") 623 } 624 625 paramsInfo := new(tmstate.ConsensusParamsInfo) 626 if err = paramsInfo.Unmarshal(buf); err != nil { 627 // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED 628 tmos.Exit(fmt.Sprintf(`LoadConsensusParams: Data has been corrupted or its spec has changed: 629 %v\n`, err)) 630 } 631 // TODO: ensure that buf is completely read. 632 633 return paramsInfo, nil 634 } 635 636 // saveConsensusParamsInfo persists the consensus params for the next block to disk. 637 // It should be called from s.Save(), right before the state itself is persisted. 638 // If the consensus params did not change after processing the latest block, 639 // only the last height for which they changed is persisted. 640 func (store dbStore) saveConsensusParamsInfo(nextHeight, changeHeight int64, params tmproto.ConsensusParams) error { 641 paramsInfo := &tmstate.ConsensusParamsInfo{ 642 LastHeightChanged: changeHeight, 643 } 644 645 if changeHeight == nextHeight { 646 paramsInfo.ConsensusParams = params 647 } 648 bz, err := paramsInfo.Marshal() 649 if err != nil { 650 return err 651 } 652 653 err = store.db.Set(calcConsensusParamsKey(nextHeight), bz) 654 if err != nil { 655 return err 656 } 657 658 return nil 659 } 660 661 func (store dbStore) Close() error { 662 return store.db.Close() 663 }