Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade module : design doc and the framework codes #46

Merged
merged 1 commit into from
Jul 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
vendor
.idea
.idea
build
53 changes: 53 additions & 0 deletions docs/modules/README.md
Original file line number Diff line number Diff line change
@@ -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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一个empty block可能有点少,只有一个空块只能说明这个块对应的proposal 节点的memory pool里面是空的,建议等多个empty block

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

社区会公告在第多少高度进行版本升级,提醒届时会终止服务,不要在升级时间段内发交易。所以这里算是一个防御检查,保守一点可以先等两个空块,后续压测发现不足的话再调整。

3. 配置新版本的路由开关,并打开check_tx接受新tx
* 发生老版本AppHash冲突,检查自己是否在switch voter list中,否则reset rootMultiStore到上一个commit,并下载新版本开始同步block
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

回滚是应该的,但是之前只考虑了App中store当前版本的回滚,目前讨论下来还需要用新版本软件对自己tendermint node中的last block进行replay,否则还是会有AppHash冲突

* 新增Module的升级方式(现有Module逻辑修改也通过新Module完成),新老版本的Module共享同一个store,对于查询需要路由到相应版本的Module

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

从iris角度看,查询操作就是直接访问store,然后返回value和proof,具体字段的解析逻辑都是iriscli里面,所以iris本身不需要对查询操作做特殊处理,可能需要在iriscli里面做改动

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

* 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 |
33 changes: 33 additions & 0 deletions modules/upgrade/error.go
Original file line number Diff line number Diff line change
@@ -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)
}
31 changes: 31 additions & 0 deletions modules/upgrade/handler.go
Original file line number Diff line number Diff line change
@@ -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()),
}
}
36 changes: 36 additions & 0 deletions modules/upgrade/keeper.go
Original file line number Diff line number Diff line change
@@ -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
}

24 changes: 24 additions & 0 deletions modules/upgrade/keeper_keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package upgrade

import (
"fmt"
sdk "github.com/cosmos/cosmos-sdk/types"
)

var (
CurrentVersionIDKey = []byte("c/")
VersionKey = "v/%010d/" // v/<proposalId>
SwitchKey = "s/%010d/%d/" // s/<proposalId>/<switchVoterAddress>
)

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))
}
32 changes: 32 additions & 0 deletions modules/upgrade/msgs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package upgrade

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个switch msg应该是中交易吧,那发起这个交易的应该是轻客户端做的吧,是不是要求一定要 validator owner的签名

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

只接受他是validator的switch


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}
}
16 changes: 16 additions & 0 deletions modules/upgrade/wire.go
Original file line number Diff line number Diff line change
@@ -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)
}