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  }