diff --git a/.changeset/entries/6f0a061bd4ae3038eb8a5a71d9a3b1e6b88397a4e785bda00fec5fbd8bd0737b.yaml b/.changeset/entries/6f0a061bd4ae3038eb8a5a71d9a3b1e6b88397a4e785bda00fec5fbd8bd0737b.yaml new file mode 100644 index 0000000000..f0add99b52 --- /dev/null +++ b/.changeset/entries/6f0a061bd4ae3038eb8a5a71d9a3b1e6b88397a4e785bda00fec5fbd8bd0737b.yaml @@ -0,0 +1,6 @@ +type: fix +module: x/relationships +pull_request: 838 +description: Replaced store keys to use less disk space +backward_compatible: false +date: 2022-04-22T12:01:24.865079216Z diff --git a/x/relationships/keeper/migrations.go b/x/relationships/keeper/migrations.go index 1b406fd3a3..642422cd07 100644 --- a/x/relationships/keeper/migrations.go +++ b/x/relationships/keeper/migrations.go @@ -3,6 +3,8 @@ package keeper import ( sdk "github.com/cosmos/cosmos-sdk/types" + v2 "github.com/desmos-labs/desmos/v3/x/relationships/legacy/v2" + profilesv4 "github.com/desmos-labs/desmos/v3/x/profiles/legacy/v4" v1 "github.com/desmos-labs/desmos/v3/x/relationships/legacy/v1" @@ -28,3 +30,8 @@ func NewMigrator(keeper Keeper, pk profilesv4.Keeper) Migrator { func (m Migrator) Migrate1To2(ctx sdk.Context) error { return v1.MigrateStore(ctx, m.pk, m.keeper.storeKey, m.keeper.cdc) } + +// Migrate1To2 migrates from version 2 to 3. +func (m Migrator) Migrate2To3(ctx sdk.Context) error { + return v2.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) +} diff --git a/x/relationships/legacy/v1/keys.go b/x/relationships/legacy/v1/keys.go new file mode 100644 index 0000000000..7c3f2e170d --- /dev/null +++ b/x/relationships/legacy/v1/keys.go @@ -0,0 +1,45 @@ +package v1 + +// DONTCOVER + +import ( + subspacestypes "github.com/desmos-labs/desmos/v3/x/subspaces/types" +) + +var ( + RelationshipsStorePrefix = []byte("relationships") + UsersBlocksStorePrefix = []byte("users_blocks") +) + +// SubspaceRelationshipsPrefix returns the prefix used to store all relationships for the given subspace +func SubspaceRelationshipsPrefix(subspaceID uint64) []byte { + return append(RelationshipsStorePrefix, subspacestypes.GetSubspaceIDBytes(subspaceID)...) +} + +// UserRelationshipsSubspacePrefix returns the prefix used to store all the relationships created by the user +// with the given address for the subspace having the given id +func UserRelationshipsSubspacePrefix(subspace uint64, user string) []byte { + return append(SubspaceRelationshipsPrefix(subspace), []byte(user)...) +} + +// RelationshipsStoreKey returns the store key used to store the relationships containing the given data +func RelationshipsStoreKey(user, counterparty string, subspace uint64) []byte { + return append(UserRelationshipsSubspacePrefix(subspace, user), []byte(counterparty)...) +} + +// SubspaceBlocksPrefix returns the store prefix used to store the blocks for the given subspace +func SubspaceBlocksPrefix(subspaceID uint64) []byte { + return append(UsersBlocksStorePrefix, subspacestypes.GetSubspaceIDBytes(subspaceID)...) +} + +// BlockerSubspacePrefix returns the store prefix used to store the blocks that the given blocker +// has created inside the specified subspace +func BlockerSubspacePrefix(subspaceID uint64, blocker string) []byte { + return append(SubspaceBlocksPrefix(subspaceID), []byte(blocker)...) +} + +// UserBlockStoreKey returns the store key used to save the block made by the given blocker, +// inside the specified subspace and towards the given blocked user +func UserBlockStoreKey(blocker, blockedUser string, subspace uint64) []byte { + return append(BlockerSubspacePrefix(subspace, blocker), []byte(blockedUser)...) +} diff --git a/x/relationships/legacy/v2/store.go b/x/relationships/legacy/v2/store.go new file mode 100644 index 0000000000..16117ea29d --- /dev/null +++ b/x/relationships/legacy/v2/store.go @@ -0,0 +1,103 @@ +package v2 + +import ( + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + + v1 "github.com/desmos-labs/desmos/v3/x/relationships/legacy/v1" + "github.com/desmos-labs/desmos/v3/x/relationships/types" +) + +// MigrateStore performs in-place store migrations from v1 to v2. +// The migration includes: +// +// - migrate all relationships keys to the new ones +// - migrate all user blocks keys to the new ones +func MigrateStore(ctx sdk.Context, storeKey sdk.StoreKey, cdc codec.BinaryCodec) error { + store := ctx.KVStore(storeKey) + + err := migrateRelationships(store, cdc) + if err != nil { + return err + } + + err = migrateUserBlocks(store, cdc) + if err != nil { + return err + } + + return nil +} + +// migrateRelationships migrates all the relationships from using the old key to using the new key +func migrateRelationships(store sdk.KVStore, cdc codec.BinaryCodec) error { + prefixStore := prefix.NewStore(store, v1.RelationshipsStorePrefix) + iterator := prefixStore.Iterator(nil, nil) + + // Read all the existing relationships and the associated keys + var keys [][]byte + var relationships []types.Relationship + for ; iterator.Valid(); iterator.Next() { + keys = append(keys, append(v1.RelationshipsStorePrefix, iterator.Key()...)) + + var relationship types.Relationship + err := cdc.Unmarshal(iterator.Value(), &relationship) + if err != nil { + return err + } + relationships = append(relationships, relationship) + } + iterator.Close() + + for i, relationship := range relationships { + // Delete the old key + store.Delete(keys[i]) + + bz, err := cdc.Marshal(&relationships[i]) + if err != nil { + return err + } + + // Store the relationship with the new key + store.Set(types.RelationshipsStoreKey(relationship.Creator, relationship.Counterparty, relationship.SubspaceID), bz) + } + + return nil +} + +// migrateUserBlocks migrates all the user blocks from using the old key to using the new key +func migrateUserBlocks(store sdk.KVStore, cdc codec.BinaryCodec) error { + prefixStore := prefix.NewStore(store, v1.UsersBlocksStorePrefix) + iterator := prefixStore.Iterator(nil, nil) + + // Read all the existing blocks and the associated keys + var keys [][]byte + var blocks []types.UserBlock + for ; iterator.Valid(); iterator.Next() { + keys = append(keys, append(v1.UsersBlocksStorePrefix, iterator.Key()...)) + + var block types.UserBlock + err := cdc.Unmarshal(iterator.Value(), &block) + if err != nil { + return err + } + blocks = append(blocks, block) + } + iterator.Close() + + for i, block := range blocks { + // Delete the old key + store.Delete(keys[i]) + + bz, err := cdc.Marshal(&blocks[i]) + if err != nil { + return err + } + + // Store the block with the new key + store.Set(types.UserBlockStoreKey(block.Blocker, block.Blocked, block.SubspaceID), bz) + } + + return nil +} diff --git a/x/relationships/legacy/v2/store_test.go b/x/relationships/legacy/v2/store_test.go new file mode 100644 index 0000000000..1f620e978d --- /dev/null +++ b/x/relationships/legacy/v2/store_test.go @@ -0,0 +1,115 @@ +package v2_test + +import ( + "testing" + + v2 "github.com/desmos-labs/desmos/v3/x/relationships/legacy/v2" + + "github.com/cosmos/cosmos-sdk/testutil" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + + "github.com/desmos-labs/desmos/v3/app" + v1 "github.com/desmos-labs/desmos/v3/x/relationships/legacy/v1" + "github.com/desmos-labs/desmos/v3/x/relationships/types" +) + +func TestMigrateStore(t *testing.T) { + cdc, _ := app.MakeCodecs() + storeKey := sdk.NewKVStoreKey(types.StoreKey) + testCases := []struct { + name string + store func(ctx sdk.Context) + shouldErr bool + check func(ctx sdk.Context) + }{ + { + name: "relationships are migrated properly", + store: func(ctx sdk.Context) { + store := ctx.KVStore(storeKey) + + relBz := cdc.MustMarshal(&types.Relationship{ + Creator: "user", + Counterparty: "counterparty", + SubspaceID: 1, + }) + store.Set(v1.RelationshipsStoreKey("user", "counterparty", 1), relBz) + }, + shouldErr: false, + check: func(ctx sdk.Context) { + store := ctx.KVStore(storeKey) + + // Make sure the old keys are deleted properly + v1Key := v1.RelationshipsStoreKey("user", "counterparty", 1) + require.False(t, store.Has(v1Key)) + + v2Key := types.RelationshipsStoreKey("user", "counterparty", 1) + require.True(t, store.Has(v2Key)) + + var stored types.Relationship + err := cdc.Unmarshal(store.Get(v2Key), &stored) + require.NoError(t, err) + require.Equal(t, types.Relationship{ + Creator: "user", + Counterparty: "counterparty", + SubspaceID: 1, + }, stored) + }, + }, + { + name: "user blocks are migrated properly", + store: func(ctx sdk.Context) { + store := ctx.KVStore(storeKey) + + blockBz := cdc.MustMarshal(&types.UserBlock{ + Blocker: "blocker", + Blocked: "blocked", + Reason: "reason", + SubspaceID: 1, + }) + store.Set(v1.UserBlockStoreKey("blocker", "blocked", 1), blockBz) + }, + shouldErr: false, + check: func(ctx sdk.Context) { + store := ctx.KVStore(storeKey) + + // Make sure the old keys are deleted properly + v1Key := v1.UserBlockStoreKey("blocker", "blocked", 1) + require.False(t, store.Has(v1Key)) + + v2Key := types.UserBlockStoreKey("blocker", "blocked", 1) + require.True(t, store.Has(v2Key)) + + var stored types.UserBlock + err := cdc.Unmarshal(store.Get(v2Key), &stored) + require.NoError(t, err) + require.Equal(t, types.UserBlock{ + Blocker: "blocker", + Blocked: "blocked", + Reason: "reason", + SubspaceID: 1, + }, stored) + }, + }, + } + + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + ctx := testutil.DefaultContext(storeKey, sdk.NewTransientStoreKey("test")) + if tc.store != nil { + tc.store(ctx) + } + + err := v2.MigrateStore(ctx, storeKey, cdc) + if tc.shouldErr { + require.Error(t, err) + } else { + require.NoError(t, err) + if tc.check != nil { + tc.check(ctx) + } + } + }) + } +} diff --git a/x/relationships/module.go b/x/relationships/module.go index 7641ca8a00..37db08910f 100644 --- a/x/relationships/module.go +++ b/x/relationships/module.go @@ -33,7 +33,7 @@ import ( ) const ( - consensusVersion = 2 + consensusVersion = 3 ) // type check to ensure the interface is properly implemented @@ -118,6 +118,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err != nil { panic(err) } + err = cfg.RegisterMigration(types.ModuleName, 2, m.Migrate2To3) + if err != nil { + panic(err) + } } // NewAppModule creates a new AppModule Object diff --git a/x/relationships/types/keys.go b/x/relationships/types/keys.go index 61db1bc08c..b7ce62c91c 100644 --- a/x/relationships/types/keys.go +++ b/x/relationships/types/keys.go @@ -18,8 +18,8 @@ const ( ) var ( - RelationshipsStorePrefix = []byte("relationships") - UsersBlocksStorePrefix = []byte("users_blocks") + RelationshipsStorePrefix = []byte{0x01} + UsersBlocksStorePrefix = []byte{0x02} ) // SubspaceRelationshipsPrefix returns the prefix used to store all relationships for the given subspace