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  }