Skip to content

Commit

Permalink
Merge PR cosmos#4471: Migrate genesis cmd
Browse files Browse the repository at this point in the history
  • Loading branch information
sabau authored and alexanderbez committed Jul 3, 2019
1 parent 4e86810 commit 00f753d
Show file tree
Hide file tree
Showing 12 changed files with 1,353 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .pending/features/sdk/4409-migration-scrip
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#4409 Implement a command that migrates exported state from one version to the next.
The `migrate` command currently supports migrating from v0.34 to v0.36 by implementing
necessary types for both versions.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@

def process_raw_genesis(genesis, parsed_args):
# update genesis with breaking changes
genesis['consensus_params']['block'] = genesis['consensus_params']['block_size']
del genesis['consensus_params']['block_size']
if 'block_size' in genesis['consensus_params']:
genesis['consensus_params']['block'] = genesis['consensus_params']['block_size']
del genesis['consensus_params']['block_size']

genesis['app_state']['crisis'] = {
'constant_fee': {
Expand Down
8 changes: 8 additions & 0 deletions docs/clients/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ Light-clients enable users to interact with your application without having to d

- [Command-Line interface for SDK-based blockchain](./cli.md)
- [Service provider doc](./service-providers.md)

## Genesis upgrade

If you need to upgrade your node you could export the genesis and migrate it to the new version through this script:

```bash
<appbinary> migrate v0.36 genesis_0_34.json [--time "2019-04-22T17:00:11Z"] [--chain-id test] > ~/.gaiad/genesis.json
```
87 changes: 87 additions & 0 deletions x/genutil/client/cli/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package cli

import (
"fmt"
"time"

"github.com/spf13/cobra"
"github.com/tendermint/tendermint/types"

"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/version"
extypes "github.com/cosmos/cosmos-sdk/x/genutil"
"github.com/cosmos/cosmos-sdk/x/genutil/legacy/v036"
)

var migrationMap = extypes.MigrationMap{
"v0.36": v036.Migrate,
}

const (
flagGenesisTime = "genesis-time"
flagChainId = "chain-id"
)

func MigrateGenesisCmd(_ *server.Context, cdc *codec.Codec) *cobra.Command {
cmd := &cobra.Command{
Use: "migrate [target-version] [genesis-file]",
Short: "Migrate genesis to a specified target version",
Long: fmt.Sprintf(`Migrate the source genesis into the target version and print to STDOUT.
Example:
$ %s migrate v0.36 /path/to/genesis.json --chain-id=cosmoshub-3 --genesis-time=2019-04-22T17:00:00Z
`, version.ServerName),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
target := args[0]
importGenesis := args[1]

genDoc, err := types.GenesisDocFromFile(importGenesis)
if err != nil {
return err
}

var initialState extypes.AppMap
cdc.MustUnmarshalJSON(genDoc.AppState, &initialState)

if migrationMap[target] == nil {
return fmt.Errorf("unknown migration function version: %s", target)
}

newGenState := migrationMap[target](initialState, cdc)
genDoc.AppState = cdc.MustMarshalJSON(newGenState)

genesisTime := cmd.Flag(flagGenesisTime).Value.String()
if genesisTime != "" {
var t time.Time

err := t.UnmarshalText([]byte(genesisTime))
if err != nil {
return err
}

genDoc.GenesisTime = t
}

chainId := cmd.Flag(flagChainId).Value.String()
if chainId != "" {
genDoc.ChainID = chainId
}

out, err := cdc.MarshalJSONIndent(genDoc, "", " ")
if err != nil {
return err
}

fmt.Println(string(out))
return nil

},
}

cmd.Flags().String(flagGenesisTime, "", "Override genesis_time with this flag")
cmd.Flags().String(flagChainId, "", "Override chain_id with this flag")

return cmd
}
58 changes: 58 additions & 0 deletions x/genutil/client/cli/migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cli

import (
"io/ioutil"
"path"
"testing"

"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
"github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/log"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/server"
"github.com/cosmos/cosmos-sdk/tests"
)

func setupCmd(genesisTime string, chainId string) *cobra.Command {
c := &cobra.Command{
Use: "c",
Args: cobra.ArbitraryArgs,
Run: func(_ *cobra.Command, args []string) {},
}

c.Flags().String(flagGenesisTime, genesisTime, "")
c.Flags().String(flagChainId, chainId, "")

return c
}

func TestMigrateGenesis(t *testing.T) {
home, cleanup := tests.NewTestCaseDir(t)
viper.Set(cli.HomeFlag, home)
viper.Set(client.FlagName, "moniker")
logger := log.NewNopLogger()
cfg, err := tcmd.ParseConfig()
require.Nil(t, err)
ctx := server.NewContext(cfg, logger)
cdc := makeCodec()

genesisPath := path.Join(home, "genesis.json")
target := "v0.36"

defer cleanup()

// Reject if we dont' have the right parameters or genesis does not exists
require.Error(t, MigrateGenesisCmd(ctx, cdc).RunE(nil, []string{target, genesisPath}))

// Noop migration with minimal genesis
emptyGenesis := []byte(`{"chain_id":"test","app_state":{}}`)
err = ioutil.WriteFile(genesisPath, emptyGenesis, 0644)
require.Nil(t, err)
cmd := setupCmd("", "test2")
require.NoError(t, MigrateGenesisCmd(ctx, cdc).RunE(cmd, []string{target, genesisPath}))
// Every migration function shuold tests its own module separately
}
26 changes: 26 additions & 0 deletions x/genutil/legacy/v036/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package v036

import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/x/genutil"
v034gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v034"
v036gov "github.com/cosmos/cosmos-sdk/x/gov/legacy/v036"
)

// Migrate migrates exported state from v0.34 to a v0.36 genesis state.
func Migrate(appState genutil.AppMap, cdc *codec.Codec) genutil.AppMap {
v034Codec := codec.New()
codec.RegisterCrypto(v034Codec)
v036Codec := codec.New()
codec.RegisterCrypto(v036Codec)

if appState[v034gov.ModuleName] != nil {
var govState v034gov.GenesisState
v034gov.RegisterCodec(v034Codec)
v034Codec.MustUnmarshalJSON(appState[v034gov.ModuleName], &govState)
v036gov.RegisterCodec(v036Codec)
delete(appState, v034gov.ModuleName) // Drop old key, in case it changed name
appState[v036gov.ModuleName] = v036Codec.MustMarshalJSON(v036gov.MigrateGovernance(govState))
}
return appState
}
109 changes: 109 additions & 0 deletions x/genutil/legacy/v036/migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package v036

import (
"testing"

"github.com/stretchr/testify/require"
"github.com/tendermint/go-amino"

"github.com/cosmos/cosmos-sdk/x/genutil"
)

var basic034Gov = []byte(`
{
"starting_proposal_id": "2",
"deposits": [
{
"proposal_id": "1",
"deposit": {
"depositor": "cosmos1grgelyng2v6v3t8z87wu3sxgt9m5s03xvslewd",
"proposal_id": "1",
"amount": [
{
"denom": "uatom",
"amount": "512000000"
}
]
}
}
],
"votes" : [
{
"proposal_id": "1",
"vote": {
"voter": "cosmos1lktjhnzkpkz3ehrg8psvmwhafg56kfss5597tg",
"proposal_id": "1",
"option": "Yes"
}
}
],
"proposals": [
{
"proposal_content": {
"type": "gov/TextProposal",
"value": {
"title": "test",
"description": "test"
}
},
"proposal_id": "1",
"proposal_status": "Passed",
"final_tally_result": {
"yes": "1",
"abstain": "0",
"no": "0",
"no_with_veto": "0"
},
"submit_time": "2019-05-03T21:08:25.443199036Z",
"deposit_end_time": "2019-05-17T21:08:25.443199036Z",
"total_deposit": [
{
"denom": "uatom",
"amount": "512000000"
}
],
"voting_start_time": "2019-05-04T16:02:33.24680295Z",
"voting_end_time": "2019-05-18T16:02:33.24680295Z"
}
],
"deposit_params": {
"min_deposit": [
{
"denom": "uatom",
"amount": "512000000"
}
],
"max_deposit_period": "1209600000000000"
},
"voting_params": {
"voting_period": "1209600000000000"
},
"tally_params": {
"quorum": "0.400000000000000000",
"threshold": "0.500000000000000000",
"veto": "0.334000000000000000"
}
}
`)

func TestDummyGenesis(t *testing.T) {
genesisDummy := genutil.AppMap{
"foo": {},
"bar": []byte(`{"custom": "module"}`),
}
cdc := amino.NewCodec()
migratedDummy := Migrate(genesisDummy, cdc)

// We should not touch custom modules in the map
require.Equal(t, genesisDummy["foo"], migratedDummy["foo"])
require.Equal(t, genesisDummy["bar"], migratedDummy["bar"])
}

func TestGovGenesis(t *testing.T) {
genesis := genutil.AppMap{
"gov": basic034Gov,
}
cdc := amino.NewCodec()

require.NotPanics(t, func() { Migrate(genesis, cdc) })
}
16 changes: 16 additions & 0 deletions x/genutil/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package genutil

import (
"encoding/json"

"github.com/cosmos/cosmos-sdk/codec"
)

type (
// AppMap map modules names with their json raw representation
AppMap map[string]json.RawMessage
// MigrationCallback converts a genesis map from the previous version to the targeted one
MigrationCallback func(AppMap, *codec.Codec) AppMap
// MigrationMap defines a mapping from a version to a MigrationCallback
MigrationMap map[string]MigrationCallback
)
Loading

0 comments on commit 00f753d

Please sign in to comment.