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.

112 lines
3.9 KiB

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