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.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 need to rollback the block store too.
  22. if height == invalidState.LastBlockHeight+1 {
  23. if err := bs.Rollback(); err != nil {
  24. return -1, nil, err
  25. }
  26. height--
  27. }
  28. // If the state store isn't one below nor equal to the blockstore height than this violates the
  29. // invariant
  30. if height != invalidState.LastBlockHeight {
  31. return -1, nil, fmt.Errorf("statestore height (%d) is not one below or equal to blockstore height (%d)",
  32. invalidState.LastBlockHeight, height)
  33. }
  34. // state store height is equal to blockstore height. We're good to proceed with rolling back state
  35. rollbackHeight := invalidState.LastBlockHeight - 1
  36. rollbackBlock := bs.LoadBlockMeta(rollbackHeight)
  37. if rollbackBlock == nil {
  38. return -1, nil, fmt.Errorf("block at height %d not found", rollbackHeight)
  39. }
  40. // we also need to retrieve the latest block because the app hash and last 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: Version{
  66. Consensus: version.Consensus{
  67. Block: version.BlockProtocol,
  68. App: previousParams.Version.AppVersion,
  69. },
  70. Software: version.TMVersion,
  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. }