diff --git a/CHANGELOG.md b/CHANGELOG.md index 280699861e..98512d49ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,14 @@ # Version 0.12.0 ## Changes -- Changed `relationships`' implementation adding a `subspace` field to identify in which app - users make relationships (#266) -- Implemented the possibility to (un)blocking a specific user (#169) +- Changed `relationships`' implementation adding a `subspace` field to identify in which app users make relationships (#266) +- Implemented the possibility to (un)block a specific user (#169) - Allow users to edit their DTag (#226) - Allow users to give their DTag away (#225) # Version 0.11.0 ## Changes - Allowed the possibility to edit a post's attachments and poll data using the `MsgEditPost` type (#202) -- Removed the `Open` field from within the `PollData` object. Now you should rely on the `CloseDate` field to determine whether a poll is closed or open. (#252) +- Removed the `Open` field from within the `PollData` object. Now you should rely on the `CloseDate` field to determine whether a poll is close or open. (#252) - Implemented users `Relationships` (#168) # Version 0.10.0 diff --git a/app/app.go b/app/app.go index 2bf370565d..01e475b6d0 100644 --- a/app/app.go +++ b/app/app.go @@ -23,12 +23,13 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/supply" - "github.com/desmos-labs/desmos/x/relationships" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" dbm "github.com/tendermint/tm-db" + "github.com/desmos-labs/desmos/x/relationships" + "github.com/cosmos/cosmos-sdk/x/upgrade" upgradeclient "github.com/cosmos/cosmos-sdk/x/upgrade/client" @@ -368,6 +369,14 @@ func NewDesmosApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest app.mm.RegisterInvariants(&app.CrisisKeeper) app.mm.RegisterRoutes(app.Router(), app.QueryRouter()) + // Register the upgrade handler for the september update + app.upgradeKeeper.SetUpgradeHandler("september-upgrade", func(ctx sdk.Context, plan upgrade.Plan) { + // Migrate the posts from v0.10.0 to v0.12.0 + if err := app.postsKeeper.MigratePostsFrom0100To0120(ctx); err != nil { + panic(err) + } + }) + // create the simulation manager and define the order of the modules for deterministic simulations // // NOTE: this is not required apps that don't use the simulator for fuzz testing diff --git a/x/posts/keeper/legacy/v0.10.0/types.go b/x/posts/keeper/legacy/v0.10.0/types.go new file mode 100644 index 0000000000..a42b612610 --- /dev/null +++ b/x/posts/keeper/legacy/v0.10.0/types.go @@ -0,0 +1,56 @@ +package v0100 + +import ( + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// PostID represents a unique post id +type PostID string + +// OptionalData represents a Posts' optional data and allows for custom +// Amino and JSON serialization and deserialization. +type OptionalData map[string]string + +// Attachment contains the information representing any type of file provided with a post. +// This file can be an image or a multimedia file (vocals, video, documents, etc.). +type Attachment struct { + URI string `json:"uri" yaml:"uri"` + MimeType string `json:"mime_type" yaml:"mime_type"` + Tags []sdk.AccAddress `json:"tags,omitempty" yaml:"tags,omitempty"` +} + +// PollData contains the information of a poll that is associated to a post +type PollData struct { + Question string `json:"question" yaml:"question"` // Describes what poll is about + ProvidedAnswers []PollAnswer `json:"provided_answers" yaml:"provided_answers"` // Lists of answers provided by the creator + EndDate time.Time `json:"end_date" yaml:"end_date"` // RFC3339 date at which the poll will no longer accept new answers + Open bool `json:"is_open" yaml:"is_open"` // Tells if the poll is still accepting answers + AllowsMultipleAnswers bool `json:"allows_multiple_answers" yaml:"allows_multiple_answers"` // Tells if the poll is a single or multiple answers one + AllowsAnswerEdits bool `json:"allows_answer_edits" yaml:"allows_answer_edits"` // Tells if the poll allows answer edits +} + +// PollAnswer contains the data of a single poll answer inserted by the creator +type PollAnswer struct { + ID AnswerID `json:"id" yaml:"id"` // Unique id inside the post, serialized as a string for Javascript compatibility + Text string `json:"text" yaml:"text"` // Text of the answer +} + +// AnswerID represents a unique answer id +type AnswerID uint64 + +// Post is a struct of a post +type Post struct { + PostID PostID `json:"id" yaml:"id" ` // Unique id + ParentID PostID `json:"parent_id" yaml:"parent_id"` // Post of which this one is a comment + Message string `json:"message" yaml:"message"` // Message contained inside the post + Created time.Time `json:"created" yaml:"created"` // RFC3339 date at which the post has been created + LastEdited time.Time `json:"last_edited" yaml:"last_edited"` // RFC3339 date at which the post has been edited the last time + AllowsComments bool `json:"allows_comments" yaml:"allows_comments"` // Tells if users can reference this PostID as the parent + Subspace string `json:"subspace" yaml:"subspace"` // Identifies the application that has posted the message + OptionalData OptionalData `json:"optional_data,omitempty" yaml:"optional_data,omitempty"` // Arbitrary data that can be used from the developers + Creator sdk.AccAddress `json:"creator" yaml:"creator"` // Creator of the Post + Attachments []Attachment `json:"attachments,omitempty" yaml:"attachments,omitempty"` // Contains all the attachments that are shared with the post + PollData *PollData `json:"poll_data,omitempty" yaml:"poll_data,omitempty"` // Contains the poll details, if existing +} diff --git a/x/posts/keeper/v0120migration.go b/x/posts/keeper/v0120migration.go new file mode 100644 index 0000000000..7055dd5bea --- /dev/null +++ b/x/posts/keeper/v0120migration.go @@ -0,0 +1,92 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + v0100 "github.com/desmos-labs/desmos/x/posts/keeper/legacy/v0.10.0" + "github.com/desmos-labs/desmos/x/posts/types" +) + +// MigratePostsFrom0100To0120 migrates all the posts from v0.10.0 to v.12.0. +// To do this it executes the following operations one post at a time: +// 1. It reads the old post +// 2. It converts the post removing the Open field from the PollData, if any +// 3. It saves the post inside the store again +func (k Keeper) MigratePostsFrom0100To0120(ctx sdk.Context) error { + store := ctx.KVStore(k.StoreKey) + iterator := sdk.KVStorePrefixIterator(store, types.PostStorePrefix) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + postKey := iterator.Value() + + // Get the v0.10.0 post + var v0100Post v0100.Post + err := k.Cdc.UnmarshalBinaryBare(postKey, &v0100Post) + if err != nil { + return err + } + + // Convert the post + v0120Post := types.Post{ + PostID: types.PostID(v0100Post.PostID), + ParentID: types.PostID(v0100Post.ParentID), + Message: v0100Post.Message, + Created: v0100Post.Created, + LastEdited: v0100Post.LastEdited, + AllowsComments: v0100Post.AllowsComments, + Subspace: v0100Post.Subspace, + OptionalData: types.OptionalData(v0100Post.OptionalData), + Creator: v0100Post.Creator, + Attachments: migrateAttachments(v0100Post.Attachments), + PollData: migratePollData(v0100Post.PollData), + } + + bz, err := k.Cdc.MarshalBinaryBare(&v0120Post) + if err != nil { + return err + } + + // Store the post + store.Set(postKey, bz) + + } + + return nil +} + +// migrateAttachments migrates the given attachments from v0.10.0 to v0.12.0 +func migrateAttachments(attachments []v0100.Attachment) types.Attachments { + var v1200Attachments = make([]types.Attachment, len(attachments)) + for index, attachment := range attachments { + v1200Attachments[index] = types.Attachment(attachment) + } + return v1200Attachments +} + +// migratePollData migrates the given pollData from v0.10.0 to v0.12.0 +func migratePollData(pollData *v0100.PollData) *types.PollData { + if pollData == nil { + return nil + } + + return &types.PollData{ + Question: pollData.Question, + ProvidedAnswers: migrateProvidedAnswers(pollData.ProvidedAnswers), + EndDate: pollData.EndDate, + AllowsMultipleAnswers: pollData.AllowsMultipleAnswers, + AllowsAnswerEdits: pollData.AllowsAnswerEdits, + } +} + +// migrateProvidedAnswers migrates the providedAnswers from v0.10.0 to v0.12.0 +func migrateProvidedAnswers(providedAnswers []v0100.PollAnswer) types.PollAnswers { + var v0120Answers = make([]types.PollAnswer, len(providedAnswers)) + for index, answer := range providedAnswers { + v0120Answers[index] = types.PollAnswer{ + ID: types.AnswerID(answer.ID), + Text: answer.Text, + } + } + return v0120Answers +} diff --git a/x/posts/types/msgs/msgs.go b/x/posts/types/msgs/msgs.go index 77a7157c96..5785642edd 100644 --- a/x/posts/types/msgs/msgs.go +++ b/x/posts/types/msgs/msgs.go @@ -20,14 +20,14 @@ import ( // MsgCreatePost defines a CreatePost message type MsgCreatePost struct { - ParentID models.PostID `json:"parent_id" yaml:"parent_id"` - Message string `json:"message" yaml:"message"` - AllowsComments bool `json:"allows_comments" yaml:"allows_comments"` - Subspace string `json:"subspace" yaml:"subspace"` - OptionalData map[string]string `json:"optional_data,omitempty" yaml:"optional_data,omitempty"` - Creator sdk.AccAddress `json:"creator" yaml:"creator"` - Attachments models.Attachments `json:"attachments,omitempty" yaml:"attachments,omitempty"` - PollData *models.PollData `json:"poll_data,omitempty" yaml:"poll_data,omitempty"` + ParentID models.PostID `json:"parent_id" yaml:"parent_id"` + Message string `json:"message" yaml:"message"` + AllowsComments bool `json:"allows_comments" yaml:"allows_comments"` + Subspace string `json:"subspace" yaml:"subspace"` + OptionalData models.OptionalData `json:"optional_data,omitempty" yaml:"optional_data,omitempty"` + Creator sdk.AccAddress `json:"creator" yaml:"creator"` + Attachments models.Attachments `json:"attachments,omitempty" yaml:"attachments,omitempty"` + PollData *models.PollData `json:"poll_data,omitempty" yaml:"poll_data,omitempty"` } // NewMsgCreatePost is a constructor function for MsgCreatePost diff --git a/x/posts/types/msgs/msgs_test.go b/x/posts/types/msgs/msgs_test.go index 963ce96d75..025a7c3c37 100644 --- a/x/posts/types/msgs/msgs_test.go +++ b/x/posts/types/msgs/msgs_test.go @@ -449,6 +449,14 @@ func TestMsgCreatePost_GetSigners(t *testing.T) { require.Equal(t, msgCreatePost.Creator, actual[0]) } +func TestMsgCreatePost_ReadJSON(t *testing.T) { + json := `{"type":"desmos/MsgCreatePost","value":{"parent_id":"","message":"","allows_comments":true,"subspace":"2bdf5932925584b9a86470bea60adce69041608a447f84a3317723aa5678ec88","optional_data":{"local_id":"2020-09-15T10:17:54.101972"},"creator":"cosmos10txl52f64zmp2j7eywawlv9t4xxc4e0wnjlhq9","poll_data":{"question":"What is it better?","end_date":"2020-10-15T08:17:45.639Z","is_open":true,"allows_multiple_answers":false,"allows_answer_edits":false,"provided_answers":[{"id":"0","text":"Sushi\t"},{"id":"1","text":"Pizza"}]}}}` + + var msg msgs.MsgCreatePost + err := msgs.MsgsCodec.UnmarshalJSON([]byte(json), &msg) + require.NoError(t, err) +} + // ---------------------- // --- MsgEditPost // ---------------------- diff --git a/x/profiles/simulation/operations_dtag_transfer.go b/x/profiles/simulation/operations_dtag_transfer.go index f3291d4df6..5e1afaf613 100644 --- a/x/profiles/simulation/operations_dtag_transfer.go +++ b/x/profiles/simulation/operations_dtag_transfer.go @@ -86,7 +86,7 @@ func randomDtagRequestTransferFields( randomDTag := RandomDTag(r) req := types.NewDTagTransferRequest(randomDTag, currentOwner.Address, receivingUser.Address) - k.AssociateDtagWithAddress(ctx, randomDTag, currentOwner.Address) + _ = k.SaveProfile(ctx, types.NewProfile(randomDTag, currentOwner.Address, ctx.BlockTime())) // skip if requests already exists requests := k.GetUserDTagTransferRequests(ctx, currentOwner.Address) diff --git a/x/relationships/simulation/genesis.go b/x/relationships/simulation/genesis.go index e97a9f89c7..501d3dff47 100644 --- a/x/relationships/simulation/genesis.go +++ b/x/relationships/simulation/genesis.go @@ -20,7 +20,7 @@ func RandomizedGenState(simsState *module.SimulationState) { // randomRelationships returns randomly generated genesis relationships and their associated users - IDs map func randomRelationships(simState *module.SimulationState) map[string]types.Relationships { - relationshipsNumber := simState.Rand.Intn(sim.RandIntBetween(simState.Rand, 1, 100)) + relationshipsNumber := simState.Rand.Intn(sim.RandIntBetween(simState.Rand, 1, 30)) usersRelationships := map[string]types.Relationships{} subspace := "4e188d9c17150037d5199bbdb91ae1eb2a78a15aca04cb35530cccb81494b36e" @@ -37,7 +37,7 @@ func randomRelationships(simState *module.SimulationState) map[string]types.Rela // randomUsersBlocks func randomUsersBlocks(simState *module.SimulationState) []types.UserBlock { - usersBlocksNumber := simState.Rand.Intn(sim.RandIntBetween(simState.Rand, 1, 100)) + usersBlocksNumber := simState.Rand.Intn(sim.RandIntBetween(simState.Rand, 1, 30)) var usersBlocks = make([]types.UserBlock, usersBlocksNumber) for index := 0; index < usersBlocksNumber; index++ {