You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

104 lines
3.4 KiB

  1. package state
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/tendermint/tendermint/version"
  6. )
  7. // Rollback overwrites the current Tendermint state (height n) with the most
  8. // recent previous state (height n - 1).
  9. // Note that this function does not affect application state.
  10. func Rollback(bs BlockStore, ss Store) (int64, []byte, error) {
  11. invalidState, err := ss.Load()
  12. if err != nil {
  13. return -1, nil, err
  14. }
  15. if invalidState.IsEmpty() {
  16. return -1, nil, errors.New("no state found")
  17. }
  18. height := bs.Height()
  19. // NOTE: persistence of state and blocks don't happen atomically. Therefore it is possible that
  20. // when the user stopped the node the state wasn't updated but the blockstore was. In this situation
  21. // we don't need to rollback any state and can just return early
  22. if height == invalidState.LastBlockHeight+1 {
  23. return invalidState.LastBlockHeight, invalidState.AppHash, nil
  24. }
  25. // If the state store isn't one below nor equal to the blockstore height than this violates the
  26. // invariant
  27. if height != invalidState.LastBlockHeight {
  28. return -1, nil, fmt.Errorf("statestore height (%d) is not one below or equal to blockstore height (%d)",
  29. invalidState.LastBlockHeight, height)
  30. }
  31. // state store height is equal to blockstore height. We're good to proceed with rolling back state
  32. rollbackHeight := invalidState.LastBlockHeight
  33. rollbackBlock := bs.LoadBlockMeta(rollbackHeight)
  34. if rollbackBlock == nil {
  35. return -1, nil, fmt.Errorf("block at height %d not found", rollbackHeight)
  36. }
  37. previousValidatorSet, err := ss.LoadValidators(rollbackHeight - 1)
  38. if err != nil {
  39. return -1, nil, err
  40. }
  41. previousParams, err := ss.LoadConsensusParams(rollbackHeight)
  42. if err != nil {
  43. return -1, nil, err
  44. }
  45. valChangeHeight := invalidState.LastHeightValidatorsChanged
  46. // this can only happen if the validator set changed since the last block
  47. if valChangeHeight > rollbackHeight {
  48. valChangeHeight = rollbackHeight
  49. }
  50. paramsChangeHeight := invalidState.LastHeightConsensusParamsChanged
  51. // this can only happen if params changed from the last block
  52. if paramsChangeHeight > rollbackHeight {
  53. paramsChangeHeight = rollbackHeight
  54. }
  55. // build the new state from the old state and the prior block
  56. rolledBackState := State{
  57. Version: Version{
  58. Consensus: version.Consensus{
  59. Block: version.BlockProtocol,
  60. App: previousParams.Version.AppVersion,
  61. },
  62. Software: version.TMVersion,
  63. },
  64. // immutable fields
  65. ChainID: invalidState.ChainID,
  66. InitialHeight: invalidState.InitialHeight,
  67. LastBlockHeight: invalidState.LastBlockHeight - 1,
  68. LastBlockID: rollbackBlock.Header.LastBlockID,
  69. LastBlockTime: rollbackBlock.Header.Time,
  70. NextValidators: invalidState.Validators,
  71. Validators: invalidState.LastValidators,
  72. LastValidators: previousValidatorSet,
  73. LastHeightValidatorsChanged: valChangeHeight,
  74. ConsensusParams: previousParams,
  75. LastHeightConsensusParamsChanged: paramsChangeHeight,
  76. LastResultsHash: rollbackBlock.Header.LastResultsHash,
  77. AppHash: rollbackBlock.Header.AppHash,
  78. }
  79. // persist the new state. This overrides the invalid one. NOTE: this will also
  80. // persist the validator set and consensus params over the existing structures,
  81. // but both should be the same
  82. if err := ss.Save(rolledBackState); err != nil {
  83. return -1, nil, fmt.Errorf("failed to save rolled back state: %w", err)
  84. }
  85. return rolledBackState.LastBlockHeight, rolledBackState.AppHash, nil
  86. }