github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/x/upgrade/internal/keeper/keeper.go (about)

     1  package keeper
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  
     7  	"github.com/fibonacci-chain/fbc/libs/tendermint/libs/log"
     8  
     9  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec"
    10  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/store/prefix"
    11  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    12  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    13  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/upgrade/internal/types"
    14  )
    15  
    16  type Keeper struct {
    17  	skipUpgradeHeights map[int64]bool
    18  	storeKey           sdk.StoreKey
    19  	cdc                *codec.Codec
    20  	upgradeHandlers    map[string]types.UpgradeHandler
    21  }
    22  
    23  // NewKeeper constructs an upgrade Keeper
    24  func NewKeeper(skipUpgradeHeights map[int64]bool, storeKey sdk.StoreKey, cdc *codec.Codec) Keeper {
    25  	return Keeper{
    26  		skipUpgradeHeights: skipUpgradeHeights,
    27  		storeKey:           storeKey,
    28  		cdc:                cdc,
    29  		upgradeHandlers:    map[string]types.UpgradeHandler{},
    30  	}
    31  }
    32  
    33  // SetUpgradeHandler sets an UpgradeHandler for the upgrade specified by name. This handler will be called when the upgrade
    34  // with this name is applied. In order for an upgrade with the given name to proceed, a handler for this upgrade
    35  // must be set even if it is a no-op function.
    36  func (k Keeper) SetUpgradeHandler(name string, upgradeHandler types.UpgradeHandler) {
    37  	k.upgradeHandlers[name] = upgradeHandler
    38  }
    39  
    40  // ScheduleUpgrade schedules an upgrade based on the specified plan.
    41  // If there is another Plan already scheduled, it will overwrite it
    42  // (implicitly cancelling the current plan)
    43  func (k Keeper) ScheduleUpgrade(ctx sdk.Context, plan types.Plan) error {
    44  	if err := plan.ValidateBasic(); err != nil {
    45  		return err
    46  	}
    47  
    48  	if !plan.Time.IsZero() {
    49  		if !plan.Time.After(ctx.BlockHeader().Time) {
    50  			return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past")
    51  		}
    52  	} else if plan.Height <= ctx.BlockHeight() {
    53  		return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past")
    54  	}
    55  
    56  	if k.GetDoneHeight(ctx, plan.Name) != 0 {
    57  		return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "upgrade with name %s has already been completed", plan.Name)
    58  	}
    59  
    60  	bz := k.cdc.MustMarshalBinaryBare(plan)
    61  	store := ctx.KVStore(k.storeKey)
    62  	store.Set(types.PlanKey(), bz)
    63  
    64  	return nil
    65  }
    66  
    67  // GetDoneHeight returns the height at which the given upgrade was executed
    68  func (k Keeper) GetDoneHeight(ctx sdk.Context, name string) int64 {
    69  	store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.DoneByte})
    70  	bz := store.Get([]byte(name))
    71  	if len(bz) == 0 {
    72  		return 0
    73  	}
    74  
    75  	return int64(binary.BigEndian.Uint64(bz))
    76  }
    77  
    78  // ClearUpgradePlan clears any schedule upgrade
    79  func (k Keeper) ClearUpgradePlan(ctx sdk.Context) {
    80  	store := ctx.KVStore(k.storeKey)
    81  	store.Delete(types.PlanKey())
    82  }
    83  
    84  // Logger returns a module-specific logger.
    85  func (k Keeper) Logger(ctx sdk.Context) log.Logger {
    86  	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
    87  }
    88  
    89  // GetUpgradePlan returns the currently scheduled Plan if any, setting havePlan to true if there is a scheduled
    90  // upgrade or false if there is none
    91  func (k Keeper) GetUpgradePlan(ctx sdk.Context) (plan types.Plan, havePlan bool) {
    92  	store := ctx.KVStore(k.storeKey)
    93  	bz := store.Get(types.PlanKey())
    94  	if bz == nil {
    95  		return plan, false
    96  	}
    97  
    98  	k.cdc.MustUnmarshalBinaryBare(bz, &plan)
    99  	return plan, true
   100  }
   101  
   102  // setDone marks this upgrade name as being done so the name can't be reused accidentally
   103  func (k Keeper) setDone(ctx sdk.Context, name string) {
   104  	store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.DoneByte})
   105  	bz := make([]byte, 8)
   106  	binary.BigEndian.PutUint64(bz, uint64(ctx.BlockHeight()))
   107  	store.Set([]byte(name), bz)
   108  }
   109  
   110  // HasHandler returns true iff there is a handler registered for this name
   111  func (k Keeper) HasHandler(name string) bool {
   112  	_, ok := k.upgradeHandlers[name]
   113  	return ok
   114  }
   115  
   116  // ApplyUpgrade will execute the handler associated with the Plan and mark the plan as done.
   117  func (k Keeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) {
   118  	handler := k.upgradeHandlers[plan.Name]
   119  	if handler == nil {
   120  		panic("ApplyUpgrade should never be called without first checking HasHandler")
   121  	}
   122  
   123  	handler(ctx, plan)
   124  
   125  	k.ClearUpgradePlan(ctx)
   126  	k.setDone(ctx, plan.Name)
   127  }
   128  
   129  // IsSkipHeight checks if the given height is part of skipUpgradeHeights
   130  func (k Keeper) IsSkipHeight(height int64) bool {
   131  	return k.skipUpgradeHeights[height]
   132  }