diff --git a/app/app.go b/app/app.go index ad3bb1082..2122bef86 100644 --- a/app/app.go +++ b/app/app.go @@ -91,7 +91,7 @@ func MakeLatestCodec() *codec.Codec { return cdc } -func (app *IrisApp) ExportOrReplay(replayHeight int64) (replay bool, height int64) { +func (app *IrisApp) ResetOrReplay(replayHeight int64) (replay bool, height int64) { lastBlockHeight := app.BaseApp.LastBlockHeight() if replayHeight > lastBlockHeight { replayHeight = lastBlockHeight diff --git a/app/v1/metrics.go b/app/v1/metrics.go index 08e21333f..5750b544f 100644 --- a/app/v1/metrics.go +++ b/app/v1/metrics.go @@ -8,7 +8,7 @@ import ( cfg "github.com/tendermint/tendermint/config" ) -const MetricsSubsystem = "v0" +const MetricsSubsystem = "v1" type Metrics struct { InvariantFailure metrics.Counter diff --git a/cmd/iris/main.go b/cmd/iris/main.go index 05e10eea7..966b62a05 100644 --- a/cmd/iris/main.go +++ b/cmd/iris/main.go @@ -61,6 +61,7 @@ func main() { server.UnsafeResetAllCmd(ctx), client.LineBreak, tendermintCmd, + server.ResetCmd(ctx, cdc, resetAppState), server.ExportCmd(ctx, cdc, exportAppStateAndTMValidators), client.LineBreak, ) @@ -84,18 +85,24 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer, config *cfg.Inst } func exportAppStateAndTMValidators(ctx *server.Context, - logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, + logger log.Logger, db dbm.DB, traceStore io.Writer, forZeroHeight bool, ) (json.RawMessage, []tmtypes.GenesisValidator, error) { + gApp := app.NewIrisApp(logger, db, ctx.Config.Instrumentation, traceStore) + return gApp.ExportAppStateAndValidators(forZeroHeight) +} + +func resetAppState(ctx *server.Context, + logger log.Logger, db dbm.DB, traceStore io.Writer, height int64) error { gApp := app.NewIrisApp(logger, db, ctx.Config.Instrumentation, traceStore) if height > 0 { - if replay, replayHeight := gApp.ExportOrReplay(height); replay { + if replay, replayHeight := gApp.ResetOrReplay(height); replay { _, err := startNodeAndReplay(ctx, gApp, replayHeight) if err != nil { - return nil, nil, err + return err } } } - return gApp.ExportAppStateAndValidators(forZeroHeight) + return nil } func startNodeAndReplay(ctx *server.Context, app *app.IrisApp, height int64) (n *node.Node, err error) { diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index bc17235b2..dce027769 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -31,7 +31,8 @@ module.exports = { ['How-to-install-irishub.md', 'Install'], ['cli-client.md', 'CLI Client'], ['light-client.md', 'Light Client'], - ['export.md', 'Export'], + ['reset.md', 'Reset Blockchain State'], + ['export.md', 'Export Blockchain State'], ['sentry.md', 'Sentry'], ['tool.md', 'Tool'], ['monitor.md', 'Monitor'], @@ -160,6 +161,7 @@ module.exports = { ['How-to-install-irishub.md', '安装'], ['cli-client.md', '命令行客户端'], ['light-client.md', '轻节点客户端(LCD)'], + ['reset.md', '重置区块链状态'], ['export.md', '导出区块链状态'], ['sentry.md', '哨兵节点'], ['tool.md', '调试工具'], diff --git a/docs/software/export.md b/docs/software/export.md index 45a67b0ed..d01e3c225 100644 --- a/docs/software/export.md +++ b/docs/software/export.md @@ -2,33 +2,30 @@ ## Description -IRISnet can export blockchain state at any height and output json format string. Save the out json string to a json file and the json file can be used as genesis file of a new blockchain. This can be accomplished by command `iris export` +IRISnet can export blockchain state and output json format string. Save the out json string to a json file and the json file can be used as genesis file of a new blockchain. This can be accomplished by command `iris export`. +If you want to export the state of the historical block height, you need to [reset](reset.md) the blockchain state to the specified height. ## Usage ``` iris export ``` -### Flags +## Flags - | Name,shorthand | type | Required | Default | Description | + | Name,shorthand | type | Required | Default | Description | | ------------------- | ----- | -------- | -------- | -------------- | | --for-zero-height | bool | false | false | Do some clean up work before exporting state. If you want use the exported state to start a new blockchain, please add this flag. Otherwise, just leave out it | - | --height | int | false | 0 | Specify the height, default value is 0 which means to export the latest state | | --home | string | false | $HOME/.iris | Specify the directory which stores node config and blockchain data | | --output-file | string | false | genesis.json | Target file to save exported state | -1. Export the latest blockchain state: -``` - iris export -``` +## Examples -2. Export blockchain state at certain height +1. Export the current blockchain state ``` - iris export --height=10000 + iris export --home= ``` -3. If you want to export the blockchain state at certain height and use the exported state as genesis state of another blockchain +2. If you want to export the the current blockchain state and use the exported state as genesis state of another blockchain ``` -iris export --height=105000 --for-zero-height --home= +iris export --for-zero-height --home= ``` \ No newline at end of file diff --git a/docs/software/reset.md b/docs/software/reset.md new file mode 100644 index 000000000..8d3f8537b --- /dev/null +++ b/docs/software/reset.md @@ -0,0 +1,23 @@ +# Reset Blockchain State + +## Description + +IRISnet can reset blockchain state at any height. This can be accomplished by command `iris reset`. + +## Usage +``` + iris reset +``` +## Flags + + | Name,shorthand | type | Required | Default | Description | + | ------------------- | ----- | -------- | -------- | -------------- | + | --height | int | false | 0 | Specify the height, default value is 0 which means to export the latest state | + | --home | string | false | $HOME/.iris | Specify the directory which stores node config and blockchain data | + +## Examples + +1. Reset the blockchain state to block 100: +``` + iris reset --height 100 --home= +``` \ No newline at end of file diff --git a/docs/zh/software/export.md b/docs/zh/software/export.md index e48ff3aed..8e2e86026 100644 --- a/docs/zh/software/export.md +++ b/docs/zh/software/export.md @@ -2,7 +2,8 @@ ## 介绍 -这里介绍一种能导出区块链状态,并以json格式返回给用户。如果把返回的json字符串保存到一个json文件里,那么这个json文件可以作为一个新区块链网络的创世块。导出区块链状态所用的命令为`iris export` +IRISnet支持导出区块链状态,并以json格式返回给用户。如果把返回的json字符串保存到一个json文件里,这个json文件可以作为一个新区块链网络的创世块。导出区块链状态所用的命令为`iris export`。 +如果想导出历史高度的状态,需要先[重置](reset.md)app状态到指定的高度。 ## 用法 @@ -15,21 +16,16 @@ iris export | 名称,速记 | 类型 | 是否必填 | 默认值 | 介绍 | | ------------------- | ----- | -------- | -------- | -------------- | | --for-zero-height | bool | false | false | 导出数据之前做一些清理性的工作,如果不想以导出的数据启动一条新链,可以不加这个标志 | -| --height | int | false | 0 | 指定的高度,默认值为0表示导出当前高度状态 | | --home | string | false | $HOME/.iris | 指定存储配置和区块链数据的目录 | | --output-file | string | false | genesis.json | 存储导出状态的文件 | ## 示例 -1. 导出最新的区块链状态: +1. 导出当前区块链状态 ``` -iris export +iris export --home= ``` -2. 导出高度10000的区块链状态 +2. 如果想导出当前高度的区块链状态,并且以这个状态启动一条新链,可以尝试这个命令 ``` -iris export --height=10000 -``` -3. 如果想导出105000高度的区块链状态,并且以这个状态启动一条新链,可以尝试这个命令 -``` -iris export --height=105000 --for-zero-height --home= +iris export --for-zero-height --home= ``` \ No newline at end of file diff --git a/docs/zh/software/reset.md b/docs/zh/software/reset.md new file mode 100644 index 000000000..bfbef4287 --- /dev/null +++ b/docs/zh/software/reset.md @@ -0,0 +1,23 @@ +# 重置区块链状态 + +## 介绍 + +IRISnet支持重置区块链状态到任意高度,这里介绍重置区块链状态所用的命令为`iris reset`。 + +## 用法 +``` + iris reset +``` +## 标志 + + | Name,shorthand | type | Required | Default | Description | + | ------------------- | ----- | -------- | -------- | -------------- | + | --height | int | false | 0 | Specify the height, default value is 0 which means to export the latest state | + | --home | string | false | $HOME/.iris | Specify the directory which stores node config and blockchain data | + +## 示例 + +1. 重置区块链状态到区块100: +``` + iris reset --height 100 --home= +``` \ No newline at end of file diff --git a/server/constructors.go b/server/constructors.go index 9cdae91af..7d56e6dc1 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -20,7 +20,10 @@ type ( // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. - AppExporter func(*Context, log.Logger, dbm.DB, io.Writer, int64, bool) (json.RawMessage, []tmtypes.GenesisValidator, error) + AppExporter func(*Context, log.Logger, dbm.DB, io.Writer, bool) (json.RawMessage, []tmtypes.GenesisValidator, error) + + // AppReset is a function that reset all app state to particular height + AppReset func(*Context, log.Logger, dbm.DB, io.Writer, int64) error ) func openDB(rootDir string) (dbm.DB, error) { diff --git a/server/export.go b/server/export.go index ee3896386..284d884d6 100644 --- a/server/export.go +++ b/server/export.go @@ -14,7 +14,6 @@ import ( ) const ( - flagHeight = "height" flagForZeroHeight = "for-zero-height" flagOutputFile = "output-file" ) @@ -51,12 +50,9 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C if err != nil { return err } - height := viper.GetInt64(flagHeight) - if height < 0 { - return errors.Errorf("Height must greater than or equal to zero") - } + forZeroHeight := viper.GetBool(flagForZeroHeight) - appState, validators, err := appExporter(ctx, ctx.Logger, db, traceWriter, height, forZeroHeight) + appState, validators, err := appExporter(ctx, ctx.Logger, db, traceWriter, forZeroHeight) if err != nil { return errors.Errorf("error exporting state: %v\n", err) } @@ -85,7 +81,6 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C return nil }, } - cmd.Flags().Int64(flagHeight, 0, "Export state from a particular height (0 means latest height)") cmd.Flags().Bool(flagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)") cmd.Flags().String(flagOutputFile, "genesis.json", "Target file to save exported state") return cmd diff --git a/server/reset.go b/server/reset.go new file mode 100644 index 000000000..1963b4a17 --- /dev/null +++ b/server/reset.go @@ -0,0 +1,58 @@ +package server + +import ( + "fmt" + + "github.com/irisnet/irishub/codec" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +const ( + flagHeight = "height" +) + +// ResetCmd reset app state to particular height +func ResetCmd(ctx *Context, cdc *codec.Codec, appReset AppReset) *cobra.Command { + cmd := &cobra.Command{ + Use: "reset", + Short: "Reset app state to the specified height", + RunE: func(cmd *cobra.Command, args []string) error { + home := viper.GetString("home") + traceWriterFile := viper.GetString(flagTraceStore) + emptyState, err := isEmptyState(home) + if err != nil { + return err + } + + if emptyState { + fmt.Println("WARNING: State is not initialized.") + return nil + } + + db, err := openDB(home) + if err != nil { + return err + } + traceWriter, err := openTraceWriter(traceWriterFile) + if err != nil { + return err + } + height := viper.GetInt64(flagHeight) + if height < 0 { + return errors.Errorf("Height must greater than or equal to zero") + } + err = appReset(ctx, ctx.Logger, db, traceWriter, height) + if err != nil { + return errors.Errorf("Error reset state: %v\n", err) + } + + fmt.Printf("Reset app state to height %d successfully\n", height) + return nil + }, + } + cmd.Flags().Int64(flagHeight, 0, "Reset state from a particular height (0 or greater than latest height means latest height)") + cmd.MarkFlagRequired(flagHeight) + return cmd +}