From cfd79fc39edc24d6c40d2ddaad5a14a7989b4faf Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Tue, 29 Nov 2022 15:00:14 +0100 Subject: [PATCH] feat: add Genesis JSON migration for group (#14024) --- x/auth/migrations/v5/doc.go | 4 ++ x/auth/module.go | 7 ++- x/auth/types/credentials.go | 7 +-- x/genutil/migrations/v047/migrate.go | 21 +++++-- x/group/migrations/v2/gen_state.go | 57 ++++++++++++++++++ x/group/migrations/v2/gen_state_test.go | 78 +++++++++++++++++++++++++ 6 files changed, 164 insertions(+), 10 deletions(-) create mode 100644 x/auth/migrations/v5/doc.go create mode 100644 x/group/migrations/v2/gen_state.go create mode 100644 x/group/migrations/v2/gen_state_test.go diff --git a/x/auth/migrations/v5/doc.go b/x/auth/migrations/v5/doc.go new file mode 100644 index 0000000000..0e04902f80 --- /dev/null +++ b/x/auth/migrations/v5/doc.go @@ -0,0 +1,4 @@ +// v5 is an empty package that exists because of the group module. +// the group module v2 migration actually migrates the auth module state (replace group policies accounts from module accounts to base accounts). +// the auth state does not migrate if the group module is not enabled. +package v5 diff --git a/x/auth/module.go b/x/auth/module.go index 3d62fd6fa9..ee1cbd137f 100644 --- a/x/auth/module.go +++ b/x/auth/module.go @@ -31,7 +31,7 @@ import ( ) // ConsensusVersion defines the current x/auth module consensus version. -const ConsensusVersion = 4 +const ConsensusVersion = 5 var ( _ module.AppModule = AppModule{} @@ -145,6 +145,11 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(types.ModuleName, 3, m.Migrate3to4); err != nil { panic(fmt.Sprintf("failed to migrate x/%s from version 3 to 4: %v", types.ModuleName, err)) } + + // see migrations/v5/doc.go + if err := cfg.RegisterMigration(types.ModuleName, 4, func(ctx sdk.Context) error { return nil }); err != nil { + panic(fmt.Sprintf("failed to migrate x/%s from version 4 to 5: %v", types.ModuleName, err)) + } } // InitGenesis performs genesis initialization for the auth module. It returns diff --git a/x/auth/types/credentials.go b/x/auth/types/credentials.go index 7632689621..0d0629e2b8 100644 --- a/x/auth/types/credentials.go +++ b/x/auth/types/credentials.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "fmt" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" @@ -76,10 +77,8 @@ func (m *ModuleCredential) Equals(other cryptotypes.PubKey) bool { } for i := range m.DerivationKeys { - for j := range m.DerivationKeys[i] { - if m.DerivationKeys[i][j] != om.DerivationKeys[i][j] { - return false - } + if !bytes.Equal(m.DerivationKeys[i], om.DerivationKeys[i]) { + return false } } diff --git a/x/genutil/migrations/v047/migrate.go b/x/genutil/migrations/v047/migrate.go index 699b24a0a5..8bfdff608a 100644 --- a/x/genutil/migrations/v047/migrate.go +++ b/x/genutil/migrations/v047/migrate.go @@ -2,11 +2,14 @@ package v047 import ( "github.com/cosmos/cosmos-sdk/client" + v1auth "github.com/cosmos/cosmos-sdk/x/auth/migrations/v1" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bankv4 "github.com/cosmos/cosmos-sdk/x/bank/migrations/v4" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/cosmos/cosmos-sdk/x/genutil/types" v4gov "github.com/cosmos/cosmos-sdk/x/gov/migrations/v4" - v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + govv1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1" + groupv2 "github.com/cosmos/cosmos-sdk/x/group/migrations/v2" ) // Migrate migrates exported state from v0.46 to a v0.47 genesis state. @@ -14,15 +17,15 @@ func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { // Migrate x/bank. bankState := appState[banktypes.ModuleName] if len(bankState) > 0 { - var oldBankState *banktypes.GenesisState - clientCtx.Codec.MustUnmarshalJSON(bankState, oldBankState) - newBankState := bankv4.MigrateGenState(oldBankState) + var oldBankState banktypes.GenesisState + clientCtx.Codec.MustUnmarshalJSON(bankState, &oldBankState) + newBankState := bankv4.MigrateGenState(&oldBankState) appState[banktypes.ModuleName] = clientCtx.Codec.MustMarshalJSON(newBankState) } if govOldState, ok := appState[v4gov.ModuleName]; ok { // unmarshal relative source genesis application state - var old v1.GenesisState + var old govv1.GenesisState clientCtx.Codec.MustUnmarshalJSON(govOldState, &old) // delete deprecated x/gov genesis state @@ -36,5 +39,13 @@ func Migrate(appState types.AppMap, clientCtx client.Context) types.AppMap { appState[v4gov.ModuleName] = clientCtx.Codec.MustMarshalJSON(new) } + // Migrate x/auth group policy accounts + if authOldState, ok := appState[v1auth.ModuleName]; ok { + var old authtypes.GenesisState + clientCtx.Codec.MustUnmarshalJSON(authOldState, &old) + newAuthState := groupv2.MigrateGenState(&old) + appState[v1auth.ModuleName] = clientCtx.Codec.MustMarshalJSON(newAuthState) + } + return appState } diff --git a/x/group/migrations/v2/gen_state.go b/x/group/migrations/v2/gen_state.go new file mode 100644 index 0000000000..a8bce3a1be --- /dev/null +++ b/x/group/migrations/v2/gen_state.go @@ -0,0 +1,57 @@ +package v2 + +import ( + "encoding/binary" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +// MigrateGenState accepts exported v0.46 x/auth genesis state and migrates it to +// v0.47 x/auth genesis state. The migration includes: +// - If the group module is enabled, replace group policy accounts from module accounts to base accounts. +func MigrateGenState(oldState *authtypes.GenesisState) *authtypes.GenesisState { + newState := *oldState + + accounts, err := authtypes.UnpackAccounts(newState.Accounts) + if err != nil { + panic(err) + } + + groupPolicyAccountCounter := uint64(0) + for i, acc := range accounts { + modAcc, ok := acc.(authtypes.ModuleAccountI) + if !ok { + continue + } + + if modAcc.GetName() != modAcc.GetAddress().String() { + continue + } + + // Replace group policy accounts from module accounts to base accounts. + // These accounts were wrongly created and the address was equal to the module name. + derivationKey := make([]byte, 8) + binary.BigEndian.PutUint64(derivationKey, groupPolicyAccountCounter) + + baseAccount, err := authtypes.NewBaseAccountWithPubKey( + authtypes.NewModuleCredential(ModuleName, [][]byte{{GroupPolicyTablePrefix}, derivationKey}), + ) + if err != nil { + panic(err) + } + + if err := baseAccount.SetAccountNumber(modAcc.GetAccountNumber()); err != nil { + panic(err) + } + accounts[i] = baseAccount + groupPolicyAccountCounter++ + } + + packedAccounts, err := authtypes.PackAccounts(accounts) + if err != nil { + panic(err) + } + newState.Accounts = packedAccounts + + return &newState +} diff --git a/x/group/migrations/v2/gen_state_test.go b/x/group/migrations/v2/gen_state_test.go new file mode 100644 index 0000000000..20d8aabd18 --- /dev/null +++ b/x/group/migrations/v2/gen_state_test.go @@ -0,0 +1,78 @@ +package v2_test + +import ( + "encoding/binary" + "testing" + + "github.com/stretchr/testify/require" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/cosmos-sdk/x/group" + v2 "github.com/cosmos/cosmos-sdk/x/group/migrations/v2" +) + +func TestMigrateGenState(t *testing.T) { + tests := []struct { + name string + oldState *authtypes.GenesisState + newState *authtypes.GenesisState + }{ + { + name: "group policy accounts are replaced by base accounts", + oldState: authtypes.NewGenesisState(authtypes.DefaultParams(), authtypes.GenesisAccounts{ + &authtypes.ModuleAccount{ + BaseAccount: &authtypes.BaseAccount{ + Address: "cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl", + AccountNumber: 3, + }, + Name: "distribution", + Permissions: []string{}, + }, + &authtypes.ModuleAccount{ + BaseAccount: &authtypes.BaseAccount{ + Address: "cosmos1q32tjg5qm3n9fj8wjgpd7gl98prefntrckjkyvh8tntp7q33zj0s5tkjrk", + AccountNumber: 8, + }, + Name: "cosmos1q32tjg5qm3n9fj8wjgpd7gl98prefntrckjkyvh8tntp7q33zj0s5tkjrk", + Permissions: []string{}, + }, + }), + newState: authtypes.NewGenesisState(authtypes.DefaultParams(), authtypes.GenesisAccounts{ + &authtypes.ModuleAccount{ + BaseAccount: &authtypes.BaseAccount{ + Address: "cosmos1jv65s3grqf6v6jl3dp4t6c9t9rk99cd88lyufl", + AccountNumber: 3, + }, + Name: "distribution", + Permissions: []string{}, + }, + func() *authtypes.BaseAccount { + baseAccount := &authtypes.BaseAccount{ + Address: "cosmos1q32tjg5qm3n9fj8wjgpd7gl98prefntrckjkyvh8tntp7q33zj0s5tkjrk", + AccountNumber: 8, + } + + derivationKey := make([]byte, 8) + binary.BigEndian.PutUint64(derivationKey, 0) + + err := baseAccount.SetPubKey(authtypes.NewModuleCredential(group.ModuleName, [][]byte{{v2.GroupPolicyTablePrefix}, derivationKey})) + if err != nil { + panic(err) + } + + return baseAccount + }(), + }, + ), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + require.Error(t, authtypes.ValidateGenesis(*tc.oldState)) + actualState := v2.MigrateGenState(tc.oldState) + require.Equal(t, tc.newState, actualState) + require.NoError(t, authtypes.ValidateGenesis(*actualState)) + }) + } +}