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

feat(cli/module)!: separate the app genesis state by module into folders when export/import/validate #13032

Closed
wants to merge 18 commits into from
Closed
4 changes: 4 additions & 0 deletions runtime/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ func (a *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.Respons

// InitChainer initializes the chain.
func (a *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
if len(a.ModuleManager.GenesisPath) > 0 {
return a.ModuleManager.InitGenesis(ctx, a.cdc, nil)
}

var genesisState map[string]json.RawMessage
if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
panic(err)
Expand Down
7 changes: 6 additions & 1 deletion runtime/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ type AppI interface {
LoadHeight(height int64) error

// Exports the state of the application for a genesis file.
ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string) (types.ExportedApp, error)
ExportAppStateAndValidators(
forZeroHeight bool,
jailAllowedAddrs []string,
modulesToExport []string,
splitModules bool,
) (types.ExportedApp, error)

// Helper for the simulation framework.
SimulationManager() *module.SimulationManager
Expand Down
37 changes: 26 additions & 11 deletions server/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package server
import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"
tmjson "github.com/tendermint/tendermint/libs/json"
Expand All @@ -20,6 +21,7 @@ const (
FlagForZeroHeight = "for-zero-height"
FlagJailAllowedAddrs = "jail-allowed-addrs"
FlagModulesToExport = "modules-to-export"
FlagSplitModules = "split-modules"
)

// ExportCmd dumps app state to JSON.
Expand Down Expand Up @@ -67,8 +69,9 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com
forZeroHeight, _ := cmd.Flags().GetBool(FlagForZeroHeight)
jailAllowedAddrs, _ := cmd.Flags().GetStringSlice(FlagJailAllowedAddrs)
modulesToExport, _ := cmd.Flags().GetStringSlice(FlagModulesToExport)
splitModules, _ := cmd.Flags().GetBool(FlagSplitModules)

Check warning

Code scanning / gosec

Returned error is not propagated up the stack.

Returned error is not propagated up the stack.

exported, err := appExporter(serverCtx.Logger, db, traceWriter, height, forZeroHeight, jailAllowedAddrs, serverCtx.Viper, modulesToExport)
exported, err := appExporter(serverCtx.Logger, db, traceWriter, height, forZeroHeight, jailAllowedAddrs, serverCtx.Viper, modulesToExport, splitModules)
if err != nil {
return fmt.Errorf("error exporting state: %v", err)
}
Expand Down Expand Up @@ -96,17 +99,28 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com
},
}

// NOTE: Tendermint uses a custom JSON decoder for GenesisDoc
// (except for stuff inside AppState). Inside AppState, we're free
// to encode as protobuf or amino.
encoded, err := tmjson.Marshal(doc)
if err != nil {
return err
}
if splitModules {
wd, err := os.Getwd()
Copy link
Member

Choose a reason for hiding this comment

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

This isn't correct, as it will export it in the current directory. You don't need it as we have homeDir.

if err != nil {
return err
}

cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.OutOrStderr())
cmd.Println(string(sdk.MustSortJSON(encoded)))
if err := doc.SaveAs(filepath.Join(wd, "genesis", "genesis.json")); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

You should use homeDir instead. Are we sure we want to override the file if it exists? If so let's add an explanation in the command description.

return err
}
} else {
// NOTE: Tendermint uses a custom JSON decoder for GenesisDoc
// (except for stuff inside AppState). Inside AppState, we're free
// to encode as protobuf or amino.
encoded, err := tmjson.Marshal(doc)
if err != nil {
return err
}

cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.OutOrStderr())
cmd.Println(string(sdk.MustSortJSON(encoded)))
}
return nil
},
}
Expand All @@ -116,6 +130,7 @@ func ExportCmd(appExporter types.AppExporter, defaultNodeHome string) *cobra.Com
cmd.Flags().Bool(FlagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)")
cmd.Flags().StringSlice(FlagJailAllowedAddrs, []string{}, "Comma-separated list of operator addresses of jailed validators to unjail")
cmd.Flags().StringSlice(FlagModulesToExport, []string{}, "Comma-separated list of modules to export. If empty, will export all modules")
cmd.Flags().Bool(FlagSplitModules, false, "Export module state and store it as separate file(s) in genesis/[module] folder")

return cmd
}
2 changes: 1 addition & 1 deletion server/types/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,5 @@ type (

// AppExporter is a function that dumps all app state to
// JSON-serializable structure and returns the current validator set.
AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string, AppOptions, []string) (ExportedApp, error)
AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string, AppOptions, []string, bool) (ExportedApp, error)
)
23 changes: 23 additions & 0 deletions simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
package simapp

import (
"bytes"
_ "embed"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"

"github.com/spf13/cast"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
Expand All @@ -17,6 +20,7 @@ import (

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/runtime"
Expand Down Expand Up @@ -166,6 +170,8 @@ type SimApp struct {

// simulation manager
sm *module.SimulationManager

appOpts servertypes.AppOptions
}

func init() {
Expand Down Expand Up @@ -239,6 +245,7 @@ func NewSimApp(
}

app.App = appBuilder.Build(logger, db, traceStore, baseAppOptions...)
app.appOpts = appOpts

// configure state listening capabilities using AppOptions
// we are doing nothing with the returned streamingServices and waitGroup in this case
Expand Down Expand Up @@ -309,6 +316,22 @@ func (app *SimApp) Name() string { return app.BaseApp.Name() }

// InitChainer application update at chain initialization
func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
jsonObj := make(map[string]json.RawMessage)
jsonObj["module_genesis_state"] = []byte("true")
loadAppStateFromFolder, err := json.Marshal(jsonObj)
if err != nil {
panic(err)
}
buf := bytes.NewBuffer(nil)
if err := json.Compact(buf, req.AppStateBytes); err != nil {
panic(err)
}

if bytes.Equal(loadAppStateFromFolder, buf.Bytes()) {
homepath := cast.ToString(app.appOpts.Get(flags.FlagHome))
app.App.ModuleManager.GenesisPath = filepath.Join(homepath, "config", "genesis")
}

app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap())
return app.App.InitChainer(ctx, req)
}
Expand Down
26 changes: 24 additions & 2 deletions simapp/app_legacy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package simapp

import (
"bytes"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -195,6 +196,8 @@ type SimApp struct {

// module configurator
configurator module.Configurator

appOpts servertypes.AppOptions
}

func init() {
Expand Down Expand Up @@ -256,6 +259,7 @@ func NewSimApp(
keys: keys,
tkeys: tkeys,
memKeys: memKeys,
appOpts: appOpts,
}

app.ParamsKeeper = initParamsKeeper(appCodec, legacyAmino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey])
Expand Down Expand Up @@ -544,10 +548,28 @@ func (a *SimApp) Configurator() module.Configurator {

// InitChainer application update at chain initialization
func (app *SimApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
var genesisState GenesisState
if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
jsonObj := make(map[string]json.RawMessage)
jsonObj["module_genesis_state"] = []byte("true")
loadAppStateFromFolder, err := json.Marshal(jsonObj)
if err != nil {
panic(err)
}

buf := bytes.NewBuffer(nil)
if err := json.Compact(buf, req.AppStateBytes); err != nil {
panic(err)
}

var genesisState GenesisState
if bytes.Equal(loadAppStateFromFolder, buf.Bytes()) {
homepath := cast.ToString(app.appOpts.Get(flags.FlagHome))
app.ModuleManager.GenesisPath = filepath.Join(homepath, "config", "genesis")
} else {
if err := json.Unmarshal(req.AppStateBytes, &genesisState); err != nil {
panic(err)
}
}

app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap())
return app.ModuleManager.InitGenesis(ctx, app.appCodec, genesisState)
}
Expand Down
2 changes: 1 addition & 1 deletion simapp/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestSimAppExportAndBlockedAddrs(t *testing.T) {
logger2 := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
// Making a new app object with the db, so that initchain hasn't been called
app2 := NewSimApp(logger2, db, nil, true, simtestutil.NewAppOptionsWithFlagHome(DefaultNodeHome))
_, err := app2.ExportAppStateAndValidators(false, []string{}, []string{})
_, err := app2.ExportAppStateAndValidators(false, []string{}, []string{}, false)
require.NoError(t, err, "ExportAppStateAndValidators should not have an error")
}

Expand Down
31 changes: 28 additions & 3 deletions simapp/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"log"
"os"

tmproto "github.com/tendermint/tendermint/proto/tendermint/types"

Expand All @@ -16,7 +17,12 @@ import (

// ExportAppStateAndValidators exports the state of the application for a genesis
// file.
func (app *SimApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs []string, modulesToExport []string) (servertypes.ExportedApp, error) {
func (app *SimApp) ExportAppStateAndValidators(
forZeroHeight bool,
jailAllowedAddrs []string,
modulesToExport []string,
splitModules bool,
) (servertypes.ExportedApp, error) {
// as if they could withdraw from the start of the next block
ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()})

Expand All @@ -28,11 +34,30 @@ func (app *SimApp) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAd
app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
}

genState := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport)
appState, err := json.MarshalIndent(genState, "", " ")
wd, err := os.Getwd()
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't get the current directory.

if err != nil {
return servertypes.ExportedApp{}, err
}
app.ModuleManager.GenesisPath = wd
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
app.ModuleManager.GenesisPath = wd
app.ModuleManager.GenesisPath = filepath.Join(cast.ToString(app.appOpts.Get(flags.FlagHome)), "config", "genesis")

genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport, splitModules)
if err != nil {
return servertypes.ExportedApp{}, err
}

var appState []byte
if splitModules {
jsonObj := make(map[string]json.RawMessage)
jsonObj["module_genesis_state"] = []byte("true")
appState, err = json.MarshalIndent(jsonObj, "", " ")
if err != nil {
return servertypes.ExportedApp{}, err
}
} else {
appState, err = json.MarshalIndent(genState, "", " ")
if err != nil {
return servertypes.ExportedApp{}, err
}
}

validators, err := staking.WriteValidators(ctx, app.StakingKeeper)
return servertypes.ExportedApp{
Expand Down
Loading