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  }