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 }