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