github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/params/upgrade_executor.go (about)

     1  package params
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  
     7  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
     8  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
     9  	"github.com/fibonacci-chain/fbc/x/common"
    10  	govtypes "github.com/fibonacci-chain/fbc/x/gov/types"
    11  	"github.com/fibonacci-chain/fbc/x/params/types"
    12  )
    13  
    14  func NewUpgradeProposalHandler(k *Keeper) govtypes.Handler {
    15  	return func(ctx sdk.Context, proposal *govtypes.Proposal) sdk.Error {
    16  		switch c := proposal.Content.(type) {
    17  		case types.UpgradeProposal:
    18  			return handleUpgradeProposal(ctx, k, proposal.ProposalID, c)
    19  		default:
    20  			return common.ErrUnknownProposalType(DefaultCodespace, fmt.Sprintf("%T", c))
    21  		}
    22  	}
    23  }
    24  
    25  func handleUpgradeProposal(ctx sdk.Context, k *Keeper, proposalID uint64, proposal types.UpgradeProposal) sdk.Error {
    26  	curHeight := uint64(ctx.BlockHeight())
    27  	confirmHeight, err := getUpgradeProposalConfirmHeight(curHeight, proposal)
    28  	if err != nil {
    29  		return err
    30  	}
    31  	effectiveHeight := confirmHeight + 1
    32  
    33  	if curHeight < confirmHeight {
    34  		k.gk.InsertWaitingProposalQueue(ctx, confirmHeight, proposalID)
    35  		_ = storeWaitingUpgrade(ctx, k, proposal, effectiveHeight) // ignore error
    36  		return nil
    37  	}
    38  
    39  	// proposal will be confirmed right now, check if ready.
    40  	cbs, ready := k.queryReadyForUpgrade(proposal.Name)
    41  	if !ready {
    42  		// if no module claims that has ready for this upgrade,
    43  		// that probably means program's version is too low.
    44  		// To avoid status machine broken, we panic.
    45  		errMsg := fmt.Sprintf("there's a upgrade proposal named '%s' has been take effective, "+
    46  			"and the upgrade is incompatible, but your binary seems not ready for this upgrade. "+
    47  			"To avoid state machine broken, the program is panic. "+
    48  			"Using the latest version binary and re-run it to avoid this panic.", proposal.Name)
    49  		k.Logger(ctx).Error(errMsg)
    50  		panic(errMsg)
    51  	}
    52  
    53  	storedInfo, err := storeEffectiveUpgrade(ctx, k, proposal, effectiveHeight)
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	for _, cb := range cbs {
    59  		if cb != nil {
    60  			cb(storedInfo)
    61  		}
    62  	}
    63  	return nil
    64  }
    65  
    66  func getUpgradeProposalConfirmHeight(currentHeight uint64, proposal types.UpgradeProposal) (uint64, sdk.Error) {
    67  	// confirm height is the height proposal is confirmed.
    68  	// confirmed is not become effective. Becoming effective will happen at
    69  	// the next block of confirm block. see `storeEffectiveUpgrade` and `IsUpgradeEffective`
    70  	confirmHeight := proposal.ExpectHeight - 1
    71  	if proposal.ExpectHeight == 0 {
    72  		// if height is not specified, this upgrade will become effective
    73  		// at the next block of the block which the proposal is passed
    74  		// (i.e. become effective at next block).
    75  		confirmHeight = currentHeight
    76  	}
    77  
    78  	if confirmHeight < currentHeight {
    79  		// if it's too late to make the proposal become effective at the height which we expected,
    80  		// refuse to effective this proposal
    81  		return 0, sdkerrors.New(DefaultCodespace, types.BaseParamsError,
    82  			fmt.Sprintf("current height '%d' has exceed "+
    83  				"the expect height '%d' of upgrade proposal '%s'",
    84  				currentHeight, proposal.ExpectHeight, proposal.Name))
    85  	}
    86  	return confirmHeight, nil
    87  }
    88  
    89  func storePreparingUpgrade(ctx sdk.Context, k *Keeper, upgrade types.UpgradeProposal) sdk.Error {
    90  
    91  	info := types.UpgradeInfo{
    92  		Name:         upgrade.Name,
    93  		ExpectHeight: upgrade.ExpectHeight,
    94  		Config:       upgrade.Config,
    95  
    96  		EffectiveHeight: 0,
    97  		Status:          types.UpgradeStatusPreparing,
    98  	}
    99  
   100  	return k.writeUpgradeInfo(ctx, info, false)
   101  }
   102  
   103  func storeWaitingUpgrade(ctx sdk.Context, k *Keeper, upgrade types.UpgradeProposal, effectiveHeight uint64) error {
   104  	info := types.UpgradeInfo{
   105  		Name:            upgrade.Name,
   106  		ExpectHeight:    upgrade.ExpectHeight,
   107  		Config:          upgrade.Config,
   108  		EffectiveHeight: effectiveHeight,
   109  		Status:          types.UpgradeStatusWaitingEffective,
   110  	}
   111  
   112  	return k.writeUpgradeInfo(ctx, info, true)
   113  }
   114  
   115  func storeEffectiveUpgrade(ctx sdk.Context, k *Keeper, upgrade types.UpgradeProposal, effectiveHeight uint64) (types.UpgradeInfo, sdk.Error) {
   116  	info := types.UpgradeInfo{
   117  		Name:            upgrade.Name,
   118  		ExpectHeight:    upgrade.ExpectHeight,
   119  		Config:          upgrade.Config,
   120  		EffectiveHeight: effectiveHeight,
   121  		Status:          types.UpgradeStatusEffective,
   122  	}
   123  
   124  	return info, k.writeUpgradeInfo(ctx, info, true)
   125  }
   126  
   127  // a upgrade valid effective height must be:
   128  //  1. zero, or
   129  //  2. bigger than current height and not too far away from current height
   130  func checkUpgradeValidEffectiveHeight(ctx sdk.Context, k *Keeper, effectiveHeight uint64) sdk.Error {
   131  	if effectiveHeight == 0 {
   132  		return nil
   133  	}
   134  
   135  	curHeight := uint64(ctx.BlockHeight())
   136  
   137  	maxHeight := k.GetParams(ctx).MaxBlockHeight
   138  	if maxHeight == 0 {
   139  		maxHeight = math.MaxInt64 - effectiveHeight
   140  	}
   141  
   142  	if effectiveHeight <= curHeight || effectiveHeight-curHeight > maxHeight {
   143  		return govtypes.ErrInvalidHeight(effectiveHeight, curHeight, maxHeight)
   144  	}
   145  	return nil
   146  }
   147  
   148  func checkUpgradeVote(ctx sdk.Context, proposalID uint64, proposal types.UpgradeProposal, _ govtypes.Vote) (string, sdk.Error) {
   149  	curHeight := uint64(ctx.BlockHeight())
   150  
   151  	if proposal.ExpectHeight != 0 && proposal.ExpectHeight <= curHeight {
   152  		return "", sdkerrors.New(DefaultCodespace, types.BaseParamsError,
   153  			fmt.Sprintf("can not vote: current height '%d' has exceed "+
   154  				"the expect height '%d' of upgrade proposal '%s'(proposal id '%d')",
   155  				curHeight, proposal.ExpectHeight, proposal.Name, proposalID))
   156  	}
   157  
   158  	return "", nil
   159  }