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