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.

109 lines
3.7 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 - 1
  33. rollbackBlock := bs.LoadBlockMeta(rollbackHeight)
  34. if rollbackBlock == nil {
  35. return -1, nil, fmt.Errorf("block at height %d not found", rollbackHeight)
  36. }
  37. // we also need to retrieve the latest block because the app hash and last results hash is only agreed upon in the following block
  38. latestBlock := bs.LoadBlockMeta(invalidState.LastBlockHeight)
  39. if latestBlock == nil {
  40. return -1, nil, fmt.Errorf("block at height %d not found", invalidState.LastBlockHeight)
  41. }
  42. previousLastValidatorSet, err := ss.LoadValidators(rollbackHeight)
  43. if err != nil {
  44. return -1, nil, err
  45. }
  46. previousParams, err := ss.LoadConsensusParams(rollbackHeight + 1)
  47. if err != nil {
  48. return -1, nil, err
  49. }
  50. valChangeHeight := invalidState.LastHeightValidatorsChanged
  51. // this can only happen if the validator set changed since the last block
  52. if valChangeHeight > rollbackHeight {
  53. valChangeHeight = rollbackHeight + 1
  54. }
  55. paramsChangeHeight := invalidState.LastHeightConsensusParamsChanged
  56. // this can only happen if params changed from the last block
  57. if paramsChangeHeight > rollbackHeight {
  58. paramsChangeHeight = rollbackHeight + 1
  59. }
  60. // build the new state from the old state and the prior block
  61. rolledBackState := State{
  62. Version: Version{
  63. Consensus: version.Consensus{
  64. Block: version.BlockProtocol,
  65. App: previousParams.Version.AppVersion,
  66. },
  67. Software: version.TMVersion,
  68. },
  69. // immutable fields
  70. ChainID: invalidState.ChainID,
  71. InitialHeight: invalidState.InitialHeight,
  72. LastBlockHeight: rollbackBlock.Header.Height,
  73. LastBlockID: rollbackBlock.BlockID,
  74. LastBlockTime: rollbackBlock.Header.Time,
  75. NextValidators: invalidState.Validators,
  76. Validators: invalidState.LastValidators,
  77. LastValidators: previousLastValidatorSet,
  78. LastHeightValidatorsChanged: valChangeHeight,
  79. ConsensusParams: previousParams,
  80. LastHeightConsensusParamsChanged: paramsChangeHeight,
  81. LastResultsHash: latestBlock.Header.LastResultsHash,
  82. AppHash: latestBlock.Header.AppHash,
  83. }
  84. // persist the new state. This overrides the invalid one. NOTE: this will also
  85. // persist the validator set and consensus params over the existing structures,
  86. // but both should be the same
  87. if err := ss.Save(rolledBackState); err != nil {
  88. return -1, nil, fmt.Errorf("failed to save rolled back state: %w", err)
  89. }
  90. return rolledBackState.LastBlockHeight, rolledBackState.AppHash, nil
  91. }