github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/internal/state/rollback.go (about) 1 package state 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/ari-anchor/sei-tendermint/config" 8 "github.com/ari-anchor/sei-tendermint/privval" 9 "github.com/ari-anchor/sei-tendermint/version" 10 ) 11 12 func resetPrivValidatorConfig(privValidatorConfig config.PrivValidatorConfig) error { 13 // Priv Val LastState needs to be rolled back if this is the case 14 filePv, loadErr := privval.LoadFilePV(privValidatorConfig.KeyFile(), privValidatorConfig.StateFile()) 15 if loadErr != nil { 16 return fmt.Errorf("failed to load private validator file: %w", loadErr) 17 } 18 19 resetErr := filePv.Reset() 20 if resetErr != nil { 21 return fmt.Errorf("failed to reset private validator file: %w", resetErr) 22 } 23 24 return nil 25 } 26 27 // Rollback overwrites the current Tendermint state (height n) with the most 28 // recent previous state (height n - 1). 29 // Note that this function does not affect application state. 30 func Rollback(bs BlockStore, ss Store, removeBlock bool, privValidatorConfig *config.PrivValidatorConfig) (int64, []byte, error) { 31 // Only the latest state is stored 32 latestState, err := ss.Load() 33 fmt.Printf("Initial tendermint state height=%d, appHash=%X, lastResultHash=%X\n", latestState.LastBlockHeight, latestState.AppHash, latestState.LastResultsHash) 34 if err != nil { 35 return -1, nil, err 36 } 37 if latestState.IsEmpty() { 38 return -1, nil, errors.New("no state found") 39 } 40 41 height := bs.Height() 42 // NOTE: persistence of state and blocks don't happen atomically. Therefore it is possible that 43 // when the user stopped the node the state wasn't updated but the blockstore was. Discard the 44 // pending block before continuing. 45 if height == latestState.LastBlockHeight+1 { 46 fmt.Printf("Invalid state in the latest block height=%d, removing it first \n", height) 47 if removeBlock { 48 if err := bs.DeleteLatestBlock(); err != nil { 49 return -1, nil, fmt.Errorf("failed to remove final block from blockstore: %w", err) 50 } 51 } 52 return latestState.LastBlockHeight, latestState.AppHash, nil 53 } 54 55 // If the state store isn't one below nor equal to the blockstore height than this violates the 56 // invariant 57 if height != latestState.LastBlockHeight { 58 return -1, nil, fmt.Errorf("statestore height (%d) is not one below or equal to blockstore height (%d)", 59 latestState.LastBlockHeight, height) 60 } 61 62 // state store height is equal to blockstore height. We're good to proceed with rolling back state 63 rollbackHeight := latestState.LastBlockHeight - 1 64 rollbackBlock := bs.LoadBlockMeta(rollbackHeight) 65 if rollbackBlock == nil { 66 return -1, nil, fmt.Errorf("block at height %d not found", rollbackHeight) 67 } 68 69 // we also need to retrieve the latest block because the app hash and last results hash is only agreed upon in the following block 70 latestBlock := bs.LoadBlockMeta(latestState.LastBlockHeight) 71 if latestBlock == nil { 72 return -1, nil, fmt.Errorf("block at height %d not found", latestState.LastBlockHeight) 73 } 74 75 previousLastValidatorSet, err := ss.LoadValidators(rollbackHeight) 76 if err != nil { 77 return -1, nil, err 78 } 79 80 previousParams, err := ss.LoadConsensusParams(rollbackHeight + 1) 81 if err != nil { 82 return -1, nil, err 83 } 84 85 valChangeHeight := latestState.LastHeightValidatorsChanged 86 // this can only happen if the validator set changed since the last block 87 if valChangeHeight > rollbackHeight { 88 valChangeHeight = rollbackHeight + 1 89 } 90 91 paramsChangeHeight := latestState.LastHeightConsensusParamsChanged 92 // this can only happen if params changed from the last block 93 if paramsChangeHeight > rollbackHeight { 94 paramsChangeHeight = rollbackHeight + 1 95 } 96 97 lastBlockHeight := rollbackBlock.Header.Height 98 rolledBackAppHash := latestBlock.Header.AppHash 99 rolledBackLastResultHash := latestBlock.Header.LastResultsHash 100 101 fmt.Printf("Rollback block Height=%d, appHash=%X\n", rollbackBlock.Header.Height, rollbackBlock.Header.AppHash) 102 fmt.Printf("Latest block Height=%d, appHash=%X\n", latestBlock.Header.Height, latestBlock.Header.AppHash) 103 104 // build the new state from the old state and the prior block 105 rolledBackState := State{ 106 Version: Version{ 107 Consensus: version.Consensus{ 108 Block: version.BlockProtocol, 109 App: previousParams.Version.AppVersion, 110 }, 111 Software: version.TMVersion, 112 }, 113 // immutable fields 114 ChainID: latestState.ChainID, 115 InitialHeight: latestState.InitialHeight, 116 117 LastBlockHeight: lastBlockHeight, 118 LastBlockID: rollbackBlock.BlockID, 119 LastBlockTime: rollbackBlock.Header.Time, 120 121 AppHash: rolledBackAppHash, 122 LastResultsHash: rolledBackLastResultHash, 123 124 NextValidators: latestState.Validators, 125 Validators: latestState.LastValidators, 126 LastValidators: previousLastValidatorSet, 127 LastHeightValidatorsChanged: valChangeHeight, 128 129 ConsensusParams: previousParams, 130 LastHeightConsensusParamsChanged: paramsChangeHeight, 131 } 132 133 // persist the new state. This overrides the invalid one. NOTE: this will also 134 // persist the validator set and consensus params over the existing structures, 135 // but both should be the same 136 if err := ss.Save(rolledBackState); err != nil { 137 return -1, nil, fmt.Errorf("failed to save rolled back state: %w", err) 138 } 139 140 // If removeBlock is true then also remove the block associated with the previous state. 141 // This will mean both the last state and last block height is equal to n - 1 142 if removeBlock { 143 if err := bs.DeleteLatestBlock(); err != nil { 144 return -1, nil, fmt.Errorf("failed to remove final block from blockstore: %w", err) 145 } 146 147 err = resetPrivValidatorConfig(*privValidatorConfig) 148 if err != nil { 149 return -1, nil, err 150 } 151 } 152 153 fmt.Printf("Saved tendermint state height=%d, appHash=%X, lastResultHash=%X\n", lastBlockHeight, rolledBackState.AppHash, rolledBackState.LastResultsHash) 154 return lastBlockHeight, rolledBackState.AppHash, nil 155 }