diff --git a/.gitignore b/.gitignore index cb756e9ec..f7bba6a08 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ vendor -.idea \ No newline at end of file +.idea +build \ No newline at end of file diff --git a/docs/modules/README.md b/docs/modules/README.md new file mode 100644 index 000000000..742280230 --- /dev/null +++ b/docs/modules/README.md @@ -0,0 +1,53 @@ +# Upgrade Module + +本module作为支持区块链软件平滑升级的基础设施,通过upgradeProposal和switch两阶段的投票来在约定高度切换到新版的代码,并对历史版本的链上数据完全兼容。 + +key point: + +* upgradeProposal投票通过后各个节点下载安装新版本软件,并启动软件发送switch投票,表明已经可以切换到新版本 +* switch的投票将在约定高度进行检查,需要95%的Voting Power才视为投票通过 +* switch通过后开启切换流程: + 1. check_tx全部返回fail,以拒绝新tx的处理 + 2. 处理mempool中留存的tx,直到生成一个empty block + 3. 配置新版本的路由开关,并打开check_tx接受新tx +* 发生老版本AppHash冲突,检查自己是否在switch voter list中,否则reset rootMultiStore到上一个commit,并下载新版本开始同步block +* 新增Module的升级方式(现有Module逻辑修改也通过新Module完成),新老版本的Module共享同一个store,对于查询需要路由到相应版本的Module +* Hardcord的升级方式(bug fix),Upgrade Module提供便利函数来决定指定的代码段在当前区块高度是否执行 + +## Data Struct + +``` +type ModuleLifeTime struct { + Start int64 + End int64 + Handler sdk.Handler + store sdk.KVStoreKey +} + +type Version struct { + Id int // should be equal with corresponding upgradeProposalID + Start int64 + ModuleList []ModuleLifeTime +} + +``` + +## TxMsg + +``` +type MsgSwitch struct { + Title string + ProposalId int + Voter sdk.AccAddress +} + +``` + +## Storage + +| Key | Type | Value | Description | Note| +| --------- | ------ | ------- | -------- | -----------| +| CurrentVersionIDKey | int | CurrentVersionID | c/ | | +| VersionKey | Version | Version | v/%010d/ | v/proposalId | +| VersionListKey | ListOfVersionKey | [][]byte{} | l/ | list of the version_key ordered by proposalId | +| SwitchKey | MsgSwitch | MsgSwitch | s/%010d/%d/ | s/proposalId/switchVoterAddress | diff --git a/modules/upgrade/error.go b/modules/upgrade/error.go new file mode 100644 index 000000000..f08addf7f --- /dev/null +++ b/modules/upgrade/error.go @@ -0,0 +1,33 @@ +package upgrade + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +const ( + DefaultCodespace sdk.CodespaceType = 1 + + CodeInvalidMsgType sdk.CodeType = 200 + CodeUnknownRequest sdk.CodeType = sdk.CodeUnknownRequest +) + +func codeToDefaultMsg(code sdk.CodeType) string { + switch code { + case CodeInvalidMsgType: + return "Invalid msg type" + default: + return sdk.CodeToDefaultMsg(code) + } +} + +func NewError(codespace sdk.CodespaceType, code sdk.CodeType, msg string) sdk.Error { + msg = msgOrDefaultMsg(msg, code) + return sdk.NewError(codespace, code, msg) +} + +func msgOrDefaultMsg(msg string, code sdk.CodeType) string { + if msg != "" { + return msg + } + return codeToDefaultMsg(code) +} \ No newline at end of file diff --git a/modules/upgrade/handler.go b/modules/upgrade/handler.go new file mode 100644 index 000000000..81ed44773 --- /dev/null +++ b/modules/upgrade/handler.go @@ -0,0 +1,31 @@ +package upgrade + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "reflect" + "fmt" +) + +func NewHandler(k Keeper) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case MsgSwitch: + return handlerSwitch(ctx, msg, k) + default: + errMsg := "Unrecognized Upgrade Msg type: " + reflect.TypeOf(msg).Name() + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +func handlerSwitch(ctx sdk.Context, msg sdk.Msg, k Keeper) sdk.Result { + msgSwitch, ok := msg.(MsgSwitch) + if !ok { + return NewError(DefaultCodespace, CodeInvalidMsgType, "Handler should only receive MsgSwitch").Result() + } + + return sdk.Result{ + Code: 0, + Log: fmt.Sprintf("Switch %s by %s", msgSwitch.Title, msgSwitch.Voter.String()), + } +} diff --git a/modules/upgrade/keeper.go b/modules/upgrade/keeper.go new file mode 100644 index 000000000..63c68a992 --- /dev/null +++ b/modules/upgrade/keeper.go @@ -0,0 +1,36 @@ +package upgrade + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/bank" +) + +type ModuleLifeTime struct { + Start int64 + End int64 + Handler sdk.Handler + store sdk.KVStoreKey +} + +type Version struct { + Id int // should be equal with corresponding upgradeProposalID + Start int64 + ModuleList []ModuleLifeTime +} + +type Keeper struct { + storeKey sdk.StoreKey + cdc *wire.Codec + coinKeeper bank.Keeper +} + +func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper) Keeper { + keeper := Keeper { + storeKey: key, + cdc: cdc, + coinKeeper: ck, + } + return keeper +} + diff --git a/modules/upgrade/keeper_keys.go b/modules/upgrade/keeper_keys.go new file mode 100644 index 000000000..739431810 --- /dev/null +++ b/modules/upgrade/keeper_keys.go @@ -0,0 +1,24 @@ +package upgrade + +import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + CurrentVersionIDKey = []byte("c/") + VersionKey = "v/%010d/" // v/ + SwitchKey = "s/%010d/%d/" // s// +) + +func GetCurrentVersionIDKey() []byte { + return CurrentVersionIDKey +} + +func GetVersionKey(proposalID int64) []byte { + return []byte(fmt.Sprintf(VersionKey, proposalID)) +} + +func GetSwitchKey(proposalID int64, switchVoterAddr sdk.AccAddress) []byte { + return []byte(fmt.Sprintf(SwitchKey, proposalID, switchVoterAddr)) +} diff --git a/modules/upgrade/msgs.go b/modules/upgrade/msgs.go new file mode 100644 index 000000000..b03e799f4 --- /dev/null +++ b/modules/upgrade/msgs.go @@ -0,0 +1,32 @@ +package upgrade + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type MsgSwitch struct { + Title string + ProposalID int + Voter sdk.AccAddress +} + + +func (msg MsgSwitch) Type() string { + return "record" +} + +func (msg MsgSwitch) GetSignBytes() []byte { + b, err := msgCdc.MarshalJSON(msg) + if err != nil { + panic(err) + } + return b +} + +func (msg MsgSwitch) ValidateBasic() sdk.Error { + return nil +} + +func (msg MsgSwitch) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.Voter} +} diff --git a/modules/upgrade/wire.go b/modules/upgrade/wire.go new file mode 100644 index 000000000..f66ff68c1 --- /dev/null +++ b/modules/upgrade/wire.go @@ -0,0 +1,16 @@ +package upgrade + +import ( + "github.com/cosmos/cosmos-sdk/wire" +) + +// Register concrete types on wire codec +func RegisterWire(cdc *wire.Codec) { + cdc.RegisterConcrete(MsgSwitch{}, "iris-hub/MsgSwitch", nil) +} + +var msgCdc = wire.NewCodec() + +func init() { + RegisterWire(msgCdc) +}